调试 Kotlin/Native
Currently, the Kotlin/Native compiler produces debug info compatible with the DWARF 2 specification, so modern debugger tools can perform the following operations:
- breakpoints
- stepping
- inspection of type information
- variable inspection
Supporting the DWARF 2 specification means that the debugger tool recognizes Kotlin as C89, because before the DWARF 5 specification, there is no identifier for the Kotlin language type in specification.
Produce binaries with debug info with Kotlin/Native compiler
To produce binaries with the Kotlin/Native compiler, use the -g option on the command line.
0:b-debugger-fixes:minamoto@unit-703(0)# cat - > hello.ktfun 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 terminatorKtFile: hello.kt0: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) rProcess 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.1frame #0: 0x00000001000012e4 terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) at hello.kt:21 fun main(args: Array<String>) {-> 2 println("Hello world")3 println("I need your clothes, your boots and your motocycle")4 }(lldb) nHello worldProcess 28473 stopped* thread #1, queue = 'com.apple.main-thread', stop reason = step overframe #0: 0x00000001000012f0 terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) at hello.kt:31 fun main(args: Array<String>) {2 println("Hello world")-> 3 println("I need your clothes, your boots and your motocycle")4 }(lldb)
Breakpoints
Modern debuggers provide several ways to set a breakpoint, see below for a tool-by-tool breakdown:
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 is optional, this flag is applied by default
by location (filename, line number)
(lldb) b -f hello.kt -l 1Breakpoint 1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4
by address
(lldb) b -a 0x00000001000012e4Breakpoint 2: address = 0x00000001000012e4
by regex, you might find it useful for debugging generated artifacts, like lambda etc. (where used
#symbol in name).3: regex = 'main\(', locations = 13.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 0x1000109b4struct ktype:kotlin.Unit &kfun:main(kotlin.Array<kotlin.String>);
by name unusable, because
:is a separator for the breakpoint by location(gdb) b kfun:main(kotlin.Array<kotlin.String>)No source file named kfun.Make breakpoint pending on future shared library load? (y or [n]) yBreakpoint 1 (kfun:main(kotlin.Array<kotlin.String>)) pending
by location
(gdb) b hello.kt:1Breakpoint 2 at 0x100001704: file /Users/minamoto/ws/.git-trees/hello.kt, line 1.
by address
(gdb) b *0x100001704Note: breakpoint 2 also set at pc 0x100001704.Breakpoint 3 at 0x100001704: file /Users/minamoto/ws/.git-trees/hello.kt, line 2.
Stepping
Stepping functions works mostly the same way as for C/C++ programs.
Variable inspection
Variable inspections for var variables works out of the box for primitive types. For non-primitive types there are custom pretty printers for lldb in konan_lldb.py:
λ cat main.kt | nl1 fun main(args: Array<String>) {2 var x = 13 var y = 24 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:5Breakpoint 1: where = program.kexe`kfun:main(kotlin.Array<kotlin.String>) + 289 at main.kt:5, address = 0x000000000040af11(lldb) rProcess 4985 stopped* thread #1, name = 'program.kexe', stop reason = breakpoint 1.1frame #0: program.kexe`kfun:main(kotlin.Array<kotlin.String>) at main.kt:52 var x = 13 var y = 24 var p = Point(x, y)-> 5 println("p = $p")6 }78 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)
Getting representation of the object variable (var) could also be done using the built-in runtime function Konan_DebugPrint (this approach also works for gdb, using a module of command syntax):
0:b-debugger-fixes:minamoto@unit-703(0)# cat ../debugger-plugin/1.kt | nl -p1 fun foo(a:String, b:Int) = a + b2 fun one() = 13 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 9Breakpoint 1: where = program.kexe`kfun:main(kotlin.Array<kotlin.String>) + 463 at 1.kt:9, address = 0x0000000100000dbf(lldb) r(a_variable) one is 1Process 80496 stopped* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1frame #0: 0x0000000100000dbf program.kexe`kfun:main(kotlin.Array<kotlin.String>) at 1.kt:96 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
- performance of Python bindings.

