偵錯 Kotlin/Native
目前,Kotlin/Native 編譯器產生的除錯資訊與 DWARF 2 規範相容,因此現代除錯工具可以執行以下操作:
- 斷點 (breakpoints)
- 單步執行 (stepping)
- 類型資訊檢查 (inspection of type information)
- 變數檢查 (variable inspection)
備註
支援 DWARF 2 規範意味著除錯工具將 Kotlin 識別為 C89,因為在 DWARF 5 規範之前,規範中沒有 Kotlin 語言類型的識別符。
使用 Kotlin/Native 編譯器產生帶有除錯資訊的二進位檔
若要使用 Kotlin/Native 編譯器產生帶有除錯資訊的二進位檔,請在命令列中使用 -g
選項。
0:b-debugger-fixes:minamoto@unit-703(0)# cat - > hello.kt
fun main(args: Array<String>) {
println("Hello world")
println("I need your clothes, your boots and your motocycle")
}
0:b-debugger-fixes:minamoto@unit-703(0)# dist/bin/konanc -g hello.kt -o terminator
KtFile: hello.kt
0:b-debugger-fixes:minamoto@unit-703(0)# lldb terminator.kexe
(lldb) target create "terminator.kexe"
Current executable set to 'terminator.kexe' (x86_64).
(lldb) b kfun:main(kotlin.Array<kotlin.String>)
Breakpoint 1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4
(lldb) r
Process 28473 launched: '/Users/minamoto/ws/.git-trees/debugger-fixes/terminator.kexe' (x86_64)
Process 28473 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x00000001000012e4 terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) at hello.kt:2
1 fun main(args: Array<String>) {
`->` 2 println("Hello world")
3 println("I need your clothes, your boots and your motocycle")
4 }
(lldb) n
Hello world
Process 28473 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
frame #0: 0x00000001000012f0 terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) at hello.kt:3
1 fun main(args: Array<String>) {
2 println("Hello world")
`->` 3 println("I need your clothes, your boots and your motocycle")
4 }
(lldb)
斷點 (Breakpoints)
現代除錯器提供幾種設定斷點的方法,以下是按工具細分的資訊:
lldb
-
依名稱 (by name)
(lldb) b -n kfun:main(kotlin.Array<kotlin.String>)
Breakpoint 4: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4
-n
是可選的,預設會套用此標誌
-
依位置 (檔案名稱、行號) (by location (filename, line number))
(lldb) b -f hello.kt -l 1
Breakpoint 1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4 -
依地址 (by address)
(lldb) b -a 0x00000001000012e4
Breakpoint 2: address = 0x00000001000012e4 -
依正規表示式 (by regex),您可能會發現它對於除錯產生的工件 (artifacts) 很有用,例如 lambda 等(在名稱中使用
#
符號)。3: regex = 'main\(', locations = 1
3.1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = terminator.kexe[0x00000001000012e4], unresolved, hit count = 0
gdb
-
依正規表示式 (by regex)
(gdb) rbreak main(
Breakpoint 1 at 0x1000109b4
struct ktype:kotlin.Unit &kfun:main(kotlin.Array<kotlin.String>); -
依名稱 (by name) 無法使用 (unusable),因為
:
是依位置設定斷點的分隔符(gdb) b kfun:main(kotlin.Array<kotlin.String>)
No source file named kfun.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (kfun:main(kotlin.Array<kotlin.String>)) pending -
依位置 (by location)
(gdb) b hello.kt:1
Breakpoint 2 at 0x100001704: file /Users/minamoto/ws/.git-trees/hello.kt, line 1. -
依地址 (by address)
(gdb) b *0x100001704
Note: breakpoint 2 also set at pc 0x100001704.
Breakpoint 3 at 0x100001704: file /Users/minamoto/ws/.git-trees/hello.kt, line 2.
單步執行 (Stepping)
單步執行函數的工作方式與 C/C++ 程式大致相同。
變數檢查 (Variable inspection)
對於基本類型,var
變數的變數檢查可以直接使用。
對於非基本類型,konan_lldb.py
中有 lldb 的自訂美化列印器 (pretty printers):
λ cat main.kt | nl
1 fun main(args: Array<String>) {
2 var x = 1
3 var y = 2
4 var p = Point(x, y)
5 println("p = $p")
6 }
7 data class Point(val x: Int, val y: Int)
λ lldb ./program.kexe -o 'b main.kt:5' -o
(lldb) target create "./program.kexe"
Current executable set to './program.kexe' (x86_64).
(lldb) b main.kt:5
Breakpoint 1: where = program.kexe`kfun:main(kotlin.Array<kotlin.String>) + 289 at main.kt:5, address = 0x000000000040af11
(lldb) r
Process 4985 stopped
* thread #1, name = 'program.kexe', stop reason = breakpoint 1.1
frame #0: program.kexe`kfun:main(kotlin.Array<kotlin.String>) at main.kt:5
2 var x = 1
3 var y = 2
4 var p = Point(x, y)
`->` 5 println("p = $p")
6 }
7
8 data class Point(val x: Int, val y: Int)
Process 4985 launched: './program.kexe' (x86_64)
(lldb) fr var
(int) x = 1
(int) y = 2
(ObjHeader *) p = 0x00000000007643d8
(lldb) command script import dist/tools/konan_lldb.py
(lldb) fr var
(int) x = 1
(int) y = 2
(ObjHeader *) p = [x: ..., y: ...]
(lldb) p p
(ObjHeader *) $2 = [x: ..., y: ...]
(lldb) script lldb.frame.FindVariable("p").GetChildMemberWithName("x").Dereference().GetValue()
'1'
(lldb)
也可以使用內建模組執行階段函數 Konan_DebugPrint
取得物件變數 (var) 的表示形式(此方法也適用於 gdb,使用命令語法模組):
0:b-debugger-fixes:minamoto@unit-703(0)# cat ../debugger-plugin/1.kt | nl -p
1 fun foo(a:String, b:Int) = a + b
2 fun one() = 1
3 fun main(arg:Array<String>) {
4 var a_variable = foo("(a_variable) one is ", 1)
5 var b_variable = foo("(b_variable) two is ", 2)
6 var c_variable = foo("(c_variable) two is ", 3)
7 var d_variable = foo("(d_variable) two is ", 4)
8 println(a_variable)
9 println(b_variable)
10 println(c_variable)
11 println(d_variable)
12 }
0:b-debugger-fixes:minamoto@unit-703(0)# lldb ./program.kexe -o 'b -f 1.kt -l 9' -o r
(lldb) target create "./program.kexe"
Current executable set to './program.kexe' (x86_64).
(lldb) b -f 1.kt -l 9
Breakpoint 1: where = program.kexe`kfun:main(kotlin.Array<kotlin.String>) + 463 at 1.kt:9, address = 0x0000000100000dbf
(lldb) r
(a_variable) one is 1
Process 80496 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000dbf program.kexe`kfun:main(kotlin.Array<kotlin.String>) at 1.kt:9
6 var c_variable = foo("(c_variable) two is ", 3)
7 var d_variable = foo("(d_variable) two is ", 4)
8 println(a_variable)
`->` 9 println(b_variable)
10 println(c_variable)
11 println(d_variable)
12 }
Process 80496 launched: './program.kexe' (x86_64)
(lldb) expression -- (int32_t)Konan_DebugPrint(a_variable)
(a_variable) one is 1(int32_t) $0 = 0
(lldb)
已知問題 (Known issues)
- Python 綁定的效能 (performance of Python bindings)。