Skip to content

Commit

Permalink
Merge pull request #28 from touchlab/kpg/lldb_speed_up
Browse files Browse the repository at this point in the history
Speeding up debugging
  • Loading branch information
kpgalligan authored Oct 12, 2020
2 parents cb9580c + 7379aa7 commit 934abda
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 21 deletions.
58 changes: 37 additions & 21 deletions Kotlin.ideplugin/Contents/Resources/konan_lldb.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import lldb
import struct
import re
import os
import sys

NULL = 'null'

Expand All @@ -32,12 +34,14 @@ def log(msg):
print(msg())

def log2(msg):
if True:
if False:
print(msg())

def exelog(stmt):
def exelog(source, stmt):
if False:
f = open(os.getenv('HOME', '') + "/lldbexelog.txt", "a")
f.write(source)
f.write(" - ")
f.write(stmt())
f.write("\n")
f.close()
Expand All @@ -47,11 +51,11 @@ def lldb_val_to_ptr(lldb_val):
return '((struct ObjHeader *) {:#x})'.format(addr)


def evaluate(expr):
def evaluate(source, expr):
result = lldb.debugger.GetSelectedTarget().EvaluateExpression(expr, lldb.SBExpressionOptions())
evallog = lambda : "{} => {}".format(expr, result)
log(evallog)
exelog(evallog)
exelog(source, evallog)
return result

def _symbol_loaded_address(name, debugger = lldb.debugger):
Expand All @@ -75,18 +79,18 @@ def _type_info_by_address(address, debugger = lldb.debugger):
return candidates

def is_instance_of(addr, typeinfo):
return evaluate("(bool)IsInstance({}, {:#x})".format(addr, typeinfo)).GetValue() == "true"
return evaluate("is_instance_of", "(bool)IsInstance({}, {:#x})".format(addr, typeinfo)).GetValue() == "true"

def is_string_or_array(value):
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
return evaluate("is_string_or_array", "(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
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 *":
if value.GetTypeName() != "ObjHeader *":
return False
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)
result = evaluate("type_info", expr)
return result.unsigned if result.IsValid() and result.unsigned != 0 else None


Expand All @@ -100,23 +104,27 @@ def type_info(value):

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))
log(lambda: "kotlin_object_type_summary({:#x}, {})".format(lldb_val.unsigned, internal_dict))
fallback = lldb_val.GetValue()
if str(lldb_val.type) != "struct ObjHeader *":
if lldb_val.GetValue() is None:
return NULL
return lldb_val.GetValueAsSigned()

if lldb_val.unsigned == 0:
return NULL
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, tip, internal_dict):
log(lambda : "select_provider: name:{} : {}, {}".format(lldb_val.name, lldb_val, internal_dict))
soa = is_string_or_array(lldb_val)
log(lambda : "select_provider: {} : {}".format(lldb_val, soa))
log(lambda : "select_provider: {} : soa: {}".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)

Expand All @@ -131,7 +139,7 @@ def __init__(self, valobj, amString, internal_dict = {}):
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_count = self._calc_child_count()
self._children = []
self._type_conversion = [
lambda address, name: self._valobj.CreateValueFromExpression(name, "(void *){:#x}".format(address)),
Expand Down Expand Up @@ -159,8 +167,12 @@ def __init__(self, valobj, amString, internal_dict = {}):
valobj.GetType().GetBasicType(lldb.eBasicTypeBool)
]

# This is a little ugly, as we need to have self._ptr set before calling, but this allows arrays to be manageable
def _calc_child_count(self):
return evaluate("KonanHelperProvider", "(int)Konan_DebugGetFieldCount({})".format(self._ptr)).signed

def _read_string(self, expr, error):
return self._process.ReadCStringFromMemory(evaluate(expr).unsigned, 0x1000, error)
return self._process.ReadCStringFromMemory(evaluate("_read_string", expr).unsigned, 0x1000, error)

def _read_value(self, index):
value_type = self._children[index].type()
Expand Down Expand Up @@ -202,18 +214,18 @@ def _deref_or_obj_summary(self, index, 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
return evaluate("_field_address", "(void *)Konan_DebugGetFieldAddress({}, {})".format(self._ptr, index)).unsigned

def _field_type(self, index):
return evaluate("(int)Konan_DebugGetFieldType({}, {})".format(self._ptr, index)).unsigned
return evaluate("_field_type", "(int)Konan_DebugGetFieldType({}, {})".format(self._ptr, index)).unsigned

class KonanStringSyntheticProvider(KonanHelperProvider):
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(
buff_addr = evaluate("KonanStringSyntheticProvider", "(void *)Konan_DebugBuffer()").unsigned
buff_len = evaluate("KonanStringSyntheticProvider",
'(int)Konan_DebugObjectToUtf8Array({}, (void *){:#x}, (int)Konan_DebugBufferSize());'.format(
self._ptr, buff_addr)
).signed
Expand Down Expand Up @@ -342,6 +354,9 @@ def __init__(self, valobj, internal_dict):
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(min(ARRAY_TO_STRING_LIMIT, self._children_count))]

def _calc_child_count(self):
self._real_child_count = evaluate("KonanHelperProvider", "(int)Konan_DebugGetFieldCount({})".format(self._ptr)).signed
return min(ARRAY_TO_STRING_LIMIT, self._real_child_count)

def cap_children_count(self):
return self._children_count
Expand All @@ -364,17 +379,18 @@ def get_child_at_index(self, index):
return result

def to_string(self):
return '[%x]' % self._children_count
return '[%x]' % self._real_child_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))
log(lambda : "KonanProxyTypeProvider: {:#x}".format(valobj.unsigned))
tip = type_info(valobj)
log(lambda : "KonanProxyTypeProvider: tip: {:#x}".format(tip))

if not tip:
return
log(lambda : "KonanProxyTypeProvider: tip: {:#x}".format(tip))
self._proxy = select_provider(valobj, tip, internal_dict)
log(lambda: "KonanProxyTypeProvider: _proxy: {}".format(self._proxy.__class__.__name__))
self.update()
Expand All @@ -387,7 +403,7 @@ def clear_cache_command(debugger, command, result, internal_dict):


def type_name_command(debugger, command, result, internal_dict):
result.AppendMessage(evaluate('(char *)Konan_DebugGetTypeName({})'.format(command)).summary)
result.AppendMessage(evaluate("type_name_command", '(char *)Konan_DebugGetTypeName({})'.format(command)).summary)

__KONAN_VARIABLE = re.compile('kvar:(.*)#internal')
__KONAN_VARIABLE_TYPE = re.compile('^kfun:<get-(.*)>\\(\\)(.*)$')
Expand Down Expand Up @@ -460,7 +476,7 @@ def konan_globals_command(debugger, command, result, internal_dict):
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))
value = evaluate("konan_globals_command", '(({0} (*)()){1:#x})()'.format(c_type, address))
str_value = extractor(value)
result.AppendMessage('{} {}: {}'.format(type, name, str_value))

Expand Down
41 changes: 41 additions & 0 deletions setup-xcode11-dev.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash

# Use this script for Xcode 11.

###################
# This script is specifically for development of the plugin. Currently, the goal is just to move the new formatting scripts
# into place. Much faster round trip than killing Xcode, etc.
# Do *not* use this script if you're not actively developing the plugin.
###################

###################
# DEFINITIONS
###################

service='Xcode'
plugins_dir=~/Library/Developer/Xcode/Plug-ins

###################
# CREATE PLUG-IN
###################

echo "Creating new Kotlin plugin"
mkdir -p $plugins_dir
cp -r Kotlin.ideplugin $plugins_dir

###################
# LLDB DEFINITIONS
###################

lldb_config="command script import ~/Library/Developer/Xcode/Plug-ins/Kotlin.ideplugin/Contents/Resources/konan_lldb_config.py"
lldb_format="command script import ~/Library/Developer/Xcode/Plug-ins/Kotlin.ideplugin/Contents/Resources/konan_lldb.py"

if grep --quiet -s konan_lldb ~/.lldbinit-Xcode
then
# code if found
echo "konan_lldb.py found in ~/.lldbinit-Xcode"
else
# code if not found
echo $lldb_config >> ~/.lldbinit-Xcode
echo $lldb_format >> ~/.lldbinit-Xcode
fi

0 comments on commit 934abda

Please sign in to comment.