iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 4
0
Modern Web

續說 Ruby on Rails系列 第 4

[Day 4] debug 小幫手 - byebug

前言

工程師最常遇到的問題之一:為什麼不會動?怎麼會動了?,落落長的程式碼中,如果前人有好好紀錄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 第一行下中斷點

檔案位置: app/service/example.rb
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 行還未執行
輸入 helph 來看有哪些指令可以用:

(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
  ...

(只列出比較有機會用到的)

指令解釋:

  1. continue 或 c
    繼續目前程式,直到下一個中斷點,若沒有下個中斷點就執行到結束

  2. quit 或 q
    離開 byebug 模式

  3. edit
    編輯原始檔案的程式碼。
    這個功能讓我很驚訝,沒想到可以在程式執行階段直接修改原始碼。
    ps.
    輸入edit時就進入 vim 的編輯界面,要先按 i 進入編輯模式
    修改完畢後,再按 esc 準備跳出,然後:q 直接退出,或:wq儲存後跳出。
    畢竟如何從 Vim 逃脫可是Stack over flow的熱門問題(笑~)

  4. next 或 n
    執行下一行程式
    在不知道錯誤在哪裡的時候,一行行檢視是保險的方法
    豆知識:在byebug模式,直接按下 enter,會執行上一個指令,就不用一直輸入n再按 enter

  5. 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"
  1. history
    列出曾經下過的指令
  2. irb
    啟動 irb cli介面
  3. pry
    啟動 pry cli介面

irb 或 pry 用槍時機

因為 byebug 的 cli 沒辦法輸入多行指令
以前想測試多行指令 (ex.宣告一個方法)就只能另外開一個console來測試。
在irb或pry模式,就沒有這個問題,而這兩個模式也都在rails 環境下,可以抓到DB的資料。
ps.如果要離開 irb 或 pry 模式,輸入 exit, quitctrl + 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
    ... 下略...
  1. var 或 v
    顯示變數
    下面列出所有var指令:
var all      -- 顯示所有self的變數
var args     -- 顯示目前scope的參數
var const    -- 顯示目前常數
var global   -- 顯示目前全域變數
var instance -- 顯示self身上的實體變數
var local    -- 顯示目前scope的區域變數

ex.

var instance 
# => @cache = {1=>1}
  1. display
    每抵達中斷點,自動執行後面的expression
    比方我想知道@cache在每一次中斷點的值:
display @cache

就會在每個中斷點執行@cache,每次用continue,就可以知道@cache的變化,依序為:

1: @cache = {}
1: @cache = {1=>1}
1: @cache = {1=>1, 0=>1, 2=>2}
  1. eval
    當指令跟內建指令衝突時,也就是要執行byebug的保留字的時候。
    比方說我有個變數叫history,但是 history 已經被byebug拿去用了
    這時用
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 快又準

參考文件


上一篇
[Day 3] Ruby Memoization
下一篇
[Day 5] 尋找 bottleneck
系列文
續說 Ruby on Rails10
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言