glider-gun's Blog

何か書きます

Debugging Lisp Part 5: その他こまごま

このエントリーは、著者の許可をいただいて http://malisper.me/category/debugging-common-lisp/ のCommon Lispのデバッグに関する連載を翻訳するものです。

目次: 第1回 第2回 第3回 第4回 第5回


このエントリーは、それだけで記事にするほど大きくはないような細かな機能についてです。これまで再コンパイルインスペクトクラスの再定義リスタートを扱いました。

あまり有名でない機能として、 SBCL の trace があります。SBCL のトレース機能はほとんどの Common Lisp 実装のそれに比べはるかに高機能です。SBCL の trace はいくつかのキーワード引数1を(訳注: Common Lispの仕様で決まっている他にも)追加で対応しています。例えば trace はキーワード変数 :break をとります。 :break の値として渡された式はトレース中の関数が呼ばれるたびに評価されます。そしてその式の値が真であるときにデバッガが呼び出されます。例えば次のようなフィボナッチ関数があったとしましょう:

1
2
3
4
5
(defun fib (n)
  (if (<= 0 n 1)
      n
      (+ (fib (- n 1))
         (fib (- n 2)))))

trace を使って、 fib が引数ゼロを渡されて呼び出されたときブレークするようにできます:

式の中で関数に渡された引数を参照したいのでちょっとしたひねりが必要になっています。 他にもtrace:break の変種いくつかに対応しています。例えば :break-after はトレースされた関数が呼ばれる前ではなく呼ばれた後に式を評価するものです。 :print:print-after は break に似ていますが、デバッガに入らず単に式の値をプリントします。:print-after を使って、例えば fib が実行から帰るたびに時刻(unix time)をプリントすることができます:

trace の取れる引数の完全なリストを見たければ、 SBCL マニュアルのこのページを見てみてください。

他にあまり知られていない機能は、相互参照(cross reference)コマンド群です。相互参照コマンドは、あるものが参照されているような場所をすべて見つけるコマンドです。これらのコマンドのキーバインディングはすべて C-c C-w で始まっています2。私がもっともよく使う相互参照コマンドは “slime-who-calls” (C-c C-w C-c にバインドされています)で、これは関数が呼ばれている全ての場所を表示します。次の図は scan 関数が cl-ppcre というライブラリの中のどこで呼ばれているか見つけて、その中をスクロールしているところです3:

slime-who-calls は、関数がどのように使われることを想定しているか見るのに便利です。全ての用例を引っ張ってくることができるので、それを眺めるのです。slice-who-call にもいくらかの親戚がいます。 slime-who-macroexpands (C-c C-w RET) はマクロが使われている全ての場所を表示するもので、 slime-who-references (C-c C-w C-r) はそれの変数用バージョンです。

もうひとつ大事な機能として、デバッガの中にいるとき、あるスタックに対応する関数のソースコードを引っ張ってくるものがあります。それを使うひとつの方法は、ソースが見たいスタックにカーソルをもっていって ‘v’ キーを押すことです。もしくは M-p ( Alt キーと ‘p’ キーを同時に押す) と M-n を使ってスタックフレーム中を上下に動くという方法もあります。C-p C-n のかわりにこの2つのコマンドを使うと、 Slime は自動的に対応するソースコードを表示します。次の図は cl-ppcre に正しくない正規表現を渡して、エラーでデバッガに入り、スタックの中を M-n でたどっているところです:

そして IDE のコマンドで一番おなじみの機能、ソースへのジャンプです。最近私が話した人などは、IDE の機能は定義を簡単に探す機能しか使っていないと言っていました。Emacs で Slime を使うと、ほとんど何の定義でも “M-.” (Escキーの後にピリオドキー(訳注: あるいはAlt+ピリオドキー)) で飛ぶことができます。関数、変数、クラス、その他いろいろなもので動作します。総称関数 generic function の定義に飛ぼうとすると、それを実際に定義する全てのメソッド定義が表示されます。例えば create-matcher-aux (これは cl-ppcre ライブラリのほとんどの仕事を行っている関数です) の定義に飛ぼうとすると、図のようになります:

元の場所に戻るには “M-,” を使います。

そしてこれが、 Common Lisp のデバッグであなたが必要になるだろう知識のすべてです。

原文: http://malisper.me/2015/08/19/debugging-lisp-part-5-miscellaneous/


  1. キーワード引数というのは名前がついたオプション引数です。キーワード引数を使うには、その名前と使いたい値を並べて関数に渡します。キーワード引数を使うと、関数は複数のオプション引数を持ったうえでその好きな一部分だけを受け取れるようになります。

  2. C-c は Slime 自体が使っているバインディングです。 C-w は “who(誰が)” の意味で、すべての相互参照コマンドがここに割り当てられています。

  3. マクロの使われている場所の中で、展開形の中でその関数が使われているような場所もすべて引っ張ってきます。

Comments