Files
integration/tools/UEDataFormatter.py

724 lines
28 KiB
Python
Raw Normal View History

2026-04-19 05:03:11 -04:00
import lldb, signal, sys, time
2026-04-19 05:03:11 -04:00
import lldb.formatters.Logger
import codelldb.value
############################################################
#
# Store pointers to some important types and globals.
#
############################################################
2026-04-19 05:03:11 -04:00
2026-04-20 05:42:34 -04:00
_FNameEntryType = None
_FNameEntryStride = None
_FUObjectItemType = None
_GNameBlocksDebug = None
_GObjectArrayForDebugVisualizers = None
2026-05-04 16:51:39 -04:00
def _init_globals(target):
global _FNameEntryType, _FNameEntryStride, _FUObjectItemType
global _GNameBlocksDebug, _GObjectArrayForDebugVisualizers
if _FNameEntryType is None:
t = target.FindFirstType('FNameEntry')
if t.IsValid():
_FNameEntryType = t
_FNameEntryStride = t.GetByteAlign()
if _FUObjectItemType is None:
t = target.FindFirstType('FUObjectItem')
if t.IsValid():
_FUObjectItemType = t
if _GNameBlocksDebug is None:
v = target.FindFirstGlobalVariable('GNameBlocksDebug')
if v.IsValid():
_GNameBlocksDebug = v
if _GObjectArrayForDebugVisualizers is None:
v = target.FindFirstGlobalVariable('GObjectArrayForDebugVisualizers')
if v.IsValid():
_GObjectArrayForDebugVisualizers = v
2026-05-04 16:51:39 -04:00
class _GlobalsInitStopHook:
def __init__(self, target, extra_args, internal_dict):
_init_globals(target)
def handle_stop(self, exe_ctx, stream):
_init_globals(exe_ctx.GetTarget())
return True
############################################################
#
# A forwarding synth provider is a provider that
# forwards all child lookups to a different SBValue.
#
############################################################
2026-04-21 01:00:45 -04:00
def TryGetSyntheticValue(v):
s = v.GetSyntheticValue()
return s if s.IsValid() else v
class ForwardingSynthProvider:
def __init__(self, valobj, dict):
self.valobj = valobj
self.update()
def num_children(self):
return self.forward_to.GetNumChildren()
def get_child_index(self, name):
return self.forward_to.GetIndexOfChildWithName(name)
def get_child_at_index(self, index):
return self.forward_to.GetChildAtIndex(index)
def update(self):
self.forward_to = lldb.SBValue()
def has_children(self):
return self.forward_to.IsValid() and self.forward_to.MightHaveChildren()
############################################################
#
# A Stored synth provider is an synth provider that
# just stores an array of children.
#
############################################################
class StoredSynthProvider:
def __init__(self, valobj, dict):
self.valobj = valobj
self.update()
def num_children(self):
return len(self.children)
def get_child_index(self, name):
for i, child in enumerate(self.children):
if child.GetName() == name:
return i
return -1
def get_child_at_index(self, index):
if 0 <= index < len(self.children):
return self.children[index]
return lldb.SBValue()
def update(self):
self.children = []
def has_children(self):
return len(self.children) > 0
2026-04-20 20:52:17 -04:00
############################################################
#
# An Indexed synth provider exposes N integer-indexed
# children [0]..[N-1]. Subclasses set self.count in update()
# and override get_child_at_index(index) to return the SBValue
# for a given index (with their own range check).
#
############################################################
MAX_ARRAY_CHILDREN = 50
class IndexedSynthProvider:
def __init__(self, valobj, dict):
self.valobj = valobj
self.update()
def num_children(self):
return min(self.count, MAX_ARRAY_CHILDREN)
def get_child_index(self, name):
try:
return int(name.lstrip('[').rstrip(']'))
except:
return -1
def has_children(self):
return self.count > 0
def update(self):
self.count = 0
############################################################
#
# Provider for FString
#
############################################################
2026-04-20 05:42:34 -04:00
def UEFStringSummaryProvider(valobj, dict):
target = valobj.GetTarget()
sbtype = valobj.GetType().GetUnqualifiedType()
if sbtype.IsPointerType():
if valobj.GetValueAsUnsigned(0) == 0:
return 'nullptr'
valobj = valobj.Dereference()
elif sbtype.IsReferenceType():
valobj = valobj.Dereference()
2026-04-20 20:52:17 -04:00
tarray = valobj.GetChildMemberWithName('Data').GetNonSyntheticValue()
2026-04-20 05:42:34 -04:00
array_num = tarray.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0)
if array_num < 0: return 'corrupted string'
if array_num == 0: return 'empty string'
data_ptr = tarray.GetChildMemberWithName('AllocatorInstance').GetChildMemberWithName('Data')
buffer_addr = data_ptr.GetValueAsUnsigned(0)
if buffer_addr == 0: return 'corrupted string'
tchar_type = tarray.GetType().GetTemplateArgumentType(0)
array_type = tchar_type.GetArrayType(array_num)
sbaddr = lldb.SBAddress(buffer_addr, target)
summary = target.CreateValueFromAddress('string', sbaddr, array_type).GetSummary()
if summary and summary.startswith('u"'): return summary[1:]
return summary
############################################################
#
# Provider for FName
#
############################################################
2026-04-20 05:42:34 -04:00
def UEFNameSummaryProvider(valobj, dict):
2026-04-21 22:28:22 -04:00
valobj = valobj.GetNonSyntheticValue()
2026-04-20 05:42:34 -04:00
target = valobj.GetTarget()
process = target.GetProcess()
entry_id = valobj.GetChildMemberWithName('DisplayIndex')
index = entry_id.GetChildMemberWithName('Value').GetValueAsUnsigned(0)
number = valobj.GetChildMemberWithName('Number').GetValueAsUnsigned(0)
block_idx = index >> 16
entry_offset = (index & 0xFFFF) * _FNameEntryStride
gname_ptr = _GNameBlocksDebug.GetValueAsUnsigned(0)
2026-04-20 05:42:34 -04:00
error = lldb.SBError()
block_addr = process.ReadPointerFromMemory(gname_ptr + block_idx * target.GetAddressByteSize(), error)
if error.Fail(): return '<read error>'
entry_addr = block_addr + entry_offset
entry = target.CreateValueFromAddress('entry', lldb.SBAddress(entry_addr, target), _FNameEntryType)
header = entry.GetChildMemberWithName('Header')
length = header.GetChildMemberWithName('Len').GetValueAsUnsigned(0)
is_wide = header.GetChildMemberWithName('bIsWide').GetValueAsUnsigned(0)
if is_wide:
name_addr = entry.GetChildMemberWithName('WideName').GetLoadAddress()
raw = process.ReadMemory(name_addr, length * 2, error)
if error.Fail(): return '<read error>'
name = raw.decode('utf-16-le')
2026-04-19 05:03:11 -04:00
else:
2026-04-20 05:42:34 -04:00
name_addr = entry.GetChildMemberWithName('AnsiName').GetLoadAddress()
raw = process.ReadMemory(name_addr, length, error)
if error.Fail(): return '<read error>'
name = raw.decode('utf-8', errors='replace')
2026-04-19 05:03:11 -04:00
2026-04-20 05:42:34 -04:00
if number > 0: return '%s_%d' % (name, number - 1)
return name
2026-04-19 05:03:11 -04:00
############################################################
#
# Providers for TObjectPtr
#
############################################################
2026-04-19 05:03:11 -04:00
2026-04-20 05:42:34 -04:00
class TObjectPtrSynthProvider(ForwardingSynthProvider):
2026-04-19 05:03:11 -04:00
def update(self):
2026-04-20 05:42:34 -04:00
debug_ptr = self.valobj.GetChildMemberWithName('DebugPtr')
addr = debug_ptr.GetValueAsUnsigned(0)
if addr == 0 or addr & 1: # null or unresolved handle (odd address)
self.forward_to = lldb.SBValue()
2026-04-19 05:03:11 -04:00
else:
2026-04-21 01:00:45 -04:00
self.forward_to = TryGetSyntheticValue(debug_ptr.Dereference())
2026-04-19 05:03:11 -04:00
2026-04-20 05:42:34 -04:00
def TObjectPtrSummaryProvider(valobj, dict):
debug_ptr = valobj.GetNonSyntheticValue().GetChildMemberWithName('DebugPtr')
2026-04-20 05:42:34 -04:00
addr = debug_ptr.GetValueAsUnsigned(0)
if addr == 0: return 'nullptr'
if addr & 1: return 'unresolved'
return '{...}'
2026-04-19 05:03:11 -04:00
############################################################
#
# Providers for TStrongObjectPtr
#
############################################################
2026-04-19 05:03:11 -04:00
class TStrongObjectPtrSynthProvider(ForwardingSynthProvider):
def update(self):
obj = self.valobj.GetChildMemberWithName('Object')
addr = obj.GetValueAsUnsigned(0)
if addr == 0:
self.forward_to = lldb.SBValue()
else:
2026-04-21 01:00:45 -04:00
self.forward_to = TryGetSyntheticValue(obj.Dereference())
2026-04-19 05:03:11 -04:00
def TStrongObjectPtrSummaryProvider(valobj, dict):
obj = valobj.GetNonSyntheticValue().GetChildMemberWithName('Object')
addr = obj.GetValueAsUnsigned(0)
if addr == 0: return 'nullptr'
return '{...}'
2026-04-19 05:03:11 -04:00
############################################################
#
# Providers for TWeakObjectPtr
#
############################################################
def _resolve_weak_object_ptr(valobj):
obj_index = valobj.GetChildMemberWithName('ObjectIndex').GetValueAsSigned(0)
serial_num = valobj.GetChildMemberWithName('ObjectSerialNumber').GetValueAsSigned(0)
if serial_num == 0 or obj_index < 0: return None
chunk_idx = obj_index >> 16 # NumElementsPerChunk = 65536
item_idx = obj_index & 0xFFFF
objects = _GObjectArrayForDebugVisualizers.Dereference().GetChildMemberWithName('Objects') # FUObjectItem**
chunk = objects.GetChildAtIndex(chunk_idx, lldb.eDynamicDontRunTarget, True) # FUObjectItem*
item = chunk.GetChildAtIndex(item_idx, lldb.eDynamicDontRunTarget, True) # FUObjectItem
actual_serial = item.GetChildMemberWithName('SerialNumber').GetValueAsSigned(0)
if actual_serial != serial_num: return None
return item.GetChildMemberWithName('Object')
class TWeakObjectPtrSynthProvider(ForwardingSynthProvider):
def update(self):
obj_ptr = _resolve_weak_object_ptr(self.valobj)
if obj_ptr is None or obj_ptr.GetValueAsUnsigned(0) == 0:
self.forward_to = lldb.SBValue()
else:
2026-04-21 01:00:45 -04:00
self.forward_to = TryGetSyntheticValue(obj_ptr.Dereference())
2026-04-19 05:03:11 -04:00
def TWeakObjectPtrSummaryProvider(valobj, dict):
raw = valobj.GetNonSyntheticValue()
serial_num = raw.GetChildMemberWithName('ObjectSerialNumber').GetValueAsSigned(0)
if serial_num == 0: return 'nullptr'
if _resolve_weak_object_ptr(raw) is None: return 'expired'
return '{...}'
2026-04-19 05:03:11 -04:00
############################################################
#
# Providers for TSharedPtr
#
############################################################
2026-04-19 05:03:11 -04:00
class TSharedPtrSynthProvider(ForwardingSynthProvider):
2026-04-19 05:03:11 -04:00
def update(self):
obj = self.valobj.GetChildMemberWithName('Object')
addr = obj.GetValueAsUnsigned(0)
if addr == 0:
self.forward_to = lldb.SBValue()
else:
2026-04-21 01:00:45 -04:00
self.forward_to = TryGetSyntheticValue(obj.Dereference())
2026-04-20 05:42:34 -04:00
def TSharedPtrSummaryProvider(valobj, dict):
obj = valobj.GetNonSyntheticValue().GetChildMemberWithName('Object')
2026-04-20 05:42:34 -04:00
addr = obj.GetValueAsUnsigned(0)
if addr == 0: return 'nullptr'
return '{...}'
############################################################
#
# Providers for TWeakPtr
#
############################################################
def _resolve_weak_ptr(valobj):
"""Returns (object SBValue, is_expired).
valobj must be the raw (non-synthetic) TWeakPtr."""
weak_ref = valobj.GetChildMemberWithName('WeakReferenceCount')
ref_ctrl_ptr_val = weak_ref.GetChildMemberWithName('ReferenceController')
if ref_ctrl_ptr_val.GetValueAsUnsigned(0) == 0: return None, False
shared_count_field = ref_ctrl_ptr_val.Dereference().GetChildMemberWithName('SharedReferenceCount')
if shared_count_field.GetValueAsSigned(0) <= 0: return None, True
obj = valobj.GetChildMemberWithName('Object')
return obj, False
2026-04-20 05:42:34 -04:00
class TWeakPtrSynthProvider(ForwardingSynthProvider):
def update(self):
obj, expired = _resolve_weak_ptr(self.valobj)
if obj is None or obj.GetValueAsUnsigned(0) == 0:
self.forward_to = lldb.SBValue()
else:
2026-04-21 01:00:45 -04:00
self.forward_to = TryGetSyntheticValue(obj.Dereference())
2026-04-20 05:42:34 -04:00
def TWeakPtrSummaryProvider(valobj, dict):
obj, expired = _resolve_weak_ptr(valobj.GetNonSyntheticValue())
if expired: return 'expired'
if obj is None: return 'nullptr'
return '{...}'
2026-04-20 05:42:34 -04:00
############################################################
#
# Providers for TUniquePtr
#
############################################################
2026-04-20 05:42:34 -04:00
class TUniquePtrSynthProvider(ForwardingSynthProvider):
def update(self):
ptr = self.valobj.GetChildMemberWithName('Ptr')
addr = ptr.GetValueAsUnsigned(0)
if addr == 0:
self.forward_to = lldb.SBValue()
else:
2026-04-21 01:00:45 -04:00
self.forward_to = TryGetSyntheticValue(ptr.Dereference())
2026-04-20 05:42:34 -04:00
def TUniquePtrSummaryProvider(valobj, dict):
ptr = valobj.GetNonSyntheticValue().GetChildMemberWithName('Ptr')
addr = ptr.GetValueAsUnsigned(0)
if addr == 0: return 'nullptr'
return '{...}'
2026-04-20 05:42:34 -04:00
############################################################
#
# Providers for UObject
#
############################################################
2026-04-20 05:42:34 -04:00
class UObjectSynthProvider(StoredSynthProvider):
def update(self):
self.children = []
raw = self.valobj
target = raw.GetTarget()
debug_ptr = raw.GetChildMemberWithName('ClassPrivate') \
.GetChildMemberWithName('ObjectPtr') \
.GetChildMemberWithName('DebugPtr')
class_val = target.CreateValueFromAddress(
'Class', lldb.SBAddress(debug_ptr.GetLoadAddress(), target), debug_ptr.GetType())
self.children.append(class_val)
name_field = raw.GetChildMemberWithName('NamePrivate')
name_val = target.CreateValueFromAddress(
'Name', lldb.SBAddress(name_field.GetLoadAddress(), target), name_field.GetType())
self.children.append(name_val)
def UObjectSummaryProvider(valobj, dict):
raw = valobj.GetNonSyntheticValue()
class_debug_ptr = raw.GetChildMemberWithName('ClassPrivate') \
.GetChildMemberWithName('ObjectPtr') \
.GetChildMemberWithName('DebugPtr')
class_addr = class_debug_ptr.GetValueAsUnsigned(0)
if not class_addr:
return 'null'
2026-04-20 20:52:17 -04:00
class_name = UEFNameSummaryProvider(
class_debug_ptr.Dereference().GetChildMemberWithName('NamePrivate'), dict)
2026-04-20 20:52:17 -04:00
instance_name = UEFNameSummaryProvider(
raw.GetChildMemberWithName('NamePrivate'), dict)
return 'Class=%s Name=%s' % (class_name, instance_name)
############################################################
#
# Providers for TArray
#
############################################################
2026-04-20 05:42:34 -04:00
2026-04-20 20:52:17 -04:00
def _resolve_tarray_data_addr(allocator):
"""Returns the base address of the element data for any TArray allocator."""
# Heap allocator (TSizedHeapAllocator): has a Data pointer directly
data = allocator.GetChildMemberWithName('Data')
if data.IsValid():
return data.GetValueAsUnsigned(0)
# Inline allocator (TSizedInlineAllocator): prefer SecondaryData.Data, fall back to InlineData
secondary = allocator.GetChildMemberWithName('SecondaryData')
if secondary.IsValid():
sec_data = secondary.GetChildMemberWithName('Data')
if sec_data.IsValid() and sec_data.GetValueAsUnsigned(0) != 0:
return sec_data.GetValueAsUnsigned(0)
# Fixed allocator or inline allocator using inline storage
inline = allocator.GetChildMemberWithName('InlineData')
if inline.IsValid():
return inline.GetLoadAddress()
return 0
def _resolve_tarray(tarray_val):
"""Given an SBValue of a TArray, return (array_num, data_sbvalue) where
data_sbvalue is a typed array of the TArray's element type. Returns
(0, None) if empty or unresolvable."""
tarray_val = tarray_val.GetNonSyntheticValue()
arr_type = tarray_val.GetType().GetUnqualifiedType()
if arr_type.IsReferenceType():
arr_type = arr_type.GetDereferencedType()
elif arr_type.IsPointerType():
arr_type = arr_type.GetPointeeType()
if arr_type.GetNumberOfTemplateArguments() == 0: return 0, None
array_num = tarray_val.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0)
if array_num <= 0: return 0, None
data_addr = _resolve_tarray_data_addr(tarray_val.GetChildMemberWithName('AllocatorInstance'))
if data_addr == 0: return 0, None
elem_type = arr_type.GetTemplateArgumentType(0)
if elem_type.GetByteSize() == 0: return 0, None
target = tarray_val.GetTarget()
data = target.CreateValueFromAddress(
'data', lldb.SBAddress(data_addr, target), elem_type.GetArrayType(array_num))
return array_num, data
class TArraySynthProvider(IndexedSynthProvider):
def get_child_at_index(self, index):
if index < 0 or index >= self.count:
return lldb.SBValue()
return self.data_array.GetChildAtIndex(index)
2026-04-19 05:03:11 -04:00
2026-04-20 20:52:17 -04:00
def update(self):
self.count, self.data_array = _resolve_tarray(self.valobj)
def TArraySummaryProvider(valobj, dict):
raw = valobj.GetNonSyntheticValue()
array_num = raw.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0)
if array_num < 0: return 'corrupted'
return 'size=%d' % array_num
############################################################
#
# Provider for TBitArray
#
############################################################
def _bitarray_words(bit_array_val):
"""Given an SBValue of a TBitArray, return its backing uint32[] as an SBValue
(or None if the array is empty/unallocated)."""
bit_array_val = bit_array_val.GetNonSyntheticValue()
num_bits = bit_array_val.GetChildMemberWithName('NumBits').GetValueAsSigned(0)
if num_bits <= 0: return None
allocator = bit_array_val.GetChildMemberWithName('AllocatorInstance')
data_addr = _resolve_tarray_data_addr(allocator)
if data_addr == 0: return None
target = bit_array_val.GetTarget()
uint32_type = target.GetBasicType(lldb.eBasicTypeUnsignedInt)
num_words = (num_bits + 31) // 32
return target.CreateValueFromAddress(
'words', lldb.SBAddress(data_addr, target),
uint32_type.GetArrayType(num_words))
class TBitArraySynthProvider(IndexedSynthProvider):
def get_child_at_index(self, index):
if index < 0 or index >= self.count:
return lldb.SBValue()
word = self.words.GetChildAtIndex(index)
target = self.valobj.GetTarget()
child = target.CreateValueFromAddress(
'[%d]' % index, lldb.SBAddress(word.GetLoadAddress(), target),
target.GetBasicType(lldb.eBasicTypeUnsignedInt))
child.SetFormat(lldb.eFormatBinary)
return child
def update(self):
self.count = 0
self.words = _bitarray_words(self.valobj)
if self.words is None: return
self.count = self.words.GetNumChildren()
def TBitArraySummaryProvider(valobj, dict):
num_bits = valobj.GetNonSyntheticValue().GetChildMemberWithName('NumBits').GetValueAsSigned(0)
return 'size=%d' % num_bits
############################################################
#
# Provider for TSparseArray
#
############################################################
class TSparseArraySynthProvider(IndexedSynthProvider):
def get_child_at_index(self, index):
if index < 0 or index >= self.count:
return lldb.SBValue()
raw_idx = self.live_indices[index]
slot = self.data_array.GetChildAtIndex(raw_idx)
target = self.valobj.GetTarget()
return target.CreateValueFromAddress(
'[%d]' % index,
lldb.SBAddress(slot.GetLoadAddress(), target),
self.element_type)
def update(self):
self.count = 0
self.live_indices = []
self.data_array = None
self.element_type = None
array_num, data = _resolve_tarray(self.valobj.GetChildMemberWithName('Data'))
if data is None: return
words = _bitarray_words(self.valobj.GetChildMemberWithName('AllocationFlags'))
if words is None: return
self.data_array = data
self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
for i in range(array_num):
word = words.GetChildAtIndex(i // 32).GetValueAsUnsigned(0)
if (word >> (i % 32)) & 1:
self.live_indices.append(i)
self.count = len(self.live_indices)
def TSparseArraySummaryProvider(valobj, dict):
raw = valobj.GetNonSyntheticValue()
array_num = raw.GetChildMemberWithName('Data').GetNonSyntheticValue().GetChildMemberWithName('ArrayNum').GetValueAsSigned(0)
num_free = raw.GetChildMemberWithName('NumFreeIndices').GetValueAsSigned(0)
return 'size=%d' % (array_num - num_free)
2026-04-21 01:00:45 -04:00
############################################################
#
# Provider for TSet
#
############################################################
class TSetSynthProvider(ForwardingSynthProvider):
def update(self):
self.forward_to = TryGetSyntheticValue(self.valobj.GetChildMemberWithName('Elements'))
def TSetSummaryProvider(valobj, dict):
elements = valobj.GetNonSyntheticValue().GetChildMemberWithName('Elements')
return TSparseArraySummaryProvider(elements, dict)
############################################################
#
# Provider for TMap / TMultiMap
#
############################################################
class TMapSynthProvider(ForwardingSynthProvider):
def update(self):
self.forward_to = TryGetSyntheticValue(self.valobj.GetChildMemberWithName('Pairs').GetNonSyntheticValue().GetChildMemberWithName('Elements'))
def TMapSummaryProvider(valobj, dict):
pairs = valobj.GetNonSyntheticValue().GetChildMemberWithName('Pairs')
elements = pairs.GetNonSyntheticValue().GetChildMemberWithName('Elements')
return TSparseArraySummaryProvider(elements, dict)
############################################################
#
# Provider for TTuple
#
############################################################
def _tuple_child_name(index):
if index == 0: return 'Key'
if index == 1: return 'Value'
return 'Value%d' % index
class TTupleSynthProvider(StoredSynthProvider):
def update(self):
self.children = []
raw = self.valobj.GetNonSyntheticValue()
target = raw.GetTarget()
container = raw
first = raw.GetChildAtIndex(0)
if first.IsValid() and 'TTupleBase<' in (first.GetType().GetName() or ''):
container = first
n = container.GetNumChildren()
for i in range(n):
base = container.GetChildAtIndex(i)
field = base.GetChildAtIndex(0)
if not field.IsValid(): continue
child = target.CreateValueFromAddress(
_tuple_child_name(i),
lldb.SBAddress(field.GetLoadAddress(), target),
field.GetType())
self.children.append(child)
############################################################
#
# Provider for TSetElement
#
############################################################
class TSetElementSynthProvider(ForwardingSynthProvider):
def update(self):
self.forward_to = TryGetSyntheticValue(self.valobj.GetChildMemberWithName('Value'))
##############################################################
#
# The getattr which is built in to codelldb.value.Value
# fails to fetch synthetic values. Monkey-patch it.
#
##############################################################
def _value_getattr(self, name):
sbv = self._Value__sbvalue
if sbv.GetType().IsPointerType():
sbv = sbv.Dereference()
child = sbv.GetChildMemberWithName(name)
if not child.IsValid():
for i in range(sbv.GetNumChildren()):
c = sbv.GetChildAtIndex(i)
if c.GetName() == name:
child = c
break
if not child.IsValid():
raise AttributeError("Attribute '%s' is not defined" % name)
return codelldb.value.Value(child)
codelldb.value.Value.__getattr__ = _value_getattr
##############################################################
#
# The standard repr for SBValue will recurse infinitely if
# you provide useful synth providers for smart pointers.
# Monkey-patch it.
#
##############################################################
def _sbvalue_repr(self):
n = self.GetNumChildren()
if n == 0: return _sbvalue_display(self)
parts = []
for i in range(n):
child = self.GetChildAtIndex(i)
name = child.GetName() or str(i)
parts.append('%s=%s\n' % (name, _sbvalue_display(child)))
return ''.join(parts)
def _sbvalue_display(v):
s = v.GetSummary()
if s: return s
s = v.GetValue()
if s: return s
if v.GetNumChildren() > 0: return '{...}'
return '?'
lldb.SBValue.__repr__ = _sbvalue_repr
##############################################################
#
# Module Initialization
#
#############################################################
2026-04-20 20:52:17 -04:00
def _register_provider(cat, pattern, summary_fn=None, synth_cls=None):
"""Register summary and/or synthetic providers for a type pattern."""
spec = lldb.SBTypeNameSpecifier(pattern, lldb.eFormatterMatchRegex)
if summary_fn:
cat.AddTypeSummary(spec, lldb.SBTypeSummary.CreateWithFunctionName(
__name__ + '.' + summary_fn, lldb.eTypeOptionCascade))
if synth_cls:
cat.AddTypeSynthetic(spec, lldb.SBTypeSynthetic.CreateWithClassName(
__name__ + '.' + synth_cls, lldb.eTypeOptionCascade))
def __lldb_init_module(debugger, dict):
print("Running lldb_init_module")
2026-05-04 16:51:39 -04:00
debugger.HandleCommand('target stop-hook add -P ' + __name__ + '._GlobalsInitStopHook')
2026-04-20 20:52:17 -04:00
debugger.HandleCommand('type category delete ' + __name__)
2026-05-04 16:51:39 -04:00
target = debugger.GetSelectedTarget()
if target.IsValid():
_init_globals(target)
2026-04-20 20:52:17 -04:00
cat = debugger.CreateCategory(__name__)
_register_provider(cat, '^FString$', summary_fn='UEFStringSummaryProvider')
_register_provider(cat, '^FName$', summary_fn='UEFNameSummaryProvider')
_register_provider(cat, '^TObjectPtr<.+>$', summary_fn='TObjectPtrSummaryProvider', synth_cls='TObjectPtrSynthProvider')
_register_provider(cat, '^TStrongObjectPtr<.+>$', summary_fn='TStrongObjectPtrSummaryProvider', synth_cls='TStrongObjectPtrSynthProvider')
_register_provider(cat, '^TSharedPtr<.+>$', summary_fn='TSharedPtrSummaryProvider', synth_cls='TSharedPtrSynthProvider')
_register_provider(cat, '^TSharedRef<.+>$', summary_fn='TSharedPtrSummaryProvider', synth_cls='TSharedPtrSynthProvider')
_register_provider(cat, '^TWeakPtr<.+>$', summary_fn='TWeakPtrSummaryProvider', synth_cls='TWeakPtrSynthProvider')
_register_provider(cat, '^TWeakObjectPtr<.+>$', summary_fn='TWeakObjectPtrSummaryProvider', synth_cls='TWeakObjectPtrSynthProvider')
_register_provider(cat, '^FWeakObjectPtr$', summary_fn='TWeakObjectPtrSummaryProvider', synth_cls='TWeakObjectPtrSynthProvider')
_register_provider(cat, '^TArray<.+>$', summary_fn='TArraySummaryProvider', synth_cls='TArraySynthProvider')
_register_provider(cat, '^TBitArray<.+>$', summary_fn='TBitArraySummaryProvider', synth_cls='TBitArraySynthProvider')
_register_provider(cat, '^TSparseArray<.+>$', summary_fn='TSparseArraySummaryProvider', synth_cls='TSparseArraySynthProvider')
2026-04-21 01:00:45 -04:00
_register_provider(cat, '^TSetElement<.+>$', synth_cls='TSetElementSynthProvider')
_register_provider(cat, '^TTuple<.+>$', synth_cls='TTupleSynthProvider')
_register_provider(cat, '^TSet<.+>$', summary_fn='TSetSummaryProvider', synth_cls='TSetSynthProvider')
_register_provider(cat, '^TMap<.+>$', summary_fn='TMapSummaryProvider', synth_cls='TMapSynthProvider')
_register_provider(cat, '^TMultiMap<.+>$', summary_fn='TMapSummaryProvider', synth_cls='TMapSynthProvider')
2026-04-20 20:52:17 -04:00
_register_provider(cat, '^UObject$', summary_fn='UObjectSummaryProvider', synth_cls='UObjectSynthProvider')
for class_name in (
'AActor', 'APawn', 'ACharacter', 'APlayerController', 'AGameModeBase',
'UActorComponent', 'USceneComponent', 'UPrimitiveComponent',
'UWidget', 'UUserWidget', 'UWorld', 'ULevel', 'UGameInstance'):
_register_provider(cat, '^%s$' % class_name, summary_fn='UObjectSummaryProvider')
cat.SetEnabled(True)