Skip to content

Commit cb9580c

Browse files
authored
Merge pull request #27 from touchlab/kpg/speed_up
Speed up lldb script
2 parents cca7a73 + 540e865 commit cb9580c

File tree

1 file changed

+138
-33
lines changed

1 file changed

+138
-33
lines changed

Kotlin.ideplugin/Contents/Resources/konan_lldb.py

Lines changed: 138 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,18 @@
2323

2424
import lldb
2525
import struct
26+
import re
2627

2728
NULL = 'null'
2829

2930
def log(msg):
3031
if False:
3132
print(msg())
3233

34+
def log2(msg):
35+
if True:
36+
print(msg())
37+
3338
def exelog(stmt):
3439
if False:
3540
f = open(os.getenv('HOME', '') + "/lldbexelog.txt", "a")
@@ -49,11 +54,31 @@ def evaluate(expr):
4954
exelog(evallog)
5055
return result
5156

57+
def _symbol_loaded_address(name, debugger = lldb.debugger):
58+
target = debugger.GetSelectedTarget()
59+
process = target.GetProcess()
60+
thread = process.GetSelectedThread()
61+
frame = thread.GetSelectedFrame()
62+
candidates = list(filter(lambda x: x.name == name, frame.module.symbols))
63+
# take first
64+
for candidate in candidates:
65+
address = candidate.GetStartAddress().GetLoadAddress(target)
66+
log(lambda: "_symbol_loaded_address:{} {:#x}".format(name, address))
67+
return address
68+
69+
def _type_info_by_address(address, debugger = lldb.debugger):
70+
target = debugger.GetSelectedTarget()
71+
process = target.GetProcess()
72+
thread = process.GetSelectedThread()
73+
frame = thread.GetSelectedFrame()
74+
candidates = list(filter(lambda x: x.GetStartAddress().GetLoadAddress(target) == address, frame.module.symbols))
75+
return candidates
76+
5277
def is_instance_of(addr, typeinfo):
53-
return evaluate("(bool)IsInstance({}, {})".format(addr, typeinfo)).GetValue() == "true"
78+
return evaluate("(bool)IsInstance({}, {:#x})".format(addr, typeinfo)).GetValue() == "true"
5479

5580
def is_string_or_array(value):
56-
return evaluate("(bool)IsInstance({0}, theStringTypeInfo) ? 1 : ((int)Konan_DebugIsArray({0}) ? 2 : 0)".format(lldb_val_to_ptr(value))).unsigned
81+
return evaluate("(int)IsInstance({0}, {1}) ? 1 : ((int)Konan_DebugIsArray({0}) ? 2 : 0)".format(lldb_val_to_ptr(value), _symbol_loaded_address('kclass:kotlin.String'))).unsigned
5782

5883
def type_info(value):
5984
"""This method checks self-referencing of pointer of first member of TypeInfo including case when object has an
@@ -73,16 +98,14 @@ def type_info(value):
7398
TO_STRING_DEPTH = 2
7499
ARRAY_TO_STRING_LIMIT = 10
75100

76-
def kotlin_object_type_summary(lldb_val, internal_dict = []):
101+
def kotlin_object_type_summary(lldb_val, internal_dict = {}):
77102
"""Hook that is run by lldb to display a Kotlin object."""
78103
log(lambda: "kotlin_object_type_summary({:#x})".format(lldb_val.unsigned))
79104
fallback = lldb_val.GetValue()
80105
if str(lldb_val.type) != "struct ObjHeader *":
81-
return fallback
82-
83-
ptr = lldb_val_to_ptr(lldb_val)
84-
if ptr is None:
85-
return fallback
106+
if lldb_val.GetValue() is None:
107+
return NULL
108+
return lldb_val.GetValueAsSigned()
86109

87110
tip = internal_dict["type_info"] if "type_info" in internal_dict.keys() else type_info(lldb_val)
88111
if not tip:
@@ -93,17 +116,20 @@ def kotlin_object_type_summary(lldb_val, internal_dict = []):
93116

94117
def select_provider(lldb_val, tip, internal_dict):
95118
soa = is_string_or_array(lldb_val)
119+
log(lambda : "select_provider: {} : {}".format(lldb_val, soa))
96120
return __FACTORY['string'](lldb_val, tip, internal_dict) if soa == 1 else __FACTORY['array'](lldb_val, tip, internal_dict) if soa == 2 \
97121
else __FACTORY['object'](lldb_val, tip, internal_dict)
98122

99123
class KonanHelperProvider(lldb.SBSyntheticValueProvider):
100-
def __init__(self, valobj, amString):
124+
def __init__(self, valobj, amString, internal_dict = {}):
101125
self._target = lldb.debugger.GetSelectedTarget()
102126
self._process = self._target.GetProcess()
103127
self._valobj = valobj
104128
self._ptr = lldb_val_to_ptr(self._valobj)
105129
if amString:
106130
return
131+
self._internal_dict = internal_dict.copy()
132+
self._to_string_depth = TO_STRING_DEPTH if "to_string_depth" not in self._internal_dict.keys() else self._internal_dict["to_string_depth"]
107133
if self._children_count == 0:
108134
self._children_count = evaluate("(int)Konan_DebugGetFieldCount({})".format(self._ptr)).signed
109135
self._children = []
@@ -142,6 +168,8 @@ def _read_value(self, index):
142168
return self._type_conversion[int(value_type)](address, str(self._children[index].name()))
143169

144170
def _create_synthetic_child(self, address, name):
171+
if self._to_string_depth == 0:
172+
return None
145173
index = self.get_child_index(name)
146174
value = self._valobj.CreateChildAtOffset(str(name),
147175
self._children[index].offset(),
@@ -184,17 +212,16 @@ def __init__(self, valobj):
184212
self._children_count = 0
185213
super(KonanStringSyntheticProvider, self).__init__(valobj, True)
186214
fallback = valobj.GetValue()
215+
buff_addr = evaluate("(void *)Konan_DebugBuffer()").unsigned
187216
buff_len = evaluate(
188-
'(int)Konan_DebugObjectToUtf8Array({}, (char *)Konan_DebugBuffer(), (int)Konan_DebugBufferSize());'.format(
189-
self._ptr)
190-
).unsigned
217+
'(int)Konan_DebugObjectToUtf8Array({}, (void *){:#x}, (int)Konan_DebugBufferSize());'.format(
218+
self._ptr, buff_addr)
219+
).signed
191220

192221
if not buff_len:
193222
self._representation = fallback
194223
return
195224

196-
buff_addr = evaluate("(char *)Konan_DebugBuffer()").unsigned
197-
198225
error = lldb.SBError()
199226
s = self._process.ReadCStringFromMemory(int(buff_addr), int(buff_len), error)
200227
if not error.Success():
@@ -249,7 +276,7 @@ def __init__(self, valobj, tip, internal_dict):
249276
else:
250277
self._children_count = 0
251278

252-
super(KonanObjectSyntheticProvider, self).__init__(valobj, False)
279+
super(KonanObjectSyntheticProvider, self).__init__(valobj, False, internal_dict)
253280

254281
if not tip in SYNTHETIC_OBJECT_LAYOUT_CACHE:
255282
SYNTHETIC_OBJECT_LAYOUT_CACHE[tip] = [
@@ -260,13 +287,11 @@ def __init__(self, valobj, tip, internal_dict):
260287
log(lambda : "TIP: {:#x} HIT".format(tip))
261288
self._children = SYNTHETIC_OBJECT_LAYOUT_CACHE[tip]
262289
self._values = [self._read_value(index) for index in range(self._children_count)]
263-
self._internal_dict = internal_dict
264-
self._to_string_depth = TO_STRING_DEPTH if "to_string_depth" not in self._internal_dict.keys() else self._internal_dict["to_string_depth"]
265290

266291

267292
def _field_name(self, index):
268293
error = lldb.SBError()
269-
name = self._read_string("(const char *)Konan_DebugGetFieldName({}, (int){})".format(self._ptr, index), error)
294+
name = self._read_string("(void *)Konan_DebugGetFieldName({}, (int){})".format(self._ptr, index), error)
270295
if not error.Success():
271296
raise DebuggerException()
272297
return name
@@ -294,17 +319,18 @@ def get_child_at_index(self, index):
294319

295320
# TODO: fix cyclic structures stringification.
296321
def to_string(self):
297-
if self._to_string_depth == 0:
298-
return "..."
299-
else:
300-
internal_dict = self._internal_dict.copy()
301-
internal_dict["to_string_depth"] = self._to_string_depth - 1
302-
return dict([(self._children[i].name(), self._deref_or_obj_summary(i, internal_dict)) for i in range(self._children_count)])
322+
return "..."
323+
# if self._to_string_depth == 0:
324+
# return "..."
325+
# else:
326+
# internal_dict = self._internal_dict.copy()
327+
# internal_dict["to_string_depth"] = self._to_string_depth - 1
328+
# return dict([(self._children[i].name(), self._deref_or_obj_summary(i, internal_dict)) for i in range(self._children_count)])
303329

304330
class KonanArraySyntheticProvider(KonanHelperProvider):
305331
def __init__(self, valobj, internal_dict):
306332
self._children_count = 0
307-
super(KonanArraySyntheticProvider, self).__init__(valobj, False)
333+
super(KonanArraySyntheticProvider, self).__init__(valobj, False, internal_dict)
308334
if self._ptr is None:
309335
return
310336
valobj.SetSyntheticChildrenGenerated(True)
@@ -314,8 +340,7 @@ def __init__(self, valobj, internal_dict):
314340
offset = zerro_address - valobj.unsigned
315341
size = first_address - zerro_address
316342
self._children = [MemberLayout(str(x), type, offset + x * size) for x in range(self.num_children())]
317-
self._values = [self._read_value(i) for i in range(self.cap_children_count())]
318-
self._internal_dict = internal_dict
343+
self._values = [self._read_value(i) for i in range(min(ARRAY_TO_STRING_LIMIT, self._children_count))]
319344

320345

321346
def cap_children_count(self):
@@ -339,28 +364,106 @@ def get_child_at_index(self, index):
339364
return result
340365

341366
def to_string(self):
342-
return [self._deref_or_obj_summary(i, self._internal_dict.copy()) for i in range(min(ARRAY_TO_STRING_LIMIT, self._children_count))]
367+
return '[%x]' % self._children_count
368+
# return [self._deref_or_obj_summary(i, self._internal_dict.copy()) for i in range(min(ARRAY_TO_STRING_LIMIT, self._children_count))]
343369

344370

345371
class KonanProxyTypeProvider:
346372
def __init__(self, valobj, internal_dict):
347373
log(lambda : "proxy: {:#x}".format(valobj.unsigned))
348374
tip = type_info(valobj)
375+
log(lambda : "KonanProxyTypeProvider: tip: {:#x}".format(tip))
349376
if not tip:
350377
return
351378
self._proxy = select_provider(valobj, tip, internal_dict)
379+
log(lambda: "KonanProxyTypeProvider: _proxy: {}".format(self._proxy.__class__.__name__))
352380
self.update()
353381

354382
def __getattr__(self, item):
355383
return getattr(self._proxy, item)
356384

357-
def print_this_command(debugger, command, result, internal_dict):
358-
pthis = lldb.frame.FindVariable('<this>')
359-
print(pthis)
360-
361385
def clear_cache_command(debugger, command, result, internal_dict):
362386
SYNTHETIC_OBJECT_LAYOUT_CACHE.clear()
363387

388+
389+
def type_name_command(debugger, command, result, internal_dict):
390+
result.AppendMessage(evaluate('(char *)Konan_DebugGetTypeName({})'.format(command)).summary)
391+
392+
__KONAN_VARIABLE = re.compile('kvar:(.*)#internal')
393+
__KONAN_VARIABLE_TYPE = re.compile('^kfun:<get-(.*)>\\(\\)(.*)$')
394+
__TYPES_KONAN_TO_C = {
395+
'kotlin.Byte': ('int8_t', lambda v: v.signed),
396+
'kotlin.Short': ('short', lambda v: v.signed),
397+
'kotlin.Int': ('int', lambda v: v.signed),
398+
'kotlin.Long': ('long', lambda v: v.signed),
399+
'kotlin.UByte': ('int8_t', lambda v: v.unsigned),
400+
'kotlin.UShort': ('short', lambda v: v.unsigned),
401+
'kotlin.UInt': ('int', lambda v: v.unsigned),
402+
'kotlin.ULong': ('long', lambda v: v.unsigned),
403+
'kotlin.Char': ('short', lambda v: v.signed),
404+
'kotlin.Boolean': ('bool', lambda v: v.signed),
405+
'kotlin.Float': ('float', lambda v: v.value),
406+
'kotlin.Double': ('double', lambda v: v.value)
407+
}
408+
409+
def type_by_address_command(debugger, command, result, internal_dict):
410+
result.AppendMessage("DEBUG: {}".format(command))
411+
tokens = command.split()
412+
target = debugger.GetSelectedTarget()
413+
process = target.GetProcess()
414+
thread = process.GetSelectedThread()
415+
types = _type_info_by_address(tokens[0])
416+
result.AppendMessage("DEBUG: {}".format(types))
417+
for t in types:
418+
result.AppendMessage("{}: {:#x}".format(t.name, t.GetStartAddress().GetLoadAddress(target)))
419+
420+
def symbol_by_name_command(debugger, command, result, internal_dict):
421+
target = debugger.GetSelectedTarget()
422+
process = target.GetProcess()
423+
thread = process.GetSelectedThread()
424+
frame = thread.GetSelectedFrame()
425+
tokens = command.split()
426+
mask = re.compile(tokens[0])
427+
symbols = list(filter(lambda v: mask.match(v.name), frame.GetModule().symbols))
428+
visited = list()
429+
for symbol in symbols:
430+
name = symbol.name
431+
if name in visited:
432+
continue
433+
visited.append(name)
434+
result.AppendMessage("{}: {:#x}".format(name, symbol.GetStartAddress().GetLoadAddress(target)))
435+
436+
def konan_globals_command(debugger, command, result, internal_dict):
437+
target = debugger.GetSelectedTarget()
438+
process = target.GetProcess()
439+
thread = process.GetSelectedThread()
440+
frame = thread.GetSelectedFrame()
441+
442+
konan_variable_symbols = list(filter(lambda v: __KONAN_VARIABLE.match(v.name), frame.GetModule().symbols))
443+
visited = list()
444+
for symbol in konan_variable_symbols:
445+
name = __KONAN_VARIABLE.search(symbol.name).group(1)
446+
447+
if name in visited:
448+
continue
449+
visited.append(name)
450+
451+
getters = list(filter(lambda v: re.match('^kfun:<get-{}>\\(\\).*$'.format(name), v.name), frame.module.symbols))
452+
if not getters:
453+
result.AppendMessage("storage not found for name:{}".format(name))
454+
continue
455+
456+
getter_functions = frame.module.FindFunctions(getters[0].name)
457+
if not getter_functions:
458+
continue
459+
460+
address = getter_functions[0].function.GetStartAddress().GetLoadAddress(target)
461+
type = __KONAN_VARIABLE_TYPE.search(getters[0].name).group(2)
462+
(c_type, extractor) = __TYPES_KONAN_TO_C[type] if type in __TYPES_KONAN_TO_C.keys() else ('struct ObjHeader *', lambda v: kotlin_object_type_summary(v))
463+
value = evaluate('(({0} (*)()){1:#x})()'.format(c_type, address))
464+
str_value = extractor(value)
465+
result.AppendMessage('{} {}: {}'.format(type, name, str_value))
466+
364467
def __lldb_init_module(debugger, _):
365468
__FACTORY['object'] = lambda x, y, z: KonanObjectSyntheticProvider(x, y, z)
366469
__FACTORY['array'] = lambda x, y, z: KonanArraySyntheticProvider(x, z)
@@ -380,5 +483,7 @@ def __lldb_init_module(debugger, _):
380483
--category Kotlin\
381484
')
382485
debugger.HandleCommand('type category enable Kotlin')
383-
debugger.HandleCommand('command script add -f {}.print_this_command print_this'.format(__name__))
384486
debugger.HandleCommand('command script add -f {}.clear_cache_command clear_kotlin_cache'.format(__name__))
487+
debugger.HandleCommand('command script add -f {}.type_name_command type_name'.format(__name__))
488+
debugger.HandleCommand('command script add -f {}.type_by_address_command type_by_address'.format(__name__))
489+
debugger.HandleCommand('command script add -f {}.symbol_by_name_command symbol_by_name'.format(__name__))

0 commit comments

Comments
 (0)