From 540e86523f94eb285fc9fc3047e1ad0c8316a966 Mon Sep 17 00:00:00 2001 From: Kevin Galligan Date: Fri, 9 Oct 2020 15:47:54 -0400 Subject: [PATCH] Speed up lldb script --- .../Contents/Resources/konan_lldb.py | 171 ++++++++++++++---- setup-xcode11.sh | 0 2 files changed, 138 insertions(+), 33 deletions(-) mode change 100644 => 100755 setup-xcode11.sh diff --git a/Kotlin.ideplugin/Contents/Resources/konan_lldb.py b/Kotlin.ideplugin/Contents/Resources/konan_lldb.py index 6691380..a3ccfa8 100644 --- a/Kotlin.ideplugin/Contents/Resources/konan_lldb.py +++ b/Kotlin.ideplugin/Contents/Resources/konan_lldb.py @@ -23,6 +23,7 @@ import lldb import struct +import re NULL = 'null' @@ -30,6 +31,10 @@ def log(msg): if False: print(msg()) +def log2(msg): + if True: + print(msg()) + def exelog(stmt): if False: f = open(os.getenv('HOME', '') + "/lldbexelog.txt", "a") @@ -49,11 +54,31 @@ def evaluate(expr): exelog(evallog) return result +def _symbol_loaded_address(name, debugger = lldb.debugger): + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + candidates = list(filter(lambda x: x.name == name, frame.module.symbols)) + # take first + for candidate in candidates: + address = candidate.GetStartAddress().GetLoadAddress(target) + log(lambda: "_symbol_loaded_address:{} {:#x}".format(name, address)) + return address + +def _type_info_by_address(address, debugger = lldb.debugger): + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + candidates = list(filter(lambda x: x.GetStartAddress().GetLoadAddress(target) == address, frame.module.symbols)) + return candidates + def is_instance_of(addr, typeinfo): - return evaluate("(bool)IsInstance({}, {})".format(addr, typeinfo)).GetValue() == "true" + return evaluate("(bool)IsInstance({}, {:#x})".format(addr, typeinfo)).GetValue() == "true" def is_string_or_array(value): - return evaluate("(bool)IsInstance({0}, theStringTypeInfo) ? 1 : ((int)Konan_DebugIsArray({0}) ? 2 : 0)".format(lldb_val_to_ptr(value))).unsigned + 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 def type_info(value): """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): TO_STRING_DEPTH = 2 ARRAY_TO_STRING_LIMIT = 10 -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.""" log(lambda: "kotlin_object_type_summary({:#x})".format(lldb_val.unsigned)) fallback = lldb_val.GetValue() if str(lldb_val.type) != "struct ObjHeader *": - return fallback - - ptr = lldb_val_to_ptr(lldb_val) - if ptr is None: - return fallback + if lldb_val.GetValue() is None: + return NULL + return lldb_val.GetValueAsSigned() tip = internal_dict["type_info"] if "type_info" in internal_dict.keys() else type_info(lldb_val) if not tip: @@ -93,17 +116,20 @@ def kotlin_object_type_summary(lldb_val, internal_dict = []): def select_provider(lldb_val, tip, internal_dict): soa = is_string_or_array(lldb_val) + log(lambda : "select_provider: {} : {}".format(lldb_val, soa)) return __FACTORY['string'](lldb_val, tip, internal_dict) if soa == 1 else __FACTORY['array'](lldb_val, tip, internal_dict) if soa == 2 \ else __FACTORY['object'](lldb_val, tip, internal_dict) class KonanHelperProvider(lldb.SBSyntheticValueProvider): - def __init__(self, valobj, amString): + def __init__(self, valobj, amString, internal_dict = {}): self._target = lldb.debugger.GetSelectedTarget() self._process = self._target.GetProcess() self._valobj = valobj self._ptr = lldb_val_to_ptr(self._valobj) if amString: return + self._internal_dict = internal_dict.copy() + self._to_string_depth = TO_STRING_DEPTH if "to_string_depth" not in self._internal_dict.keys() else self._internal_dict["to_string_depth"] if self._children_count == 0: self._children_count = evaluate("(int)Konan_DebugGetFieldCount({})".format(self._ptr)).signed self._children = [] @@ -142,6 +168,8 @@ def _read_value(self, index): return self._type_conversion[int(value_type)](address, str(self._children[index].name())) def _create_synthetic_child(self, address, name): + if self._to_string_depth == 0: + return None index = self.get_child_index(name) value = self._valobj.CreateChildAtOffset(str(name), self._children[index].offset(), @@ -184,17 +212,16 @@ def __init__(self, valobj): self._children_count = 0 super(KonanStringSyntheticProvider, self).__init__(valobj, True) fallback = valobj.GetValue() + buff_addr = evaluate("(void *)Konan_DebugBuffer()").unsigned buff_len = evaluate( - '(int)Konan_DebugObjectToUtf8Array({}, (char *)Konan_DebugBuffer(), (int)Konan_DebugBufferSize());'.format( - self._ptr) - ).unsigned + '(int)Konan_DebugObjectToUtf8Array({}, (void *){:#x}, (int)Konan_DebugBufferSize());'.format( + self._ptr, buff_addr) + ).signed if not buff_len: self._representation = fallback return - buff_addr = evaluate("(char *)Konan_DebugBuffer()").unsigned - error = lldb.SBError() s = self._process.ReadCStringFromMemory(int(buff_addr), int(buff_len), error) if not error.Success(): @@ -249,7 +276,7 @@ def __init__(self, valobj, tip, internal_dict): else: self._children_count = 0 - super(KonanObjectSyntheticProvider, self).__init__(valobj, False) + super(KonanObjectSyntheticProvider, self).__init__(valobj, False, internal_dict) if not tip in SYNTHETIC_OBJECT_LAYOUT_CACHE: SYNTHETIC_OBJECT_LAYOUT_CACHE[tip] = [ @@ -260,13 +287,11 @@ def __init__(self, valobj, tip, internal_dict): log(lambda : "TIP: {:#x} HIT".format(tip)) self._children = SYNTHETIC_OBJECT_LAYOUT_CACHE[tip] self._values = [self._read_value(index) for index in range(self._children_count)] - self._internal_dict = internal_dict - self._to_string_depth = TO_STRING_DEPTH if "to_string_depth" not in self._internal_dict.keys() else self._internal_dict["to_string_depth"] def _field_name(self, index): error = lldb.SBError() - name = self._read_string("(const char *)Konan_DebugGetFieldName({}, (int){})".format(self._ptr, index), error) + name = self._read_string("(void *)Konan_DebugGetFieldName({}, (int){})".format(self._ptr, index), error) if not error.Success(): raise DebuggerException() return name @@ -294,17 +319,18 @@ def get_child_at_index(self, index): # TODO: fix cyclic structures stringification. def to_string(self): - if self._to_string_depth == 0: - return "..." - else: - internal_dict = self._internal_dict.copy() - internal_dict["to_string_depth"] = self._to_string_depth - 1 - return dict([(self._children[i].name(), self._deref_or_obj_summary(i, internal_dict)) for i in range(self._children_count)]) + return "..." +# if self._to_string_depth == 0: +# return "..." +# else: +# internal_dict = self._internal_dict.copy() +# internal_dict["to_string_depth"] = self._to_string_depth - 1 +# return dict([(self._children[i].name(), self._deref_or_obj_summary(i, internal_dict)) for i in range(self._children_count)]) class KonanArraySyntheticProvider(KonanHelperProvider): def __init__(self, valobj, internal_dict): self._children_count = 0 - super(KonanArraySyntheticProvider, self).__init__(valobj, False) + super(KonanArraySyntheticProvider, self).__init__(valobj, False, internal_dict) if self._ptr is None: return valobj.SetSyntheticChildrenGenerated(True) @@ -314,8 +340,7 @@ def __init__(self, valobj, internal_dict): offset = zerro_address - valobj.unsigned size = first_address - zerro_address self._children = [MemberLayout(str(x), type, offset + x * size) for x in range(self.num_children())] - self._values = [self._read_value(i) for i in range(self.cap_children_count())] - self._internal_dict = internal_dict + self._values = [self._read_value(i) for i in range(min(ARRAY_TO_STRING_LIMIT, self._children_count))] def cap_children_count(self): @@ -339,28 +364,106 @@ def get_child_at_index(self, index): return result def to_string(self): - return [self._deref_or_obj_summary(i, self._internal_dict.copy()) for i in range(min(ARRAY_TO_STRING_LIMIT, self._children_count))] + return '[%x]' % self._children_count +# return [self._deref_or_obj_summary(i, self._internal_dict.copy()) for i in range(min(ARRAY_TO_STRING_LIMIT, self._children_count))] class KonanProxyTypeProvider: def __init__(self, valobj, internal_dict): log(lambda : "proxy: {:#x}".format(valobj.unsigned)) tip = type_info(valobj) + log(lambda : "KonanProxyTypeProvider: tip: {:#x}".format(tip)) if not tip: return self._proxy = select_provider(valobj, tip, internal_dict) + log(lambda: "KonanProxyTypeProvider: _proxy: {}".format(self._proxy.__class__.__name__)) self.update() def __getattr__(self, item): return getattr(self._proxy, item) -def print_this_command(debugger, command, result, internal_dict): - pthis = lldb.frame.FindVariable('') - print(pthis) - def clear_cache_command(debugger, command, result, internal_dict): SYNTHETIC_OBJECT_LAYOUT_CACHE.clear() + +def type_name_command(debugger, command, result, internal_dict): + result.AppendMessage(evaluate('(char *)Konan_DebugGetTypeName({})'.format(command)).summary) + +__KONAN_VARIABLE = re.compile('kvar:(.*)#internal') +__KONAN_VARIABLE_TYPE = re.compile('^kfun:\\(\\)(.*)$') +__TYPES_KONAN_TO_C = { + 'kotlin.Byte': ('int8_t', lambda v: v.signed), + 'kotlin.Short': ('short', lambda v: v.signed), + 'kotlin.Int': ('int', lambda v: v.signed), + 'kotlin.Long': ('long', lambda v: v.signed), + 'kotlin.UByte': ('int8_t', lambda v: v.unsigned), + 'kotlin.UShort': ('short', lambda v: v.unsigned), + 'kotlin.UInt': ('int', lambda v: v.unsigned), + 'kotlin.ULong': ('long', lambda v: v.unsigned), + 'kotlin.Char': ('short', lambda v: v.signed), + 'kotlin.Boolean': ('bool', lambda v: v.signed), + 'kotlin.Float': ('float', lambda v: v.value), + 'kotlin.Double': ('double', lambda v: v.value) +} + +def type_by_address_command(debugger, command, result, internal_dict): + result.AppendMessage("DEBUG: {}".format(command)) + tokens = command.split() + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + types = _type_info_by_address(tokens[0]) + result.AppendMessage("DEBUG: {}".format(types)) + for t in types: + result.AppendMessage("{}: {:#x}".format(t.name, t.GetStartAddress().GetLoadAddress(target))) + +def symbol_by_name_command(debugger, command, result, internal_dict): + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + tokens = command.split() + mask = re.compile(tokens[0]) + symbols = list(filter(lambda v: mask.match(v.name), frame.GetModule().symbols)) + visited = list() + for symbol in symbols: + name = symbol.name + if name in visited: + continue + visited.append(name) + result.AppendMessage("{}: {:#x}".format(name, symbol.GetStartAddress().GetLoadAddress(target))) + +def konan_globals_command(debugger, command, result, internal_dict): + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + + konan_variable_symbols = list(filter(lambda v: __KONAN_VARIABLE.match(v.name), frame.GetModule().symbols)) + visited = list() + for symbol in konan_variable_symbols: + name = __KONAN_VARIABLE.search(symbol.name).group(1) + + if name in visited: + continue + visited.append(name) + + getters = list(filter(lambda v: re.match('^kfun:\\(\\).*$'.format(name), v.name), frame.module.symbols)) + if not getters: + result.AppendMessage("storage not found for name:{}".format(name)) + continue + + getter_functions = frame.module.FindFunctions(getters[0].name) + if not getter_functions: + continue + + address = getter_functions[0].function.GetStartAddress().GetLoadAddress(target) + type = __KONAN_VARIABLE_TYPE.search(getters[0].name).group(2) + (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)) + value = evaluate('(({0} (*)()){1:#x})()'.format(c_type, address)) + str_value = extractor(value) + result.AppendMessage('{} {}: {}'.format(type, name, str_value)) + def __lldb_init_module(debugger, _): __FACTORY['object'] = lambda x, y, z: KonanObjectSyntheticProvider(x, y, z) __FACTORY['array'] = lambda x, y, z: KonanArraySyntheticProvider(x, z) @@ -380,5 +483,7 @@ def __lldb_init_module(debugger, _): --category Kotlin\ ') debugger.HandleCommand('type category enable Kotlin') - debugger.HandleCommand('command script add -f {}.print_this_command print_this'.format(__name__)) debugger.HandleCommand('command script add -f {}.clear_cache_command clear_kotlin_cache'.format(__name__)) + debugger.HandleCommand('command script add -f {}.type_name_command type_name'.format(__name__)) + debugger.HandleCommand('command script add -f {}.type_by_address_command type_by_address'.format(__name__)) + debugger.HandleCommand('command script add -f {}.symbol_by_name_command symbol_by_name'.format(__name__)) diff --git a/setup-xcode11.sh b/setup-xcode11.sh old mode 100644 new mode 100755