Jetbrains 系 IDE Debugger 的使用方法
前言
記得最初開始寫程式語言的時候,使用的是 C ,那最簡單又迅速地除錯方法便是將訊息印出來了,是阿,大家都愛 printf ,能夠把變數名稱印出來看這得清晰,也就能夠除錯了。
但在過了許久後,接觸到許多許要使用框架和各式各樣 Library ,很多時候,就是呼叫了一個函式,接著他輸出了一個結果:
fun main() {
val testClass = SomeClassWeNamedItClass(10, 20)
callThisFunction(testClass)
println(testClass.value1)
println(testClass.value2)
}
輸出的結果會是:
20
800
恩,這情況很有意思,我丟了一個 class 進去, class 的 value1 和 value2 分別是 10 和 20 ,過了那個 callThisFunction 之後,value1 和 value2 成了 20 和 800 ,中間到底發生了甚麼事,我輸入的數字如果是 11 和 22,出來的又會是什麼?
有些時候,我們想甚麼直接寫成 Code 讓它跑就行了。
fun main() {
val testClass = SomeClassWeNamedItClass(11, 22)
callThisFunction(testClass)
println(testClass.value1)
println(testClass.value2)
}
輸出的結果會是:
21
903
不行,真的推斷不出他中間到底發生了什麼事情,所以假如我今天放的 13 和 23 ,那結果會是啥,我又只得改 code 再去輸入,但是當你專案本身量體龐大,需要花費大量時間 compile 的話,那也是很耗時間的。
為了避免這種情況,這時候我們做的第一件事情,會是去 trance code 。
當我們點進第一步看到他在裏頭呼叫了一個 functionToCall2。
點進去發現他呼叫了三個 method …. trance code 的人這時會開始思考,這都什麼鬼玩意。
這也不讓人氣餒,我們細細看下去。
可以發現 method 讓 class 的 value 加了 10。
接著 method2 讓 class 的 value2 加了 value1。
接著 method3 讓 class 的 value2 乘了 value1 。
那就開始計算吧 , 13 + 10 =23 然後 23+23 = 46 ,那 46*23 …好像 compile 會比較快。
那有沒有不用改 Code 就能算出答案的方法呢?有的,如果是用 C 或是 Go ,有個工具叫做 GDB ,是個很棒的工具。那麼假如是使用 Jetbrains 系的 IDE 呢?大多都有提供 Debugger ,是介面相當人性化的工具。
關於 IDE 的 Debugger
那使用 IDE 的 Debugger 的也相當的簡單,我這邊使用 Intellij 的 Kotlin 來舉例:
在我們認為需要觀察的行數上點一下,就會出現一個紅色點,那就是我們稱為 breakpoint ,中文稱為 " 中斷點 " 的符號。
按一下上頭 Toolbar 的 Debug 符號:
或者乾脆在直接 build 的地方右鍵選擇 Debug 讓它去 run 即可。
那開始跑的時候會發現程式停在那行 Breakpoint 上頭,正如同中文字面意思,程式就中斷在這裡。
那此時下方的工具列也出現了這東西。
那左邊有一排,很像播放器的控制按鍵的東西,其實也正如同我們看到的那樣,
是接續下一個 Breakpoint 。
則是直接中止程式的 debug 。
不過我們都已經下了 Breakpoint 了,除非 Breakpoint 用一大堆,不然用這兩個按鈕就有點浪費 build time 了。
這邊我們要用的是上面那一排按鈕。
頭一個注意到的應該會是這顆三明治,它叫做 Show Execution Point,其實就字面的意思,它會把畫面帶到目前的中斷點處。
再來看到這顆,它叫做 Step Over ,恩,字面意思是踏出一步,直接點一下就會懂了。
可以看到現在 Execution Point 指到下一行了。真棒,我要看他執行 function 的結果,於是我又按了一次。
發現他直接跳到下一行了,我還是沒能明白中間到底經歷了什麼。
莫慌莫慌。
此時我們注意到第三顆按鈕
它叫做 Step into ,就是進入裡面的意思,因為我呼叫的 method 並不在同一個 kt 底下,所以在 step over 會被當成是一個可忽略的過程,所以會被略過,但這邊的情況是我們是想看看 method 裏頭的東西怎麼跑,我們就需要用到 Step into 來去查看 method 裏頭的步驟。
但..但是,我這邊已經跑到 callThisFunction 之後了,總不能反轉熵讓程式倒回去吧?
不怕不怕,這邊還有一個按鈕
它叫做 Drop Frame ,可以想像是,在使用 Debugger 的過程中一個接著一個的 step 就是一個 Frame ,那你 drop 掉它,就會回到最初設定的 Breakpoint 上頭。
那要注意,Frame 通常是以一個 method 為單位的,這不用特別記,只要看Debugger 內的 Frames 就好,拿 AS 來說,因為 Android 執行架構的關係,總是預設會疊特別多個 method:
就視自己的情況看是要 drop 到哪就好。
那這邊的話,因為還沒有使用到 Step Into ,所以只需要按一次。
可以發現,按了之後似乎沒有 Execution 的 Line 了,但沒關係,再按一次 Step Over 就沒問題了。
一切又都重新開始了,好的,我們來看看 callThisFunction 裏頭到底發生了甚麼事情。
那可以看到經過 method 和 method2 ,原本是 11 的 value1 變成了 21 ,而 原本是 22 的 value2 則成了 43。
也就是說下一步 method3 會是 21*43,所以我們可以看到輸出的結果是 903。
這邊其實也可以利用 Debugger 的 Variables。
點一下那個 + 號,就可以直接將 testClass 加進去觀察中,這樣就可以觀察 testClass 裡的 value1 和 value2 的變化了,這邊也可以直接輸入 testClass.value1 或者 testClass.value2 ,單獨觀察這兩者的變化。
好那回頭看看,我想要知道當 value1 為 13, value2 為 23 時會輸出甚麼東西呢?
怎麼總覺得好像要改寫個 Code ?
其實不用,Variables 有個好東西叫做 set value
我可以直接在 debug 的過程中更改變數內容。
那我這裡把它改成 13 和 23 後,來看看跑的結果是甚麼。
可以發現變成 23 和 46 都正如我想的那樣,那 23 x 46 呢?
答案是 1058。
那這邊還有最後一顆按鈕
它叫做 Evaluate Expression ,字面意思是 " 評估表達 " ,它的目的很簡單,可以讓你利用現有的 Variables 做點簡單的運算。
在中斷點之中,可以在這邊利用這個工具,簡單的計算一下東西,想好下一次 Code 要怎麼樣修改。
結語
說實話,我以前是 Log 派的,不過經歷過 Compile 時間非常長的洗禮後,慢慢的可以體會到 ,純粹加 Log ,只為了看當下一個變數會輸出什麼樣的值,是一間多麼耗時的事情,更不用說,如果 invoke 的 method 是個 Library 或者是框架的東西,也根本沒辦法在裏頭加上 Log 了,這時,用 Debugger 就可以觀察到不少沒辦法用 Log 觀察的東西,而且 setValue 的特性,也能夠在 Library 內嘗試更多的如果這樣會發生什麼事情的可能性。
有時雖然 compile 的時間沒有多少,但累積起來也是蠻驚人的。因此如果可以減少寫 Log 然後 compile 的時間,是能夠有效提升開發的速度的。