要怎麼用 Julia debugger?
有鑑於 Debugger.jl 的說明文件有點難懂,就自己來寫一篇教學。
Debugger.jl 是在 Julia 上,由官方開發及維護的除錯器(debugger),debugger 如同其他語言的 debugger 一樣,可以預先設定斷點(breakpoint)、在丟出例外(exception)時停下來、逐行執行及檢查變數等等功能。這篇文章會示範最基礎的 debugger 的用法。
安裝 Debugger
安裝 Julia 套件都是差不多的方式,切換到套件模式,並且 add PACKAGE
。
1 | ]add Debugger |
等他跑好就把 Debugger
安裝好了。
在 REPL 執行 Debugger
基本上,Debugger
是一種直譯器,所以比較好的執行環境是在 REPL 中。
當然在 VS code 也有內建,但是我個人嘗試過,他跑得蠻慢的,不知道為什麼。= ="
所以以下的示範都是在 REPL 上。
逐行執行
為了示範,所以就先定義兩個函式。
1 | julia> using Debugger |
逐步執行
Debugger 都會有個進入點,進入點需要在前面標記 @enter
,後面可以接著一個表示式。
1 | julia> 3) bar( |
以上進入了 bar(3)
這個函式呼叫。剛進入的時候,他會停在函式呼叫的第一步:(foo)(3, 5)
。
接著,他會進入除錯(debug)模式,在這個模式下,就可以藉由指令來操作這個 debugger。
如果輸入 s
,他會逐步執行程式碼,所以可以看到他進入到 foo
函式中,並且指標指到第二行的 z = x + 2
。
如果再輸入一次 s
,讓他執行下一步,這時候加法就會被執行,所以就會進到 (Core.Intrinsics.add_int)(3, 2)
的執行。
逐行執行
如果我們希望他可以逐行執行,可以用 n
。
1 | julia> 3) bar( |
如果輸入 n
之後,debugger 會直接計算該行的計算結果,foo(x, 5)
就會被執行,並回傳 50
。
執行到丟出例外
比較實用的是讓他執行直到一個例外被拋出。這時我們需要修改一下函式:
1 | function foo(x, y) |
在第二行中插入一條拋出 ErrorException
例外的表示式。
要執行到直到丟出例外,並且停下,有幾種方法。
在除錯模式,例外前插入斷點法
1 | julia> 3) bar( |
一樣是利用 @enter
來製造進入點,接著利用 bp on throw
來設定斷點,他的意思是在拋出例外時設定斷點。
相對,這個斷點是可以關閉的,用 bp off throw
。
接著,可以輸入 c
讓程式持續執行,最後他就會停在斷點處。
在 REPL 模式,例外前插入斷點法
1 | julia> break_on(:error) |
跟前面的想法差不多,只是這次是在 REPL 下操作。
這次就不用急著進入除錯模式,可以直接在 REPL 下設定在拋出例外前的斷點,可以用 break_on(:error)
。
@run
這個 macro 就等同於 @enter
+ c
,所以他會執行直到斷點發生。
查看變數的值
我們停在斷點之後可以做什麼事情呢?除錯就是要在執行環境中去檢查執行時期的變數是不是符合預期。
這時候我們可以把 backtrace 給列出來。
1 | 1|debug> bt |
輸入 bt
可以列出停在目前的斷點,在 foo
及 bar
函式中存有的區域變數以及他的值有哪些。
我們可以發現我們首先呼叫 bar
,bar
會去呼叫 foo
。我們知道函式呼叫會在堆疊(stack)中,推入一個新的呼叫框(frame),呼叫框中包含著函式呼叫的引數(arguments)、區域變數等等資訊。
foo
的呼叫會在整個堆疊的最上面,也就是第一個 frame,所以看到他標示 [1]
,而 bar
的呼叫就是位於第二個 frame。
叫出指定 frame 中的變數
我們可以叫出指定的 frame 的區域變數,只要在除錯模式中輸入 fr [i::Int]
,i
則是指定第幾個 frame。
1 | 1|debug> fr 1 |
我們就可以看到在這個階段第一個 frame 的 x
、y
跟 z
變數的值各是多少。
我們可以再試試看第二個 frame 的內容。
1 | 1|debug> fr 2 |
切換目前的 frame
剛進入斷點的話,我們都會是位於第一個 frame 中。有時候我們會想要知道在其他 frame 的狀況,這時候我們就需要切換 frame 的位置。
這時候只要輸入 f [i::Int]
就可以切換不同的 frame,例如,想要切換到第二個 frame 就可以輸入 f 2
。
1 | 1|debug> f 2 |
還可以發現在除錯模式的 prompt 2|debug>
,最開頭的數字改變了,一開始是 1
,後來變成 2
。
這就表示目前所在第幾個 frame。
切換成 Julia REPL 模式
在除錯模式輸入 `(鍵盤左上),就可以切換到 Julia REPL 模式,並且存取變數。
1 | 1|debug> fr 2 |
切換之後,你會看到 2|debug>
切換成 2|julia
,並且可以直接存取該變數 x
。如果要切換回除錯模式,只要按 backspace 鍵,把 ` 刪除即可。
1 | 2|julia> x |
也可以對該變數做運算。
1 | 2|julia> x + 2 |
但是沒辦法存新的變數。
1 | 2|julia> y = x + 2 |
結論
我們有了這些工具跟方法,可以讓我們在不同的 frame 之間做跳躍,去看在上層或是下層的 frame 中變數的數值各是多少。
同時,也可以測試你自己寫的函式是否正常執行。
我們就可以用這些方法來除錯程式碼。