工程師最常遇到的問題之一:為什麼不會動?
跟怎麼會動了?
,落落長的程式碼中,如果前人有好好紀錄log,當然會幫助我們快速縮小debug的時間跟可能出錯的範圍,再來就要透過下中斷點來看原因了,今天就是來學如何使用 Ruby 中斷點神器- byebug。
在gemfile
裡面加上這行
gem 'byebug'
或是在command line 下這行來全域安裝
gem install byebug
如果是Rails 4.2以後,預設在開發模式就已經幫你裝好
安裝好記得要執行bundle install
這裡拿昨天[Day 3] Ruby Memoization的範例 code修改一下來用
我想知道每次執行前的 @cache,可以在fibonacci_with_cache
這個function 第一行下中斷點
class Fibonacci
def fibonacci_with_cache(n)
byebug # => 中斷點在這
if @cache[n]
@cache[n]
else
result =
if n < 2
1
else
fibonacci_with_cache(n - 1) + fibonacci_with_cache(n - 2)
end
@cache[n] = result
end
end
def count_fibonacci_with_cache(n)
@cache = {}
fibonacci_with_cache(n)
end
end
Fibonacci.new.count_fibonacci_with_cache(5)
直接在 cli 輸入 byebug app/services/test2.rb
可以進入byebug模式
這裡使用另一個方法,在Rails console執行
啟動rails console後
pry(main)> load 'app/services/test2.rb'
現在command line長這樣
[1, 10] in /Users/maxhuang/Desktop/stock_position/app/services/example.rb
1: class Fibonacci
2: def fibonacci_with_cache(n)
3: byebug
=> 4: if @cache[n]
5: @cache[n]
6: else
7: result =
8: if n < 2
9: 1
10: else
會停在byebug的下一行,也就是第 4 行,且第 4 行還未執行
輸入 help
或 h
來看有哪些指令可以用:
(byebug) help
continue -- Runs until program ends, hits a breakpoint or reaches a line
edit -- Edits source files
finish -- Runs the program until frame returns
frame -- Moves to a frame in the call stack
help -- Helps you using byebug
history -- Shows byebug's history of commands
info -- Shows several informations about the program being debugged
irb -- Starts an IRB session
kill -- Sends a signal to the current process
list -- Lists lines of source code
next -- Runs one or more lines of code
pry -- Starts a Pry session
quit -- Exits byebug
skip -- Runs until the next breakpoint as long as it is different from the current one
step -- Steps into blocks or methods one or more times
thread -- Commands to manipulate threads
untracevar -- Stops tracing a global variable
up -- Moves to a higher frame in the stack trace
var -- Shows variables and its values
where -- Displays the backtrace
...
(只列出比較有機會用到的)
continue 或 c
繼續目前程式,直到下一個中斷點,若沒有下個中斷點就執行到結束
quit 或 q
離開 byebug 模式
edit
編輯原始檔案的程式碼。
這個功能讓我很驚訝,沒想到可以在程式執行階段直接修改原始碼。
ps.
輸入edit時就進入 vim 的編輯界面,要先按 i 進入編輯模式
修改完畢後,再按 esc 準備跳出,然後:q 直接退出,或:wq儲存後跳出。
畢竟如何從 Vim 逃脫可是Stack over flow的熱門問題(笑~)
next 或 n
執行下一行程式
在不知道錯誤在哪裡的時候,一行行檢視是保險的方法
豆知識:在byebug模式,直接按下 enter,會執行上一個指令,就不用一直輸入n再按 enter
info 或 i
如果下了很多中斷點,或是一直按n,在複雜專案中容易迷路知道自己在哪,這時找 i 就對了
同理,世界各地旅遊迷路了...
也是找 i
就對了(誤)
i file
所在檔案的資訊,產生結果如下:
File /Users/maxhuang/Desktop/stock_position/app/services/example.rb (23 lines)
Breakpoint line numbers: 1 2 3 4 7 8 10 12 14 15 17 18 19 20 21 23
Modification time: 2020-09-19 12:50:32 +0800
Sha1 Signature: 715625e21215995e8462b1c140c99d962e9d868f
i line
所在檔案及行數,產生結果如下:
Line 10 of "/Users/maxhuang/Desktop/stock_position/app/services/example.rb"
因為 byebug 的 cli 沒辦法輸入多行指令
以前想測試多行指令 (ex.宣告一個方法)就只能另外開一個console來測試。
在irb或pry模式,就沒有這個問題,而這兩個模式也都在rails 環境下,可以抓到DB的資料。
ps.如果要離開 irb 或 pry 模式,輸入 exit
, quit
或 ctrl + d
就可以了
9. where 或 w
顯示目前中斷點依序是被哪些程式碼呼叫,數字越小的就越底層
範例如下:
--> #0 Fibonacci.fibonacci_with_cache(n#Integer) at /Users/maxhuang/Desktop/stock_position/app/services/example.rb:4
#1 Fibonacci.count_fibonacci_with_cache(n#Integer) at /Users/maxhuang/Desktop/stock_position/app/services/example.rb:19
#2 <top (required)> at /Users/maxhuang/Desktop/stock_position/app/services/example.rb:23
ͱ-- #3 Kernel.load at /Users/maxhuang/.rvm/gems/ruby-2.5.1/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:70
#4 rescue in Object.rescue in load(path#String, wrap#FalseClass) at /Users/maxhuang/.rvm/gems/ruby-2.5.1/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:70
#5 Object.load(path#String, wrap#FalseClass) at /Users/maxhuang/.rvm/gems/ruby-2.5.1/gems/bootsnap-1.4.6/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:53
#6 Object.irb_binding at /Users/maxhuang/Desktop/stock_position/(irb):3
ͱ-- #7 Kernel.eval(*args) at /Users/maxhuang/.rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/irb/workspace.rb:85
... 下略...
var all -- 顯示所有self的變數
var args -- 顯示目前scope的參數
var const -- 顯示目前常數
var global -- 顯示目前全域變數
var instance -- 顯示self身上的實體變數
var local -- 顯示目前scope的區域變數
ex.
var instance
# => @cache = {1=>1}
display @cache
就會在每個中斷點執行@cache,每次用continue,就可以知道@cache的變化,依序為:
1: @cache = {}
1: @cache = {1=>1}
1: @cache = {1=>1, 0=>1, 2=>2}
eval hsitory
就可以取得history變數,而不是byebug 原生的指令
這也是好用的功能,尤其在迴圈裡面,大推~
比方說我想知道當 n 是14,也就是費波納西數列產生第14個數字前,@cache會長什麼樣子。
在不知道這個功能之前,我只會一直按n,來切換到我想要的遞迴次數,心酸...
現在只要:
def fibonacci_with_cache(n)
byebug if @cache[13] # 給byebug 下條件
if @cache[n]
@cache[n]
else
result =
if n < 2
1
else
fibonacci_with_cache(n - 1) + fibonacci_with_cache(n - 2)
end
@cache[n] = result
end
end
fibonacci_with_cache(14)
(byebug) @cache
# => {1=>1, 0=>1, 2=>2, 3=>3, 4=>5, 5=>8, 6=>13, 7=>21, 8=>34, 9=>55, 10=>89, 11=>144, 12=>233, 13=>377}
學會 byebug,包管你debug 快又準