diff --git a/llvmDebugInfoC/src/scripts/konan_lldb.py b/llvmDebugInfoC/src/scripts/konan_lldb.py index 757bfff338a..b6b13d51d31 100644 --- a/llvmDebugInfoC/src/scripts/konan_lldb.py +++ b/llvmDebugInfoC/src/scripts/konan_lldb.py @@ -52,29 +52,17 @@ def evaluate(expr): def is_instance_of(addr, typeinfo): return evaluate("(bool)IsInstance({}, {})".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 -def is_string(value): - return is_instance_of(lldb_val_to_ptr(value), "theStringTypeInfo") - - -def is_array(value): - return evaluate("(int)Konan_DebugIsArray({})".format(lldb_val_to_ptr(value))).unsigned == 1 - - -def check_type_info(value): +def type_info(value): """This method checks self-referencing of pointer of first member of TypeInfo including case when object has an meta-object pointed by TypeInfo. Two lower bits are reserved for memory management needs see runtime/src/main/cpp/Memory.h.""" if str(value.type) != "struct ObjHeader *": return False - expr = "*(void **)((uintptr_t)(*(void**){0:#x}) & ~0x3) == **(void***)((uintptr_t)(*(void**){0:#x}) & ~0x3)".format(value.unsigned) + expr = "*(void **)((uintptr_t)(*(void**){0:#x}) & ~0x3) == **(void***)((uintptr_t)(*(void**){0:#x}) & ~0x3) ? *(void **)((uintptr_t)(*(void**){0:#x}) & ~0x3) : (void *)0".format(value.unsigned) result = evaluate(expr) - return result.IsValid() and result.GetValue() == "true" - -def type_info(value): - return evaluate("*(void **)((uintptr_t)(*(void**){0:#x}) & ~0x3)".format(value.unsigned)).unsigned - -# -# Some kind of forward declaration. + return result.unsigned if result.IsValid() and result.unsigned != 0 else None __FACTORY = {} @@ -83,37 +71,39 @@ __FACTORY = {} # Cache type info pointer to [ChildMetaInfo] SYNTHETIC_OBJECT_LAYOUT_CACHE = {} -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 - if not check_type_info(lldb_val): - return NULL - ptr = lldb_val_to_ptr(lldb_val) if ptr is None: return fallback - return select_provider(lldb_val).to_string() + tip = internal_dict["type_info"] if "type_info" in internal_dict.keys() else type_info(lldb_val) + if not tip: + return fallback + + return select_provider(lldb_val, tip, internal_dict).to_string() -def select_provider(lldb_val): - return __FACTORY['string'](lldb_val) if is_string(lldb_val) else __FACTORY['array'](lldb_val) if is_array( - lldb_val) else __FACTORY['object'](lldb_val) - +def select_provider(lldb_val, tip, internal_dict): + soa = is_string_or_array(lldb_val) + 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): + def __init__(self, valobj, amString): self._target = lldb.debugger.GetSelectedTarget() self._process = self._target.GetProcess() self._valobj = valobj self._ptr = lldb_val_to_ptr(self._valobj) - if is_string(valobj): + if amString: return - self._children_count = evaluate("(int)Konan_DebugGetFieldCount({})".format(self._ptr)).signed + if self._children_count == 0: + self._children_count = evaluate("(int)Konan_DebugGetFieldCount({})".format(self._ptr)).signed self._children = [] self._type_conversion = [ lambda address, name: self._valobj.CreateValueFromExpression(name, "(void *){:#x}".format(address)), @@ -163,15 +153,23 @@ class KonanHelperProvider(lldb.SBSyntheticValueProvider): log(lambda: "type:{0} of {1:#x} of {2:#x}".format(type, self._valobj.unsigned, self._valobj.unsigned + self._children[index].offset())) return type - def _deref_or_obj_summary(self, index): + def _deref_or_obj_summary(self, index, internal_dict): value = self._values[index] if not value: log(lambda : "_deref_or_obj_summary: value none, index:{}, type:{}".format(index, self._children[index].type())) return None - if check_type_info(value): - return kotlin_object_type_summary(value, None) - else: - return kotlin_object_type_summary(value.deref, None) + + tip = type_info(value) + if tip: + internal_dict["type_info"] = tip + return kotlin_object_type_summary(value, internal_dict) + tip = type_info(value.deref) + + if tip: + internal_dict["type_info"] = tip + return kotlin_object_type_summary(value.deref, internal_dict) + + return kotlin_object_type_summary(value.deref, internal_dict) def _field_address(self, index): return evaluate("(void *)Konan_DebugGetFieldAddress({}, {})".format(self._ptr, index)).unsigned @@ -181,7 +179,8 @@ class KonanHelperProvider(lldb.SBSyntheticValueProvider): class KonanStringSyntheticProvider(KonanHelperProvider): def __init__(self, valobj): - super(KonanStringSyntheticProvider, self).__init__(valobj) + self._children_count = 0 + super(KonanStringSyntheticProvider, self).__init__(valobj, True) fallback = valobj.GetValue() buff_len = evaluate( '(int)Konan_DebugObjectToUtf8Array({}, (char *)Konan_DebugBuffer(), (int)Konan_DebugBufferSize());'.format( @@ -239,8 +238,16 @@ class MemberLayout: return self._offset class KonanObjectSyntheticProvider(KonanHelperProvider): - def __init__(self, valobj, tip): - super(KonanObjectSyntheticProvider, self).__init__(valobj) + def __init__(self, valobj, tip, internal_dict): + # Save an extra call into the process + if tip in SYNTHETIC_OBJECT_LAYOUT_CACHE: + log(lambda : "TIP: {:#x} EARLYHIT".format(tip)) + self._children = SYNTHETIC_OBJECT_LAYOUT_CACHE[tip] + self._children_count = len(self._children) + else: + self._children_count = 0 + + super(KonanObjectSyntheticProvider, self).__init__(valobj, False) if not tip in SYNTHETIC_OBJECT_LAYOUT_CACHE: SYNTHETIC_OBJECT_LAYOUT_CACHE[tip] = [ @@ -274,16 +281,26 @@ class KonanObjectSyntheticProvider(KonanHelperProvider): return next(i for i,v in enumerate(self._children) if v.name() == name) def get_child_at_index(self, index): - return self._values[index] + result = self._values[index] + if result is None: + result = self._read_value(index) + self._values[index] = result + return result + # TODO: fix cyclic structures stringification. def to_string(self): - return dict([(self._children[i].name(), self._deref_or_obj_summary(i)) for i in range(self._children_count)]) - + 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): - super(KonanArraySyntheticProvider, self).__init__(valobj) + def __init__(self, valobj, internal_dict): + self._children_count = 0 + super(KonanArraySyntheticProvider, self).__init__(valobj, False) if self._ptr is None: return valobj.SetSyntheticChildrenGenerated(True) @@ -293,10 +310,15 @@ class KonanArraySyntheticProvider(KonanHelperProvider): 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._children_count)] + self._values = [self._read_value(i) for i in range(self.cap_children_count())] + self._internal_dict = internal_dict + + + def cap_children_count(self): + return self._children_count def num_children(self): - return self._children_count + return self.cap_children_count() def has_children(self): return self._children_count > 0 @@ -306,18 +328,23 @@ class KonanArraySyntheticProvider(KonanHelperProvider): return index if (0 <= index < self._children_count) else -1 def get_child_at_index(self, index): - return self._values[index] + result = self._values[index] + if result is None: + result = self._read_value(index) + self._values[index] = result + return result def to_string(self): return [self._deref_or_obj_summary(i) for i in range(self._children_count)] class KonanProxyTypeProvider: - def __init__(self, valobj, _): + def __init__(self, valobj, internal_dict): log(lambda : "proxy: {:#x}".format(valobj.unsigned)) - if not check_type_info(valobj): + tip = type_info(valobj) + if not tip: return - self._proxy = select_provider(valobj) + self._proxy = select_provider(valobj, tip, internal_dict) self.update() def __getattr__(self, item): @@ -331,9 +358,9 @@ def clear_cache_command(debugger, command, result, internal_dict): SYNTHETIC_OBJECT_LAYOUT_CACHE.clear() def __lldb_init_module(debugger, _): - __FACTORY['object'] = lambda x: KonanObjectSyntheticProvider(x, type_info(x)) - __FACTORY['array'] = lambda x: KonanArraySyntheticProvider(x) - __FACTORY['string'] = lambda x: KonanStringSyntheticProvider(x) + __FACTORY['object'] = lambda x, y, z: KonanObjectSyntheticProvider(x, y, z) + __FACTORY['array'] = lambda x, y, z: KonanArraySyntheticProvider(x, z) + __FACTORY['string'] = lambda x, y, _: KonanStringSyntheticProvider(x) debugger.HandleCommand('\ type summary add \ --no-value \