[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Emacs Lispプログラムの問題点を調べるには、 問題が発生したときにどのようにプログラムを使っているかに依存して、 3つの方法があります。
他の有用なデバッグツールは、ドリブルファイルです。 ドリブルファイルをオープンしてあると、 Emacsはすべてのキーボード入力をこのファイルにコピーします。 あとでこのファイルを調べれば、どんな入力があったかわかります。 See 節 37.8 端末入力。
端末設定に関した問題を解決するには、 関数open-termscript
が有用です。 See 節 37.9 端末出力。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
通常のLispデバッガは、フォームの評価を一時停止する機能を提供します。 評価を一時停止しているあいだ(ブレーク(break)と呼ばれる状態)は、 実行時スタックを調べたり、ローカルやグローバル変数の値を調べたり、 それらの値を変更できます。 ブレークは再帰編集なので、Emacsの通常の編集機能すべてを使えます。 デバッガを再帰的に起動するようなプログラムを実行することさえできます。 See 節 20.11 再帰編集。
17.1.1 エラーによるデバッガの起動 Entering the debugger when an error happens. 17.1.2 無限ループのデバッグ Stopping and debugging a program that doesn't exit. 17.1.3 関数呼び出し時のデバッガの起動 Entering it when a certain function is called. 17.1.4 デバッガの明示的な起動 Entering it at a certain point in the program. 17.1.5 デバッガの使い方 What the debugger does; what you see while in it. 17.1.6 デバッガコマンド Commands used while in the debugger. 17.1.7 デバッガの起動 How to call the function debug
.17.1.8 デバッガの内部 Subroutines of the debugger, and global variables.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
デバッガへ入るもっとも重要な時期は、Lispエラーが発生したときです。 これにより、エラーの直接原因を調べることができます。
しかし、デバッガに入るのは、エラーの通常の帰結ではありません。 多くのコマンドは (バッファの末尾でC-fを使うなどの)不適切に起動されると しばしばLispエラーを生じますが、 通常の編集ではそのたびにデバッガに入ったのではとても不便です。 そのため、エラーによってデバッガに入りたい場合には、 変数debug-on-error
にnil
以外を設定します。 (コマンドtoggle-debug-on-error
はこれを簡単に行う。)
debug-on-error
がt
であると、 すべての種類のエラー(debug-ignored-errors
に指定したものを除く) はデバッガを呼び出す。 nil
であるとデバッガを呼び出さない。
その値はデバッガを呼び出すエラー条件のリストでもよい。 たとえば、リスト(void-variable)
にすると、 「値を持たない変数に関したエラー」のみがデバッガを起動する。
この変数がnil
以外であると、 Emacsはプロセスのフィルタ関数や番兵に対してエラーハンドラを作成しない。 したがって、これらの関数でのエラーもデバッガを起動する。 see 節 36. プロセス。
debug-on-error
の値に関わらず 当該エラーではデバッガに入らない。
この変数の通常の値は、 編集ではしばしば発生するが、Lispプログラムのバグではほとんど発生しないような エラー群のリストである。 しかし、『ほとんど』は『けっして』ではない。 このリストに一致するようなエラーで読者のプログラムが失敗する場合、 エラーをデバッグするにはこのリストを変更する必要がある。 もっとも簡単な方法は、debug-ignored-errors
にnil
を 設定することである。
condition-case
で捕捉したエラーは、 たとえdebug-on-error
がnil
以外であっても、 けっしてデバッガを起動しない。 いいかえれば、デバッガを起動するまえに、 condition-case
はエラー処理の機会を得るのである。
debug-on-signal
にnil
以外の値を設定すると、 各エラーごとにデバッガがまず機会を得る。 debug-on-error
とdebug-ignored-errors
の値で指定される 条件に一致すれば、condition-case
に関わらず エラーはデバッガを起動する。
警告: この変数は強力な処方である!
Emacsのさまざまな部分では通常の動作としてエラーを処理し、 読者にはエラーが発生したことさえわからない。
debug-on-signal
にnil
以外の値を設定すると それらのエラーでデバッガに入る。
警告:
debug-on-error
がnil
であると、 debug-on-signal
は意味を持たない。
ファイル`.emacs'をロード中に発生するエラーをデバッグするには、 オプション`--debug-init'を使います。 これにより、`.emacs'のロード中はdebug-on-error
をt
に束縛し、 初期化ファイルでのエラーを捕捉するcondition-case
を迂回します。
読者のファイル`.emacs'でdebug-on-error
を設定しても、 その効果は`.emacs'のロードを終ると持続しません。 (これはコマンド行オプション`--debug-init'の実装における 好ましくない特性である。) `.emacs'でdebug-on-error
を恒久的に設定する最良の方法は、 つぎのように、after-init-hook
を用いることです。
(add-hook 'after-init-hook '(lambda () (setq debug-on-error t))) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
プログラムが無限にループし戻ってこないときには、 まず、ループを停止する必要があります。 ほとんどのオペレーティングシステムでは、 中断を意味するC-gを使います。
普通に中断したのでは、 プログラムが無限ループした理由に関する情報は得られません。 より詳しい情報を得るには、 変数debug-on-quit
にnil
以外を設定します。 C-gによる中断はエラーとは扱わないため、 C-gの処理に関してdebug-on-error
はなんの効果もありません。 同様に、debug-on-quit
はエラーに関してなんの効果もありません。
無限ループの途中でデバッガを起動できれば、 デバッガでステップ実行コマンドを使って先へ進めます。 ループひとまわりをステップ実行すれば、 問題を解決するに十分な情報を得られるはずです。
quit
が通知され処理されなかった場合に、 デバッガを呼び出すかどうかを決定する。 debug-on-quit
がnil
以外である場合、 (C-gを打って)中断するとデバッガを呼び出す。 debug-on-quit
がnil
であると、 中断してもデバッガを呼び出さない。 see 節 20.9 中断。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
プログラムの途中で発生する問題点を調べるための1つの有用な技法は、 ある関数を呼び出すたびにデバッガに入ることです。 問題を生じる関数に対してこのようにしておき、 当該関数をステップ実行するか、あるいは、 問題が発生する直前に呼ばれる関数に対してこのようにしておき、 その関数の呼び出しを終えてから、呼び出し側をステップ実行します。
(debug 'debug)
を挿入することでこれを行う。
Lispコードで定義した任意の関数は、 解釈実行コードであろうとコンパイル済みのコードであろうと、 関数に入るときにブレークするようにできる。 関数がコマンドであると、Lispから呼ばれたときや 対話的に呼ばれたときに(引数を読み取ってから)デバッガに入る。 (Cで書いた)基本関数は、この方法ではデバッグできない。
debug-on-entry
を対話的に呼び出すと、 ミニバッファでfunction-nameを問い合わせる。 その関数がすでに呼び出し時にデバッガを起動するようになっていると、 debug-on-entry
はなにもしない。 debug-on-entry
はつねにfunction-nameを返す。
注意:
debug-on-entry
を使ったあとに当該関数を再定義すると、 デバッガに入るためのコードがなくなる。 実質的には、関数を再定義すると呼び出し時にブレークする機能を 取り消すことになる。
(defun fact (n) (if (zerop n) 1 (* n (fact (1- n))))) => fact (debug-on-entry 'fact) => fact (fact 3) ------ Buffer: *Backtrace* ------ Entering: * fact(3) eval-region(4870 4878 t) byte-code("...") eval-last-sexp(nil) (let ...) eval-insert-last-sexp(nil) * call-interactively(eval-insert-last-sexp) ------ Buffer: *Backtrace* ------ (symbol-function 'fact) => (lambda (n) (debug (quote debug)) (if (zerop n) 1 (* n (fact (1- n))))) |
debug-on-entry
の効果 (呼び出し時にブレークする)を取り消す。 対話的に呼び出すと、 ミニバッファでfunction-nameを問い合わせる。 function-nameがnil
であったり空文字列であると、 すべての関数について、呼び出し時にブレークすることを取り消す。
呼び出し時にブレークする設定をしていない関数に対して cancel-debug-on-entry
を呼び出してもなにもしない。 つねにfunction-nameを返す。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
読者のプログラムに式(debug)
を書くと、 その箇所でデバッガを呼び出すことができます。 つまり、ソースファイルを訪問して適当な箇所にテキスト`(debug)'を挿入し、 C-M-xと打ちます。 警告: 一時的なデバッグ目的でこれを行う場合には、 ファイルを保存するまえにこの挿入箇所をもとに戻すこと!
`(debug)'を挿入する箇所は、 余分なフォームを評価してもその値を無視できる場所でなければなりません。 ((debug)
の値が無視されないと、 プログラムの実行を変えてしまう!) もっとも適した一般的な場所はprogn
や暗黙のprogn
の内側です (see 節 9.1 逐次実行)。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
デバッガに入ると、それまで選択していたバッファをあるウィンドウに、 `*Backtrace*'という名前のバッファを別のウィンドウに表示します。 バックトレースバッファでは、各行は現在実行中のLisp関数の各レベルです。 このバッファの先頭には、デバッガを起動するに至った理由 (エラーで起動されたときにはエラーメッセージと関連データ) を表すメッセージがあります。
バックトレースバッファは読み出し専用であり、 各文字をデバッガコマンドであると定義した 特別なメジャーモード、debuggerモードを使います。 Emacsの通常の編集コマンドも使えます。 したがって、エラー発生時に編集していたバッファを調べるためにウィンドウを 切り替えたり、バッファを切り替えたり、ファイルを訪れたり、 その他のどんな編集でもできます。 しかし、デバッガは再帰編集レベル(see 節 20.11 再帰編集)であるので、 デバッグを終えるときには、バックトレースバッファに戻ってから デバッガを(コマンドqで)終了するのが賢い方法です。 デバッガを終了すると、再帰編集から抜けバックトレースバッファを削除します。
バックトレースバッファでは、実行中の関数とその引数の値を表示します。 また、スタックフレームを記述する行へポイントを移動することで スタックフレームを指定できます。 (スタックフレームとは、Lispインタープリタが関数の起動に関する情報を 記録しておく場所である。) ポイントがある行に対応するフレームをカレントフレーム(current frame)と 呼びます。 デバッガのある種のコマンドはカレントフレームに作用します。
デバッガ自身はバイトコンパイルしたものを実行する必要があります。 というのは、デバッガ自身が使用するスタックフレームのサイズを 仮定しているからです。 解釈実行だとこの仮定が成り立ちません。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
デバッガ内(debuggerモード)では、 通常のカーソル移動コマンドに加えて以下の特別なコマンドを使えます。 (ウィンドウやバッファの切り替えなどのEmacsの通常の機能も使えることに留意。)
デバッガコマンドのもっとも重要な使い方はステップ実行であり、 これにより制御の流れを調べることです。 デバッガは、解釈実行版の関数の制御構造をステップ実行できますが、 バイトコンパイルした関数ではできません。 バイトコンパイルした関数をステップ実行したい場合には、 同じ関数を解釈実行版の定義に置き換える必要があります。 (これには、関数のソースを訪れて、その定義内でC-M-xと打つ。)
debuggerモードのコマンド一覧を以下に示します。
継続が可能なのは、 関数呼び出し時や終了時、明示的な起動、中断によりデバッガに入った場合である。 エラーが原因でデバッガが起動されたときには継続できない。
このようにしてデバッガを起動した関数呼び出しのスタックフレームには 自動的に印が付き、そのスタックから抜けるとデバッガがふたたび呼び出される。 この印を消すにはコマンドuを使う。
C-gでデバッガへ入ったが、 実際には中断したいのであってデバッグはしたくない場合には コマンドqを使う。
(bで指定したりdでフレームに入ることで) Lispの呼び出しフレームから抜けでたためにデバッガが起動された場合に、 コマンドrは有用である。 コマンドrで指定した値は、当該フレームの値として使われる。 このコマンドは、debug
を呼び出してその戻り値を使う場合にも有用である。 さもなければ、rはcと同じ効果であり、指定した戻り値は関係ない。
エラーでデバッガに入った場合にはrは使えない。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
ここでは、デバッガを起動するために使われる関数debug
の詳細を述べます。
debuggerモードのコマンドcやrで再帰編集から抜けだし、 debug
はそれ以前のバッファに切り替え debug
を呼び出したところへ戻る。 これは、関数debug
が呼び出し側へ戻る唯一の手段である。
debugger-argsの使い途は、 debug
が引数の残りをバッファ`*Backtrace*'の先頭に表示し、 ユーザーが読めるようにすることである。 以下に述べる場合を除いて、これがこれらの引数の唯一の用途である。
debug
の第1引数が特定の値を持つ場合、特別な意味がある。 (通常、これらの値はEmacs内部で用いるだけであり、 プログラマがdebug
を呼ぶときには使わない。) 以下にこれらの特別な値を示す。
lambda
lambda
であると、 debug-on-next-call
がnil
以外であるために 関数に入るときにdebug
を呼び出したことを意味する。 デバッガはバッファの先頭にテキスト行`Entering:'を表示する。
debug
debug
であると、 関数に入るときにデバッガを起動するようになっていたために debug
を呼び出したことを示す。 デバッガは、lambda
の場合と同様に、`Entering:'を表示する。 さらに、当該関数のスタックフレームに関数から 戻るときにデバッガを起動するように印を付ける。
t
t
であると、 debug-on-next-call
がnil
以外であるときに フォームの並びを評価したために debug
を呼び出したことを示す。 デバッガはバッファの先頭行につぎの行を表示する。
Beginning evaluation of function call form: |
exit
exit
であると、 スタックフレームから抜けるときにデバッガを呼び出すように印を 付けたスタックフレームから抜けたことを示す。 この場合、debug
の第2引数はフレームからの戻り値である。 デバッガはバッファの先頭行に`Return value:'に続けて戻り値を表示する。
error
error
であると、 エラーやquit
が通知されたが処理されないためにデバッガに入ったことを示し、 `Signaling:'に続けて通知されたエラーとsignal
の引数を表示する。 たとえばつぎのとおり。
(let ((debug-on-error t)) (/ 1 0)) ------ Buffer: *Backtrace* ------ Signaling: (arith-error) /(1 0) ... ------ Buffer: *Backtrace* ------ |
エラーが通知されたときには、 変数debug-on-error
はnil
以外であるはずである。 quit
が通知されたときには、 変数debug-on-quit
はnil
以外であるはずである。
nil
nil
を使う。 debugger-argsの残りはバッファの先頭行に表示される。 この機能を用いてメッセージを表示でき、 たとえば、debug
を呼び出した条件の覚え書きにする。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
本節では、デバッガが内部的に使用する関数や変数について述べます。
debug
。
Lispが関数に渡す最初の引数で、呼び出した理由を表す。 引数の規約はdebug
に記述してある。
debug
がバッファ`*Backtrace*'を 満たすために用いる関数である。 どの関数呼び出しが活性であるかを 判断するためにスタックを参照する必要があるためCで書いてある。 戻り値はつねにnil
。
以下の例では、Lisp式で明示的にbacktrace
を呼び出す。 これにより、バックトレースをストリームstandard-output
に出力する。 ここではバッファ`backtrace-output'に出力する。 バックトレースの各行は、1つの関数呼び出しを表す。 関数の引数値すべてが判ればそれらを行に表示する。 それらが計算途中であれば、その旨を行に表示する。 スペシャルフォームの引数は省略する。
(with-output-to-temp-buffer "backtrace-output" (let ((var 1)) (save-excursion (setq var (eval '(progn (1+ var) (list 'testing (backtrace)))))))) => nil ----------- Buffer: backtrace-output ------------ backtrace() (list ...computing arguments...) (progn ...) eval((progn (1+ var) (list (quote testing) (backtrace)))) (setq ...) (save-excursion ...) (let ...) (with-output-to-temp-buffer ...) eval-region(1973 2142 # |
文字`*'は、 抜け出るときにデバッガを起動する印が付いているフレームを表す。
nil
以外であると、 つぎにeval
、apply
、funcall
を呼び出すまえに デバッガを呼び出すことを指定する。 デバッガに入るとdebug-on-next-call
をnil
に設定する。
デバッガのコマンドdは、この変数を設定することで動作する。
nil
以外であると、 のちに当該フレームから抜けるとデバッガに入る。 非ローカルな脱出で当該フレームから抜けるときにもデバッガに入る。
この関数はデバッガのみが使用する。
nil
に束縛される。 デバッガはこの変数に設定することで、 同じコマンドの起動中にデバッガが将来起動された場合に備えて 情報を残すことができる。
デバッガにとっては、通常のグローバル変数ではなくこの変数を使う利点は、 以降のコマンド起動にデータが繰り越さないことである。
backtrace-frame
は、Lispデバッガで使うことを意図している。 深さframe-numberのスタックフレームで進行中の計算に関する情報を返す。
当該フレームで引数の評価を完了していなければ(あるいはスペシャルフォーム)、 値は(nil function arg-forms...)
。
当該フレームで引数の評価を完了し関数を呼び出していれば、 値は(t function arg-values...)
。
戻り値において、functionは評価したリストのCARであるか、 マクロ呼び出しではlambda
式である。 関数に引数&rest
があれば、リストarg-valuesの残りで表現される。
frame-numberが範囲外であると、backtrace-frame
はnil
を返す。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugはEmacs Lispプログラムのソースレベルデバッガであり、 つぎのことを行えます。
以下の最初の3つの節では、edebugを使うのに十分な情報を与えます。
17.2.1 edebugの使い方 Introduction to use of Edebug. 17.2.2 edebug向けの処置 You must instrument your code in order to debug it with Edebug. 17.2.3 edebugの実行モード Execution modes, stopping more or less often. 17.2.4 ジャンプ Commands to jump to a specified place. 17.2.5 edebugのその他のコマンド Miscellaneous commands. 17.2.6 ブレークポイント Setting breakpoints to make the program stop. 17.2.7 エラーの捕捉 trapping errors with Edebug. 17.2.8 edebugのビュー Views inside and outside of Edebug. 17.2.9 評価 Evaluating expressions within Edebug. 17.2.10 評価リストバッファ Expressions whose values are displayed each time you enter Edebug. 17.2.11 edebugでの出力 Customization of printing. 17.2.12 トレースバッファ How to produce trace output in a buffer. 17.2.13 カバレッジテスト How to test evaluation coverage. 17.2.14 外側の文脈 Data that Edebug saves and restores. 17.2.15 マクロ呼び出しの処置 Specifying how to handle macro calls. 17.2.16 edebugのオプション Option variables for customizing Edebug.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugでLispプログラムをデバッグするには、 デバッグしたいLispコードをまず処置(instrument)しておく必要があります。 これを行う簡単な方法は、関数やマクロの定義にポイントを移動してから、 C-u C-M-x(前置引数を指定したeval-defun
)を実行します。 コードを処置する別の方法については、See 節 17.2.2 edebug向けの処置。
関数をいったん処置しておけば、当該関数を呼び出すとedebugを活性にします。 edebugが活性になると実行を停止し、 読者が選択したedebugの実行モードに応じて、 関数をステップ実行したりデバッグコマンドを検査しながら 表示を更新しつつ実行を継続します。 デフォルトの実行モードはステップ実行であり、 いったん実行を停止します。 See 節 17.2.3 edebugの実行モード。
edebugでは、デバッグ中のLispコードのソースを 表示したEmacsバッファを読者は見ます。 このバッファをソースコードバッファと呼びます。 このバッファは一時的に読み出し専用です。
左端の矢印は、関数の実行中の行を表します。 ポイントの初期位置は関数の実行中の行にありますが、 読者自身がポイントを移動すると変わります。
(以下の)fac
の定義を処置してから(fac 3)
を実行したとすると、 つぎのようになります。 ポイントはif
のまえの開き括弧にあります。
(defun fac (n) =>-!-(if (< 0 n) (* n (fac (1- n))) 1)) |
edebugが関数内で実行を停止できる箇所を停止位置(stop point)と呼びます。 これらは、リストである各部分式の前後と各変数参照のうしろにあります。 関数fac
の中にある停止位置をピリオドで示します。
(defun fac (n) .(if .(< 0 n.). .(* n. .(fac (1- n.).).). 1).) |
ソースコードバッファでは、Emacsのlispモードのコマンドに加えて edebugの特別なコマンドを使えます。 たとえば、つぎの停止位置まで実行するには edebugコマンドSPCを打ちます。 fac
に入ったあとでSPCを1回打つと、 つぎのような表示になります。
(defun fac (n) =>(if -!-(< 0 n) (* n (fac (1- n))) 1)) |
式のうしろでedebugが実行を停止すると、 式の値をエコー領域に値を表示します。
多用される他のコマンドには、 停止位置にブレークポイントを設定するb、 ブレークポイントに達するまで実行するg、 edebugを終了してトップレベルのコマンドループへ戻るqがあります。 edebugのコマンド一覧を表示するには?を打ちます。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispコードのデバッグにedebugを使うためには、 コードをまず処置する必要があります。 コードを処置すると、適当な箇所でedebugを起動する追加のコードを挿入します。
いったんedebugをロードすると、 コマンドC-M-x(eval-defun
)は再定義されます。 定義内で前置引数を指定して起動すると 定義を評価するまえに処置するようになります。 (ソースコード自体は変更しない。) 変数edebug-all-defs
がnil
以外であると、 前置引数の意味を逆にします。 つまり、前置引数を指定しない限り、 C-M-xは関数定義を処置します。 変数edebug-all-defs
のデフォルト値はnil
です。 コマンドM-x edebug-all-defsは変数edebug-all-defs
の値を トグルします。
edebug-all-defs
がnil
以外であると、 コマンドeval-region
、eval-current-buffer
、eval-buffer
も それらが評価する定義を処置します。 同様に、edebug-all-forms
は、 定義以外のフォームであってもeval-region
が 任意のフォームを処置するかどうか制御します。 これは、ミニバッファでのロードや評価には適用されません。 コマンドM-x edebug-all-formsはこのオプションをトグルします。
別のコマンドM-x edebug-eval-top-level-formは、 edebug-all-defs
とedebug-all-forms
の値に関わらず 任意のトップレベルのフォームを処置するために使えます。
edebugが動作中は、 コマンドI(edebug-instrument-callee
)で、 ポイントのうしろのフォームから呼ばれる関数やマクロの定義を 処置済みでなければ処置できます。 これは、edebugが当該関数のソースを探せる場合にのみ可能です。 edebugをロード後には、 eval-region
は、処置していないものも含めて、 評価した各定義の位置を記録しています。 関数を処置後に呼び出してステップ実行する コマンドi(see 節 17.2.4 ジャンプ)も参照してください。
edebugは、標準のスペシャルフォームすべて、 式を引数とするinteractive
フォーム、 無名ラムダ式、他の定義フォームをどのように処置するかわかっています。 edebugは、マクロ呼び出しを引数に持つユーザー定義マクロをどのように 処置すべきかわかりませんから、読者がそれを指示する必要があります。 詳しくは、See 節 17.2.15 マクロ呼び出しの処置。
edebugは、あるセッションで初めてコードを処置する場合、 フックedebug-setup-hook
を実行してから それにnil
を設定します。 これを利用すると、読者が使用するパッケージに対応した edebug用仕様(see 節 17.2.15 マクロ呼び出しの処置)を edebugを使用する場合にのみロードするようにできます。
定義から処置を取り除くには、 処置しないような方法でその定義を単に再評価するだけです。 けっして処置せずにフォームを評価する方法は2つあります。 ファイルをload
するか、 ミニバッファでeval-expression
(M-:)を使います。
edebugが処置中に構文エラーを検出すると、 コードのエラー箇所にポイントを置いて、 エラーinvalid-read-syntax
を通知します。
edebugの内側で使える他の評価関数についてはSee 節 17.2.9 評価。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugには読者がデバッグしているプログラムを実行するための 実行モードが複数あります。 これらをedebugの実行モードと呼びます。 これらをメジャーモードやマイナモードと混同しないでください。 edebugの実行モードは、停止するまでにどの程度edebugが実行を継続するか、 たとえば、各停止位置で停止するのかつぎのブレークポイントまで継続するのか、 また、停止するまでに評価の進行状況をどの程度edebugが表示するのかを 決定します。
通常、あるモードにおいて、 プログラムを継続するコマンドを打つことでedebugの実行モードを指定します。 以下にそれらのコマンドの一覧を示します。 Sを除くすべてのコマンドは、 少なくともある程度プログラムの実行を再開します。
プログラムをいっさい実行せずに、 edebugコマンドの入力を待つ(edebug-stop
)。
つぎに出会う停止位置で止まる(edebug-step-mode
)。
式のうしろでつぎに出会う停止位置で止まる (edebug-next-mode
)。 17.2.5 edebugのその他のコマンドのedebug-forward-sexp
も参照。
edebugの各停止位置で1秒間休止する (edebug-trace-mode
)。
各停止位置で表示を更新するが休止しない (edebug-Trace-fast-mode
)。
つぎのブレークポイントまで実行する (edebug-go-mode
)。 see 節 17.2.6 ブレークポイント。
各ブレークポイントで1秒間休止してから継続する (edebug-continue-mode
)。
各ブレークポイントへポイントを移動するが休止しない (edebug-Continue-fast-mode
)。
ブレークポイントを無視する (edebug-Go-nonstop-mode
)。 Sや編集コマンドを打てば停止できる。一般に、上記一覧の上にある実行モードほど下にあるものに比べると プログラムをゆっくり実行、つまり、早く停止します。
実行中やトレース中には、edebugコマンドをなにか打てば実行に割り込めます。 edebugはつぎの停止位置でプログラムを止め、 読者が打ったコマンドを実行します。 たとえば、実行中にtを打てば、つぎの停止位置でトレースモードに 切り替わります。 単に実行を停止するにはSを使います。
読者の関数が入力を読み取る場合、実行に割り込むつもりで打った文字を 関数が読み取ってしまうかもしれません。 読者のプログラムがいつ入力するかに注意していれば、 このような意図しない結果を避けることができます。
本節で述べたコマンドを含むキーボードマクロは動作しません。 つまり、プログラムを再開するためにedebugから抜けると キーボードマクロの制御を失ってしまいます。 これを修正するのは簡単ではありません。 また、edebugの外側でキーボードマクロを定義したり実行しても、 edebug内のコマンドにはなんの影響もありません。 これは普通は利点です。 しかし、オプションedebug-continue-kbd-macro
(see 節 17.2.16 edebugのオプション)も参照してください。
edebugの新たなレベルに入ると、変数edebug-initial-mode
の値を 実行モードの初期値とします。 デフォルトでは、これはステップ実行モードを指定します。 処置した関数を1つのコマンドから複数回呼び出すなどして edebugの同一レベルに再度入ることができることに注意してください。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
本節で述べるコマンドは、指定した位置に達するまで実行します。 iを除くすべてのものは、停止する場所に一時的なブレークポイントを 設定してから実行モードに移行します。 意図したブレークポイントより先に別のブレークポイントに達しても 実行を停止します。 ブレークポイントについて詳しくはSee 節 17.2.6 ブレークポイント。
非ローカル脱出は、読者が意図したプログラムの停止すべき 一時的なブレークポイントを迂回するため、 これらのコマンドは非ローカル脱出があると意図したように動作しません。
edebug-goto-here
)。
edebug-forward-sexp
)。
コマンドhは、一時的なブレークポイントを使って、 ポイント位置付近の停止位置まで進みます。 ブレークポイントについて詳しくはSee 節 17.2.6 ブレークポイント。
コマンドfは、プログラムの式1つ分先へ進みます。 より正確には、C-M-fによる移動箇所へ一時的なブレークポイントを設定し、 プログラムがブレークポイントで停止するような実行モードで実行します。
前置引数nを指定すると、 ポイント位置からn個先のS式に一時的なブレークポイントを設定します。 囲んでいるリストの残り要素数がnより少なければ、 囲んでいる式の末尾で停止します。
C-M-fの移動先はプログラムが実際に停止するであろう箇所です。 これが正しくない場合もあり、たとえば、cond
では正しくありません。
コマンドfは、柔軟性のために、 停止位置ではなくポイント位置でforward-sexp
を使います。 現在の停止位置から式1つだけ実行したい場合には、 まずwと打ってポイントを停止位置に移動してからfを打ちます。
コマンドoは式から『出る』まで実行します。 ポイントを含むS式の末尾に一時的なブレークポイントを置きます。 このS式が関数定義そのものである場合には、 oは定義の最後のS式の手前まで実行します。 現在この箇所にいた場合には、関数から戻ってから停止します。 いいかえれば、最後のS式のあとに位置していない限り、 このコマンドは現在実行中の関数から抜けません。
コマンドiは、 ポイント位置のあとにあるリストフォームから呼ばれる関数やマクロへ進み、 最初に出会った停止位置で止まります。 そのフォームはこれから評価されるフォームである必要はありません。 しかし、評価されるフォームが関数呼び出しである場合には、 引数を評価するまえにこのコマンドを使うことを覚えておいてください。 さもないとこのコマンドを使う時期が遅すぎます。
コマンドiは、呼び出す関数やマクロが処置されていないと それらを処置します。 これは便利ですが、それらの関数やマクロは、明示的に処置を取り除かない限り、 処置したままになります。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugの他のコマンドを以下に示します。
edebug-help
)。
abort-recursive-edit
)。
top-level
)。 edebugのすべての動作中のレベルを含めて、すべての再帰編集レベルから抜ける。 しかし、フォームunwind-protect
やcondition-case
で保護した 処置済みのコードがあるとデバッガを再開する。
top-level-nonstop
)。
edebug-previous-result
)。
edebug-backtrace
)。
edebugのバックトレースバッファでは、 標準のデバッガのようにはデバッガのコマンドを使えない。
実行を継続するとバックトレースバッファは自動的に削除される。
edebugの再帰編集から、edebugを再帰的に活性にするコマンドを起動できます。 edebugが活性であるときにはいつでもqでトップレベルへ戻るか、 C-]で1つの再帰編集レベルを抜けることができます。 保留している評価すべてのバックトレースはdで表示できます。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugのステップ実行モードは、つぎの停止位置に達すると実行を停止します。 edebugが実行を止める方法は3つあります。 ブレークポイント、グローバルブレーク条件、ソースのブレークポイントです。
edebugを使用中には、読者がテスト中のプログラムにブレークポイント (breakpoint)、つまり、実行を停止すべき箇所を設定できます。 17.2.1 edebugの使い方で定義した任意の停止位置にブレークポイントを設定できます。 ブレークポイントの設定や解除において対象となる停止位置は、 ソースコードバッファのポイント位置かそのあとにある停止位置です。 ブレークポイントに関するedebugコマンドはつぎのとおりです。
edebug-set-breakpoint
)。 前置引数を指定すると、一時的なブレークポイントになる (そこでプログラムが停止すると解除される)。
edebug-unset-breakpoint
)。
nil
以外の値に評価される場合にのみ プログラムを停止する条件付きブレークポイントを設定する (edebug-set-conditional-breakpoint
)。 前置引数を指定すると、一時的なブレークポイントになる。
edebug-next-breakpoint
)。edebug内では、bでブレークポイントを設定し、 uで解除できます。 まず目的のedegugの停止位置にポイント位置を移動し、 bを打ってその箇所にブレークポイントを設定したり、 uを打ってその箇所のブレークポイントを解除します。 設定されていないブレークポイントを解除しても、なにも起こりません。
定義を再評価したり再処置すると、その中のブレークポイントすべてを解除します。
条件付きブレークポイント(conditional breakpoint)は、 プログラムがこの箇所に達するたびに条件を検査します。 条件を評価中に発生するどんなエラーも無視し、 nil
として扱います。 条件付きブレークポイントを設定するにはxを使い、 条件式はミニバッファで指定します。 すでに条件付きブレークポイントを設定してある停止位置に 条件付きブレークポイントを設定し直すと、 それまでの条件式がミニバッファに入るので編集できます。
ブレークポイントを設定するコマンドに前置引数を指定すると、 条件付き/無条件ブレークポイントを一時的なものにできます。 一時的ブレークポイントでプログラムが停止すると、 そのブレークポイントは自動的に解除されます。
edebugのモードが非停止実行でなければ、 edebugはブレークポイントでつねに停止するか休止します。 非停止実行モードでは、ブレークポイントを完全に無視します。
ブレークポイントの場所を確認するには、コマンドBを使います。 同じ関数内のポイント箇所のうしろにあるブレークポイントか、 後続のものがなければ最初のブレークポイントにポイント位置を移動します。 このコマンドは実行を継続しません。 バッファ内で単にポイントを移動するだけです。
17.2.6.1 グローバルブレーク条件 Breaking on an event. 17.2.6.2 ソース上のブレークポイント Embedding breakpoints in source code.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
グローバルブレーク条件(global break condition)は、 指定した条件が満たされると、その場所に関わらず、実行を停止させます。 edebugは各停止位置においてグローバルブレーク条件を評価します。 これがnil
以外の値であると、 ブレークポイントに達したかのように、 実行モードに依存して実行を停止するか休止します。 条件の評価中にエラーが発生しても実行は停止しません。
条件式はedebug-global-break-condition
に保存されます。 コマンドXで新たな条件式を指定できます (edebug-set-global-break-condition
)。
グローバルブレーク条件は、読者のコードのどこでイベントが発生するかを 調べるもっとも簡単な方法ですが、コードの実行速度をかなり遅くします。 ですから、使用しない場合には条件をnil
に再設定すべきです。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
定義内のすべてのブレークポイントは、定義を処置し直すたびに失われます。 ブレークポイントを失いたくない場合には、 ソース上のブレークポイント(source breakpoint)を指定できます。 これはソースコード上で関数edebug
を呼び出すだけです。 もちろん、条件付けして呼び出せます。 たとえば、関数fac
において、引数がゼロの場合に停止するには、 以下に示すように最初の行を挿入します。
(defun fac (n) (if (= n 0) (edebug)) (if (< 0 n) (* n (fac (1- n))) 1)) |
関数fac
を処置してこの関数を呼び出すと、 edebug
の呼び出しはブレークポイントのように動作します。 実行モードに応じて、edebugはその箇所で停止するか休止します。
edebug
を呼び出したコードが処置済みでなければ、 この関数はdebug
を呼び出します。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Emacsは、通常、エラーが通知されてもcondition-case
で処理されなかった 場合、エラーメッセージを表示します。 edebugが活性であり処置済みのコードを実行していると、 edebugは処理されなかったエラーすべてに反応します。 この動作をedebug-on-error
とedebug-on-quit
で カスタマイズできます。 See 節 17.2.16 edebugのオプション。
edebugがエラーに反応すると、 エラーを起こすまえ出会った最後の停止位置を表示します。 この位置は、実際にエラーを起こした処置してない関数の呼び出し位置である 場合もあります。 未束縛な変数のエラーでは、最後の停止位置は、 当該変数の参照位置からかなり離れている場合があります。 そのような場合には、完全なバックトレースを表示したいでしょう (see 節 17.2.5 edebugのその他のコマンド)。
edebugが活性なときにdebug-on-error
やdebug-on-quit
を変更しても、 edebugが不活性になったときにそれらの変更を取り消してしまいます。 さらに、edebugの再帰編集中は、これらの変数はedebugの外側での値に 束縛されます。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
これらのedebugのコマンドは、edebugに入るまえのバッファやウィンドウの 状態を調べるものです。 外部ウィンドウ構成は、edebugの外側でのウィンドウの集まりや内容に 関するものです。
edebug-view-outside
)。
edebug-bounce-point
)。 前置引数nは、かわりに休止秒数を指定する。
edebug-where
)。
同じバッファを表示している別のウィンドウでこのコマンドを使うと、 それ以後、そのウィンドウに現在の定義が表示されるようになる。
edebug-toggle-save-windows
)。
前置引数を指定すると選択したウィンドウだけの保存/復元をトグルする。 ソースコードバッファを表示していないウィンドウを指定するには、 グローバルキーマップのC-x X Wを使う必要がある。
vで外部ウィンドウ構成を見ることができます。 あるいは、(edebugの外側での)カレントバッファが表示されていなくても pでカレントバッファのポイント位置を見ることができます。 ポイント位置を移動したら、 wでソースコードバッファの停止位置へ戻れます。
外部ウィンドウ構成を保存しないようにWを使うたびに、 edebugは保存しておいた外部ウィンドウ構成を破棄します。 そのため、保存するように戻しても、 (プログラムを続行することで)edebugを抜けると、 現在のウィンドウ構成は変更されません。 しかし、`*edebug*'と`*edebug-trace*'の自動再表示は、 十分なウィンドウが開いてないと、 読者が見たいバッファと衝突するかもしれません。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugの内側では、edebugが動作していないがごとく式を評価できます。 edebugは、式の評価と表示に対して見えないようにします。 edebugが明示的に保存/復元する場合を除いて、 副作用を持つ式の評価も期待どおり動作します。 この処理に関して詳しくはSee 節 17.2.14 外側の文脈。
edebug-eval-expression
)。 つまり、edebugは評価への干渉を最小限にとどめようとする。
edebug-eval-last-sexp
)。 edebugは`cl.el'(版2.03以降)内の構文 lexical-let
、macrolet
、symbol-macrolet
で 作成されるレキシカル(テキスト上の)束縛を参照する式の評価を扱えます。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
`*edebug*'と呼ばれる評価リストバッファを使って、 式を対話的に評価できます。 さらに、edebugが表示を更新するたびに自動的に評価される 式の評価リストを設定することもできます。
edebug-visit-eval-list
)。バッファ`*edebug*'では、以下の特別なコマンドに加えて lisp対話モード (see 節 `lisp対話バッファ' in
edebug-eval-print-last-sexp
)。
edebug-eval-last-sexp
)。
edebug-update-eval-list
)。
edebug-delete-eval-item
)。
edebug-where
)。`*scratch*'で行うのと同様に、 評価リストウィンドウではC-jやC-x C-eで式を評価できますが、 それらはedebugの外側の文脈で評価されます。
実行を継続すると、対話的に入力した式(やその結果)は破棄されますが、 実行を停止するたびに評価される式から成る評価リスト(evaluation list) を設定できます。
これを行うには、評価リストバッファにて、 1つ以上の評価リストグループ(evaluation list group)を書きます。 評価リストグループは、1つ以上のLisp式から成ります。 グループはコメント行で区切ります。
コマンドC-c C-u(edebug-update-eval-list
)は、 バッファを走査して各グループの最初の式を使って 評価リストを再構築します。 (各グループの2番目の式は計算結果を表示した値とみなす。)
edebugに入るたびに、各式に続けてその現在値をバッファに挿入することで 評価リストを再表示します。 このとき、各式がそれぞれグループになるようにコメント行も挿入します。 したがって、バッファのテキストを変更せずに再度C-c C-uと打つと、 評価リストは実質的には変更されません。
評価リストの評価中にエラーが発生すると、 エラーメッセージを評価結果とみなして文字列で表示します。 したがって、現在の文脈では不正な変数を式に使っても 読者のデバッグを遮ることはありません。
評価リストウィンドウに数個の式を追加したときのようすを以下に示します。
(current-buffer) # |
グループを削除するには、そこへポイントを移動してC-c C-dと打ちます。 あるいは、グループのテキストを単に削除してからC-c C-uで 評価リストを更新します。 評価リストに新たに式を追加するには、 適切な位置に式を挿入し、新たなコメント行を挿入します。 (コメント行にマイナス記号を挿入する必要はない。 コメントの内容は関係ない。) そして、C-c C-uと打ちます
`*edebug*'を選択したあとは、 C-c C-wでソースコードバッファへ戻れます。 読者が実行を継続するとバッファ`*edebug*'は削除され、 つぎに必要なときに再度作成されます。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
読者のプログラムの式が循環したリスト構造を含む値を作り出す場合、 edebugがそれを出力しようとするとエラーになります。
循環構造を扱う1つの方法は、出力を切り詰めるために print-length
やprint-level
を設定することです。 edebugが読者のためにこれを行います。 それらがnil
であると、 edebugはprint-length
とprint-level
を50に束縛します。 (実際は、edebugが使う値は e-debug-print-length
とe-debug-print-level
が指定する。) See 節 18.6 出力に影響する変数。
nil
以外であると、edebugが結果を出力するときには、 これをprint-length
に束縛する。 デフォルト値は50
。nil
以外であると、edebugが結果を出力するときには、 これをprint-level
に束縛する。 デフォルト値は50
。パッケージ`cust-print'を使えば、 循環構造や要素を共有する構造をより的確に出力することもできます。
`cust-print'をロードしてedebugでのみこの特別な出力を使うようにするには、 単にコマンドM-x edebug-install-custom-printを使うだけです。 標準の出力関数に戻すには、M-x edebug-uninstall-custom-printを使います。
循環構造を作るコードの例を示します。
(setq a '(x y)) (setcar a a) |
特別な出力ではこれを`Result: #1=(#1# y)'と出力します。 `#1='の記法は、これに続く構造に`1'というラベルを付けます。 また、`#1#'の記法はすでにラベル付けした構造を参照します。 この記法は、リストやベクトルの任意の共有された要素に使われます。
nil
以外であると、edebugが結果を出力するときには、 これをprint-circle
に束縛する。 デフォルト値はnil
。他のプログラムでもこの特別な出力を使えます。 詳しくは、`cust-print.el'を参照してください。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugは、実行トレースを`*edebug-trace*'というバッファに保存することで それらを記録できます。 これは、関数名とそれらの引数、戻り値から成る 関数呼び出しとその戻りの記録です。 トレース記録を有効にするには、 edebug-trace
にnil
以外の値を設定します。
トレースバッファを作成することとトレース実行モードとは 同じではありません(see 節 17.2.3 edebugの実行モード)。
トレース記録を有効にしていると、 各関数へ入るときと出るときに、トレースバッファに行が追加されます。 関数へ入るときの記録は、`::::{'に関数名と引数値が続きます。 関数から出るときの記録は、`::::}'に関数名とその結果が続きます。
入るときの`:'の個数は、再帰の深さを表します。 関数呼び出しの対応する開始や対応する終了を探すために トレースバッファでは中括弧を使えます。
関数edebug-print-trace-before
とedebug-print-trace-after
を 再定義すれば、関数へ入ったときと出るときのトレース記録をカスタマイズできます。
edebug-tracing
はbodyの最後のフォームの値を返す。(apply 'format format-string format-args)
で 計算する。 区切りとして改行も挿入する。edebug-tracing
とedebug-trace
は、 edebugが活性でない場合であっても呼ばれるとトレースバッファに行を挿入します。 トレースバッファにテキストを挿入するとき、 挿入した最後の行が見えるようにウィンドウをスクロールします。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugでは、初歩的なカバレッジテストや実行頻度を表示できます。
カバレッジテストでは、各式の結果を以前の結果と比較します。 現在のEmacsセッションでカバレッジテストを始めて以降、 プログラムの各フォームが異なる2つの値を返せば、 当該フォームを『カバーした』とみなします。 したがって、読者のプログラムについてカバレッジテストを行うには、 さまざまな条件でそれを実行して正しく動作しているか注意します。 読者が各フォームが異なる2つの値を返すように試行し終れば、 edebugはそのように通知します。
カバレッジテストは実行速度を遅くするので、 edebug-test-coverage
がnil
以外の場合にのみテストします。 すべての処置済み関数の実行に関する頻度数計測は、 非停止実行モードであってもカバレッジテストのオン/オフに関わらず行います。
ある定義に関するカバレッジテストと頻度数計測を表示するには M-x edebug-display-freq-countを使います。
頻度数は、コードの各行のあとにコメント行として表示され、 コマンドundo
でそれらのコメント行の挿入をアンドゥできる。 頻度数は、式のまえの`('や式のうしろの`)'の直下、 あるいは、変数の最後の文字に表示される。 表示を簡素にするために、頻度数が同じ行のまえのほうの式の頻度数と同じであると 表示しない。
式の頻度数に続く文字`='は、 その式を評価するたびに同じ値を返したことを意味する。 いいかえれば、カバレッジテストとしては、 その式はまだ『カバーして』いないことになる。
ある定義に関する頻度数計測とカバレッジデータをクリアするには、 eval-defun
で単に再処置すればよい。
たとえば、edebug-test-coverage
をt
とし、 ソース上のブレークポイントを設定して(fac 5)
を評価すると、 ブレークポイントに達したときの頻度数データはつぎのようになります。
(defun fac (n) (if (= n 0) (edebug)) ;#6 1 0 =5 (if (< 0 n) ;#5 = (* n (fac (1- n))) ;# 5 0 1)) ;# 0 |
コメント行は、fac
が6回呼ばれたことを表します。 最初のif
文は、5回とも同じ結果を返したのです。 2番目のif
についても同じです。 fac
の再帰呼び出しは1度も戻っていません。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugは、読者がデバッグ中のプログラムに対しては透過であるように努めますが、 完全にうまくいくとは限りません。 また、eで読者が式を評価するときや評価リストバッファでも、 外側の文脈を一時的に復元して透過であるように努めます。 本節では、edebugが復元する文脈について正確に説明し、 edebugが透過にならない場合についても説明します。
17.2.14.1 停止すべきかどうかの検査 When Edebug decides what to do. 17.2.14.2 edebugの表示の更新 When Edebug updates the display. 17.2.14.3 edebugの再帰編集 When Edebug stops execution.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugに入ると、トレース情報を作るのかプログラムを停止するのかを 決定するまえであってもある種のデータを保存/復元する必要が つねにあります。
max-lisp-eval-depth
とmax-specpdl-size
を一度増加する。 しかし、こうしてもedebugを使うときにスタックを使い切ってしまうことがある。
executing-macro
は edebug-continue-kbd-macro
に束縛される。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugが(トレースモードなどで)なにかを表示する必要があると、 edebugの『外側』の現在のウィンドウ構成を保存します (see 節 27.16 ウィンドウ構成)。 (プログラムを続行して)edebugを抜けるときには、 以前のウィンドウ構成を復元します
Emacsは休止するときにのみ表示を更新します。 通常、プログラムを続行しても、休止したり入力を読むことなく ブレークポイントやステップ実行によりedebugへ戻ります。 そのような場合、Emacsには(edebugの)『外側』のようすを再表示する 機会が与えられません。 見かけ上、ウィンドウの表示は直前にedebugが活性であったときと同じになります。
なにかを表示するためにedebugに入っても以下のデータを保存/復元しますが、 エラーや中断が起こると、故意に復元しないものもあります。
edebug-save-windows
がnil
以外ならば、 外側でのウィンドウ構成を保存/復元する (see 節 17.2.14.2 edebugの表示の更新)。
エラーや中断が起こるとウィンドウ構成は復元されない。 しかし、save-excursion
を使っていれば、 エラーや中断が起こっても、 外側で選択していたウィンドウは選択される。 edebug-save-windows
の値がリストであると、 リストに指定したウィンドウのみを保存/復元する。
ソースコードバッファのウィンドウ開始位置やスクロールは復元しないが、 これは、edebug内での表示が統一されるようにするためである。
edebug-save-displayed-buffer-points
がnil
以外であれば、 表示されている各バッファのポイント位置の値を保存/復元する。
overlay-arrow-position
とoverlay-arrow-string
は、 保存/復元される。 そのため、同じバッファで再帰編集からedebugを起動しても安全である。
cursor-in-echo-area
はnil
にローカルに束縛する。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugに入ってユーザーコマンドを読み取るとき、 以下のデータを保存し(のちに復元し)ます。
last-command
、this-command
、last-command-char
、 last-input-char
、last-input-event
、 last-command-event
、last-event-frame
、 last-nonmenu-event
、track-mouse
。 edebug内で使ったコマンドは、edebugの外側でのこれらの変数には影響しない。
this-command-keys
が返すキー列は edebug内でコマンドを実行すると変更されてしまい、 Lispからキー列を設定し直す方法はない。
edebugはunread-command-events
の値を保存/復元できない。 この変数に変な値が入っているときにedebugに入ると、 読者がデバッグするプログラムの実行に干渉することがある。
command-history
に追加される。 これにより実行結果を変更することはほとんどない。
recursive-edit
は standard-output
とstandard-input
をnil
に束縛するが、 edebugは評価中にはそれらを一時的に復元する。
defining-kbd-macro
は edebug-continue-kbd-macro
に束縛される。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebugがLispマクロを呼び出す式を処置するとき、 それを正しく行うにはマクロに関する余分な情報を必要とします。 マクロ呼び出しのどの部分式が評価されるフォームであるかを 明確に判定する方法がないからです。 (マクロ本体で明示的に評価されるか、 結果の展開形が評価されるときか、あるいは、さらにあと)
したがって、edebugが出会う各マクロについて、 当該マクロの呼び出し形式を記述するedebug用仕様を定義する必要があります。 これには、def-edebug-spec
を使います。
引数macroはマクロ名だけでなく任意のシンボルでよい。
例題マクロfor
(see 節 12.6.1 マクロ引数の複数回評価)の edebug用仕様の等価な定義例2つを示します。
(def-edebug-spec for (symbolp "from" form "to" form "do" &rest form)) (def-edebug-spec for (symbolp ['from form] ['to form] ['do body])) |
specificationに指定するものとその引数の処理方法は次表のとおりです。
t
0
17.2.15.1 仕様リスト How to specify complex patterns of evaluation. 17.2.15.2 仕様内でのバックトラック What Edebug does when matching fails. 17.2.15.3 仕様の例 To help understand specifications.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
マクロ呼び出しの引数のあるものは評価し別のものは評価しない場合には、 edebug用仕様に仕様リスト(specification list)が必要になります。 複数の引数に一致する仕様リストの要素もありますが、 後続の要素の処理を修飾する要素もあります。 後者は仕様キーワード(specification keyword)と呼ばれ、 (&optional
のように)`&'で始まるシンボルです。
仕様リストには、それ自体がリストである引数に一致する部分リストや グループ化に使うベクトルを含んでもかまいません。 部分リストやグループは仕様リストを階層に分けます。 仕様キーワードはそれらを含む部分リストやグループの残りに適用されます。
仕様リストに選択肢や繰り返しが含まれる場合、 実際のマクロ呼び出しに一致させるにはバックトラックが必要な場合もあります。 詳しくはSee 節 17.2.15.2 仕様内でのバックトラック。
edebug用仕様では、正規表現による一致と文脈自由文法の構文を使えます。 対応した括弧に囲まれた部分リスト、フォームの再帰的処理、 間接仕様による再帰です。
仕様リストの要素に指定できるものとそれらの意味を以下に示します。
sexp
form
place
setf
構文のように値を格納する場所。
body
&rest form
の省略形。 以下の&rest
を参照。
function-form
function
ではなくquote
でクォートされるときに有用である。 というのは、ラムダ式の本体をいずれかの方法で処置するからである。
lambda-expr
&optional
数個の省略可能な要素に省略不可な要素を続けるには、 [&optional specs...]
を使う。 数個の要素がすべて一致するかまったく一致しないことを指定するには、 &optional [specs...]
を使う。 以下のdefun
の例を参照。
&rest
数個の要素のみを繰り返すには[&rest specs...]
を使う。 各繰り返しですべてが一致するような数個の要素を指定するには、 &rest [specs...]
を使う。
&or
&or
は失敗。
&or
に続く各要素は1つの選択肢を表す。 複数の要素を1つの選択肢としてグループにまとめるには、 それらを[...]
で囲む。
¬
&or
を使ったかように後続の要素を選択肢として一致させるが、 どれかが一致すると仕様は失敗。 どれにも一致しなければ、仕様¬
は成功。
&define
&define
はリスト仕様の最初の要素である必要がある。
nil
gate
let
を参照。
その他のシンボル
シンボルにedebug用仕様があれば、 この間接仕様は、シンボルのかわりに使われる仕様リストであるか、 引数を処理するために呼び出される関数であること。 仕様は、マクロ向けにdef-edebug-spec
で定義した仕様であってもよい。 以下のdefun
の例を参照。
さもなければ、シンボルは述語であること。 述語は引数で呼び出され、述語がnil
を返すと仕様は失敗する。 いずれの場合でも、当該引数は処置されない。
適当な述語には、symbolp
、integerp
、 stringp
、vectorp
、atom
がある。
[elements...]
"string"
'symbol
と等価であるが、 文字列のほうが望ましい。
(vector elements...)
(elements...)
部分リスト仕様はドット対リストでもよく、その場合、 対応するリスト引数はドット対リストである。 あるいは、ドット対リスト仕様の最後のCDRは ((spec . [(more specs...)])
などの グループや間接仕様を介した) 別の部分リスト仕様であってもよいが、 それらの要素はドット対ではないリスト引数に一致する。 これは、以下のバッククォートの例のような再帰仕様に有用である。 このような再帰を終らせるうえの仕様nil
も参照。
(specs . nil)
や (specs . (sublist-elements...))
のような部分リスト仕様は (specs sublist-elements...)
と等価であることに注意。
&define
のうしろに追加できる仕様の一覧を以下に示します。 以下のdefun
の例を参照してください。
name
定義フォームには単一の名前フィールドがある必要はなく、 複数の名前フィールドを持っていてもよい。
:name
:name
に続く要素はシンボルであること。 定義に対する追加の名前要素として使う。 定義の名前に一意で静的な要素を追加するために使う。 複数あってもよい。
arg
lambda-list
def-body
body
に似ているが、 定義本体は定義に関連した情報を調べる異なるedebug呼び出しで処置する必要がある。 定義内のフォームの最上位レベルのリストにはdef-body
を使う。
def-form
def-body
に似ているが、 フォームのリストではなく単一のフォームに一致するものに使う。 特別な場合として、def-form
は フォームを実行したときにトレース情報を出力しないことを意味する。 以下のinteractive
の例を参照。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
仕様の一致がある箇所で失敗しても、 必ずしも構文エラーが通知されるとは限りません。 そのかわりに、選択肢すべてを試し尽くすまでバックトラックします。 最終的に、引数リストの各要素は仕様内のいずれかの要素に一致する必要があり、 仕様内の各必須要素はいずれかの引数に一致する必要があります。 構文エラーを検出しても、より高いレベルの選択肢を使い切るまでは報告されず、 実際のエラー箇所から離れた箇所にポイントが置かれます。 しかし、エラー発生時にバックトラックが禁止されていれば、 ただちにエラーが報告されます。 さまざまな状況でバックトラックが自動的に再許可されることに注意してください。 &optional
や&rest
や&or
で新たに選択肢が指定されたり、 部分リストやグループや間接仕様を処理し始めると、 自動的に再許可されます。 バックトラックの許可/禁止の効果は、 現在処理しているレベルやそれより低いレベルに限定されます。
任意のフォーム仕様(つまり、form
、body
、def-form
、 def-body
)の一致処理中には、バックトラックを禁止します。 これらの仕様は任意のフォームに一致するので、 エラーはより上のレベルではなくフォーム自身にあるはずです。
また、クォートしたシンボルや文字列の仕様に一致すると バックトラックを禁止します。 というのは、通常、これは構造を認識したことを意味するからです。 しかし、すべてが同一シンボルで始まる選択肢を指定する場合には、 ["foo" &or [first case] [second case] ...]
のように、 そのシンボルを選択肢から括り出せばバックトラックするようにできます。
多くの場合では、バックトラックを自動的に禁止するこれらの2つの方法で十分ですが、 仕様gate
を使ってバックトラックを明示的に禁止すると有用な場合もあります。 上位の選択肢が適用できないとわかっている場合に有用です。 仕様let
の例を参照してください。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
以下の例を参考にするとedebug用仕様を理解しやすいでしょう。
スペシャルフォームlet
には束縛と本体の並びがあります。 各束縛は、シンボル、あるいは、シンボルと省略可能な式から成る部分リストです。 以下のedebug用仕様では、部分リストの内側にあるgate
で、 部分リストを一度みつけるとバックトラックを禁止していることに 注意してください。
(def-edebug-spec let ((&rest &or symbolp (gate symbolp &optional form)) body)) |
edebugは、defun
とdefmacro
、 および、対応する引数リストと仕様interactive
に対しては、 以下のedebug用仕様を使います。 式の引数は実際には関数本体の外側で評価されるので、 対話宣言フォームを特別扱いする必要があります。
(def-edebug-spec defmacro defun) ; 仕様 |
以下のバッククォートに対する仕様は、 ドット対リストの一致の仕方と再帰を終らせるnil
の使い方を示します。 また、ベクトルの要素の一致の仕方も示します。 (edebugが実際に定義している仕様では、 失敗の可能性がある非常に深い再帰をもたらすためドット対リストを扱わない。)
(def-edebug-spec ` (backquote-form)) ; わかりやすいように別名を付ける (def-edebug-spec backquote-form (&or ([&or "," ",@"] &or ("quote" backquote-form) form) (backquote-form . [&or nil backquote-form]) (vector &rest backquote-form) sexp)) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
以下のオプションはedebugの動作に影響します。
edebug-setup-hook
をnil
に再設定する。 これを用いて、edebugを使用する場合にのみ、 使用するパッケージに対応するedebug用仕様をロードできる。 see 節 17.2.2 edebug向けの処置。nil
以外であると、 defun
やdefmacro
のような定義フォームを普通に評価すると、 edebug用にそれらを処置する。 これは、eval-defun
、eval-region
、eval-buffer
、 eval-current-buffer
にも適用される。
このオプションの値をトグルするにはコマンドM-x edebug-all-defsを使う。 see 節 17.2.2 edebug向けの処置。
eval-defun
、 eval-region
、eval-buffer
、eval-current-buffer
は、 定義しないフォームの場合であってもすべてのフォームを処置する。 これは、ロードやミニバッファでの評価には適用されない。
このオプションの値をトグルするにはコマンドM-x edebug-all-formsを使う。 see 節 17.2.2 edebug向けの処置。
nil
以外であると、edebugはウィンドウ構成を保存/復元する。 これには時間がかかるので、読者のプログラムがウィンドウ構成に 依存しないのならば、この変数はnil
に設定しておくほうがよい。
値がリストであると、リスト内のウィンドウのみを保存/復元する。
edebugのコマンドWを使ってこの変数を対話的に変更できる。 see 節 17.2.14.2 edebugの表示の更新。
nil
以外であると、edebugは表示されているすべてのバッファの ポイントを保存/復元する。
選択していないウィンドウに表示されたバッファのポイントを変更する コードをデバッグ中には、別のバッファのポイントを保存/復元する必要がある。 edebugやユーザーが当該ウィンドウを選択すると、 そのバッファのポイントはウィンドウのポイント位置に移動する。
すべてのバッファでポイントを保存/復元するには 各ウィンドウを2度選択する必要があるため手間がかかる。 そのため、必要な場合にのみこの機能を有効にする。 see 節 17.2.14.2 edebugの表示の更新。
nil
以外であれば、 edebugが初めて動作するときの初期の動作モードを指定する。 可能な値は、 step
、next
、go
、Go-nonstop
, trace
、 Trace-fast
、continue
、Continue-fast
。
デフォルト値はstep
。 see 節 17.2.3 edebugの実行モード。
nil
以外であると、関数へ入るときと出るときのトレースを表示することを 意味する。 トレース出力は、`*edebug-trace*'という名前のバッファに、 関数へ入るときと出るときを各行に再帰の深さで字下げして表示する。
デフォルト値はnil
。
17.2.12 トレースバッファのedebug-tracing
も参照。
nil
以外であれば、edebugはデバッグ対象のすべての式のカバレッジ テストを行う。 see 節 17.2.13 カバレッジテスト。nil
以外であれば、 edebugの外側で実行するキーボードマクロを定義したり実行する。 デバッグしないので注意してこのオプションを使うこと。debug-on-error
の以前の値がnil
であると、 edebugはdebug-on-error
にこの値を束縛する。 see 節 17.2.7 エラーの捕捉。debug-on-quit
の以前の値がnil
であると、 edebugはdebug-on-quit
にこの値を束縛する。 see 節 17.2.7 エラーの捕捉。edebugが動作中にedebug-on-error
やedebug-on-quit
の値を 変更しても、新たなコマンドでedebugをつぎに起動するまでは これらの値は使用されない。
nil
以外であると、各停止位置で検査される式である。 結果がnil
以外であるとブレークする。 エラーは無視する。 see 節 17.2.6.1 グローバルブレーク条件。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispリーダは不正な構文を報告しますが、どこに問題があるかは報告できません。 たとえば、式を評価中のエラー『End of file during parsing』 (構文解析中にファイルの終り)は、開き括弧(あるいは開き角括弧)が 多すぎることを表します。 Lispリーダは括弧が対応していないことをファイルの末尾で検出しますが、 どこに閉じ括弧があるべきかは判断できません。 同様に、『Invalid read syntax: ")"』(不正な構文:")")は 閉じ括弧が多すぎるか開き括弧が足りないことを表しますが、 どこに括弧が足りないかは判断できません。 それでは、どこを変更すべきかどのように調べるのでしょう?
問題が単純な括弧の非対応でなければ、 各関数定義の先頭でC-M-eを試し、 関数定義の末尾に移動するかどうかをみるのは有用な技法です。 正しく移動しなければ、その関数に問題があります。
Lispによくある構文エラーは括弧の非対応なので、 これらの場面について詳しい助言を述べておきます。 (さらに、対応括弧表示モードをオンにしてポイントを移動すると非対応を 探しやすい。)
17.3.1 開き括弧の過剰 How to find a spurious open paren or missing close. 17.3.2 閉じ括弧の過剰 How to find a spurious close paren or missing open.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
最初の手順は、括弧が対応していない関数定義を探すことです。 開き括弧が過剰であれば、ファイルの末尾に閉じ括弧を挿入し C-M-b(backward-sexp
)を打ちます。 こうすると、括弧が対応していない関数定義の先頭へ移動します。 (そうしたら、C-SPC C-_ C-u C-SPCと打って、 当該箇所にマークを設定してから閉じ括弧の挿入を取り消し、 最終的にマークへ戻る。)
つぎの手順は、なにが悪いか正確に判断することです。 プログラムを調べる以外にこれを確実に行う方法はありませんが、 しばしば、既存の字下げが括弧のありかたを予想する鍵になります。 これを利用するもっとも簡単な方法はC-M-qで字下げし直し、 どのようになるか見ることです。 まだやらないでください! まず読み進んてください。
これを行うまえに、関数定義に充分な数の閉じ括弧があることを確認してください。 さもないと、C-M-qがエラーになったり、 ファイルの末尾までを字下げし直してしまいます。 ですから、関数定義の末尾へ移動して閉じ括弧を挿入しておきます。 C-M-eを使って移動しないでください。 というのは、関数定義の括弧の対応が取れていないと失敗するからです。
関数定義の先頭へ移動してC-M-qを打ちます。 通常、ある場所から関数の末尾までの行が右へずれます。 その場所の近くで、閉じ括弧が足りなかったり開き括弧が多すぎるのです。 (しかし、これが正しいと仮定してはならない。 コードを調べて確認すること。) 不具合箇所がみつかったならば、 意図した括弧に対しては古い字下げが適しているでしょうから C-_でC-M-qをアンドゥします。
問題を解決できたと思ったら、再度C-M-qを使います。 古い字下げが意図した括弧の入れ子に対応していて、 必要な括弧を挿入できているならば、 C-M-qはなにも変えないはずです。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
過剰な閉じ括弧に対処するには、まず、ファイルの先頭に開き括弧を挿入し、 その括弧のまえでC-M-fを打って、 括弧が対応していない関数定義の末尾を探します。 (そして、C-SPC C-_ C-u C-SPCと打って、 当該箇所にマークを設定して開き括弧の挿入をアンドゥし、 最終的にマークへ戻る。)
その関数定義の先頭でC-M-fと打って、 実際に対応している閉じ括弧を探します。 これにより、関数定義が終るべき場所より手前の箇所に移動するはずです。 この付近に余分な閉じ括弧がみつかることもあるでしょう。
その場所に問題がなければ、つぎにすべきことは、 関数定義の先頭でC-M-qと打つことです。 ある範囲の行が左にずれるでしょう。 もしそうならば、開き括弧が足りないか余分な閉じ括弧は、 そのような行の先頭付近にあるでしょう。 (しかし、これが正しいと仮定してはならない。 コードを調べて確認すること。) 不具合箇所がみつかったならば、 意図した括弧に対しては古い字下げが適しているでしょうから C-_でC-M-qをアンドゥします。
問題を解決できたと思ったら、再度C-M-qを使います。 古い字下げが意図した括弧の入れ子に対応していて、 必要な括弧を挿入できているならば、 C-M-qはなにも変えないはずです。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
バイトコンパイル時にエラーが発生したときは、 通常、読者がコンパイルしているプログラムの不正な構文に原因があります。 コンパイラはバッファ`*Compile-Log*'に適切なエラーメッセージを 表示してから停止します。 メッセージにはエラーとなった関数の名前があったりなかったりします。 いずれにしても、つぎのようにしてファイルのどこでエラーが生じたかを調べます。
まず、バッファ` *Compiler Input*'に切り替えます。 (バッファ名が空白で始まり、そのため、 M-x list-buffersでは表示されないことに注意。) このバッファにはコンパイルしたプログラムが入っていて、 ポイント位置はバイトコンパイラがどこまで読み取ったかを表します。
エラーの原因が不正なLisp構文であるならば、 ポイント位置が不正構文を検出した箇所を正確に表します。 エラー原因が近くにあるとは限りません! エラーを探すために前節の方法を使ってください。
正しく読み取ったフォームのコンパイル中にエラーを検出したときには、 ポイントはそのフォームの末尾に位置しています。 この場合、この方法ではエラー箇所を正確に判別できませんが、 どの関数を確認すべきかを示しています。
[ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |