-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b671aa1
commit 3e311d5
Showing
5 changed files
with
905 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,300 @@ | ||
#!/usr/bin/python | ||
|
||
## | ||
# Copyright 2010-2017 JetBrains s.r.o. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
# | ||
# (lldb) command script import llvmDebugInfoC/src/scripts/konan_lldb.py | ||
# (lldb) p kotlin_variable | ||
# | ||
|
||
import lldb | ||
import struct | ||
|
||
NULL = 'null' | ||
|
||
|
||
def lldb_val_to_ptr(lldb_val): | ||
addr = lldb_val.GetValueAsUnsigned() | ||
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 | ||
|
||
|
||
def check_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}) & ~0x3) == **(void***)((uintptr_t)(*(void**){0}) & ~0x3)".format(value.unsigned) | ||
result = evaluate(expr) | ||
return result.IsValid() and result.GetValue() == "true" | ||
|
||
|
||
# | ||
# 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.""" | ||
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() | ||
|
||
|
||
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 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 = int(evaluate("(int)Konan_DebugGetFieldCount({})".format(self._ptr)).GetValue()) | ||
self._children = [] | ||
self._children_types = [] | ||
self._children_type_names = [] | ||
self._children_type_addresses = [] | ||
self._type_conversion = [ | ||
lambda address, name: self._valobj.CreateValueFromExpression(name, "(void *){:#x}".format(address)), | ||
lambda address, name: self._create_synthetic_child(address, name), | ||
lambda address, name: self._valobj.CreateValueFromExpression(name, "(int8_t *){:#x}".format(address)), | ||
lambda address, name: self._valobj.CreateValueFromExpression(name, "(int16_t *){:#x}".format(address)), | ||
lambda address, name: self._valobj.CreateValueFromExpression(name, "(int32_t *){:#x}".format(address)), | ||
lambda address, name: self._valobj.CreateValueFromExpression(name, "(int64_t *){:#x}".format(address)), | ||
lambda address, name: self._valobj.CreateValueFromExpression(name, "(float *){:#x}".format(address)), | ||
lambda address, name: self._valobj.CreateValueFromExpression(name, "(double *){:#x}".format(address)), | ||
lambda address, name: self._valobj.CreateValueFromExpression(name, "(void **){:#x}".format(address)), | ||
lambda address, name: self._valobj.CreateValueFromExpression(name, "(bool *){:#x}".format(address)), | ||
lambda address, name: None] | ||
|
||
self._types = [ | ||
valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType(), | ||
valobj.GetType(), | ||
valobj.GetType().GetBasicType(lldb.eBasicTypeChar), | ||
valobj.GetType().GetBasicType(lldb.eBasicTypeShort), | ||
valobj.GetType().GetBasicType(lldb.eBasicTypeInt), | ||
valobj.GetType().GetBasicType(lldb.eBasicTypeLongLong), | ||
valobj.GetType().GetBasicType(lldb.eBasicTypeFloat), | ||
valobj.GetType().GetBasicType(lldb.eBasicTypeDouble), | ||
valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType(), | ||
valobj.GetType().GetBasicType(lldb.eBasicTypeBool) | ||
] | ||
self._children_types = [ | ||
evaluate("(int)Konan_DebugGetFieldType({}, {})".format(self._ptr, child)).GetValueAsUnsigned() | ||
for child in range(self._children_count)] | ||
self._children_type_addresses = [ | ||
int(evaluate("(void *)Konan_DebugGetFieldAddress({}, {})".format(self._ptr, index)).GetValue(), 0) for | ||
index in range(self._children_count)] | ||
|
||
def _read_string(self, expr, error): | ||
return self._process.ReadCStringFromMemory(int(evaluate(expr).GetValue(), 0), 0x1000, error) | ||
|
||
def _read_value(self, index): | ||
value_type = self._children_types[index] | ||
address = self._children_type_addresses[index] | ||
return self._type_conversion[int(value_type)](address, str(self._children[index])) | ||
|
||
def _create_synthetic_child(self, address, name): | ||
index = self.get_child_index(name) | ||
value = self._valobj.CreateChildAtOffset(str(name), | ||
self._children_type_addresses[ | ||
index] - self._valobj.GetValueAsUnsigned(), | ||
self._read_type(index)) | ||
value.SetSyntheticChildrenGenerated(True) | ||
value.SetPreferSyntheticValue(True) | ||
return value | ||
|
||
def _read_type(self, index): | ||
return self._types[int(evaluate("(int)Konan_DebugGetFieldType({}, {})".format(self._ptr, index)).GetValue())] | ||
|
||
def _deref_or_obj_summary(self, index): | ||
value = self._values[index] | ||
if not value: | ||
print("_deref_or_obj_summary: value none, index:{}, type:{}".format(index, self._children_types[index])) | ||
return None | ||
if check_type_info(value): | ||
return kotlin_object_type_summary(value, None) | ||
else: | ||
return kotlin_object_type_summary(value.deref, None) | ||
|
||
|
||
class KonanStringSyntheticProvider(KonanHelperProvider): | ||
def __init__(self, valobj): | ||
super(KonanStringSyntheticProvider, self).__init__(valobj) | ||
fallback = valobj.GetValue() | ||
buff_len = evaluate( | ||
'(int)Konan_DebugObjectToUtf8Array({}, (char *)Konan_DebugBuffer(), (int)Konan_DebugBufferSize());'.format( | ||
self._ptr) | ||
).unsigned | ||
|
||
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(): | ||
raise DebuggerException() | ||
self._representation = s if error.Success() else fallback | ||
self._logger = lldb.formatters.Logger.Logger() | ||
|
||
def update(self): | ||
pass | ||
|
||
def num_children(self): | ||
return 0 | ||
|
||
def has_children(self): | ||
return False | ||
|
||
def get_child_index(self, _): | ||
return None | ||
|
||
def get_child_at_index(self, _): | ||
return None | ||
|
||
def to_string(self): | ||
return self._representation | ||
|
||
|
||
class DebuggerException(Exception): | ||
pass | ||
|
||
|
||
class KonanObjectSyntheticProvider(KonanHelperProvider): | ||
def __init__(self, valobj): | ||
super(KonanObjectSyntheticProvider, self).__init__(valobj) | ||
error = lldb.SBError() | ||
self._children = [ | ||
self._read_string("(const char *)Konan_DebugGetFieldName({}, (int){})".format(self._ptr, i), error) for i in | ||
range(self._children_count) if error.Success()] | ||
if not error.Success(): | ||
raise DebuggerException() | ||
self._values = [self._read_value(index) for index in range(self._children_count)] | ||
|
||
def num_children(self): | ||
return self._children_count | ||
|
||
def has_children(self): | ||
return self._children_count > 0 | ||
|
||
def get_child_index(self, name): | ||
if not name in self._children: | ||
return -1 | ||
return self._children.index(name) | ||
|
||
def get_child_at_index(self, index): | ||
return self._values[index] | ||
|
||
# TODO: fix cyclic structures stringification. | ||
def to_string(self): | ||
return dict([(self._children[i], self._deref_or_obj_summary(i)) for i in range(self._children_count)]) | ||
|
||
|
||
class KonanArraySyntheticProvider(KonanHelperProvider): | ||
def __init__(self, valobj): | ||
super(KonanArraySyntheticProvider, self).__init__(valobj) | ||
if self._ptr is None: | ||
return | ||
valobj.SetSyntheticChildrenGenerated(True) | ||
self._children = [x for x in range(self.num_children())] | ||
self._values = [self._read_value(i) for i in range(self._children_count)] | ||
|
||
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._children_count) else -1 | ||
|
||
def get_child_at_index(self, index): | ||
return self._read_value(index) | ||
|
||
def to_string(self): | ||
return [self._deref_or_obj_summary(i) for i in range(self._children_count)] | ||
|
||
|
||
class KonanProxyTypeProvider: | ||
def __init__(self, valobj, _): | ||
if not check_type_info(valobj): | ||
return | ||
self._proxy = select_provider(valobj) | ||
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 __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 \ | ||
--expand \ | ||
--python-function konan_lldb.kotlin_object_type_summary \ | ||
"struct ObjHeader *" \ | ||
--category Kotlin\ | ||
') | ||
debugger.HandleCommand('\ | ||
type synthetic add \ | ||
--python-class konan_lldb.KonanProxyTypeProvider\ | ||
"ObjHeader *" \ | ||
--category Kotlin\ | ||
') | ||
debugger.HandleCommand('type category enable Kotlin') | ||
debugger.HandleCommand('command script add -f {}.print_this_command print_this'.format(__name__)) |
Oops, something went wrong.