From bae852a42749a3d3fa9e13fca380fd498a5724e4 Mon Sep 17 00:00:00 2001 From: Vasily Levchenko Date: Thu, 24 May 2018 17:59:26 +0300 Subject: [PATCH] [lldb][python binding] using new RTTI support --- llvmDebugInfoC/src/scripts/konan_lldb.py | 253 +++++++++++++++-------- runtime/src/main/cpp/KDebug.cpp | 6 +- 2 files changed, 169 insertions(+), 90 deletions(-) diff --git a/llvmDebugInfoC/src/scripts/konan_lldb.py b/llvmDebugInfoC/src/scripts/konan_lldb.py index ba780e72819..747de503d92 100644 --- a/llvmDebugInfoC/src/scripts/konan_lldb.py +++ b/llvmDebugInfoC/src/scripts/konan_lldb.py @@ -22,13 +22,37 @@ # import lldb -import ctypes +import struct + +NULL = 'null' + def lldb_val_to_ptr(lldb_val): addr = lldb_val.GetValueAsUnsigned() - if addr == 0: - return None - return '((struct ObjHeader *) {})'.format(addr) + return '((struct ObjHeader *) {:#x})'.format(addr) + + +def evaluate(expr): + return lldb.debugger.GetSelectedTarget().EvaluateExpression(expr, lldb.SBExpressionOptions()) + + +def is_instance_of(addr, typeinfo): + return evaluate("(bool)IsInstance({}, {})".format(addr, typeinfo)).GetValue() == "true" + + +def is_string(value): + return is_instance_of(lldb_val_to_ptr(value), "theStringTypeInfo") + + +def is_array(value): + return int(evaluate("(int)Konan_DebugIsArray({})".format(lldb_val_to_ptr(value))).GetValue()) == 1 + +# +# Some kind of forward declaration. + + +__FACTORY = {} + def kotlin_object_type_summary(lldb_val, internal_dict): """Hook that is run by lldb to display a Kotlin object.""" @@ -36,115 +60,170 @@ def kotlin_object_type_summary(lldb_val, internal_dict): if str(lldb_val.type) != "struct ObjHeader *": return fallback + if int(fallback, 0) == 0: + return NULL + ptr = lldb_val_to_ptr(lldb_val) if ptr is None: return fallback - def evaluate(expr): - return lldb_val.GetTarget().EvaluateExpression(expr, lldb.SBExpressionOptions()) + return select_provider(lldb_val).to_string() - buff_len = evaluate( - "(int)Konan_DebugObjectToUtf8Array({}, (char *)Konan_DebugBuffer(), (int)Konan_DebugBufferSize());".format(ptr) - ).unsigned - - if not buff_len: - return fallback - - buff_addr = evaluate("(char *)Konan_DebugBuffer()").unsigned - - error = lldb.SBError() - s = lldb_val.GetProcess().ReadCStringFromMemory(int(buff_addr), int(buff_len), error) - return s if error.Success() else fallback +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) -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 +class KonanHelperProvider(lldb.SBSyntheticValueProvider): + def __init__(self, valobj): + 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): + return + self._children_count = 0 + self._children = [] + self._type_conversion = {0: lambda address, _: "{:#x}".format(address), + 1: lambda address, _: kotlin_object_type_summary(evaluate("(*(struct ObjHeader **){:#x})".format(address)), {}), + 2: lambda address, error: self.__read_memory(address, " 0 + def get_child_index(self, _): + return None + + def get_child_at_index(self, _): + return None + + def to_string(self): + return self._representation + + +class KonanObjectSyntheticProvider(KonanHelperProvider): + def __init__(self, valobj): + super(KonanObjectSyntheticProvider, self).__init__(valobj) + self.update() + pass + + def update(self): + super(KonanObjectSyntheticProvider, self).update() + error = lldb.SBError() + self._children = [ + self._read_string("(const char *)Konan_DebugGetFieldName({}, (int){})".format(self._ptr, i), error) for i in + range(0, self._children_count) if error.Success()] + + def num_children(self): + return self._children_count + + def has_children(self): + return self._children_count > 0 def get_child_index(self, name): - if self._element_type is None: - return None + return self._children.index(name) - 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 + return evaluate("(void *)Konan_DebugGetFieldAddress({}, {})".format(self._ptr, index)).GetValue() - 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 to_string(self): + error = lldb.SBError() + return dict([(self._children[i], self._read_value(i, error)) for i in range(0, self._children_count)]) -def __lldb_init_module(debugger, internal_dict): + +class KonanArraySyntheticProvider(KonanHelperProvider): + def __init__(self, valobj): + super(KonanArraySyntheticProvider, self).__init__(valobj) + if self._ptr is None: + return + self.update() + + def update(self): + super(KonanArraySyntheticProvider, self).update() + self._children = [x for x in range(0, self.num_children())] + pass + + def num_children(self): + return self._children_count + + def has_children(self): + return self._children_count > 0 + + def get_child_index(self, name): + index = int(name) + return index if (0 <= index < self._num_children) else None + + def get_child_at_index(self, index): + error = lldb.SBError() + return self._read_value(index, error) if error.Success() else None + + def to_string(self): + error = lldb.SBError() + return [self._read_value(i, error) for i in range(0, self._children_count)] + + +class KonanProxyTypeProvider: + def __init__(self, valobj, _): + self._proxy = select_provider(valobj) + self.update() + + def __getattr__(self, item): + return getattr(self._proxy, item) + + +def __lldb_init_module(debugger, _): + __FACTORY['object'] = lambda x: KonanObjectSyntheticProvider(x) + __FACTORY['array'] = lambda x: KonanArraySyntheticProvider(x) + __FACTORY['string'] = lambda x: KonanStringSyntheticProvider(x) debugger.HandleCommand('\ type summary add \ --no-value \ @@ -155,7 +234,7 @@ def __lldb_init_module(debugger, internal_dict): ') debugger.HandleCommand('\ type synthetic add \ - --python-class konan_lldb.KonanArraySyntheticChildrenProvider\ + --python-class konan_lldb.KonanProxyTypeProvider\ "ObjHeader *" \ --category Kotlin\ ') diff --git a/runtime/src/main/cpp/KDebug.cpp b/runtime/src/main/cpp/KDebug.cpp index 5d4ffb760ea..6937d4aff2c 100644 --- a/runtime/src/main/cpp/KDebug.cpp +++ b/runtime/src/main/cpp/KDebug.cpp @@ -108,7 +108,7 @@ RUNTIME_USED int Konan_DebugGetFieldType(KRef obj, int index) { return Konan_RuntimeType::RT_INVALID; if (extendedTypeInfo->fieldsCount_ < 0) - return -typeInfo->fieldsCount_; + return -extendedTypeInfo->fieldsCount_; if (index >= extendedTypeInfo->fieldsCount_) return Konan_RuntimeType::RT_INVALID; @@ -127,10 +127,10 @@ RUNTIME_USED void* Konan_DebugGetFieldAddress(KRef obj, int index) { return nullptr; if (extendedTypeInfo->fieldsCount_ < 0) { - if (index >= obj->array()->count_) + if (index > obj->array()->count_) return nullptr; - return reinterpret_cast(obj + 1) + index * runtimeTypeSize[-typeInfo->fieldsCount_]; + return reinterpret_cast(obj->array() + 1) + index * runtimeTypeSize[-extendedTypeInfo->fieldsCount_]; } if (index >= extendedTypeInfo->fieldsCount_)