byebugのガイドをおおざっぱになぞってみる(1)
GUIDE.mdに沿って実際に動かしてみて、自分で自分に説明してみた記録。翻訳ではない。(というか英語的にどうかって言われると自信がない。)
Byebugのバージョンは9.0.6、Rubyのバージョンは2.4.0p0。
GUIDE.mdの最初のサンプルコードについて、元のコードではデバッガの説明の入り口としては少し分かりにくいと思ったので以下の変更を加えている。
# もともとのコード
0.upto(n) {|i| tri += i }
# 変更したコード
0.upto(n) do |i|
tri += i
end
Introduction
First Steps
最初のサンプルとして以下の内容のtriangle.rbを作る。
#
# 三角数: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n
#
def triangle(n)
tri = 0
0.upto(n) do |i|
tri += i
end
tri
end
t = triangle(5)
puts t
byebug
コマンドで実行する。
$ byebug triangle.rb
[1, 10] in /private/tmp/triangle.rb
1: #
2: # 三角数: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n
3: #
=> 4: def triangle(n)
5: tri = 0
6:
7: 0.upto(n) do |i|
8: tri += i
9: end
10:
(byebug)
byebug
コマンドは、最初に実行される行の前でプログラムをストップさせる。この例では4行目の前がその場所であり、=>
によって次に実行される行を示している。
メソッド定義(def
)の前でストップしたのは、Rubyが動的なプログラミング言語だからだ。
プログラムがストップすると、その場所の前後を含めた10行分を表示し、続いて命令待ちのプロンプトを表示する。(byebug)
がそれだ。
byebug
コマンドをpost-mortemモード(-m
オプション)で実行していて、例外をキャッチしたときには、プロンプトが(byebug:post_mortem)
になる。--no-quit
オプション付きで実行していれば、プログラムが終了した後にも命令待ちとなり、(byebug:ctrl)
というプロンプトを表示する。
(メモ: --no-stop
オプションを指定すると最初の行でストップせず、そのまま実行を続ける。プログラム中でbyebug
メソッドを呼び出すなど、明示的にストップする要因がなければそのまま終了する。)
プログラムを一行ずつ実行してみよう。
(byebug) step
[6, 15] in /private/tmp/triangle.rb
6:
7: 0.upto(n) do |i|
8: tri += i
9: end
10:
11: tri
12: end
13:
=> 14: t = triangle(5)
15: puts t
step
命令を使うと一行分だけプログラムを進められる。
この例では4行目のdef
が実行され、次に実行される14行目の前でストップしている。
(byebug) # ここでリターンキーをたたく
[1, 10] in /private/tmp/triangle.rb
1: #
2: # 三角数: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n
3: #
4: def triangle(n)
=> 5: tri = 0
6:
7: 0.upto(n) do |i|
8: tri += i
9: end
10:
何も入力せず、リターンキーだけたたくと、直前の命令をもう一度繰り返すことができる。
ここではstep
命令が繰り返され、14行目が実行される。そして次に実行される5行目の前でストップする。
(byebug) eval tri
nil
eval
を使うと変数の値を表示できる。
eval tri
は変数tri
の値を表示するものだが、この時点ではtri = 0
の前でストップしているため、その値はnil
となる。
(byebug) step
[2, 11] in /private/tmp/triangle.rb
2: # 三角数: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n
3: #
4: def triangle(n)
5: tri = 0
6:
=> 7: 0.upto(n) do |i|
8: tri += i
9: end
10:
11: tri
(byebug) eval tri
0
さらにstep
命令によりプログラムを進めると、tri = 0
が実行される。再度eval
を使うと今度は0
が表示される。
ストップするたびにtri
の値を表示して、どんな具合いに変化するかを見たければdisplay
命令を使うとよい。
(byebug) display tri
1: tri = 0
step
命令を使ってプログラムを進めてみよう。
(byebug) step
1: tri = 0
[3, 12] in /private/tmp/triangle.rb
3: #
4: def triangle(n)
5: tri = 0
6:
7: 0.upto(n) do |i|
=> 8: tri += i
9: end
10:
11: tri
12: end
一行分進んでブロックの中身の前でストップ。この時点ではまだtri
には何の変化もない。
さらにプログラムを進めてみる。
(byebug)
1: tri = 0
[3, 12] in /private/tmp/triangle.rb
3: #
4: def triangle(n)
5: tri = 0
6:
7: 0.upto(n) do |i|
=> 8: tri += i
9: end
10:
11: tri
12: end
(byebug)
1: tri = 1
[3, 12] in /private/tmp/triangle.rb
3: #
4: def triangle(n)
5: tri = 0
6:
7: 0.upto(n) do |i|
=> 8: tri += i
9: end
10:
11: tri
12: end
tri
の値が、ループ一回目の実行で0
、二回目の実行で1
、と変化しているのが分かる。
この後どのようにプログラムが進み、tri
がどう変化していくか一息に見てみたい。
具体的には、このループを抜けたところまでいちいち表示を止めず(プロンプトを出さず)実行させる。それにはfinish
命令が使える。
メソッドやブロックはいくつかの呼び出し階層を作ることがある。finish
命令を使うと、そうした階層を引数で指定した数だけ抜け出すまでプログラムを進めることができる。階層数を指定しなければ、現在の階層を抜ける。これは引数に1
を指定したのと同じ動作になる。(0
を指定すると、現在の階層を抜ける前まで実行が進む。)
現在、ストップしているのはトップ→triangle
→0.upto(n)
→ブロック(ループの中身)と数えていくと四階層目になる。プログラムを進めたいのはuptoループを抜けたところまでだから、ブロックと、0.upto(n)
の二階層分。finish 2
と入力すればよい。
ただ一息にプログラムを進めると、途中の経過が分からなくなってしまう。そこでlinetrace
オプションを設定する。
linetrace
オプションが設定していると、プログラムをどこまで進めたかを示すために、実行中のファイル名と進んだ先の行が表示される。(=>
と同様の表示) また、display
命令に従った表示も行われる。ここではtri
の値を表示させているため、プログラムの進行に応じたtri
の変化を見て取れる。
ついでに表示を少しばかりシンプルにするためにbasename
オプションを指定して、ファイル名の表示からディレクトリを除くことにする。(指定しなければフルパスで表示される。)
(byebug) set linetrace
linetrace is on
(byebug) set basename
basename is on
それではプログラムを進めよう。
(byebug) finish 2
Tracing: triangle.rb:8 tri += i
1: tri = 3
Tracing: triangle.rb:8 tri += i
1: tri = 6
Tracing: triangle.rb:8 tri += i
1: tri = 10
Tracing: triangle.rb:11 tri
1: tri = 15
1: tri = 15
[6, 15] in /private/tmp/triangle.rb
6:
7: 0.upto(n) do |i|
8: tri += i
9: end
10:
=> 11: tri
12: end
13:
14: t = triangle(5)
15: puts t
0.upto(n)
の実行を終え、次の11行目、tri
だけの行の直前でストップしている。
以上で最初のサンプルは終了。quit
命令を使用してbyebug
コマンドを終了させる。
(byebug) quit
Really quit? (y/n) y
quit
を略してq
でも同じく終了させられる。終了の確認(Really quit? (y/n)
)が必要なければ、命令に!
を付けてquit!
あるいはq!
と入力すると確認なしで終了させられる。
(メモ: Byebug.tracing
がtrue
の間、Byebug.commands.select {|cmd| cmd.always_run >= 2 }
で得られる命令が進むごとに実行される。また、ストップするごとに同じくcmd.always_run >= 1
の命令が実行される。対象になるのはByebug::DisplayCommandとByebug::ListCommandで、それぞれalways_run
が2と1、display
命令とlist
命令に対応する。1: tri = 15
の表示が重複しているのは、linetrace
によるものと、ストップによるものの二回行われたため。なお、list
命令は実行中のコード表示をする。)