^KT-65478 fixed
3.5 KiB
Introduction
The Kotlin debugger uses inline marker variables ($i$f$..., $i$a$...) that are produced by the Kotlin compiler
to handle stepping into/over/out of inline functions and lambdas and also to build inline stack traces. This scheme
does not work for dex code, where the same variable may be duplicated in different registers and where basic blocks can be reordered.
This leads to many debugging issues on Android from inconsistent locals in the variables view to bad stepping in inline functions.
New format description
The new format affects 3 types of variables:
- Inline function marker variables
$i$f$name\[scope number]\[call site line number] - Inline lambda marker variables
$i$a$name\[scope number]\[call site line number]\[surrounding scope number] - Local variables from inline functions or lambdas
name\[scope number]
Let’s break it down on the following example:
fun main() {
val inMain = 0
g(0) {
val inLambdaG1 = 1 // breakpoint 1
h()
g(4) {
val inLambdaG2 = 2 // breakpoint 2
}
}
}
inline fun g(gParam: Int, block: () -> Unit) {
block()
}
inline fun h() {
val inH = 3
i()
}
inline fun i() {
val inI = 4
}
How LVT used to look:
LocalVariableTable:
Start Length Slot Name Signature
20 4 7 $i$f$i I
23 1 8 inI$iv$iv I
14 11 5 $i$f$h I
17 8 6 inH$iv I
34 4 7 $i$a$-g-SandboxKt$main$1$1 I
37 1 8 inLambdaG2 I
31 9 6 $i$f$g I
28 12 5 gParam$iv I
8 33 3 $i$a$-g-SandboxKt$main$1 I
11 30 4 inLambdaG1 I
6 37 2 $i$f$g I
4 39 1 gParam$iv I
2 42 0 inMain I
How LVT looks with the new format:
LocalVariableTable:
Start Length Slot Name Signature
20 4 7 $i$f$i\4\30 I
23 1 8 inI\4 I
14 11 5 $i$f$h\3\7 I
17 8 6 inH\3 I
34 4 7 $i$a$-g-SandboxKt$main$1$1\6\36\2 I
37 1 8 inLambdaG2\6 I
31 9 6 $i$f$g\5\8 I
28 12 5 gParam\5 I
8 33 3 $i$a$-g-SandboxKt$main$1\2\28\0 I
11 30 4 inLambdaG1\2 I
6 37 2 $i$f$g\1\5 I
4 39 1 gParam\1 I
2 42 0 inMain I
Previously the compiler added the $iv suffixes to distinguish locals that belong to different inline functions.
Now we assign scope numbers to marker and local variables. Locals with a scope number belong to the function
which is represented by a marker variable with the same scope number.
Let’s see how it works in our example:
$i$f$g\1\5has scope number 1, as well asgParam\1.$i$a$-g-SandboxKt$main$1\2\28\0has scope number 2, as well asinLambdaG1\2. It also has a surrounding scope number equal to 0. The scope number 0 belongs to the top frame, and basically means that theinMainvariable should be included in the variables view when we stop at breakpoint 1.$i$f$h\3\7has scope number 3, as well asinH\3.$i$f$i\4\30has scope number 4, as well asinI\4.$i$f$g\5\8has scope number 5, as well asgParam\5.$i$a$-g-SandboxKt$main$1$1\6\36\2has scope number 6, as well asinLambdaG2\6, and surrounding scope number 2, which means that we should include variables that are visible in scope 2 when we stop at breakpoint 2. In our example these variables areinMainandinLambdaG1\2.