LoginSignup
12
9

More than 5 years have passed since last update.

GNU Emacs25.3/etc/DEBUGの翻訳

Last updated at Posted at 2018-02-24

Table of Contents

  1. 前置き
    1. デバッグ用Emacsのconfigure
    2. GDBの設定
    3. EmacsのGDB UIフロントエンドの使用
    4. 初期ブレークポイントのセット
  2. assertの失敗やバックトレースの
  3. GDB(および他の適切なデバッガ)配下で
  4. Emacsが固まったり、何らかの無限ループに
  5. デバッガに制御を渡す
  6. Lispオブジェクトの値の調査
  7. GDBでLispレベルのバックトレース情報を取得する
  8. Emacsの再表示に関する問題のデバッグ
  9. longjmp呼び出しの追跡。
  10. Emacs内でのGDBの使用
  11. preloadやEmacsのダンプ中に何が起こっているかデバッグする
  12. Xプロトコルエラーに遭遇した場合
  13. EmacsがXサーバー内でエラーやメモリーリークを起こす場合
  14. Emacsが応答しない症状のバグ
  15. 特定の操作においてEmacsが本来考えられる速度より遅い場合は、その理由を探す方法についていくつかアドバイスがあります。
  16. GDBが実行できず、あなたのデバッガではEmacsをロードできない場合。
  17. テキスト端末での不正な画面更新のデバッグ
  18. LessTifのデバッグ
  19. GC内で発生する問題のデバッグ
  20. 非ASCII文字に関連する問題のデバッグ
  21. TTY(非ウィンドウ)バージョンのデバッグ
  22. mallocデバッグパッケージとともにビルドされたEmacsの実行
  23. Emacsのcoreダンプファイルからバッファーを復旧する方法

GNU Emacsのデバッグ

Copyright (C) 1985, 2000-2017 Free Software Foundation, Inc. See the end of
the file for license conditions.

前置き

このセクションは、あなたがすでにdebug情報つきEmacsのビルドや、GDBの設定と開始、GDBによる簡単なデバッグテクニックに親しんでいる場合はスキップできます。

デバッグ用Emacsのconfigure

デバッグを簡単にするには、特別なオプションでEmacsをconfigureしてビルドするのが最善です。以下はわたしたちが推奨するconfigure時のオプションです(これらのオプションは、--prefixのようなあなたが必要とするかもしれない他のオプションに追加します)。

CFLAGS='-O0 -g3' ./configure --enable-checking='yes,glyphs' --enable-check-lisp-object-type

CFLAGSの値は重要です。なぜなら最適化されたコードのデバッグはとても困難だからです。(最適化されたコードでのみ問題が発生する場合は、最適化を有効にする必要があるかもしれません。このような場合は-O2のかわりにまず-Ogの使用を試みてください。-Ogはある種のコードのデバッグを極めて困難にする、いくつかの最適化を無効にします。)

GCCのモダンなバージョンはコンパイラースイッチに-g3を使用するだけで利用できる、より精緻なdebug情報をサポートします。-g3に加えて-gdwarf-4を、それが失敗するようなら-gdwarf-3を試してみてください。これは最適化されたコードをデバッグする必要がある場合、とても重要になります。これに関する追加情報は以下の"analyze failed assertionsの検索"にあります。

2つの--enable-*スイッチはオプションです。これらはGDBによるデバッグに影響を与えませんが、デバッグしている問題をより早くcatchするかもしれない追加コードをassertion violation形式でコンパイルします。--enable-checkingオプションは表示に関する問題をデバッグするのに有用な追加機能も有効にします。詳細は、以下の"Emacsの再表示に関する問題のデバッグ"を参照してください。

デバッグ用のEmacsをインストールする必要はありません。'src'ディレクトリーに作成されるバイナリーをデバッグできます。

GDBの設定

GDBでEmacをデバッグするときは、Emacs実行形式が作成されたディレクトリー(Emacsソースツリー内の'src'ディレクトリー)から直接GDBを開始するべきです。このディレクトリーには、Emacsをデバッグするためのさまざまなユーザー定義コマンドを定義する、.gdbinitファイルがあります(これらのコマンドは以下の"Lispオブジェクトの値の調査"および"Emacsの再表示に関する問題のデバッグ"で説明されています)。

カレントバッファーでEmacsのCソースファイルのどれか1つをvisitしているときに、Emacsから"M-x gdb"コマンド(以下参照)によりデバッガーを開始すると、'src'ディレクトリー内で自動的にGDBが開始されます。

GDBのいくつかのバージョンでは、デフォルトではGDBを呼び出したディレクトリー内の.gdbinitファイルを自動的にロードしません。このようなバージョンのGDBでは、GDBを開始したとき以下のような警告を目にするかもしれません:

warning: File ".../src/.gdbinit" auto-loading has been declined by your 'auto-load safe-path' set to "$debugdir:$datadir/auto-load".

これを修正するには~/.gdbinitファイルに以下の行を追加するのが、もっとも簡単な方法です:

add-auto-load-safe-path *path/to/emacs/src*.gdbinit

この問題を克服するための方法は他にもいくつかあり、それらはすべてGDBユーザーマニュアルの"Auto-loading safe path"ノードで説明されています。それらでも効果がない場合は、無条件でGDB initファイルをロードするために、GDBプロンプトで"source /path/to/.gdbinit RET"とタイプしてください。

EmacsのGDB UIフロントエンドの使用

わたしたちはEmacsが提供するGDB用のGUIフロントエンドの使用を推奨します。このフロントエンドは、"M-x gdb RET"とタイプしてGDBを開始できます。このコマンドはデバッグするデフォルトのバイナリーのファイル名を提案します。この提案されるデフォルトが、デバッグしたいEmacsバイナリーと異なる場合は、必要に応じてファイル名を変更してください。かわりに、すでに実行中のEmacsプロセスにデバッガをアタッチしたい場合は、ミニバッファーに表示されているGDBコマンドにそれを告げるように変更します:

gdb -i=mi -p PID

ここでPIDはPosixホストでは'top'や'ps'のようなシステムユーティリティーで、MS-WindowsではTask Managerで表示される、実行中EmacsのPID数値です。

一度デバッガが起動すると"M-x gdb-many-windows RET"とタイプすることにより、GDB UIが提供する追加のウィンドウがオープンします(メニューバーの"Gud->GDB-MI->Display Other Windows"をクリックしてもオープンできる)。この時点では、オープンしたウィンドウが水平スクロールせずに内容を表示するのに十分なスペースがあるよう、フレームを十分に大きく(またはフルスクリーンに)しておいてください。

ウィンドウ構成は後でコマンド"M-x gdb-restore-windows RET"、またはメニューバーの"Display Other Windows"を選択解除すればリストアできます。

初期ブレークポイントのセット

Emacsを実行させる前の今こそ、デバッガしたいコードにブレークポイントをセットすべきときです。ブレークポイントをセットすればEmacsはそこでストップして、GDBに制御を渡せます。デバッグしたいコードが、ある稀な状況下で実行されるコードであったり、特定のEmacsコマンドを手動で呼び出したときだけ実行されるコードの場合は、そこにブレークポイントをセットしてEmacsを実行すれば、そのコマンドの呼び出し、またはそれらの稀な状況を再現することにより、ブレークポイントがトリガーされます。

あなたがそれほど幸運でなく、問題となっているコードが非常に頻繁に実行される場合は、バグのある振る舞いが発生する条件までブレークポイントがトリガーされないようにする方法を見つける必要があります。これにたいする唯一の処方はありません。あなたはより創造的になるとともに、何が適切か判断するためにコードを学ぶ必要があるでしょう。以下に、これにたいする有用なトリックをいくつか挙げます:

. ブレークポイント条件を特定のバッファーや文字列の位置にする。
たとえば:

(gdb) break foo.c:1234 if PT >= 9876

. 稀にしか呼び出されないいくつかの関数にブレークポイントをセットしてからバグにたいする条件を作成して、その稀にしか呼び出されない関数を呼び出す。そしてGDBに制御が渡ったときバグの疑いのあるコードにブレークポイントをセットして、バグが発生するときそのコードが呼び出されるか確認する。

. エラーメッセージ自体としてバグが顕在化するのであれば、Fsignalにブレークポイントをセットして、ブレークしたらbacktraceを調べて何がエラーのトリガーなのか確認する。

他の追加テクニックは、以下の"デバッガに制御を渡す"で説明されています。

これでデバッグセッションを開始する準備ができました。

新たなEmacsセッションを開始した場合は、gud-emacsバッファー内で"run"とタイプした後にコマンドライン引数(例 "-Q")を続けてからRETを押下します。

実行中のEmacsにデバッガをアタッチした場合は、gud-emacsバッファー内で"continue"とタイプしてからRETを押下します。

Lispオブジェクトをデバッグする間、多くの変数に出くわすことでしょう。これらは整数値(configure時に"--enable-check-lisp-object-type"オプションを使用した場合は構造体)なので解釈が困難であり、とりわけ長いリストで表現されている場合は解釈が困難です。これらをLisp形式で表示するために、'pp'コマンドを使用できます。このコマンドは出力を標準エラーストリーム(GNU/Linuxシステムでは"M-x redirect-debugging-output"を使用してファイルにリダイレクトできます)に表示します。これはデスクトップアイコンから呼び出された実行中のEmacsにGDBをアタッチした場合に、出力を確認できなかったり、出力がどこか不明な場所(デスクトップ環境のドキュメントをチェックしてください)に捨てられる危険があることを意味します。

Lispオブジェクトの表示に関する追加情報は、以下の"Lispオブジェクトの値の調査"で見つけることができます。

このドキュメントの残りの部分は、Emacsのデバッグにおいて特に有用なテクニックを説明します。初めてEmacsをデバッグしようとするときは、このドキュメント全体をありのまま目を通しておいて、必要なときに特定の問題を参照するよう、わたしたちは提案します。

Good luck!

assertの失敗やバックトレースの

分析をするときはデバッグに適したフラグでEmacsをコンパイルするのが不可欠です。GCC4.8以降では、CFLAGS="-Og -g3"で'make'を呼び出すことができます。それより古いGCCやGCC以外のコンパイラーではCFLAGS="-O0 -g3"を使用できます。GCCで-O2のような、より高レベルの最適化においては、-fno-omit-frame-pointerおよび-fno-crossjumpingオプションが不可欠な場合がしばしばあります。後者のオプションは与えられた関数内のすべてのassertionにたいしてGCCが同じabort呼び出しを使用することを抑制し、失敗した特定のassertionを識別できないときはスタックバックトレースで表します。GCCのいくつかのバージョンはデバッグ情報にたいし最新バージョンのDWARF標準をサポートしますが、デフォルトは古いバージョンになっています。たとえば、それらのGCCは-gdwarf-4(DWARF v4)をサポートしますが、デフォルトはDWARF標準のバージョン2です。デバッグ手法において最良の結果を得るために、あなたのGCCがサポート可能な最新バージョンのDWARFを調べて、単なる-gのかわりに、対応するバージョンの-gdwarf-Nスイッチを使用してください。(とはいえ、"-gdwarf-4 -g3"のように-g3の指定は依然として必要です。)

GDB(および他の適切なデバッガ)の配下で

Emacsを実行するのはいかなるときもよいアイデアです。その後にEmacsがクラッシュしても、単なるcoreダンプではなく、生きたプロセスをデバッグできます。(これはcoreファイルをサポートしないシステムでは特に重要で、単にレジスターといくつかのスタックアドレスをプリントするかわりになります。)

Emacsが固まったり、何らかの無限ループに

嵌っているように見える場合は、"kill -TSTP PID"とタイプします。ここでPIDはEmacsのプロセスIDです。これによりGDB配下で実行されていれば、GDBが動作を開始します。

デバッガに制御を渡す

デバッガにEmacsをロードした後で、それを実行する前に効果的な場所にブレークポイントをセットすることは、必要なときにデバッガに確実に制御をリターンさせるもっとも効果的な方法です。

'Fsignal'はブレークポイントを配すのに、とても役に立つ場所です。すべてのLispエラーはここを通過します。Lispデバッガを起動させるエラーだけに関心がある場合は、'maybe_call_debugger'にブレークポイントを置くのが役に立つでしょう。

デバッガに制御を渡す他のテクニックとして、稀に使用されるいくつかの関数にブレークポイントを配す方法があります。そのような便利な関数の1つがFredraw_displayで、これは"M-x redraw-display RET"でインタラクティブに呼び出すことができます。

任意のタイミングでデバッガにリターンするために、保証された方法があるのも便利です。これはXを使用しているときは簡単です。GDBで対話中のウィンドウでC-zをタイプすれば、普通のプログラムと同様にEmacsもストップします。(これは実行中のEmacsにアタッチされたGDBでは機能しません。この場合はEmacsを起動したシェルのウィンドウでC-zとタイプするか、以下で説明している"kill -TSTP"手法を使用する必要があるでしょう。)

Emacsがテキスト端末で表示されているときは、事はそれほど簡単ではないので、以下で他のさまざまな方法を説明します(とはいえ、それらはPosixシステムだけで機能するシグナルを使用した場合のみ機能します)。

Emacsディストリビューション内のsrc/.gdbinitファイルはSIGINT(Emacsのtext-modeのフレームでのC-g)を、GDBに制御を戻さずにEmacsに渡されるようアレンジしています。モダンなシステムでは、以下のコマンドでこれをオーバーライドできます:

handle SIGINT stop nopass

この'handle'コマンドの後は、SIGINTにより制御がGDBにリターンされるでしょう。Emacsにも同様にC-gでQUITを送りたい場合は、'nopass'を省略してください。シグナルのハンドリングと'handle'コマンドについての詳細は、GDBのマニュアルを参照してください。

'handle SIGINT'が機能しない場合に使えるテクニックは、何らかの文字コードを変数stop_characterに格納する方法です。したがって、

set stop_character = 29

これはControl-](10進の29)をストップ文字にします。これでControl-]をタイプすれば、即座にストップします。下位プロセスが開始されるまでsetコマンドは使用できないので、上述の'set'コマンドの機会を得るために、Emacsを'start'コマンドで開始してください。

Posixホストでは、以下のようにシェルプロンプトから'kill'コマンドを使用してシグナルを送ることもできます。

kill -TSTP Emacs-PID

ここでEmacs-PIDはデバッグされているEmacsのプロセスIDです。他に送信するシグナルとして有用なのがSIGUSR1とSIGUSR2です。これらのシグナルの使い方は、ELispマニュアルの"Error Debugging"を参照してください。

Emacsがテキスト端末で表示されているときは、デバッグセッション用に別の端末があると便利です。これは通常どおりEmacsを開始して、gdbから'attach'コマンドでアタッチして行うことができます。このコマンドは、GDBマニュアルの"Attach"ノードで説明されています。

MS-WindowsではGDB配下でEmacsを実行する前にnew-consoleオプションをセットすることにより、別のコンソールからEmacsを開始できます。

(gdb) set new-console 1
(gdb) run

これを行なった場合、GDBと対話しているコンソールウィンドウからC-cやC-BREAKをタイプすると、EmacsがGUIかtext-modeフレームで表示されているかに関わらず、Emacsはストップして制御がGDBにリターンされるでしょう。これは、事前にブレークポイントをセットする以外に、MS-Windowsでデバッガが制御を得るための唯一信頼できる代替方法です。

Lispオブジェクトの値の調査

デバッグ中の生きたプロセスがあり、それが致命的なエラーに遭遇戦していなければ、GDBコマンド'pr'を使用できます。最初は通常の方法、'p'コマンドで値をプリントしてください。それから引数なしで'pr'とタイプします。これはLispプリンターを使用するサブルーチンを呼び出します。

emacsの値を直接プリントするために、'pp value'を使用することもできます。

Lisp変数のカレント値を確認するためには、'pv value'を使用します。

これらのコマンドは出力をstderrに送ります。stderrがcloseされていたり、あなたには不明な何らかのファイルにリダイレクトされている場合、これらの出力を確認することはできないでしょう。これは特にMS-Windows上でデスクトップショートカットからEmacsが呼び出された場合が該当します。GNU/Linuxではコマンド'redirect-debugging-output'を使用して、stderrをファイルにリダイレクトできます。

注意:
Emacsが深刻なトラブル、スタックが破壊(たとえばスタックオーバーフローによるSIGSEGVなど)されていたり、'obarray'のようなデータ構造の循環参照や不正構造があると解っている状況で'pr'、'pp'、'pv'を試みるのは、良いアイデアではありません。このような場合、'pr'により呼び出されるEmacsサブルーチンは、元の問題をデバッグするために重要な何らかのデータを上書きしてしまう等、より多くのダメージを与えてしまうかもしれません。

さらに'select'の内部にいる間にEmacsをストップした場合は、'pr'の使用が不可能なシステムもいくつかあります。Emacsがwaitしている間にEmacsをストップすると、これは実際に発生します。このような状況では、'pr'の使用を試みてはなりません。かわりにシステムコールをステップアウトするために、's'を使用してください。その後、Emacsは命令文の間に、'pr'を処理することができるようになるでしょう。

理由が何であれ、'pr'コマンドが使用できない場合は、直前のデータ値のデータ型と値をプリントする'xpr'コマンドを使用できます。

p it->object
xpr

低レベルコマンドを使用して、データ値を分析するかもしれません。直前のデータ値のデータ型のプリントには、'xtype'コマンドを使用します。一度データ型が解れば、その型に対応するコマンドを使用できます。以下はそれらのコマンドです:

xint xptr xwindow xmarker xoverlay xmiscfree xintfwd xboolfwd xobjfwd
xbufobjfwd xkbobjfwd xbuflocal xbuffer xsymbol xstring xvector xframe
xwinconfig xcompiled xcons xcar xcdr xsubr xprocess xfloat xscrollbar
xchartable xsubchartable xboolvector xhashtable xlist xcoding
xcharset xfontset xfont xbytecode

これらの1つ1つは、特定の型やクラス型を受け入れます(これらの型のいくつかは内部的にのみ存在する型なので、Lispでは不可視です)。

それぞれのx...コマンドは、その内容の残りから得られるものを通じて、値についてのいくつかの情報と、GDB値(続けて$により利用可能)をプリントします。

一般的に内容の残りのほとんどはx...コマンドで順に検証していくことができる、追加のLispオブジェクトでしょう。

生きたプロセスにおいても、これらのx...コマンドはバッファー、ウィンドウ、プロセス、マーカー内の検証に役立ちます。以下はフレームと呼ばれる変数に関連する値をプリントするために、GDBマニュアル"Value History"ノードで説明されている概念を使用した例です:

cd src
gdb emacs
b set_frame_buffer_list
r -q

その後、Emacsはブレークポイントに達します:

(gdb) p frame
$1 = 139854428
(gdb) xpr
Lisp_Vectorlike
PVEC_FRAME
$2 = (struct frame *) 0x8560258
  "emacs@localhost"
(gdb) p *$
$3 = {
  size = 1073742931,
  next = 0x85dfe58,
  name = 140615219,
  [...]
}

これで'pp'を使用してフレームパラメーターをプリントできます:

(gdb) pp $->param_alist
((background-mode . light) (display-type . color) [...])

EmacsのCコードは、lisp.h内で定義されたマクロを多用します。keyboard.cの'add_command_key'の最後の方にある左辺値式(l-value expression)のアドレスが知りたいとします:

XVECTOR (this_command_keys)->contents[this_command_key_count++] = key;

XVECTORはマクロなので、Emacsがプリプロセッサーマクロ情報とともにコンパイルされた場合だけそれを知ることができます。オプション'-gdwarf-N'(Nは2以上)と'-g3'を指定した場合、GCCはこの情報を提供します。この場合、GDBは"p XVECTOR (this_command_keys)"のような式を評価できます。

この情報が利用できない場合は、同じ結果を得るためにGDB内でxvectorコマンドを使用できます。以下は使用方法です:

(gdb) p this_command_keys
$1 = 1078005760
(gdb) xvector
$2 = (struct Lisp_Vector *) 0x411000
0
(gdb) p $->contents[this_command_key_count]
$3 = 1077872640
(gdb) p &$
$4 = (int *) 0x411008

以下はマクロとGDBの'define'コマンドに関する例です。'recent_keys'(最後のキーストローク300個を含む)のようなLispベクターが多数存在します。このLispベクターを以下のようにしてプリントできます

p recent_keys
pr

'recent_keys'は'C-h l'に比べて出力が多く、これは不便かもしれません。わたしたちなら、このベクターの最後の10要素だけをプリントしたいと望むでしょう。'recent_keys'はkeyboard.c内でコマンドにより更新されます

XVECTOR (recent_keys)->contents[recent_keys_index] = c;

わたしたちはGDBコマンド'xvector-elts'を定義して、以下のように最後のキーストローク10個をプリントします

xvector-elts recent_keys recent_keys_index 10

ここでxvector-eltsは以下のように定義します:

define xvector-elts
set $i = 0
p $arg0
xvector
set $foo = $
while $i < $arg2
p $foo->contents[$arg1-($i++)]
pr
end
document xvector-elts
Prints a range of elements of a Lisp vector.
xvector-elts  v n i
prints 'i' elements of the vector 'v' ending at the index 'n'.
end

GDBでLispレベルのバックトレース情報を取得する

'xbacktrace'コマンドを使用するのがもっとも便利な方法です。これはカレントでアクティブなLisp関数の名前を表示します。

もしこれが機能しない(たとえば'backtrace_list'構造が不正等により)場合は、Cレベルのバックトレースを生成するために、GDBプロンプトで"bt"とタイプして、Ffuncallを呼び出しているスタックフレームを探してください。"up
N"とタイプして、それらのスタックフレームをGDBで1つずつ選択していきます。ここでNは上位フレームに移動するための適切な数字です。そしてFfuncallを呼び出しているフレームごとに以下をタイプします:

p *args
pr

これにより、そのレベルの関数呼び出しにより呼び出されているLisp関数の名前がプリントされます。

argsの残りの要素をプリントすることにより、Lisp関数の引数の値を確認できます。以下は最初の引数をプリントする方法です:

p args[1]
pr

生きたプロセスがない場合は、xtypeと他のx...コマンドを使用できます。便利さでは劣りますが、このような情報はxsymbolのようなコマンドで取得します。たとえば、

p *args
xtype

"xtype"が、args[0]をsymbolだと告げた場合は:

xsymbol

Emacsの再表示に関する問題のデバッグ

--enable-checking='glyphs'でEmacsをconfigureした場合は、実行中のEmacsセッションから再表示(redisplay)トレース機能を使用できます。

コマンド"M-x trace-redisplay RET"は再表示が行うことのトレースを標準エラーストリームに生成します。さまざまな状況下でディスプレイエンジンにより選択されるコードパス(code path:実際に実行されるコードの道筋)を理解するために、特にある再表示最適化が誤った結果を生成する場合に、非常に役に立ちます(承知のとおり、"M-x redraw-display RET"、あるいは"M-x"とタイプするだけでも、Emacsが誤った表示を修正するために再表示最適化がよびだされます)。カーソル点滅機能(cursor blinking feature)は定期的な再表示サイクルをトリガーするので、余計なものをトレースから減らすために、'trace-redisplay'を呼び出す前に'blink-cursor-mode'を無効にすることを推奨します。dump-redisplay-history'コマンドを呼び出して、最後のトレースメッセージを30個まで標準エラーにダンプすることもできます。

ディスプレイエンジンが選択するコードパスを見つけるには、トレースメッセージからxdisp.cを探します。

コマンド'dump-glyph-matrix'は、選択されたウィンドウのグリフマトリクス(glyph matrix)の完全なダンプを標準エラーストリームに生成するのに役立ちます。詳細は関数のドキュメント文字列を参照してください。text-modeフレームで再表示に関する問題をデバッグしている場合は、コマンド'dump-frame-glyph-matrix'が便利なことに気づくかもしれません。

再表示をデバッグするとき有用な他のコマンドには、'dump-glyph-row'と'dump-tool-bar-row'があります。

GDB配下でEmacsを実行している場合は、マトリクスを引数にその関数を呼び出すだけで、任意のグリフマトリクスの内容をプリントできます。たとえば、以下のコマンドは、ウィンドウ(ポインター'w')のカレントマトリクスの内容をプリントします:

(gdb) p dump_glyph_matrix (w->current_matrix, 2)

(2つ目の引数2はdump_glyph_matrixにグリフを長い形式プリントするよう指定します。)

ディスプレイ関係のEmacsのコードには特別なデバッグ用コードが含まれていますが、通常は無効になっています。--enable-checking='yes,glyphs'でEmacsをconfigureすることにより、これが有効になります。

このようにEmacsをビルドすると、ディスプレイコード処理を検査する多くのassertionが、通常Emacsが行うより多く活性化されます(これらのassertionが何をテストしているかは、'eassert'マクロ呼び出しを確認してください)。失敗を報告するassertionは精査するべきです。

X配下で実行中のEmacsにたいしてディスプレイの問題をデバッグするときは、'ff'コマンドを使用して、待機中のすべての画面表示の更新をflushできます。

src/.gdbinitファイルでは、再表示関連のデータ構造を簡潔でユーザーフレンドリーなフォーマットでダンプするためのコマンドが多数定義されています:

'ppt' カレントバッファーのPT値(ポイント値)、ナロー、ギャップをプリント。
'pit' カレントディスプレイイテレータ'it'をダンプ。
'pwin' カレントウィンドウ'win'をダンプ。
'prow' カレントグリフ行glyph_row 'row'をダンプ。
'pg' カレントグリフ'glyph'をダンプ。
'pgi' 次のグリフをダンプ。
'pgrow' カレントグリフ行glyph_row 'row'内のすべてのグリフをダンプ。
'pcursor' カレント出力カーソルoutput_cursorをダンプ。

上記のコマンドには関連する型のオブジェクトを引数とする、サフィックス'x'のついたバージョンも存在します。たとえば、'pgrowx'は引数内のすべてのグリフをダンプします。引数の型は'struct glyph_row'でなければなりません。

再表示はEmacsにより非常に頻繁に行われるので、デバッグ中の問題が(まだ)発生していないのに、再表示のたびに毎回ブレークしてしまうのを避けるため、ブレークポイントを巧妙に配す必要があります。これに関する有用なテクニックを以下に挙げます:

. Emacsを実行する前に'Fredraw_display'にブレークポイントを置きます。その後で誤った表示が再現するのに必要な何かを行い、"M-x redraw-display"を呼び出してください。するとブレークによりデバッグに制御が渡ります。誤った表示がスクラッチから再描画されるのが解るので、戦略的な場所にブレークポイントをセットしたり有効にできます。

. 間違ったカーソル位置をデバッグする場合は、ブレークポイントを置くのに適した場所は、'set_cursor_from_row'です。この関数は最初、'redraw-display'の一部として呼び出されます。これはEmacsがミニバッファーを再描画するためで、通常これはあなたが望むものではないでしょう。そこで"continue"をタイプしてあなたが望む呼び出しを取得します。一般的には、w->contentsの値を検査して、常に'set_cursor_from_row'が正しいウィンドウとバッファーにたいして呼び出されているか検証してください。それはデバッグ中に表示されているバッファーであるべきです。

. スクリーン行(別名はグリフ行)の内容をGDBコマンド'pgrow'で調べるのにもset_cursor_from_row'は適した場所です。もちろん最初に調査したいスクリーン行にカーソルがあるか確認する必要があります。上記のアドバイスにしたがい'Fredraw_display'にブレークポイントをセットした場合は、'redraw-display'を呼び出す前にその行にカーソルを移動してください。

. 特定のバッファー位置や稀にしか使用されない特定の文字だけで問題が発生する場合は、ブレークポイント条件をそれらの値にすることができます。ディスプレイエンジンはバッファーと文字列位置を保守しており、それはit->currentメンバーを処理します。たとえば、バッファーの文字位置はit->current.pos.charposの中です。ほとんどの再表示関数は引数に'struct it'オブジェクトを受け取るので、以下のようにそれらの関数内に条件つきブレークポイントをセットできます:

(gdb) break x_produce_glyphs if it->current.pos.charpos == 1234

表示される文字にたいする条件には、it->cまたはit->char_to_displayを使用してください。

. 表示のためのグリフ生成に使用されるオブジェクトの種類にたいしてブレークポイント条件をセットすることもできます。it->methodメンバーの値がGET_FROM_BUFFERならバッファー内容の表示、GET_FROM_STRINGならLisp文字列(たとえば'display'プロパティやオーバーレイ文字列)の表示、GET_FROM_IMAGEならイメージの表示、...です。この値の完全なリストは、dispextern.h内の'enum it_method'を確認してください。

longjmp呼び出しの追跡。

最新バージョンのglibc(2.4+?)は、GDBの'next'使用によるlongjmp呼び出しの追跡を防ぐため、setjmp/longjmpにたいする値を暗号化して格納します。この保護を無効にするためには、環境変数LD_POINTER_GUARDに0をセットする必要があります。

Emacs内でのGDBの使用

Emacs内でのGDBによるデバッグには、コマンドラインでのデバッグにたいしていくつかの利点があります(EmacsマニュアルのGDB Graphical Interfaceノードを参照)。Emacsでのデバッグだけで利用できる機能もいくつかあります:

  1. ツールバー('p'アイコン)からコマンドgud-printが利用でき、GUDバッファーではポイント位置の変数のS式をプリントできます。

  2. スピードバーのwatch式のコンポーネント(Lispオブジェクト)で'p'を押下すると、GUDバッファーにそのLispオブジェクトのS式がプリントされます。

  3. ツールバーのSTOPボタンとメニューバーのメニューアイテムSignals->STOPは調整済みなので、通常のSIGINTのかわりにSIGTSTPが送られます。

  4. ポイント位置のLisp変数の値をプリントするコマンドgud-pvは、'C-x C-a C-v'にグローバルにバインドされます。

preloadやEmacsのダンプ中に何が起こっているかデバッグする

ダンプされていないEmacsで発生する問題なのか確証を得たい場合は、'temacs'をデバッグするのが役立ちます。デバッガ配下で'temacs'を実行するには、"gdb temacs"とタイプしてから'r -batch -l loadup'で開始します。

ダンプ中に何が起こっているかデバッグする必要がある場合は、かわりに'r -batch -l loadup dump'で開始します。bootstrapのダンプのデバッグには、"loadup dump"のかわりに"loadup bootstrap"を使用してください。

この方法によりGDB配下でtemacsの実行に実際に成功した場合は、ダンプされたEmacsの実行を試みないでください。なぜならそれはGDBのブレークポイントがセットされた状態でダンプされているからです。

Xプロトコルエラーに遭遇した場合

Xサーバーは通常、プロトコルエラーを非同期で報告するので、エラーがリターンされる原因の根本よりかなり後にそれらの見つけることになります。

エラー原因についての明快な情報を入手するために、(x-synchronize t)を評価してみてください。これはEmacsを同期モードにして、それぞれのXlib呼び出しにたいしてリターン前にエラーをチェックします。このモードはかなり低速ですが、エラーが発生したとき、どの呼び出しがエラーを発生させたか正確に確認できるでしょう。

以下のように-xrmオプションとともに呼び出すことにより、同期モードでEmacsを開始できます:

emacs -xrm "emacs.synchronous: true"

関数'x_error_quitter'内にブレークポイントをセットして、Emacsがストップしたときにその関数内のバックトレースを調べれば、Xプロトコルエラーの原因となったコードを確認できるでしょう。

同期モードでEmacsが実行中のときは発生しないXプロトコル関連のエラーもいくつかあります。これらのバグを追跡するために、以下の手順を提案します:

  • デバッガ配下でEmacsを実行して、Lisp関数から呼び出されたときXプロトコルエラーを発生するプリミティブ関数の中にブレークポイントを置きます。たとえばフレームを削除したときエラーが発生する場合は、'Fdelete_frame'の中にブレークポイントを置いてください。

  • ブレークポイントでブレークしたとき、コードをステップ実行してX関数("X"、"Xt"、"Xm"で始まる関数)の呼び出しを探してください。

  • 以下のように、それぞれのX関数呼び出しの前後に'XSync'呼び出しを挿入します。

    XSync (f->output_data.x->display_info->display, 0);

    ここで'f'は選択されたフレームの'struct frame'で、通常はXFRAME(selected_frame)を通じて利用できます(Xを呼び出すほとんどの関数はフレームへのポインターを保持するために、もしかしたら'f'や'sf'と呼ばれる変数をすでにもっているので、その場合はそれを計算する必要はありません)。

    あなたのデバッガがデバッグ中のプログラム内の関数を呼び出せる場合は、Emacsをリコンパイルせずに'XSync'を呼び出せて然るべきです。GDBでは単に:

    call XSync (f->output_data.x->display_info->display, 0)

    これを疑わしいX呼び出しの前後でタイプするだけです。デバッガがこれをサポートしなければ、これらの呼び出しの組をソースに追加して、Emacsをリビルドする必要があるでしょう。

    どちらの方法でも、'XSync'の後に'x_error_quitter'関数に遷移するようなEmacsに呼び出される最初のX関数が見つかるまで、体系的にコードをステップ実行してこれらの呼び出しを行います。これが発生する最初のX関数呼び出しがXプロトコルエラーを発生しています。

  • これであなたは目障りなX呼び出しを見つけ出して、何が悪いのか解決を試みることができます。

EmacsがXサーバー内でエラーやメモリーリークを起こす場合

xmon(ftp://ftp.x.org/contrib/devel_tools/から入手可能)のようなツールで、EmacsとXサーバー間のトラフィックをトレースできます。

xmonはXプロトコルエラー発生時にEmacsが何を送信したのか正確に知るために使用できます。EmacsがXサーバーのメモリー使用を増大させている場合は、Xサーバー内にEmacsがどんなアイテム(windows、graphical contexts、pixmaps)を作成、または削除したかをxmonを使用して確認できます。もし終始一貫して削除より作成が多い場合は、そのアイテムの型とそのアイテムが作成されるとき行っていたアクティビティが、デバッグ開始のヒントになり得ます。

Emacsが応答しない症状のバグ

Emacsが'hung'したと決めつけないでください -- それはもしかしたら無限ループかもしれません。どちらか調べるには、GDB配下で問題を発生させて応答しないEmacsを一度ストップします(EmacsがXウィンドウを直接使用する場合は、GDBからC-zをタイプしてEmacsをストップできます)。MS-Windowsでは、通常どおりEmacsを実行してからGDBでアタッチします。これは通常、Emacsが何を行っていても中断させるので、以下で説明するステップを実行できます。

それから'step'でステップ実行を試みます。Emacsがhungしている場合、'step'コマンドはリターンしません。ループしている場合、'step'はリターンします。

これによりEmacsがシステムコールでhungしていることが解った場合は、もう一度ストップしてその呼び出しの引数を調べてください。バグを報告する場合、ソース内のどのシステムコールかと、引数が何なのかを正確に示すのがとても重要です。

Emacsが無限ループにある場合は、そのループの開始と終了の判断を試みてください。GDBコマンド'finish'を使用するのが、これを行うもっとも簡単な方法です。このコマンドを使用するたびに、Emacsは1つのスタックフレームをexitするまで実行を再開します。リターンしなくなるまで'finish'をタイプしていきます。リターンしないスタックフレームの中には無限ループがあることを意味します。

ふたたびEmacsをストップして、そのフレームまで'finish'を繰り返し使用します。それから'next'を使用して、そのフレームをステップ実行します。ステップ実行することにより、ループの開始と終了を確認できるでしょう。さらにそのループで使用されているデータを調べて、本来終了すべきときになぜそのループがexitしないか判断を試みてください。

GNUおよびUnixシステムでは、EmacsにSIGUSR2の送信を試みることもできます。このシグナルは、'debug-on-event'がデフォルト値の場合、Emacsはカレントループを抜けてブレークし、Lispデバッガに入ることを試みます(Lispデバッガについての詳細は、ELispマニュアルの"Debugging"ノードを参照)。この機能はCレベルのデバッガが手軽に利用できないとき有用です。

特定の操作においてEmacsが本来考えられる速度より遅い場合は、その理由を探す方法についていくつかアドバイスがあります。

その遅い処理の間、繰り返しEmacsをストップして、その都度バックトレースを作成します。パターン(あなたが期待するより頻繁に特定の関数が呼ばれてないか)を見つけるためにバックトレースを比較してください。

Cのバックトレースでパターンが見つからない場合は、"xbacktrace"をタイプしてLispのバックトレース情報を取得するか、Ffuncallフレーム(上記参照)を調べて、再度パターンを探してください。

Xを使用しているときは、GDBからC-zをタイプすればいつでもEmacsをストップできます。Xを使用していないときは、C-gでこれを行うことができます。MS-DOSのような非Unixプラットフォームでは、かわりにC-BREAKを押下する必要があるかもしれません。

GDBが実行できず、あなたのデバッガではEmacsをロードできない場合。

いくつかのシステムには、シンボルテーブルとともにEmacsをロードできるデバッガがありません。それは恐らくそれらすべてにシンボル数の固定された上限があり、Emacsのシンボル数がその上限を超えるからでしょう。以下はそのような窮地で使用できる手法です。

nm -n temacs > nmout
strip temacs
adb temacs
0xd:i
0xe:i
14:i
17:i
:r -l loadup   (等など)

数値アドレスからシンボルへの変換、および逆変換のために、ファイル'nmout'への参照が必要です。

これはウィンドウシステム配下で実行されている場合に有用です。その場合、もし不幸にもEmacsが処理に行き詰まったら、kill -9を行うために新たなウィンドウを作成できます。kill -ILLが有用な場合も多々あります。これによりEmacsはcoreをダンプするか、adbにリターンするでしょう。

テキスト端末での不正な画面更新のデバッグ

誤った画面更新の問題をデバッグする際、入力として何をタイプしたかとEmacsが画面に何を送ったかの記録があれば便利です。以下のようにして、この記録を作成します

(open-dribble-file "~/.dribble") 
(open-termscript "~/.termscript")

dribbleファイルには端末からEmacsに読み取られたすべての文字が含まれ、termscriptファイルにはEmacsが端末に送ったすべての文字が含まれます。ディレクトリー'~/'を使用しているのは、他のユーザーによる干渉を防ぐためです。

再現できない表示問題を抱えている場合は、これら2つの式を~/.emacsファイルに記述してください。問題が発生したときは実行中のEmacsをexitして、この2つのファイルをリネームします。それから他のEmacsを起動すれば、これらのファイルを壊さずに、内容を調査できます。

端末上でテキストが過剰に再描画されていないか確認する簡単な方法は、過剰な再描画は発生すると思われる操作を試みる前に、'(setq inverse-video t)'を評価する方法です。これは画面をリフレッシュしないので、新たに描画されるテキストだけが反転表示されます。

LessTifのデバッグ

LessTifとともにビルドされたEmacsで、すべてのマウスおよびキーボードイベントを奪われてしまったり、LessTifメニューが奇妙な振る舞いをするバグに遭遇した場合は、環境変数'DEBUGSOURCES'および'DEBUG_FILE'のセットが助けになるかもしれません。それにより、その時点でLessTifが何を行っているか確認できます。たとえば

export DEBUGSOURCES="RowColumn.c:MenuShell.c:MenuUtil.c"
export DEBUG_FILE=/usr/tmp/LESSTIF_TRACE
emacs &

これはLessTifにたいし、名前指定された3つのソースファイルから、'/usr/tmp'内のファイル(このファイルは非常に大きくなるかもしれない)にトレースをプリントさせます。上記のコマンドは最後の行に示されるとおり、Emacsを呼び出す前にシェルプロンプトでタイプしてください。

他の端末からGDBを実行するのも、この種の問題には助けになります。1つのマシンでGDBを実行して、Emacsは他のマシンに表示するようにアレンジすることもできます。その後バグが発生したときにGDBを開始したマシンに戻って、そこからデバッガを使用できます。

GC内で発生する問題のデバッグ

配列'last_marked'(alloc.cで定義)はガベージコレクションプロセスにより最後にマークされたオブジェクトを、500個まで表示するのに使用できます。ガベージコレクターがLispオブジェクトをマークするときは、常に'last_marked'配列内のオブジェクトへのポインターを記録し、それらは循環バッファーとして保守されます。変数'last_marked_index'は、'last_marked'配列の1つ先の場所(一番最後にマークされたオブジェクトの格納場所へのポインター)のインデックスを保持します。

GCに関する問題のデバッグにおいてもっとも重要な単一のゴールは、不正なLispデータ構造を見つけることです。GCはtagビットを変更して、文字列を再配置するため、これは簡単ではなく、これらにより'pr'のようなコマンドでのLispオブジェクトの長は困難です。ときにはLisp_Object変数からC構造体ポインターに手動で変換する必要があります。

そのオブジェクトがマークされた順番を再構築するためには、'last_marked'配列とソースコードを使用します。一般的には、'last_marked'配列に記録された値と、バックトレース内の対応するスタックフレームを、最内フレームから関連付ける必要があります。'mark_object'内のサブルーチンのいくつかは再帰的に呼び出され、それ以外はデータ構造の一部をループしてそれらがGCされるようマークします。これらのルーチンのコードを調べて、バックトレース内のフレームを'last_marked'内の値と比較することにより、'last_marked'内の値との関連を見つけることができるでしょう。たとえば、GCがコンスセルを見つけたときは、再帰的にcarとcdrをマークしていきます。同様なことはシンボルのプロパティ、ベクターの要素などでも発生します。そきで見つかるシンボルの文字列と名前に特に注意を払いながら、マークされたデータ構造の再構築にこれらの関連を使用してください。これらの文字列とシンボル名は、クラッシュを招いているより高レベルのシンボルとグローバル変数を探すとき、ソースをgrepするために使用できます。

不正なLispオブジェクトまたはデータ構造を一度発見すれば、ソースをgrepしてそれらの使用箇所を見つけて、何が不正の原因なのか解決を試みてください。ソースコードの調査が役に立たない場合は、不正なデータにwatchpointをセットしてどのコードが無効な方法でデータを変更するか確認することができます(このテクニックは、そのデータが非常に稀にしか変更されない場合のみ有用なのは明らかです)。

フレッシュなEmacsセッション内で不正なオブジェクトまたはデータ構造を調べて、デバッグ中のセッションのデータ内容と比較するのも役に立ちます。

非ASCII文字に関連する問題のデバッグ

バッファー内やファイル内に\201のような文字が出現する等の、非ASCII文字に関連するように思われる問題に遭遇した場合は、変数byte-debug-flagをtにセットしてください。これにより、Emacsはバッファーや文字列内の、バイトと文字列間の壊れた関連付け等の追加のチェックを行います。診断結果は問題原因を正確に指摘するでしょう。

TTY(非ウィンドウ)バージョンのデバッグ

文字端末ディスプレイのデバッグ手法としてもっとも便利なのは、Xのようなウィンドウシステム上でデバッグを行う方法です。xtermウィンドウを開始してから、そのウィンドウで以下のコマンドをタイプします。

$ tty
$ echo $TERM

これらのコマンドがそれぞれ"/dev/ttyp4"、"xterm"と応えたとしましょう。

次にEmacsを開始('-nw'オプションを指定しない、通常のウィンドウ表示セッション)して、そこから"M-x gdb RET emacs RET"を呼び出します。そしてGDBプロンプトで以下のコマンドをタイプしてください:

(gdb) set args -nw -t /dev/ttyp4
(gdb) set environment TERM xterm
(gdb) run

デバッグされるEmacsが非ウィンドウモードで前にオープンしたxtermウィンドウ内に表示されます。

'screen'パッケージを使用して、文字端末上での同様なアレンジが可能です。

MS-Windowsでは、GDB配下でEmacsを実行する前にnew-consoleオプションをセットして、別のコンソールでEmacsを開始できます。

(gdb) set new-console 1
(gdb) run

mallocデバッグパッケージとともにビルドされたEmacsの実行

Emacsがヒープ外に割り当てられたメモリー使用に関係があるように見えるバグを示す場合は、そのような問題を探す助けとなるElectric Fence(別名efence)やGNU Checkerのような特別なデバッグ用ライブラリーとEmacsをリンクするのが役に立つかもしれません。

そのようなパッケージとともにコンパイルされたEmacsはいくつかのハッキングなしでは実行できないでしょう。なぜならEmacsはシステムのメモリー割り当て関数を置き換え、さらにダンププロセスはそれらのパッケージが割り当てられたメモリーをの追跡に使用するのとは互換性のない方法だからです。あなたが必要だと見出すかもしれないいくつかの変更を以下に挙げます:

  • configureを修正し、system_mallocをセットしてCANNOT_DUMPを"yes"に変更します。

  • configureに異なる--prefix=オプションを指定します。GCCを使用する場合はバージョン2.95以降よりもバージョン2.7.2のほうが、いくつかのデバッグパッケージがより機能するので適しています。

  • "make"の後に"make -k install"とタイプしてください。

  • 必要ならsrc/temacsの実行準備のためにパッケージ固有のコマンドを呼び出します。

  • cd ..; src/temacs

(これは通常のEmacs実行可能形式のかわりに'temacs'を実行することに注意してください。これは上記で言及したEmacsダンプにともなう問題を避けるためです。)

いくつかのmallocデバッグライブラリーは、Emacsがいくつかのデータ構造に使用するビットフィールドにたいして偽のアラームを大量にプリントするかもしれません。偽のアラームを除外したい場合は、それぞれのヘッダーにあるこれらのデータ構造の定義をハックしてビットフィールド定義':N'を削除する必要があるでしょう(これによりそれらのフィールドは完全なintを使用するようになります)。

Emacsのcoreダンプファイルからバッファーを復旧する方法

ファイルetc/emacs-buffer.gdbでは、coreダンプファイルからEmacsバッファーのコンテンツを復旧するための一連のGDBコマンドが定義されています。あなたはこれらのコマンドが、デバッガ内から人間が読める形式でバッファーリストを表示するためにも役に立つと気づくかもしれません。

This file is part of GNU Emacs.

GNU Emacs is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.

GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.

You should have received a copy of the GNU General Public License along with
GNU Emacs. If not, see http://www.gnu.org/licenses/.

Local variables:
mode: org
paragraph-separate: "[ ]*$"
end:

12
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9