| [ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
GNU Emacs は二つのデバッガを持っている。debug と edebug で
ある。最初のものは、Emacs の内部に組み込まれていて、常に使える状態にある。
二番目のものは拡張機能として与えられているもので、version 19 では標準の
配布の中に含まれている。
これらのデバッガは共に、以下の所に非常に詳しく説明されている。 section `Debugging Lisp Program' in The GNU Emacs Lisp Reference Manual. この章では、各々の簡単な例を見ていくことにする。
17.1 debug組み込みデバッガの使い方 17.2 debug-on-entry関数呼び出し時にデバッガを起動する 17.3 debug-on-quitと(debug)C-g を押した時にデバッガを起動する 17.4 ソースレベルのデバッガ edebugソースレベルのデバッガ Edebug 17.5 デバッグについての練習問題
debug
例えばあなたが、1から与えられた数までの和を計算するための関数の定義を書
いたとしよう。(これは以前に説明した triangle 関数である。次の所で
詳しく議論されている。
減少するカウンタを使ったループ.)
しかしながら、あなたの関数にはバグがあったとしよう。例えば `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 と表示さ
れるはずだ。
さて、triangle-bugged を引数 4 とともに評価してみよう。
(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 インタプリタが次に何を評価していったかが分る。
上から三行目は
(setq number (1= number)) |
である。Emacs はこのS式を評価しようとした。そのために、内部のS式を評価 しようとした。それが上から二行目の式である。
(1= number) |
エラーが生じたのは、ここである。一行目にはそう書かれている。
Signalling: (void-function 1=) |
そこでエラーを修正し、再び関数定義を評価して、もう一度テストしてみること になる。
もし、Info でこれを読んでいるなら、ここで下の式を評価して
debug-on-error を nil に戻しておこう。
(setq debug-on-error nil) |
debug-on-entry
ある関数に対して debug を実行する二番目の方法は、関数を呼び出す時
にデバッグに入る方法である。それには 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 の最初のS式を評価する。このときバッファ
は次のようになる。
---------- 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* ----------
|
ここで、ゆっくりと、更に8回 d をタイプしてみよう。d をタイプ するごとに、Emacs は関数定義の中の別のS式を評価していく。結果として、こ のバッファは次のようになる。
---------- 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* ----------
|
最後に、更に 2 回 d をタイプすると Emacs はエラーの箇所まで辿り着 く。その結果 `*Backtrace*' の上から二行は次のようになる。
---------- 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-bugged RET |
(もしこれを Info で読んでいるなら、ここで debug-on-entry を無効に
しておこう。)
debug-on-quit と (debug)
debug-on-error をセットしたり debug-on-entry を呼び出したり
する以外にも二つ程、debug を開始する方法がある。
一つは変数 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 関数は次の中で詳しく説明されている。 section `The Lisp Debugger' in The GNU Emacs Lisp Reference Manual.
edebug Edebug は通常デバッグをしているコードのソースを表示してくれる。そして、 現在どの行を実行しているかを左側の矢印で示してくれる。
関数を一行ごとに実行することも出来るし、実行を止める breakpoint の所まで一気に走らせることも出来る。
Edebug については次の所に説明がある。section `Edebug' in The GNU Emacs Lisp Reference Manual.
次にバグを含んだ triangle-recursively の関数定義を挙げておく。こ
れについて復習したい場合には、次を参照。カウンタの代わりに再帰を使う. この例は、後で説明されている
ように 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 mode か Lisp
Interaction mode でしか動作しない。)
しかしながら、Edebug を使ってこの関数をデバッグする際には、まず最初に別 の関数を使って、コードの 膳立て (instrument) をする必要があ る。Emacs version 19 では関数定義内で次のようにタイプする。
M-x edebug-defun RET |
こうすることで、もし Edebug が Emacs にインストールされていなければ自動
的にインストールし、関数を適切に膳立てする。(Edebug をロードした後は、
Edebug の標準的なキーバインディングが使えるようになる。例えば
edebug-defun を C-u C-M-x (eval-defun の前置引数付き
の実行) で実行出来る。)
Emacs version 18 では自分で Edebug をロードしなければならない。それには
`.emacs' に適切な load コマンドを書いておけばよい。
もしこれを Info の中で読んでいるなら、ここで上の
triangle-recursively-bugged 関数の膳立てをする。その際に
関数定義の defun の行がインデントされていると、
edebug-defun が定義の範囲を特定出来なくなってしまうので、上に挙げ
た例では今まで入れていた defun の左のスペースを除いてある。
関数の膳立てをした後、カーソルを次のS式のすぐ後に持っていって
C-x C-e (eval-last-sexp) とタイプしよう。
(triangle-recursively-bugged 3) |
すると triangle-recursively-bugged のソースに戻ってカーソルがこの
関数内の if の行の最初に移動する。また、`=>' という矢印がそ
の行の左側に表示される。この矢印は関数が実行されている場所を指している。
=>-!-(if (= number 1) |
例の中で、`-!-' はポイントの位置を表している (これは印刷された 本の中では五つの点からなる星印になっている)。
さて、ここで SPC を押すと、ポイントは次に実行されるS式に移動する。 従って、この行が次のようになる。
=>(if -!-(= number 1) |
続けて SPC を押していくと、ポイントはS式からS式へと移動していく。
同時にS式が値を返すごとにその値がエコー領域に表示される。例えばポイント
が number の所を通過すると、次のように表示されるはずだ。
Result: 3 = C-c |
これは number の値が3、つまり三番目の ASCII コードである
ASCII CTL-C であることを意味している。(C はアルファベットの三
番目の文字である。) (訳註:暇な人は (char-to-string 3) 等を評価し
てみよう。)
こうしてエラーのある行まで移動することが出来る。これを表示する直前には、 この行は次のように表示されている。
=> -!-(1= number))))) ; ここでエラー |
ここでもう一度 SPC を押すと次のようなエラーメッセージが表示される。
Symbol's function definition is void: 1= |
これがバグである。
Edebug を終了するには `q' を押せばよい。
この関数定義の膳立てを解くには、 単に膳立てしないようなコマンドでその関 数を評価しなおせばよい。例えば、関数定義の最後の閉じ括弧の後で C-x C-e とタイプするだけでよい。
Edebug は単に関数を一つずつ実行していくだけではなく他にも沢山のことをし てくれる。自動的に関数を実行していってエラーが起きた所で止まるように設定 することも出来るし、特定の場所で止まるようにすることも可能である。様々な S式の値の変化を表示させることも出来るし、ある関数が何回実行されたかも調 べることも出来る。その他にもいろいろなことが出来る。
Edebug については次で説明されている。 section `Edebug' in The GNU Emacs Lisp Reference Manual.
count-words-region 関数をインストールし、それを呼び出した時に内部
デバッガが起動するようにしなさい。二つの単語を含むような region の上でこ
の関数を実行しなさい。d をかなりの回数押す必要があるだろう。あなた
の システムではこのコマンドが終了した時点で何かフックが呼ばれますか?
(フックについての情報は次を参照。 section `Command Loop Overview' in The GNU Emacs Lisp Reference Manual.)
count-words-region を `*scratch*' バッファにコピーして、必要
なら defun の先頭のスペースを取り除いてから、この関数を Edebug の
ために膳立てしなさい。そして、この関数を1ステップずつ実行しなさい。この
場合、必ずしも関数にはバグがある必要はない。バグがあった方がよければバグ
を設定してもよい。もしこの関数にバグがなければ、問題なく最後まで実行出来
るはずだ。
global-edebug-prefix は普通 C-x X、つ
まり CTL-x に続く大文字の X になっている。Edebug の
デバッグバッファの外でのコマンドにはこの前置キーを使うようにする。)
edebug-bounce-point) コマンドを使ってリージョンの何処で
count-words-region が動作しているかを見なさい。
edebug-goto-here) コマンドをタイプすることでその場所にジャンプし
なさい。
edebug-trace-mode) コマンドを使って、Edebug が自分自身で
関数の上を走るようにしなさい。同様に大文字の T を使って
edebug-Trace-fast-mode に入りなさい。
| [ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |