[debug] it's possible to inspect array elements in debugger
This commit is contained in:
committed by
Vasily Levchenko
parent
579e4a0d4c
commit
b5befcf5d5
+10
-1
@@ -133,7 +133,16 @@ private class LldbSessionSpecification private constructor(
|
|||||||
&& commands.zip(executedCommands).all { (cmd, h) -> h == "(lldb) $cmd" }
|
&& commands.zip(executedCommands).all { (cmd, h) -> h == "(lldb) $cmd" }
|
||||||
|
|
||||||
if (!responsesMatch) {
|
if (!responsesMatch) {
|
||||||
fail("Responses do not match commands.\nResponses: $executedCommands\nCommands: $commands")
|
val message = """
|
||||||
|
Responses do not match commands.
|
||||||
|
|
||||||
|
COMMANDS: $commands
|
||||||
|
RESPONSES: $executedCommands
|
||||||
|
|
||||||
|
FULL SESSION:
|
||||||
|
$output
|
||||||
|
"""
|
||||||
|
fail(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((patternBody, command) in patterns.zip(bodies).zip(executedCommands)) {
|
for ((patternBody, command) in patterns.zip(bodies).zip(executedCommands)) {
|
||||||
|
|||||||
+16
@@ -94,4 +94,20 @@ class LldbTests {
|
|||||||
(ObjHeader *) xs = [1, 2, 3]
|
(ObjHeader *) xs = [1, 2, 3]
|
||||||
(ObjHeader *) ys = [Point(x=1, y=2), null]
|
(ObjHeader *) ys = [Point(x=1, y=2), null]
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can inspect array children`() = lldbTest("""
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val xs = intArrayOf(3, 5, 8)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Point(val x: Int, val y: Int)
|
||||||
|
""", """
|
||||||
|
> type summary add "ObjHeader *" --inline-children
|
||||||
|
> b main.kt:3
|
||||||
|
> r
|
||||||
|
> fr var xs
|
||||||
|
(ObjHeader *) xs = [..] (0 = 3, 1 = 5, 2 = 8)
|
||||||
|
""")
|
||||||
}
|
}
|
||||||
@@ -24,17 +24,28 @@
|
|||||||
import lldb
|
import lldb
|
||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
|
def lldb_val_to_ptr(lldb_val):
|
||||||
|
addr = lldb_val.GetValueAsUnsigned()
|
||||||
|
if addr == 0:
|
||||||
|
return None
|
||||||
|
return '((struct ObjHeader *) {})'.format(addr)
|
||||||
|
|
||||||
def kotlin_object_type_summary(lldb_val, internal_dict):
|
def kotlin_object_type_summary(lldb_val, internal_dict):
|
||||||
"""Hook that is run by lldb to display a Kotlin object."""
|
"""Hook that is run by lldb to display a Kotlin object."""
|
||||||
fallback = lldb_val.GetValue()
|
fallback = lldb_val.GetValue()
|
||||||
if str(lldb_val.type) != "struct ObjHeader *":
|
if str(lldb_val.type) != "struct ObjHeader *":
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
|
ptr = lldb_val_to_ptr(lldb_val)
|
||||||
|
if ptr is None:
|
||||||
|
return fallback
|
||||||
|
|
||||||
def evaluate(expr):
|
def evaluate(expr):
|
||||||
return lldb_val.GetTarget().EvaluateExpression(expr, lldb.SBExpressionOptions())
|
return lldb_val.GetTarget().EvaluateExpression(expr, lldb.SBExpressionOptions())
|
||||||
|
|
||||||
|
|
||||||
buff_len = evaluate(
|
buff_len = evaluate(
|
||||||
"(int)Konan_DebugObjectToUtf8Array((struct ObjHeader *) %s, (char *)Konan_DebugBuffer(), (int)Konan_DebugBufferSize());" % lldb_val.GetValueAsUnsigned()
|
"(int)Konan_DebugObjectToUtf8Array({}, (char *)Konan_DebugBuffer(), (int)Konan_DebugBufferSize());".format(ptr)
|
||||||
).unsigned
|
).unsigned
|
||||||
|
|
||||||
if not buff_len:
|
if not buff_len:
|
||||||
@@ -47,12 +58,105 @@ def kotlin_object_type_summary(lldb_val, internal_dict):
|
|||||||
return s if error.Success() else fallback
|
return s if error.Success() else fallback
|
||||||
|
|
||||||
|
|
||||||
|
class KonanArraySyntheticChildrenProvider:
|
||||||
|
def __init__(self, valobj, internal_dict):
|
||||||
|
self.valobj = valobj
|
||||||
|
|
||||||
|
self._element_type = None
|
||||||
|
self._fn_prefix = None
|
||||||
|
self._num_children = None
|
||||||
|
self._ptr = None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self._element_type = None
|
||||||
|
self._fn_prefix = None
|
||||||
|
self._num_children = None
|
||||||
|
self._ptr = lldb_val_to_ptr(self.valobj)
|
||||||
|
|
||||||
|
if self._ptr is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
types = [("Boolean", "uint8_t"),
|
||||||
|
("Byte", "int8_t"),
|
||||||
|
("Char", "uint16_t"),
|
||||||
|
("Short", "int16_t"),
|
||||||
|
("Int", "int32_t"),
|
||||||
|
("Long", "int64_t"),
|
||||||
|
("Float", "float"),
|
||||||
|
("Double", "double")]
|
||||||
|
|
||||||
|
for (ktype, ctype) in types:
|
||||||
|
instance_check = "IsInstance({self._ptr}, the{ktype}ArrayTypeInfo)".format(
|
||||||
|
self=self, ktype=ktype
|
||||||
|
)
|
||||||
|
if self._evaluate_bool(instance_check):
|
||||||
|
self._element_type = ctype
|
||||||
|
self._fn_prefix = "Kotlin_{}Array".format(ktype)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._num_children = self._evaluate_int(
|
||||||
|
"%s_getArrayLength(%s)" % (self._fn_prefix, self._ptr)
|
||||||
|
)
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
if self._element_type is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self._num_children
|
||||||
|
|
||||||
|
def has_children(self):
|
||||||
|
if self._element_type is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self._num_children > 0
|
||||||
|
|
||||||
|
def get_child_index(self, name):
|
||||||
|
if self._element_type is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
index = int(name)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return index if (0 <= index < self._num_children) else None
|
||||||
|
|
||||||
|
def get_child_at_index(self, index):
|
||||||
|
if self._element_type is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not (0 <= index < self._num_children):
|
||||||
|
return None
|
||||||
|
return self._evaluate(
|
||||||
|
"({self._element_type}){self._fn_prefix}_get({self._ptr}, {index})".format(self=self, index=index),
|
||||||
|
name=str(index)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _evaluate(self, expr, name="tmp"):
|
||||||
|
return self.valobj.CreateValueFromExpression(name, expr)
|
||||||
|
|
||||||
|
def _evaluate_bool(self, expr):
|
||||||
|
return self._evaluate("(uint8_t)" + expr).unsigned == 1
|
||||||
|
|
||||||
|
def _evaluate_int(self, expr):
|
||||||
|
return self._evaluate("(int32_t)" + expr).signed
|
||||||
|
|
||||||
|
|
||||||
def __lldb_init_module(debugger, internal_dict):
|
def __lldb_init_module(debugger, internal_dict):
|
||||||
debugger.HandleCommand('\
|
debugger.HandleCommand('\
|
||||||
type summary add \
|
type summary add \
|
||||||
--no-value \
|
--no-value \
|
||||||
|
--expand \
|
||||||
--python-function konan_lldb.kotlin_object_type_summary \
|
--python-function konan_lldb.kotlin_object_type_summary \
|
||||||
"ObjHeader *" \
|
"ObjHeader *" \
|
||||||
--category Kotlin\
|
--category Kotlin\
|
||||||
')
|
')
|
||||||
|
debugger.HandleCommand('\
|
||||||
|
type synthetic add \
|
||||||
|
--python-class konan_lldb.KonanArraySyntheticChildrenProvider\
|
||||||
|
"ObjHeader *" \
|
||||||
|
--category Kotlin\
|
||||||
|
')
|
||||||
debugger.HandleCommand('type category enable Kotlin')
|
debugger.HandleCommand('type category enable Kotlin')
|
||||||
|
|||||||
Reference in New Issue
Block a user