207 lines
6.6 KiB
Python
207 lines
6.6 KiB
Python
'''
|
|
This file contains LLDB pretty-printers for common ScummVM data types.
|
|
To enable it, add the following line to the .lldbinit file for this project:
|
|
|
|
command script import devtools.lldb_pretty_printers
|
|
|
|
LLDB disallows loading .lldbinit files from source directories by default, so
|
|
you may need to add the following to your global ~/.lldbinit:
|
|
|
|
settings set target.load-cwd-lldbinit true
|
|
|
|
|
|
'''
|
|
from dataclasses import dataclass
|
|
import lldb
|
|
|
|
|
|
class ArrayProvider:
|
|
'''Formatter for Common::Array'''
|
|
def __init__(self, valobj, internal_dict):
|
|
self.valobj = valobj
|
|
self.data_type = self.valobj.GetChildMemberWithName("_storage").GetType().GetPointeeType()
|
|
self.data_size = self.data_type.GetByteSize()
|
|
self.update()
|
|
|
|
def update(self):
|
|
self.size = \
|
|
self.valobj.GetChildMemberWithName("_size").GetValueAsUnsigned()
|
|
return False
|
|
|
|
def num_children(self, _max_children=None):
|
|
return self.size
|
|
|
|
def has_children(self):
|
|
return self.size > 0
|
|
|
|
def get_child_index(self, name):
|
|
try:
|
|
return int(name.lstrip("[").rstrip("]"))
|
|
except ValueError:
|
|
return -1
|
|
|
|
def get_child_at_index(self, index):
|
|
if index < 0:
|
|
return None
|
|
if index >= self.num_children():
|
|
return None
|
|
base = self.valobj.GetChildMemberWithName("_storage")
|
|
offset = index * self.data_size
|
|
return base.CreateChildAtOffset(
|
|
f"[{index}]", offset, self.data_type
|
|
)
|
|
|
|
|
|
class HashMapProvider:
|
|
'''Formatter for Common::HashMap'''
|
|
def __init__(self, valobj, internal_dict):
|
|
self.valobj = valobj
|
|
self.update()
|
|
|
|
def update(self):
|
|
self.children_with_values = []
|
|
self.size = \
|
|
self.valobj.GetChildMemberWithName("_size").GetValueAsUnsigned()
|
|
self.mask = \
|
|
self.valobj.GetChildMemberWithName("_mask").GetValueAsUnsigned()
|
|
self.storage = self.valobj.GetChildMemberWithName("_storage")
|
|
for i in range(self.mask + 1):
|
|
child = self.storage.GetChildAtIndex(i, lldb.eNoDynamicValues, True)
|
|
if child.GetValueAsUnsigned() != 0:
|
|
self.children_with_values.append(child)
|
|
return False
|
|
|
|
def num_children(self, _max_children=None):
|
|
return self.size
|
|
|
|
def has_children(self):
|
|
return self.size > 0
|
|
|
|
def get_child_index(self, name):
|
|
return next((c for c in self.children_with_values if c.GetName() == name), -1)
|
|
|
|
def get_child_at_index(self, index):
|
|
if index < 0:
|
|
return None
|
|
if index >= self.num_children():
|
|
return None
|
|
|
|
return self.children_with_values[index].Dereference()
|
|
|
|
|
|
class ListProvider:
|
|
'''Formatter for Common::List'''
|
|
def __init__(self, valobj, internal_dict):
|
|
self.valobj = valobj
|
|
self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
|
|
self.node_offset = self.valobj.GetType().FindDirectNestedType("NodeBase").GetByteSize()
|
|
self.update()
|
|
|
|
def update(self):
|
|
self.anchor = self.valobj.GetChildMemberWithName("_anchor")
|
|
|
|
self.size = 0
|
|
cur = self.anchor
|
|
while True:
|
|
cur = cur.GetChildMemberWithName("_next").Dereference()
|
|
if cur.AddressOf().GetValueAsUnsigned() == self.anchor.AddressOf().GetValueAsUnsigned():
|
|
break
|
|
self.size = self.size + 1
|
|
return False
|
|
|
|
def num_children(self, _max_children=None):
|
|
return self.size
|
|
|
|
def has_children(self):
|
|
_prev = self.anchor.GetChildMemberWithName("_prev").GetValueAsUnsigned()
|
|
_next = self.anchor.AddressOf().GetValueAsUnsigned()
|
|
return _prev != _next
|
|
|
|
def get_child_index(self, name):
|
|
try:
|
|
return int(name.lstrip("[").rstrip("]"))
|
|
except:
|
|
return -1
|
|
|
|
def get_child_at_index(self, index):
|
|
if index < 0 or index >= self.size:
|
|
return None
|
|
|
|
cur = self.anchor
|
|
for _ in range(index):
|
|
cur = cur.GetChildMemberWithName("_next").Dereference()
|
|
|
|
return cur.CreateChildAtOffset(f'[{index}]', self.node_offset, self.element_type)
|
|
|
|
|
|
def array_summary(valobj, internal):
|
|
size = valobj.GetNonSyntheticValue().GetChildMemberWithName('_size').GetValueAsUnsigned()
|
|
if size == 0:
|
|
return "(empty)"
|
|
if size == 1:
|
|
return "1 item"
|
|
return f"{size} items"
|
|
|
|
|
|
def construct_matrix_summary(rows, cols):
|
|
lines = []
|
|
for r in range(rows):
|
|
line = " ".join(f'${{var._values[{r*cols+c}]}}' for c in range(cols))
|
|
lines.append(line)
|
|
return '[' + "; ".join(lines) + ']'
|
|
|
|
@dataclass(frozen=True)
|
|
class Pattern:
|
|
pattern: str
|
|
|
|
summaries = {
|
|
'Common::String': r'${var._str%s}',
|
|
|
|
'Common::Rect': r'(${var.left},${var.top}) -> (${var.right}, ${var.bottom})',
|
|
'Math::Vector2d': r'(${var._values[0]}, ${var._values[1]})',
|
|
'Math::Vector3d': r'(${var._values[0]}, ${var._values[1]}, ${var._values[2]})',
|
|
'Math::Vector4d': r'(${var._values[0]}, ${var._values[1]}, ${var._values[2]}, ${var._values[3]})',
|
|
'Math::Quaternion': r'(${var._values[0]}, ${var._values[1]}, ${var._values[2]}, ${var._values[3]})',
|
|
'Math::Matrix4': construct_matrix_summary(4,4),
|
|
'Math::Matrix3': construct_matrix_summary(3,3),
|
|
|
|
'Graphics::Surface': r'(${var.w},${var.h}) (bpp=${var.format.bytesPerPixel:d})',
|
|
Pattern('^Common::HashMap<.*>::Node'): r'\{key: ${var._key}, value: ${var._value}\}',
|
|
Pattern('^Common::Array<.*>$'): array_summary,
|
|
Pattern('^Common::List<.*>$'): r'${svar%#} items',
|
|
}
|
|
|
|
providers = {
|
|
Pattern('^Common::Array<.*>$'): ArrayProvider,
|
|
Pattern('^Common::HashMap<.*>$'): HashMapProvider,
|
|
Pattern('^Common::List<.*>$'): ListProvider,
|
|
}
|
|
|
|
|
|
def __lldb_init_module(debugger, unused):
|
|
#lldb.formatters.Logger._lldb_formatters_debug_level = 2
|
|
print('Loading ScummVM formatters...')
|
|
for klass, summary in summaries.items():
|
|
if callable(summary):
|
|
summary = f'-F {__name__}.{summary.__name__}'
|
|
else:
|
|
summary = f'--summary-string "{summary}"'
|
|
|
|
if isinstance(klass, Pattern):
|
|
debugger.HandleCommand(
|
|
f'type summary add -x "{klass.pattern}" --category scummvm {summary}'
|
|
)
|
|
else:
|
|
debugger.HandleCommand(
|
|
f'type summary add "{klass}" --category scummvm {summary}'
|
|
)
|
|
|
|
for klass, provider in providers.items():
|
|
debugger.HandleCommand(
|
|
f'type synthetic add -x --category scummvm --python-class {__name__}.{provider.__name__} "{klass.pattern}"'
|
|
)
|
|
|
|
debugger.HandleCommand(
|
|
r'type category enable scummvm'
|
|
)
|