コマンドラインでデバッガを使ってみるRuby には、標準でデバッガが付いています。デバッガは Ruby で記述されています。 $ ruby -r debug filename.rb このコマンドを実行すると、filename.rb のデバッグ、という意味になります。 では、下記のスクリプトを test.rb として保存し、デバッガで起動してみましょう。
下記のような画面になるはずです。 Debug.rb Emacs support available. test.rb:3:for i in 1..1000 (rdb:1) _ ← コマンド入力待ちのプロンプト プロンプトが、test.rb の3行目にある 「for i in 1..1000」の行の実行直前であることを示しています。つまり、このスクリプトでいちばん始めに解釈されるのは3行目、ということになります。3行目がぴんとこない場合は、「list num」(num は任意の数字) を入れると、前後5行分のソースコードを表示することができます。(list には省略形 l があるので、l num としても OK です。l は L の小文字なのですが、数字のいち(1) とまぎらわしいのであえて list にしてます) test.rb:3:for i in 1..1000 (rdb:1) list 3[Enter] [-2, 7] in test.rb 1 2 => 3 for i in 1..1000 4 j = i * i 5 printf("%04d^2 = %07d / ", i, j) 6 7 if(i % 4 == 0) then (rdb:1) _ → (=> 表記) で現在行が表示されていて、ぐっとわかりやすくなりました。さて、プログラムを1行ずつ実行してみましょう。プログラムを1行ずつ実行するには、step あるいは s (step の省略形) と入力して、Enter キーです。step を繰り返せば、それだけプログラムが進みます。 (rdb:1) s [Enter] test.rb:4: j = i * i (rdb:1) s [Enter] test.rb:5: printf("%04d^2 = %07d / ", i, j) (rdb:1) s [Enter] 0001^2 = 0000001 / test.rb:7: if(i % 4 == 0) then (rdb:1) s [Enter] test.rb:7: if(i % 4 == 0) then (rdb:1) s [Enter] test.rb:7: if(i % 4 == 0) then (rdb:1) s [Enter] test.rb:4: j = i * i (rdb:1) _ ちょっと注意したいのは5行目でしょうか。画面出力が伴うと、デバッガ表示が行末や次の行以降にずれてしまうことがあります。「0001^2 = 0000001 / test.rb:7: if(i % 4 == 0) then」の前半部分はスクリプト側のprintfが、後半部分の test.rb:7: 以降はデバッガが表示してるというわけですね。 その後、7行目の if が3回表示されます。これは、「i%4」で1回、 i%4 と 0 の比較(true/false に変換)で1回、if 分岐で1回とカウントされているから、と考えられます。そして、次は4行目に戻ったということがわかります。考えてみれば、i は初期値 1 で、1 % 4 はゼロではないので、8 と 9 行目はスキップされ、10行目は for 繰り返しのブロック終了になるので、繰り返しの最初に戻った、ということですね。 s を何回か繰り返していけば、8行目の実行の瞬間に立ち会うことができるでしょう。けれど、ちょっと待ってください。s よりももっと便利なコマンド、break (省略形 b)と cont (省略形 c) というコマンドがあるんです。break というのはブレークポイントのことで、プログラムの実行を指定行で中断させることができます。そして、cont がブレークポイントにぶつかるまで一気にプログラムをすすめるコマンドです。では、そのブレークポイントというのをまずはしかけてみましょう。 (rdb:1) list 1-10 [Enter] [1, 10] in test.rb 1 2 3 for i in 1..1000 => 4 j = i * i 5 printf("%04d^2 = %07d / ", i, j) 6 7 if(i % 4 == 0) then 8 printf("\n") 9 end 10 end (rdb:1) b 8 [Enter] Set breakpoint 1 at test.rb:8 (rdb:1) b [Enter] Breakpoints: 1 test.rb:8 (rdb:1) _ list 1-10 で、1~10行目のコードを表示し、ブレークポイントをしかけたい場所(if分岐の中身)が確かに8行目であることを再確認してます。そして、b 8 で8行目をブレークポイントとする、と指定してます。最後の b (数字無し) は、今までにしかけたブレークポイントを一覧表示するコマンドです。ちゃんと8行目にブレークポイントがしかけられていることを確認できます。では、if 分岐の中に入るまで、プログラムを一気に進めましょう。c コマンドを打ち込んでみてください。 (rdb:1) c [Enter] 0002^2 = 0000004 / 0003^2 = 0000009 / 0004^2 = 0000016 / Breakpoint 1, toplevel at test.rb:8 test.rb:8: printf("\n") (rdb:1) _ ちゃんと8行目の実行直前で一時停止状態になりましたね。list すれば現在位置が if の中、ということを再確認できます。printf の表示によると、i は 4 で j は 16 ですね。 i は 4 で、4 % 4 はゼロですので if の中に入ってきましたよ、と。では、i や j は本当に printf の通りの数値なのでしょうか? 変数については、p 変数名 で表示が可能です。もっとも、変数はオブジェクトですから、実際にはクラスやメソッドの返値に応用することもできます。 (rdb:1) p i [Enter] 4 (rdb:1) p j [Enter] 16 (rdb:1) _ 確かに、変数は予想通りですね。もっとも、この方法だと、変数が十数個あったりしたときに大変ですので、もっと簡単に一覧表示する方法があります。それが、var local (省略名 v l) です。 (rdb:1) v l [Enter] i => 4 j => 16 (rdb:1) _ puts や printf をしないでも変数をいつでも確認できるようになると、「j が 1000 を超える瞬間はいつなのかな」といったようなことを調べてみたくなるかもしれません。j が 100 になるのは i が 10 のとき (10*10) と思い浮かびますが、このようなケースの場合は暗算ですぐに出るものではありません。そして、プログラムのデバッグでは暗算や電卓では推し量れないことも、また多いのです。このようなケースの場合は、watch 条件式 (省略形は wat 条件式)という、「行番号ではないもうひとつのブレークポイント(ウォッチポイント と言うそうです)」を仕掛けることにより解決することができます。今回の場合は、wat (j > 1000) というコマンドを実行すればいいことになります。 (rdb:1) wat (j > 1000) [Enter] Set watchpoint 2:(j > 1000) (rdb:1) _ では、c と v l で、いざ 1000 超えの世界へ...おや? (rdb:1) c [Enter] 0005^2 = 0000025 / 0006^2 = 0000036 / 0007^2 = 0000049 / 0008^2 = 0000064 / Brea kpoint 1, toplevel at test.rb:8 test.rb:8: printf("\n") (rdb:1) v l [Enter] i => 8 j => 64 (rdb:1) _ j が 64 なのに止まってしまいました。なぜなんでしょう?ちょっと考えてみましょう。 犯人は、b コマンドでわかりますよ。 (rdb:1) b [Enter] Breakpoints: 1 test.rb:8 Watchpoints: 2 (j > 1000) (rdb:1) _ これを見ると、ウォッチポイントもブレークポイントの仲間扱いされていることがわかりますが、注目なのは 1 test.rb:8 です。初めてしかけたブレークポイントですね。そして、c コマンドはブレークポイントにぶつかったら実行中断してしまいます。j > 1000 よりも前に、8行目に入ってくる機会がたくさんあるので、次の i % 4 == 0 である i は 8 のときにブレークポイントに捕まってしまった、というわけです。でも、このままじゃあ j > 1000 にくるまでに何回8行目で止まるか検討がつかないですよね。ということで、最初のブレークポイントを削除してしまいましょう。ブレークポイントの削除は delete num (省略形 del num) と指定します。注意したい点はふたつあって、ひとつは num には行数ではなく、b でリストアップされた一番左側の番号を指定すると言うことがまずひとつ、もう一つは数字を指定し忘れると「全ブレークポイント削除」になってしまう、ということです。では、8行目指定のブレークポイントを削除(del 1) して、続きを実行(c) してみましょうか。 (rdb:1) del 1 [Enter] (rdb:1) b [Enter] Breakpoints: Watchpoints: 2 (j > 1000) (rdb:1) c [Enter] 0009^2 = 0000081 / 0010^2 = 0000100 / 0011^2 = 0000121 / 0012^2 = 0000144 / 0013^2 = 0000169 / 0014^2 = 0000196 / 0015^2 = 0000225 / 0016^2 = 0000256 / 0017^2 = 0000289 / 0018^2 = 0000324 / 0019^2 = 0000361 / 0020^2 = 0000400 / 0021^2 = 0000441 / 0022^2 = 0000484 / 0023^2 = 0000529 / 0024^2 = 0000576 / 0025^2 = 0000625 / 0026^2 = 0000676 / 0027^2 = 0000729 / 0028^2 = 0000784 / 0029^2 = 0000841 / 0030^2 = 0000900 / 0031^2 = 0000961 / Watchpoint 2, toplevel at test.rb:5 test.rb:5: printf("%04d^2 = %07d / ", i, j) (rdb:1) v l [Enter] i => 32 j => 1024 (rdb:1) list [Enter] [0, 9] in test.rb 1 2 3 for i in 1..1000 4 j = i * i => 5 printf("%04d^2 = %07d / ", i, j) 6 7 if(i % 4 == 0) then 8 printf("\n") 9 end (rdb:1) _ 以上の結果から、i が 32 になって、j に 32*32 が代入された直後 (j が 1024 になった)に一時停止になったことを確認できます。 さて、デバッグは終了して別のことをやりたくなってきました。そんなときは quit (省略形 q) コマンドを実行すれば、y/n の確認を経てデバッグ終了できます。 (rdb:1) q [Enter] Really quit? (y/n) y [Enter] コマンドラインのデバッガのヘルプを見るコマンドの使い方をちょっと調べたいとき、help (省略形 h) というコマンドの実行により、簡易リファレンスが表示されます。ここでは踏み込んで説明しなかった「グローバル変数の表示」から、「スレッドのデバッグ」「フレームの位置確認や上下移動」といったコマンドの存在も確認できますね。 (rdb:1) h [Enter] Debugger help v.-0.002b Commands b[reak] [file|class:]<line|method> b[reak] [class.]<line|method> set breakpoint to some position wat[ch] <expression> set watchpoint to some expression cat[ch] <an Exception> set catchpoint to an exception b[reak] list breakpoints cat[ch] show catchpoint : : (表示中略) : th[read] stop <nnn> stop thread nnn th[read] resume <nnn> resume thread nnn p expression evaluate expression and print its value h[elp] print this help <everything else> evaluate (rdb:1) _ 英語は苦手で...という方は、せっかくの国産スクリプト言語なので、日本語ドキュメントを活用しちゃいましょう。ちゃんと Rubyリファレンスマニュアルのdebug項 にコマンドの一覧と詳細が掲載されていますので。 簡易リファレンスとりあえず、簡単にまとめてみました。クラスやスレッドを使いこなすプログラミングをする場合は、ここにリストアップしていない thread や method や var instance <object> 等のコマンドを使わなければ行けなくなるかもしれません。とりあえず、最初のうちは quit list break cont step p を覚えておけば、あまり苦労しないかなと思います。
その他、参考になりそうな情報
|