elisp のデバッグ方法について以下の3つの方法を説明します. - printf デバッグ - backtrace - edebug ■■■ printf デバッグ elisp で printf デバッグを行なうには message 関数を使います.message 関数の結果は *Messages* バッファに出力されます. 例えば以下の<リスト1>のように使います. ---------------- <リスト1> message 関数を使った printf デバッグ (defun message-sample () (let (list) (dotimes (i 10) (push i list) (message "%s" list)))) ---------------- 実行中に目視したい場合は sit-for と message の組み合わせか y-or-n-p を使うのが良いでしょう. - sit-for はキーボード入力があるか,指定した時間まで処理を停止します.message と組合せる事で,一定時間 message の結果を minibuffer に表示できます. - y-or-n-p は y か n を入力するまで操作を止めます. <リスト1>の message 関数を使っている個所を<リスト2>のそれぞれで置き換えてみて下さい. ---------------- <リスト2> sit-for,y-or-n-p を使った printfデバッグ ;; sit-for と message の組み合わせの使い方 (message "%s" list) (sit-for 3) ; 何かキー入力があるか,3秒経つと先に進む ;; y-or-n-pの使い方 (y-or-n-p (format "%s" list)) ; y か n を押すまで処理を止める ---------------- ■■■ backtrace backtrace は Java で言う stacktrace みたいなものです.backtrace を出力するには変数 debug-on-error を t にしておく必要があります. <リスト3>の関数を定義してから実行すると,<リスト4>の backtrace が出力されます. backtrace の先頭行にはエラーの概要が表示され,2行目以降には callstack が表示されます. 今回出力された backtrace を見てみましょう. まず先頭行に (wrong-type-argument number-or-marker-p "20") とあるので,数値が要求された個所に文字列を渡している事がわかります. 2行目に +(-10 "20") とあるので,+ 関数に "20" を渡したためにこのエラーが発生した事がわかります. 3行目に (if (< n 0) (+ n m) (- n m)) とあるので,2行目は if 分の中で呼び出された + 関数だと言う事がわかります.つまり m が文字列な為エラーが発生した事が原因である事が分かりました. ■■ 注意点等 - backtrace モードを抜けるには backtrace バッファで q を押して下さい. - bytecompile された関数の backtrace を取るときは eval-defun でその関数を再定義する必要があります. - backtrace バッファ で e を押すとその時点での変数の値を評価できます. ---------------- <リスト3> 間違った関数 (defun backtrace-sample () (let ((n -10) (m "20")) (if (< n 0) (+ n m) (- n m)))) ---------------- ---------------- <リスト4> backtrace の出力例 Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p "20") +(-10 "20") (if (< n 0) (+ n m) (- n m)) (let ((n -10) (m "20")) (if (< n 0) (+ n m) (- n m))) backtrace-sample() eval((backtrace-sample)) eval-last-sexp-1(t) eval-last-sexp(t) eval-print-last-sexp() call-interactively(eval-print-last-sexp) ---------------- ■■■ edebug elisp でステップ実行をするには edebug を使います. edebug を使うにはブレークポイントを設定したい関数の上で C-u C-M-x (前置引数付きの eval-defun)をします.その後で,その関数が呼び出されると edebug モードに入ります. <リスト5>を一度評価してから,edebug-sample の上で C-u C-M-x をして,M-x edebug-sample として下さい.let の上にカーソルが移動して,画面左側にインジケータが表示されるかと思います. ---------------- <リスト5> edebug のサンプル (defun edebug-sample () (interactive) (let ((i 5)) (message "sample1 %d" i) (edebug-sample2 i))) (defun edebug-sample2 (i) (message "sample2 %d" (+ i 10))) ---------------- ■■ ステップ実行 それではステップ実行してみましょう.ステップ実行をするには SPC を押します. - 一度 SPC を押すと「(message "sample1 %d" i)」の行に移動します. - もう一度 SPC を押すとカーソルが「i」の後ろに移動してミニバッファに「Result: 5 (#o5, #x5, ?\C-e)」と表示されます. - もう一度 SPC を押すとカーソルが「(message "sample1 %d" i)」の後ろに移動してミニバッファに「Result: Result: "sample1 5"」と表示されます. - 同じように繰り返し SPC を押すとカーソルが関数・変数単位で移動してその結果がミニバッファに表示されます. <表1>に edebug のキーバインドを載せておきます。 ---------------- <表1> edebug のキーバインド SPC ステップ実行 g 次のブレークポイントまで実行 c ブレークポイントを表示しながら最後まで実行(C-g で止めるとその位置から再開できる) h カーソル位置まで実行 i 直後の関数に入る b ブレークポイントを張る u ブレークポイントを削除 q edebug を抜ける e 式を評価する(実行中の値が使える) C-h v 変数の値を表示する(実行中の値が見れる) ----------------