| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
GNU Emacsには2つのデバッガ、debugとedebugがある。 最初のものは、Emacs内部に組み込まれていていつでも使える。 2番目のものはEmacsに対する拡張であり、 第19版で標準ディストリビューションの一部になった。
どちらのデバッガも節 `Debugging Lisp Programs' in
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
debug
debug"へのコメント(無し)
1から指定した数までの総和を求める関数定義を書いたとしよう (まえに説明した関数triangleである。 説明は、See 節 減少カウンタの例)。
しかし、関数定義にはバグがある。 `1-'を`1='とタイプミスした。 まちがった定義はつぎのとおりである。
(defun triangle-bugged (number)
"Return sum of numbers 1 through NUMBER inclusive."
(let ((total 0))
(while (> number 0)
(setq total (+ total number))
(setq number (1= number))) ; ここがエラー
total))
|
Infoで読んでいる場合には、いつものようにこの定義を評価できる。 エコー領域にtriangle-buggedと表示される。
引数4で関数triangle-buggedを評価してみる。
(triangle-bugged 4) |
つぎのようなエラーメッセージが出る。 Symbol's function definition is void: 1= |
実際、このような単純なバグならば、このエラーメッセージから 定義を修正するために必要なことがらがわかる。 しかし、何が起こっているのかわからなかったとしよう。
debug-on-errorの値をtに設定してデバッガを有効にする。
(setq debug-on-error t) |
これは、Emacsにつぎにエラーを検出したらデバッガに入るように指示する。
debug-on-errorにnilを設定すればデバッガを無効にできる。
(setq debug-on-error nil) |
debug-on-errorにtを設定し、つぎの式を評価する。
(triangle-bugged 4) |
こんどは、Emacsは、つぎのようなバッファ`*Backtrace*'を作成する。
---------- Buffer: *Backtrace* ----------
Signalling: (void-function 1=)
(1= number))
(setq number (1= number)))
(while (> number 0) (setq total (+ total number))
(setq number (1= number))))
(let ((total 0)) (while (> number 0) (setq total ...)
(setq number ...)) total))
triangle-bugged(4)
eval((triangle-bugged 4))
eval-last-sexp(nil)
* call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------
|
(この例は少々整形してある。 デバッガは長い行を折り畳まない。)
バッファ`*Backtrace*'は、下から上へ向けて読む。 Emacsが何を行ってエラーに至ったかを教えてくれる。 この場合は、EmacsはC-x C-e(eval-last-sexp)を対話的に呼び出し、 これはtriangle-bugged式の評価につながった。 各行はLispインタープリタが、つぎに何を評価したのかを教えてくれる。
バッファの先頭から3行目はつぎのとおりである。
(setq number (1= number)) |
Emacsはこの式を評価しようとした。 そのために、先頭から2行目に示されたもっとも内側の式を評価しようとした。
(1= number) |
これがエラーを起こした場所であり、 先頭の行にはつぎのように表示されている。
Signalling: (void-function 1=) |
この誤りを修正して、関数定義を再評価し、再度試せばよい。
Infoで読んでいる場合には、 debug-on-errorにnilを設定してデバッガを無効にする。
(setq debug-on-error nil) |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
debug-on-entry
debug-on-entry"へのコメント(無し)
関数に対してdebugを起動する2番目の方法は、 関数を呼び出したときにデバッガに入ることである。 これにはdebug-on-entryを呼ぶ。
つぎのようにタイプする。
M-x debug-on-entry RET triangle-bugged RET |
続いて、つぎの式を評価する。
(triangle-bugged 5) |
Emacsはバッファ`*Backtrace*'を作成し、 関数triangle-buggedを評価し始めたことを伝える。
---------- Buffer: *Backtrace* ---------- Entering: * triangle-bugged(5) eval((triangle-bugged 5)) eval-last-sexp(nil) * call-interactively(eval-last-sexp) ---------- Buffer: *Backtrace* ---------- |
バッファ`*Backtrace*'の中で、dとタイプする。 Emacsはtriangle-buggedの最初の式を評価し、 バッファはつぎのようになる。
---------- Buffer: *Backtrace* ----------
Beginning evaluation of function call form:
* (let ((total 0)) (while (> number 0) (setq total ...)
(setq number ...)) total))
triangle-bugged(5)
* eval((triangle-bugged 5))
eval-last-sexp(nil)
* call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------
|
さらにdをゆっくりと8回タイプする。 dをタイプするたびに、Emacsは関数定義内の別の式を評価する。 最終的に、バッファはつぎのようになる。
---------- Buffer: *Backtrace* ----------
Beginning evaluation of function call form:
* (setq number (1= number)))
* (while (> number 0) (setq total (+ total number))
(setq number (1= number))))
* (let ((total 0)) (while (> number 0)
(setq total ...) (setq number ...)) total))
triangle-bugged(5)
* eval((triangle-bugged 5))
eval-last-sexp(nil)
* call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------
|
最後にさらにdを2回タイプすると、 Emacsはエラーに到達し、バッファ`*Backtrace*'の先頭の2行には つぎのように表示される。
---------- Buffer: *Backtrace* ---------- Signalling: (void-function 1=) * (1= number)) ... ---------- Buffer: *Backtrace* ---------- |
dをタイプすることで、関数を順を追って実行することができるのである。
バッファ`*Backtrace*'を終えるには、qとタイプする。 これは、関数の追跡を止めるが、debug-on-entryを取り消さない。
debug-on-entryの効果を無効にするには、 つぎのように、cancel-debug-on-entryを呼んで関数名を与える。
M-x cancel-debug-on-entry RET triangle-debugged RET |
(Infoで読んでいる場合には、ここでdebug-on-entryを取り消す。)
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
debug-on-quitと(debug)
debug-on-quitと(debug)"へのコメント(無し)
debug-on-errorを設定したりdebug-on-entryを呼んだりすることに 加えて、debugを起動するには別に2つの方法がある。
変数debug-on-quitをtに設定すると、 C-g(keyboard-quit)とタイプすると いつでもdebugを起動できる。 これは、無限ループのデバッグに役立つ。
あるいは、つぎのように、コード内のデバッガを起動したい場所に(debug)を 入れておくことである。
(defun triangle-bugged (number)
"Return sum of numbers 1 through NUMBER inclusive."
(let ((total 0))
(while (> number 0)
(setq total (+ total number))
(debug) ; デバッガを起動
(setq number (1= number))) ; ここがエラー
total))
|
関数debugは、節 `The Lisp Debugger' in
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
edebug
edebug"へのコメント(無し)
Edebugはデバッグ中のソースコードを表示して、 現在評価中の行の左端に矢印を表示する。
関数の実行を行ごとに制御したり、実行を停止する ブレークポイント(breakpoint)まで素早く実行させたりできる。
Edebugは、節 `Edebug' in
バグのあるtriangle-recursivelyの関数定義をつぎに示す。 復習するには、See 節 11.2.2 カウンタの代用としての再帰。 以下で説明するが、この例ではdefunの字下げを書いてない。
(defun triangle-recursively-bugged (number)
"Return sum of numbers 1 through NUMBER inclusive.
Uses recursion."
(if (= number 1)
1
(+ number
(triangle-recursively-bugged
(1= number))))) ; ここがエラー
|
この定義をインストールするには、普通は、関数の閉じ括弧の直後にカーソルを 置いてC-x C-e(eval-last-sexp)とタイプするか、 あるいは、定義の途中にカーソルを置いてC-M-x(eval-defun) とタイプする (デフォルトでは、コマンドeval-defunは、Emacs Lispモードか Lisp Interactiveモードのみで使える)。
しかし、この関数定義をEdebugで使うには準備する必要があり、 別のコマンドを使ってコードを処置する必要がある。 Emacs第19版では、定義の途中にカーソルを置いてつぎのようにタイプする。
M-x edebug-defun RET |
これにより、Emacsは必要ならばEdebugを自動的にロードし、 関数を正しく処置する (Edebugをロードしたあとは、C-u C-M-x (前置引数を指定したeval-defun)などの標準的なキーバインドで edebug-defunを呼び出せる)。
Emacs第18版では、読者自身がEdebugをロードする必要がある。 `.emacs'ファイルに適切なloadコマンドを入れておく。
Infoで読んでいる場合には、上に示した関数triangle-recursively-buggedを 処置できる。 edebug-defunは、defunの行が字下げされていると 定義の切れ目を検出できない。 そのために、例ではdefunの左側の空白を取ってある。
関数を処置したら、つぎの式の直後にカーソルを置いて C-x C-e(eval-last-sexp)とタイプする。
(triangle-recursively-bugged 3) |
triangle-recursively-buggedのソースに戻り、 カーソルが関数のifの行の先頭に位置付けされる。 さらに、その行の左端に`=>'のような矢印が表示される。 矢印は、関数のどの行を実行中かを示す。
=>-!-(if (= number 1) |
In the example, the location of point is displayed as `-!-' (in a printed book, it is displayed with a five pointed star).
SPCを押すと、ポイントはつぎに実行すべき式へ移動し、 つぎのようになる。
=>(if -!-(= number 1) |
SPCを押し続けると、ポイントは式のあいだを移動する。 同時に、式が値を返すと、その値がエコー領域に表示される。 たとえば、numberのあとにポイントが移動したときには、 つぎのように表示される。
Result: 3 = C-c |
これは、numberの値は3であり、ASCIIコードではCTL-C (アルファベットの3番目の文字)であることを意味する。
エラーに達するまで、コードの中を移動できる。 評価するまえには、その行はつぎのようである。
=> -!-(1= number))))) ; ここがエラー |
もう一度SPCを押すと、つぎのようなエラーメッセージが表示される。
Symbol's function definition is void: 1= |
これがバグである。
`q'を押してEdebugを終了する。
処置した関数定義を削除するには、 単に処置しない普通のコマンドで関数定義を再評価すればよい。 たとえば、関数定義の閉じ括弧の直後にカーソルを 移動してC-x C-eとタイプする。
Edebugでは、関数の中を動き廻るよりも多くのことができる。 エラーを検出したり指定した停止位置に達した場合だけ停止するとか、 さまざまな式の値の変化を表示したり、 関数が何回呼ばれたかを調べたりなどである。
Edebugは節 `Edebug' in
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
count-words-regionをインストールし、 これを呼び出すと組み込みのデバッガを起動するようにせよ。 2つの単語を含んだリージョンに対してこのコマンドを実行せよ。 dを何度も押す必要がある。 読者のシステムでは、コマンドが終了したあとに「フック」が呼ばれるか? (フックについて詳しくは、節 `Command Loop Overview' in
count-words-regionをバッファ`*scratch*'にコピーし、 defunの行のまえの空白を取り去ってから、 Edebug用に関数を処置し、実行を追ってみよ。 この関数にはバグはないはずであるが、必要ならばバグを入れてみよ。 関数にバグがなければ、何の問題にも出会わずに実行を完了する。
global-edebug-prefixは、普通、C-x Xである。 つまり、CTL-xに続けて大文字のXである。 Edebugのバッファ以外でこのキー列を試してみよ)。
count-words-regionがバッファのどのリージョンで 動作しているかをコマンドp(edebug-bounce-point)を 使って調べてみよ。
edebug-goto-here)と タイプしてその場所へジャンプしてみよ。
edebug-trace-mode)コマンドを使ってみよ。 edebug-Trace-fast-modeを使うには大文字のTを使う。
| [ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |