Add synthetic objects with dynamic fields and add an example
This commit is contained in:
parent
ce10f372d1
commit
246f232f0a
2 changed files with 114 additions and 15 deletions
|
|
@ -12,27 +12,50 @@
|
|||
## return any Nim object, array, or sequence. Returning a Nim object
|
||||
## will display the fields and values of the object as children.
|
||||
## Returning an array or sequence will display children with the index
|
||||
## and value.
|
||||
## surrounded by square brackets as the key name
|
||||
##
|
||||
## You may also return a Nim table that contains the string
|
||||
## "LLDBDynamicObject" (case insensitive). This allows for dynamic
|
||||
## fields to be created at runtime instead of at compile time if you
|
||||
## return a Nim object as mentioned above. See the proc
|
||||
## `lldbDebugSyntheticDynamicFields` below for an example
|
||||
|
||||
import intsets
|
||||
import tables
|
||||
|
||||
type
|
||||
CustomType* = object of RootObj # RootObj is not necessary, but can be used
|
||||
myField*: int
|
||||
|
||||
DynamicFields* = object
|
||||
customField*: string
|
||||
|
||||
CustomSyntheticReturn* = object
|
||||
differentField*: float
|
||||
|
||||
LLDBDynamicObject = object
|
||||
fields: TableRef[string, int]
|
||||
|
||||
LLDBDynamicObjectDynamicFields = object
|
||||
fields: TableRef[string, string]
|
||||
|
||||
proc lldbDebugSummaryCustomType*(ty: CustomType): string {.exportc.} =
|
||||
## Will display "CustomType(myField: 0)" as a summary
|
||||
## Will display "CustomType(myField: <int_val>)" as a summary
|
||||
result = "CustomType" & $ty
|
||||
|
||||
proc lldbDebugSyntheticCustomType*(ty: CustomType): CustomSyntheticReturn {.exportc.} =
|
||||
## Will display the fields of CustomSyntheticReturn as children of
|
||||
## CustomType
|
||||
## Will display differentField: <float_val> as a child of CustomType instead of
|
||||
## myField: <int_val>
|
||||
result = CustomSyntheticReturn(differentField: ty.myField.float)
|
||||
|
||||
proc lldbDebugSyntheticDynamicFields*(ty: DynamicFields): LLDBDynamicObjectDynamicFields {.exportc.} =
|
||||
## Returning an object that contains "LLDBDynamicObject" in the type name will expect an
|
||||
## object with one property that is a Nim Table/TableRef. If the key is a string,
|
||||
## it will appear in the debugger like an object field name. The value will be whatever you
|
||||
## set it to here as well.
|
||||
let fields = {"customFieldName": ty.customField & " MORE TEXT"}.newTable()
|
||||
return LLDBDynamicObjectDynamicFields(fields: fields)
|
||||
|
||||
proc lldbDebugSummaryIntSet*(intset: IntSet): string {.exportc.} =
|
||||
## This will print the object in the LLDB summary just as Nim prints it
|
||||
result = $intset
|
||||
|
|
@ -42,6 +65,7 @@ proc lldbDebugSyntheticIntSet*(intset: IntSet): seq[int] {.exportc.} =
|
|||
## will appear as a Nim object in the LLDB debugger window
|
||||
##
|
||||
## returning a seq here will display children like:
|
||||
## [0]: <child_value>
|
||||
##
|
||||
result = newSeqOfCap[int](intset.len)
|
||||
for val in intset:
|
||||
|
|
|
|||
|
|
@ -1012,14 +1012,45 @@ class StringTableChildrenProvider:
|
|||
return bool(self.num_children())
|
||||
|
||||
|
||||
class CustomObjectChildrenProvider:
|
||||
"""
|
||||
This children provider handles values returned from lldbDebugSynthetic*
|
||||
Nim procedures
|
||||
"""
|
||||
|
||||
class LLDBDynamicObjectProvider:
|
||||
def __init__(self, value: lldb.SBValue, internalDict):
|
||||
self.value: lldb.SBValue = get_custom_synthetic(value) or value
|
||||
value = value.GetNonSyntheticValue()
|
||||
self.value: lldb.SBValue = value[0]
|
||||
self.children: OrderedDict[str, int] = OrderedDict()
|
||||
self.child_list: list[lldb.SBValue] = []
|
||||
|
||||
while self.value.type.is_pointer:
|
||||
self.value = self.value.Dereference()
|
||||
|
||||
self.update()
|
||||
|
||||
def num_children(self):
|
||||
return len(self.child_list)
|
||||
|
||||
def get_child_index(self, name: str):
|
||||
return self.children[name]
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
return self.child_list[index]
|
||||
|
||||
def update(self):
|
||||
self.children.clear()
|
||||
self.child_list = []
|
||||
|
||||
for i, child in enumerate(self.value.children):
|
||||
name = child.name.strip('"')
|
||||
new_child = child.CreateValueFromAddress(name, child.GetLoadAddress(), child.GetType())
|
||||
|
||||
self.children[name] = i
|
||||
self.child_list.append(new_child)
|
||||
|
||||
def has_children(self):
|
||||
return bool(self.num_children())
|
||||
|
||||
|
||||
class LLDBBasicObjectProvider:
|
||||
def __init__(self, value: lldb.SBValue, internalDict):
|
||||
self.value: lldb.SBValue = value
|
||||
|
||||
def num_children(self):
|
||||
if self.value is not None:
|
||||
|
|
@ -1039,6 +1070,35 @@ class CustomObjectChildrenProvider:
|
|||
return self.num_children() > 0
|
||||
|
||||
|
||||
class CustomObjectChildrenProvider:
|
||||
"""
|
||||
This children provider handles values returned from lldbDebugSynthetic*
|
||||
Nim procedures
|
||||
"""
|
||||
|
||||
def __init__(self, value: lldb.SBValue, internalDict):
|
||||
self.value: lldb.SBValue = get_custom_synthetic(value) or value
|
||||
if "lldbdynamicobject" in self.value.type.name.lower():
|
||||
self.provider = LLDBDynamicObjectProvider(self.value, internalDict)
|
||||
else:
|
||||
self.provider = LLDBBasicObjectProvider(self.value, internalDict)
|
||||
|
||||
def num_children(self):
|
||||
return self.provider.num_children()
|
||||
|
||||
def get_child_index(self, name: str):
|
||||
return self.provider.get_child_index(name)
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
return self.provider.get_child_at_index(index)
|
||||
|
||||
def update(self):
|
||||
self.provider.update()
|
||||
|
||||
def has_children(self):
|
||||
return self.provider.has_children()
|
||||
|
||||
|
||||
def echo(debugger: lldb.SBDebugger, command: str, result, internal_dict):
|
||||
debugger.HandleCommand("po " + command)
|
||||
|
||||
|
|
@ -1117,13 +1177,27 @@ def get_custom_synthetic(value: lldb.SBValue) -> Union[lldb.SBValue, None]:
|
|||
value = value.Dereference()
|
||||
|
||||
if first_type.is_pointer:
|
||||
command = f"{fn.name}(({first_type.name})" + str(value.GetLoadAddress()) + ");"
|
||||
first_arg = f"({first_type.name}){value.GetLoadAddress()}"
|
||||
else:
|
||||
command = f"{fn.name}(*({first_type.GetPointerType().name})" + str(value.GetLoadAddress()) + ");"
|
||||
first_arg = f"*({first_type.GetPointerType().name}){value.GetLoadAddress()}"
|
||||
|
||||
if arg_types.GetSize() > 1 and fn.GetArgumentName(1) == "Result":
|
||||
ret_type = arg_types.GetTypeAtIndex(1)
|
||||
ret_type = get_base_type(ret_type)
|
||||
|
||||
command = f"""
|
||||
{ret_type.name} lldbT;
|
||||
nimZeroMem((void*)(&lldbT), sizeof({ret_type.name}));
|
||||
{fn.name}(({first_arg}), (&lldbT));
|
||||
lldbT;
|
||||
"""
|
||||
else:
|
||||
command = f"{fn.name}({first_arg});"
|
||||
|
||||
res = executeCommand(command)
|
||||
|
||||
if res.error.fail:
|
||||
print(res.error)
|
||||
return None
|
||||
|
||||
return res
|
||||
|
|
@ -1202,8 +1276,9 @@ def breakpoint_function_wrapper(frame: lldb.SBFrame, bp_loc, internal_dict):
|
|||
fn_type: lldb.SBType = fn.type
|
||||
arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
|
||||
|
||||
# There should only be one argument to send to the function call
|
||||
if arg_types.GetSize() != 1:
|
||||
if arg_types.GetSize() > 1 and fn.GetArgumentName(1) == "Result":
|
||||
pass # don't continue
|
||||
elif arg_types.GetSize() != 1:
|
||||
continue
|
||||
|
||||
arg_type: lldb.SBType = arg_types.GetTypeAtIndex(0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue