Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 138 additions & 33 deletions Kotlin.ideplugin/Contents/Resources/konan_lldb.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@

import lldb
import struct
import re

NULL = 'null'

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")
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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 = []
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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] = [
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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):
Expand All @@ -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('<this>')
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:<get-(.*)>\\(\\)(.*)$')
__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:<get-{}>\\(\\).*$'.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)
Expand All @@ -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__))
Empty file modified setup-xcode11.sh
100644 → 100755
Empty file.