Files
integration/tools/UEDataFormatter.py

1078 lines
44 KiB
Python

import lldb, signal, sys, time
import lldb.formatters.Logger
import codelldb.value
############################################################
#
# Store pointers to some important types and globals.
#
############################################################
_FNameEntryType = None
_FNameEntryStride = None
_FUObjectItemType = None
_GNameBlocksDebug = None
_GObjectArrayForDebugVisualizers = None
def _init_globals(target):
global _FNameEntryType, _FNameEntryStride, _FUObjectItemType
global _GNameBlocksDebug, _GObjectArrayForDebugVisualizers
_FNameEntryType = target.FindFirstType('FNameEntry')
if not _FNameEntryType.IsValid():
print('UEDataFormatter: FNameEntry type not found')
_FNameEntryStride = _FNameEntryType.GetByteAlign()
_FUObjectItemType = target.FindFirstType('FUObjectItem')
if not _FUObjectItemType.IsValid():
print('UEDataFormatter: FUObjectItem type not found')
_GNameBlocksDebug = target.FindFirstGlobalVariable('GNameBlocksDebug')
if not _GNameBlocksDebug.IsValid():
print('UEDataFormatter: GNameBlocksDebug not found')
_GObjectArrayForDebugVisualizers = target.FindFirstGlobalVariable('GObjectArrayForDebugVisualizers')
if not _GObjectArrayForDebugVisualizers.IsValid():
print('UEDataFormatter: GObjectArrayForDebugVisualizers not found')
############################################################
#
# A forwarding synth provider is a provider that
# forwards all child lookups to a different SBValue.
#
############################################################
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
############################################################
#
# Provider for FString
#
############################################################
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()
tarray = valobj.GetChildMemberWithName('Data')
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
#
############################################################
def UEFNameSummaryProvider(valobj, dict):
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)
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')
else:
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')
if number > 0: return '%s_%d' % (name, number - 1)
return name
############################################################
#
# Providers for TObjectPtr
#
############################################################
class TObjectPtrSynthProvider(ForwardingSynthProvider):
def update(self):
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()
else:
self.forward_to = debug_ptr.Dereference()
def TObjectPtrSummaryProvider(valobj, dict):
debug_ptr = valobj.GetNonSyntheticValue().GetChildMemberWithName('DebugPtr')
addr = debug_ptr.GetValueAsUnsigned(0)
if addr == 0: return 'nullptr'
if addr & 1: return 'unresolved'
return '{...}'
############################################################
#
# Providers for TStrongObjectPtr
#
############################################################
class TStrongObjectPtrSynthProvider(ForwardingSynthProvider):
def update(self):
obj = self.valobj.GetChildMemberWithName('Object')
addr = obj.GetValueAsUnsigned(0)
if addr == 0:
self.forward_to = lldb.SBValue()
else:
self.forward_to = obj.Dereference()
def TStrongObjectPtrSummaryProvider(valobj, dict):
obj = valobj.GetNonSyntheticValue().GetChildMemberWithName('Object')
addr = obj.GetValueAsUnsigned(0)
if addr == 0: return 'nullptr'
return '{...}'
############################################################
#
# 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:
self.forward_to = obj_ptr.Dereference()
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 '{...}'
############################################################
#
# Providers for TSharedPtr
#
############################################################
class TSharedPtrSynthProvider(ForwardingSynthProvider):
def update(self):
obj = self.valobj.GetChildMemberWithName('Object')
addr = obj.GetValueAsUnsigned(0)
if addr == 0:
self.forward_to = lldb.SBValue()
else:
self.forward_to = obj.Dereference()
def TSharedPtrSummaryProvider(valobj, dict):
obj = valobj.GetNonSyntheticValue().GetChildMemberWithName('Object')
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
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:
self.forward_to = obj.Dereference()
def TWeakPtrSummaryProvider(valobj, dict):
obj, expired = _resolve_weak_ptr(valobj.GetNonSyntheticValue())
if expired: return 'expired'
if obj is None: return 'nullptr'
return '{...}'
############################################################
#
# Providers for TUniquePtr
#
############################################################
class TUniquePtrSynthProvider(ForwardingSynthProvider):
def update(self):
ptr = self.valobj.GetChildMemberWithName('Ptr')
addr = ptr.GetValueAsUnsigned(0)
if addr == 0:
self.forward_to = lldb.SBValue()
else:
self.forward_to = ptr.Dereference()
def TUniquePtrSummaryProvider(valobj, dict):
ptr = valobj.GetNonSyntheticValue().GetChildMemberWithName('Ptr')
addr = ptr.GetValueAsUnsigned(0)
if addr == 0: return 'nullptr'
return '{...}'
############################################################
#
# Providers for UObject
#
############################################################
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'
return UEFNameSummaryProvider(
class_debug_ptr.Dereference().GetChildMemberWithName('NamePrivate'), dict)
# class UEChunkedArraySynthProvider:
# def __init__(self, valobj, dict):
# logger = lldb.formatters.Logger.Logger()
# self.valobj = valobj
# def num_children(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# NumElementsVal = self.NumElements.GetValueAsSigned(0)
# return NumElementsVal;
# except:
# return 0;
# def get_child_index(self,name):
# logger = lldb.formatters.Logger.Logger()
# try:
# return int(name.lstrip('[').rstrip(']'))
# except:
# return None
# def get_child_at_index(self,index):
# logger = lldb.formatters.Logger.Logger()
# logger >> "Retrieving child %s" % index
# Expr = '(unsigned)sizeof(%s::FChunk)/%s' % (self.valobj.GetType().GetUnqualifiedType().GetName(), self.ElementTypeSize)
# self.ChunkBytes = self.valobj.CreateValueFromExpression('[%s]' % index, Expr)
# self.ChunkBytesSize = self.ChunkBytes.GetValueAsUnsigned(0)
# assert self.ChunkBytesSize != 0
# Expr = '*(*(((%s**)%s)+%s)+%s)' % (self.ElementType.GetName(), self.AllocatorData.GetValue(), index / self.ChunkBytesSize, index % self.ChunkBytesSize)
# return self.valobj.CreateValueFromExpression('[%s]' % index, Expr)
# def extract_type(self):
# logger = lldb.formatters.Logger.Logger()
# ArrayType = self.valobj.GetType().GetUnqualifiedType()
# if ArrayType.IsReferenceType():
# ArrayType = ArrayType.GetDereferencedType()
# elif ArrayType.IsPointerType():
# ArrayType = ArrayType.GetPointeeType()
# if ArrayType.GetNumberOfTemplateArguments() > 0:
# ElementType = ArrayType.GetTemplateArgumentType(0)
# else:
# ElementType = None
# return ElementType
# def update(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# self.ElementType = self.extract_type()
# self.ElementTypeSize = self.ElementType.GetByteSize()
# assert self.ElementTypeSize != 0
# self.NumElements = self.valobj.GetChildMemberWithName('NumElements')
# self.Chunks = self.valobj.GetChildMemberWithName('Chunks')
# self.ArrayNum = self.Chunks.GetChildMemberWithName('ArrayNum')
# self.AllocatorInstance = self.Chunks.GetChildMemberWithName('AllocatorInstance')
# self.AllocatorData = self.AllocatorInstance.GetChildMemberWithName('Data')
# except:
# pass
# def has_children(self):
# return True
# def UEChunkedArraySummaryProvider(valobj,dict):
# return 'size=%s' % valobj.GetNumChildren()
# class UESparseArraySynthProvider:
# def __init__(self, valobj, dict):
# logger = lldb.formatters.Logger.Logger()
# self.valobj = valobj
# def num_children(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# NumBitsVal = self.NumFreeIndices.GetValueAsSigned(0)
# ArrayNumVal = self.Data.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0)
# return ArrayNumVal - NumBitsVal;
# except:
# return 0;
# def get_child_index(self,name):
# logger = lldb.formatters.Logger.Logger()
# try:
# return int(name.lstrip('[').rstrip(']'))
# except:
# return None
# def get_child_at_index(self,index):
# logger = lldb.formatters.Logger.Logger()
# logger >> "Retrieving child %s" % index
# if index < 0:
# return None;
# if index >= self.num_children():
# return None;
# Val = None
# if self.SecondaryDataDataVal > 0:
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataData.GetAddress(), index, index)
# Val = self.SecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr)
# else:
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.InlineData.GetAddress(), index, index)
# Val = self.InlineData.CreateValueFromExpression('[5s]' % index, Expr)
# if Val.GetValueAsSigned(0) != 0:
# offset = index * self.ElementTypeSize
# return self.AllocatorData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
# else:
# return None
# def extract_type(self):
# logger = lldb.formatters.Logger.Logger()
# ArrayType = self.valobj.GetType().GetUnqualifiedType()
# if ArrayType.IsReferenceType():
# ArrayType = ArrayType.GetDereferencedType()
# elif ArrayType.IsPointerType():
# ArrayType = ArrayType.GetPointeeType()
# if ArrayType.GetNumberOfTemplateArguments() > 0:
# ElementType = ArrayType.GetTemplateArgumentType(0)
# else:
# ElementType = None
# return ElementType
# def update(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# self.ElementType = self.extract_type()
# self.ElementTypeSize = self.ElementType.GetByteSize()
# assert self.ElementTypeSize != 0
# self.NumFreeIndices = self.valobj.GetChildMemberWithName('NumFreeIndices')
# self.Data = self.valobj.GetChildMemberWithName('Data')
# self.AllocatorInstance = self.Data.GetChildMemberWithName('AllocatorInstance')
# self.AllocatorData = self.AllocatorInstance.GetChildMemberWithName('Data')
# self.AllocationFlags = self.valobj.GetChildMemberWithName('AllocationFlags')
# self.InlineData = self.AllocationFlags.GetChildMemberWithName('InlineData')
# self.SecondaryData = self.AllocationFlags.GetChildMemberWithName('SecondaryData')
# self.SecondaryDataData = self.SecondaryData.GetChildMemberWithName('Data')
# self.SecondaryDataDataVal = self.SecondaryDataData.GetValueAsSigned(0)
# except:
# pass
# def has_children(self):
# return True
# def UESparseArraySummaryProvider(valobj,dict):
# return 'size=%s' % valobj.GetNumChildren()
# class UEBitArraySynthProvider:
# def __init__(self, valobj, dict):
# logger = lldb.formatters.Logger.Logger()
# self.valobj = valobj
# def num_children(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# NumBitsVal = self.NumBits.GetValueAsSigned(0)
# return NumBitsVal;
# except:
# return 0;
# def get_child_index(self,name):
# logger = lldb.formatters.Logger.Logger()
# try:
# return int(name.lstrip('[').rstrip(']'))
# except:
# return None
# def get_child_at_index(self,index):
# logger = lldb.formatters.Logger.Logger()
# logger >> "Retrieving child %s" % index
# if index < 0:
# return None;
# if index >= self.num_children():
# return None;
# if self.SecondaryDataDataVal > 0:
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataData.GetAddress(), index, index)
# return self.SecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr)
# else:
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.InlineData.GetAddress(), index, index)
# return self.InlineData.CreateValueFromExpression('[%s]' % index, Expr)
# return None
# def update(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# self.SecondaryDataData = None
# self.SecondaryDataDataVal = 0
# self.NumBits = self.valobj.GetChildMemberWithName('NumBits')
# self.MaxBits = self.valobj.GetChildMemberWithName('MaxBits')
# self.InlineData = self.valobj.GetChildMemberWithName('InlineData')
# self.SecondaryData = self.valobj.GetChildMemberWithName('SecondaryData')
# if self.SecondaryData != None:
# self.SecondaryDataData = self.SecondaryData.GetChildMemberWithName('Data')
# self.SecondaryDataDataVal = self.SecondaryDataData.GetValueAsUnsigned(0)
# except:
# pass
# def has_children(self):
# return True
# def UEBitArraySummaryProvider(valobj,dict):
# return 'size=%s' % valobj.GetNumChildren()
# class UEArraySynthProvider:
# def __init__(self, valobj, dict):
# logger = lldb.formatters.Logger.Logger()
# self.valobj = valobj
# def num_children(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# ArrayNumVal = self.ArrayNum.GetValueAsSigned(0)
# return ArrayNumVal + self.NumChildren;
# except:
# return 0;
# def get_child_index(self,name):
# logger = lldb.formatters.Logger.Logger()
# try:
# return self.NumChildren + int(name.lstrip('[').rstrip(']'))
# except:
# return self.valobj.GetIndexOfChildWithName(name)
# def get_child_at_index(self,index):
# logger = lldb.formatters.Logger.Logger()
# logger >> "Retrieving child %s" % index
# if index < 0:
# return None;
# if index < self.NumChildren:
# return self.valobj.GetChildAtIndex(index)
# else:
# index -= self.NumChildren
# if index >= self.num_children():
# return None;
# try:
# offset = index * self.ElementTypeSize
# if self.Data != None:
# return self.Data.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
# elif self.SecondaryDataDataVal > 0:
# return self.SecondaryDataData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
# else:
# return self.InlineData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
# except:
# return None
# def extract_type(self):
# logger = lldb.formatters.Logger.Logger()
# ArrayType = self.valobj.GetType().GetUnqualifiedType()
# if ArrayType.IsReferenceType():
# ArrayType = ArrayType.GetDereferencedType()
# elif ArrayType.IsPointerType():
# ArrayType = ArrayType.GetPointeeType()
# if ArrayType.GetNumberOfTemplateArguments() > 0:
# ElementType = ArrayType.GetTemplateArgumentType(0)
# else:
# ElementType = None
# return ElementType
# def update(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# self.NumChildren = self.valobj.GetNumChildren()
# self.ArrayNum = self.valobj.GetChildMemberWithName('ArrayNum')
# self.ArrayMax = self.valobj.GetChildMemberWithName('ArrayMax')
# self.AllocatorInstance = self.valobj.GetChildMemberWithName('AllocatorInstance')
# if self.AllocatorInstance.GetType().IsReferenceType():
# self.AllocatorInstance = self.AllocatorInstance.Dereference()
# self.Data = self.AllocatorInstance.GetChildMemberWithName('Data')
# self.InlineData = self.AllocatorInstance.GetChildMemberWithName('InlineData')
# self.SecondaryData = self.AllocatorInstance.GetChildMemberWithName('SecondaryData')
# if self.SecondaryData != None:
# self.SecondaryDataData = self.SecondaryData.GetChildMemberWithName('Data')
# self.SecondaryDataDataVal = self.SecondaryDataData.GetValueAsUnsigned(0)
# else:
# self.SecondaryDataData = None
# self.SecondaryDataDataVal = 0
# except:
# logger >> "UEArraySynthProvider::update failed accessing members"
# pass
# try:
# self.ElementType = self.extract_type()
# self.ElementTypeSize = self.ElementType.GetByteSize()
# assert self.ElementTypeSize != 0
# except:
# logger >> "UEArraySynthProvider::update failed accessing element type"
# pass
# def has_children(self):
# return True
# def UEArraySummaryProvider(valobj,dict):
# return 'size=%s' % valobj.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0)
# class UESetSynthProvider:
# def __init__(self, valobj, dict):
# logger = lldb.formatters.Logger.Logger()
# self.valobj = valobj
# # Can't cast to TSetElement - the template instantiation won't allow it
# Expr = '(size_t)sizeof(FSetElementId) + sizeof(int32)'
# self.TSetElement = self.valobj.CreateValueFromExpression('TSetElement', Expr)
# def num_children(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# ArrayNumVal = self.ArrayNum.GetValueAsUnsigned(0)
# NumFreeIndicesVal = self.NumFreeIndices.GetValueAsUnsigned(0)
# return ArrayNumVal - NumFreeIndicesVal;
# except:
# return 0;
# def get_child_index(self,name):
# logger = lldb.formatters.Logger.Logger()
# try:
# return int(name.lstrip('[').rstrip(']'))
# except:
# return 0
# def get_child_at_index(self,index):
# logger = lldb.formatters.Logger.Logger()
# logger >> "Retrieving child %s" % index
# if index < 0:
# return None;
# if index >= self.num_children():
# return None;
# try:
# offset = index * self.ElementTypeSize
# HasObject = 0
# if self.SecondaryDataDataVal > 0:
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataDataVal, index, index)
# HasObject = 1 ##self.AllocationFlagsSecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0)
# else:
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.AllocationFlagsInlineDataAddr, index, index)
# HasObject = 1 ##self.AllocationFlagsInlineData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0)
# if HasObject == 1:
# return self.AllocatorInstanceData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
# else:
# return self.valobj.CreateValueFromExpression('[%s]' % index, '(void*)0xDEADBEEF')
# except:
# return None
# def extract_type(self):
# logger = lldb.formatters.Logger.Logger()
# ArrayType = self.valobj.GetType().GetUnqualifiedType()
# if ArrayType.IsReferenceType():
# ArrayType = ArrayType.GetDereferencedType()
# elif ArrayType.IsPointerType():
# ArrayType = ArrayType.GetPointeeType()
# if ArrayType.GetNumberOfTemplateArguments() > 0:
# ElementType = ArrayType.GetTemplateArgumentType(0)
# else:
# ElementType = None
# return ElementType
# def update(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# self.Elements = self.valobj.GetChildMemberWithName('Elements')
# self.ElementsData = self.Elements.GetChildMemberWithName('Data')
# self.ArrayNum = self.ElementsData.GetChildMemberWithName('ArrayNum')
# self.NumFreeIndices = self.Elements.GetChildMemberWithName('NumFreeIndices')
# self.AllocationFlags = self.Elements.GetChildMemberWithName('AllocationFlags')
# self.AllocationFlagsAllocatorInstance = self.AllocationFlags.GetChildMemberWithName('AllocatorInstance')
# self.AllocatorInstance = self.ElementsData.GetChildMemberWithName('AllocatorInstance')
# self.AllocatorInstanceData = self.AllocatorInstance.GetChildMemberWithName('Data')
# self.AllocationFlagsInlineData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('InlineData')
# self.AllocationFlagsInlineDataAddr = self.AllocationFlagsInlineData.AddressOf().GetValueAsUnsigned(0)
# self.AllocationFlagsSecondaryData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('SecondaryData')
# self.AllocationFlagsSecondaryDataData = self.AllocationFlagsSecondaryData.GetChildMemberWithName('Data')
# self.SecondaryDataDataVal = self.AllocationFlagsSecondaryDataData.GetValueAsUnsigned(0)
# self.ElementType = self.extract_type()
# # This may fail due to C++ struct padding - will have to check
# self.ElementTypeSize = self.ElementType.GetByteSize() + self.TSetElement.GetValueAsUnsigned(0)
# assert self.ElementTypeSize != 0
# except:
# pass
# def has_children(self):
# return True
# def UESetSummaryProvider(valobj,dict):
# return 'size=%s' % valobj.GetNumChildren()
# class UEMapSynthProvider:
# def __init__(self, valobj, dict):
# logger = lldb.formatters.Logger.Logger()
# self.valobj = valobj
# # Can't cast to TSetElement - the template instantiation won't allow it
# Expr = '(size_t)sizeof(FSetElementId) + sizeof(int32)'
# self.TSetElement = self.valobj.CreateValueFromExpression('TSetElement', Expr)
# def num_children(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# ArrayNumVal = self.ArrayNum.GetValueAsUnsigned(0)
# NumFreeIndicesVal = self.NumFreeIndices.GetValueAsUnsigned(0)
# return ArrayNumVal - NumFreeIndicesVal;
# except:
# return 0;
# def get_child_index(self,name):
# logger = lldb.formatters.Logger.Logger()
# try:
# return int(name.lstrip('[').rstrip(']'))
# except:
# return 0
# def get_child_at_index(self,index):
# logger = lldb.formatters.Logger.Logger()
# logger >> "Retrieving child %s" % index
# if index < 0:
# return None;
# if index >= self.num_children():
# return None;
# try:
# offset = index * self.ElementTypeSize
# HasObject = 0
# if self.SecondaryDataDataVal != 0:
# Expr = '(bool)((((unsigned int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataDataVal, index, index)
# HasObject = 1 ##self.AllocationFlagsSecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0)
# else:
# Expr = '(bool)((((unsigned int*)%s)[%s/32] >> %s) & 1)' % (self.AllocationFlagsInlineDataAddr, index, index)
# HasObject = 1 ##self.AllocationFlagsInlineData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0)
# if HasObject == 1:
# return self.AllocatorInstanceData.CreateChildAtOffset('[%s]' % index,offset,self.ElementType)
# else:
# return self.valobj.CreateValueFromExpression('[%s]' % index, '(void*)0xDEADBEEF')
# except:
# return None
# def extract_type(self):
# logger = lldb.formatters.Logger.Logger()
# ArrayType = self.Pairs.GetType().GetUnqualifiedType()
# if ArrayType.IsReferenceType():
# ArrayType = ArrayType.GetDereferencedType()
# elif ArrayType.IsPointerType():
# ArrayType = ArrayType.GetPointeeType()
# if ArrayType.GetNumberOfTemplateArguments() > 0:
# ElementType = ArrayType.GetTemplateArgumentType(0)
# else:
# ElementType = None
# return ElementType
# def update(self):
# logger = lldb.formatters.Logger.Logger()
# try:
# self.Pairs = self.valobj.GetChildMemberWithName('Pairs')
# self.Elements = self.Pairs.GetChildMemberWithName('Elements')
# self.ElementsData = self.Elements.GetChildMemberWithName('Data')
# self.ArrayNum = self.ElementsData.GetChildMemberWithName('ArrayNum')
# self.NumFreeIndices = self.Elements.GetChildMemberWithName('NumFreeIndices')
# self.AllocationFlags = self.Elements.GetChildMemberWithName('AllocationFlags')
# self.AllocationFlagsAllocatorInstance = self.AllocationFlags.GetChildMemberWithName('AllocatorInstance')
# self.AllocatorInstance = self.ElementsData.GetChildMemberWithName('AllocatorInstance')
# self.AllocatorInstanceData = self.AllocatorInstance.GetChildMemberWithName('Data')
# self.AllocationFlagsInlineData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('InlineData')
# self.AllocationFlagsInlineDataAddr = self.AllocationFlagsInlineData.AddressOf().GetValueAsUnsigned(0)
# self.AllocationFlagsSecondaryData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('SecondaryData')
# self.AllocationFlagsSecondaryDataData = self.AllocationFlagsSecondaryData.GetChildMemberWithName('Data')
# self.SecondaryDataDataVal = self.AllocationFlagsSecondaryDataData.GetValueAsUnsigned(0)
# self.ElementType = self.extract_type()
# # This may fail due to C++ struct padding - will have to check
# self.ElementTypeSize = self.ElementType.GetByteSize() + self.TSetElement.GetValueAsUnsigned(0)
# assert self.ElementTypeSize != 0
# except:
# pass
# def has_children(self):
# return True
# def UEMapSummaryProvider(valobj,dict):
# return 'size=%s' % valobj.GetNumChildren()
# class UETObjectPtrSynthProvider:
# def __init__(self, valobj, dict):
# self.valobj = valobj
# def num_children(self):
# return self.target.GetNumChildren() if self.resolved else 0
# def get_child_index(self, name):
# if not self.resolved:
# return None
# return self.target.GetIndexOfChildWithName(name)
# def get_child_at_index(self, index):
# if not self.resolved:
# return None
# return self.target.GetChildAtIndex(index)
# def update(self):
# try:
# self.DebugPtr = self.valobj.GetChildMemberWithName('DebugPtr')
# addr = self.DebugPtr.GetValueAsUnsigned(0)
# self.resolved = addr != 0 and (addr & 1) == 0
# self.target = self.DebugPtr.Dereference() if self.resolved else None
# except:
# self.resolved = False
# self.target = None
# def has_children(self):
# return self.resolved and self.target.GetNumChildren() > 0
# def UETObjectPtrSummaryProvider(valobj, dict):
# raw = valobj.GetNonSyntheticValue()
# DebugPtr = raw.GetChildMemberWithName('DebugPtr')
# addr = DebugPtr.GetValueAsUnsigned(0)
# if addr == 0:
# return 'nullptr'
# if addr & 1:
# return 'unresolved'
# return DebugPtr.Dereference().GetSummary() or ('0x%x' % addr)
# class UETSharedPtrSynthProvider:
# def __init__(self, valobj, dict):
# self.valobj = valobj
# def num_children(self):
# return self.target.GetNumChildren() if self.valid else 0
# def get_child_index(self, name):
# if not self.valid:
# return None
# return self.target.GetIndexOfChildWithName(name)
# def get_child_at_index(self, index):
# if not self.valid:
# return None
# return self.target.GetChildAtIndex(index)
# def update(self):
# try:
# self.Object = self.valobj.GetChildMemberWithName('Object')
# addr = self.Object.GetValueAsUnsigned(0)
# self.valid = addr != 0
# self.target = self.Object.Dereference() if self.valid else None
# except:
# self.valid = False
# self.target = None
# def has_children(self):
# return self.valid and self.target.GetNumChildren() > 0
# def UETSharedPtrSummaryProvider(valobj, dict):
# raw = valobj.GetNonSyntheticValue()
# Object = raw.GetChildMemberWithName('Object')
# addr = Object.GetValueAsUnsigned(0)
# if addr == 0:
# return 'nullptr'
# return Object.Dereference().GetSummary() or ('0x%x' % addr)
# def _tweakptr_alive(raw):
# """True if a TWeakPtr's referenced object is still alive.
# Walks WeakReferenceCount -> ReferenceController -> SharedReferenceCount.
# SharedReferenceCount may be std::atomic<int32>, in which case we drill into
# the atomic's inner storage.
# """
# wrc = raw.GetChildMemberWithName('WeakReferenceCount')
# rc = wrc.GetChildMemberWithName('ReferenceController')
# if rc.GetValueAsUnsigned(0) == 0:
# return False
# src = rc.Dereference().GetChildMemberWithName('SharedReferenceCount')
# if not src.IsValid():
# return False
# count = src.GetValueAsSigned(-1)
# # std::atomic<int32> has no direct value; drill into its inner storage
# while count == -1 and src.GetNumChildren() > 0:
# src = src.GetChildAtIndex(0)
# count = src.GetValueAsSigned(-1)
# return count > 0
# class UETWeakPtrSynthProvider:
# def __init__(self, valobj, dict):
# self.valobj = valobj
# def num_children(self):
# return self.target.GetNumChildren() if self.alive else 0
# def get_child_index(self, name):
# if not self.alive:
# return None
# return self.target.GetIndexOfChildWithName(name)
# def get_child_at_index(self, index):
# if not self.alive:
# return None
# return self.target.GetChildAtIndex(index)
# def update(self):
# try:
# self.Object = self.valobj.GetChildMemberWithName('Object')
# self.alive = self.Object.GetValueAsUnsigned(0) != 0 and _tweakptr_alive(self.valobj)
# self.target = self.Object.Dereference() if self.alive else None
# except:
# self.alive = False
# self.target = None
# def has_children(self):
# return self.alive and self.target.GetNumChildren() > 0
# def UETWeakPtrSummaryProvider(valobj, dict):
# raw = valobj.GetNonSyntheticValue()
# Object = raw.GetChildMemberWithName('Object')
# addr = Object.GetValueAsUnsigned(0)
# if addr == 0:
# return 'nullptr'
# if not _tweakptr_alive(raw):
# return 'expired'
# return Object.Dereference().GetSummary() or ('0x%x' % addr)
##############################################################
#
# 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
#
#############################################################
def __lldb_init_module(debugger, dict):
print("Running lldb_init_module")
_init_globals(debugger.GetSelectedTarget())
debugger.HandleCommand('type category delete UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.UEFStringSummaryProvider -e FString -w UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.UEFNameSummaryProvider -e FName -w UEDataFormatters')
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TObjectPtrSynthProvider -x "^TObjectPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.TObjectPtrSummaryProvider -e -x "^TObjectPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TStrongObjectPtrSynthProvider -x "^TStrongObjectPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.TStrongObjectPtrSummaryProvider -e -x "^TStrongObjectPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TSharedPtrSynthProvider -x "^TSharedPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.TSharedPtrSummaryProvider -e -x "^TSharedPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TSharedPtrSynthProvider -x "^TSharedRef<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.TSharedPtrSummaryProvider -e -x "^TSharedRef<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TWeakPtrSynthProvider -x "^TWeakPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.TWeakPtrSummaryProvider -e -x "^TWeakPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TWeakObjectPtrSynthProvider -x "^TWeakObjectPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.TWeakObjectPtrSummaryProvider -e -x "^TWeakObjectPtr<.+>$" -w UEDataFormatters')
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TWeakObjectPtrSynthProvider -x "^FWeakObjectPtr$" -w UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.TWeakObjectPtrSummaryProvider -e FWeakObjectPtr -w UEDataFormatters')
debugger.HandleCommand('type synthetic add -l UEDataFormatter.UObjectSynthProvider UObject -w UEDataFormatters')
debugger.HandleCommand('type summary add -F UEDataFormatter.UObjectSummaryProvider -e UObject -w UEDataFormatters')
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UEArraySynthProvider -x "TArray<.+,.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEArraySummaryProvider -e -x "TArray<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UEBitArraySynthProvider -x "TBitArray<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEBitArraySummaryProvider -e -x "TBitArray<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UESparseArraySynthProvider -x "TSparseArray<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type summary add -F UEDataFormatter.UESparseArraySummaryProvider -e -x "TSparseArray<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UESetSynthProvider -x "TSet<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type summary add -F UEDataFormatter.UESetSummaryProvider -e -x "TSet<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UEMapSynthProvider -x "TMap<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEMapSummaryProvider -e -x "TMap<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UEMapSynthProvider -x "TMapBase<.+>$" -w UEDataFormatters')
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEMapSummaryProvider -e -x "TMapBase<.+>$" -w UEDataFormatters')
debugger.HandleCommand("type category enable UEDataFormatters")