# Copyright Epic Games, Inc. All Rights Reserved. # /*============================================================================= # LLDB Data Formatters for Unreal Types # =============================================================================*/ import lldb import lldb.formatters.Logger import faulthandler import signal import sys import time # Uncomment the line below to have the data formatters emit debug logging # lldb.formatters.Logger._lldb_formatters_debug_level=1 # What documentation there is for parsing values in LLDB can be found here: # https://lldb.llvm.org/python_reference/index.html # https://lldb.llvm.org/python_reference/lldb.SBValue-class.html # To install: # 1) Open Terminal and run: # touch ~/.lldbinit # open ~/.lldbinit # 2) Add the following text to .lldbini and save - modifying the path as appropriate: # settings set target.inline-breakpoint-strategy always # command script import "/Path/To/Epic/UE/Engine/Extras/LLDBDataFormatters/UEDataFormatter.py" # 3) Restart Xcode _FNameEntryType = None _FNameEntryStride = 2 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 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_var = target.FindFirstGlobalVariable('GNameBlocksDebug') gname_ptr = gname_var.GetValueAsUnsigned(0) error = lldb.SBError() block_addr = process.ReadPointerFromMemory(gname_ptr + block_idx * target.GetAddressByteSize(), error) if error.Fail(): return '' 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 '' name = raw.decode('utf-16-le') else: name_addr = entry.GetChildMemberWithName('AnsiName').GetLoadAddress() raw = process.ReadMemory(name_addr, length, error) if error.Fail(): return '' name = raw.decode('utf-8', errors='replace') if number > 0: return '%s_%d' % (name, number - 1) return name 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.MightHaveChildren() 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.GetChildMemberWithName('DebugPtr') addr = debug_ptr.GetValueAsUnsigned(0) if addr == 0: return 'nullptr' if addr & 1: return 'unresolved' return '{...}' class TSharedPtrSynthProvider: def __init__(self, valobj, dict): self.valobj = valobj self.update() def num_children(self): return 1 def has_children(self): return true def get_child_index(self, name): if name == 'Object': return 0 return -1 def get_child_at_index(self, index): if index == 0: return self.object return lldb.SBValue() def update(self): self.object = self.valobj.GetChildMemberWithName('Object') def TSharedPtrSummaryProvider(valobj, dict): obj = valobj.GetChildMemberWithName('Object') addr = obj.GetValueAsUnsigned(0) if addr == 0: return 'nullptr' return '{...}' # def UEUObjectBaseSummaryProvider(valobj,dict): # Name = valobj.GetChildMemberWithName('NamePrivate') # return Name.GetSummary() # def UEFFieldClassSummaryProvider(valobj,dict): # Name = valobj.GetChildMemberWithName('Name') # return Name.GetSummary() # def UEFFieldSummaryProvider(valobj,dict): # Name = valobj.GetChildMemberWithName('NamePrivate') # return Name.GetSummary() # class UETWeakObjectPtrSynthProvider: # def __init__(self, valobj, dict): # logger = lldb.formatters.Logger.Logger() # self.valobj = valobj # def num_children(self): # logger = lldb.formatters.Logger.Logger() # return 1 # def get_child_index(self,name): # logger = lldb.formatters.Logger.Logger() # return 0 # def get_child_at_index(self,index): # logger = lldb.formatters.Logger.Logger() # logger >> "Retrieving child %s" % index # if self.ObjectSerialNumberVal >= 1: # Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].SerialNumber == %s' % (int(self.ObjectIndexVal/65536), self.ObjectIndexVal%65536, self.ObjectSerialNumberVal) # Val = self.valobj.CreateValueFromExpression("%s" % self.ObjectIndexVal, Expr) # Value = Val.GetValueAsUnsigned(0) # if Value != 0: # Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].Object' % (int(self.ObjectIndexVal/65536), self.ObjectIndexVal%65536) # return self.valobj.CreateValueFromExpression('Object', Expr) # else: # Expr = '(void*)0xDEADBEEF' # return self.valobj.CreateValueFromExpression('Object', Expr) # Expr = 'nullptr' # return self.valobj.CreateValueFromExpression('Object', Expr) # def update(self): # logger = lldb.formatters.Logger.Logger() # try: # self.ObjectSerialNumber = self.valobj.GetChildMemberWithName('ObjectSerialNumber') # self.ObjectSerialNumberVal = self.ObjectSerialNumber.GetValueAsSigned(0) # self.ObjectIndex = self.valobj.GetChildMemberWithName('ObjectIndex') # self.ObjectIndexVal = self.ObjectIndex.GetValueAsSigned(0) # except: # pass # def has_children(self): # return True # def UEFWeakObjectPtrSummaryProvider(valobj,dict): # ObjectSerialNumber = valobj.GetChildMemberWithName('ObjectSerialNumber') # ObjectSerialNumberVal = ObjectSerialNumber.GetValueAsSigned(0) # if ObjectSerialNumberVal < 1: # return 'object=nullptr' # ObjectIndex = valobj.GetChildMemberWithName('ObjectIndex') # ObjectIndexVal = ObjectIndex.GetValueAsSigned(0) # Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].SerialNumber == %s' % (int(ObjectIndexVal/65536), ObjectIndexVal%65536, ObjectSerialNumberVal) # Val = valobj.CreateValueFromExpression('%s' % ObjectIndexVal, Expr) # ValRef = Val.GetValueAsUnsigned(0) # if ValRef == 0: # return 'object=STALE' # else: # Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].Object' % (int(ObjectIndexVal/65536), ObjectIndexVal%65536) # Val = valobj.CreateValueFromExpression("%s" % ObjectIndexVal, Expr) # return 'object=' + Val.GetValue() # 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, 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 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) def __lldb_init_module(debugger,dict): global _FNameEntryType, _FNameEntryStride _FNameEntryType = debugger.GetSelectedTarget().FindFirstType('FNameEntry') _FNameEntryStride = _FNameEntryType.GetByteAlign() 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.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 summary add -F UEDataFormatter.UEUObjectBaseSummaryProvider -e UObject -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UEUObjectBaseSummaryProvider -e UObjectBase -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UEUObjectBaseSummaryProvider -e UObjectBaseUtility -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UEFFieldClassSummaryProvider -e FFieldClass -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UEFFieldSummaryProvider -e FField -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UEFWeakObjectPtrSummaryProvider -e FWeakObjectPtr -w UEDataFormatters') # debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETWeakObjectPtrSynthProvider -x "TWeakObjectPtr<.+>$" -w UEDataFormatters') # debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETWeakObjectPtrSynthProvider -x "TAutoWeakObjectPtr<.+>$" -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.UEChunkedArraySynthProvider -x "TChunkedArray<.+>$" -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UEChunkedArraySummaryProvider -e -x "TChunkedArray<.+>$" -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 synthetic add -l UEDataFormatter.UETObjectPtrSynthProvider -x "^TObjectPtr<.+>$" -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UETObjectPtrSummaryProvider -e -x "^TObjectPtr<.+>$" -w UEDataFormatters') # debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETSharedPtrSynthProvider -x "^TSharedPtr<.+>$" -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UETSharedPtrSummaryProvider -e -x "^TSharedPtr<.+>$" -w UEDataFormatters') # debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETSharedPtrSynthProvider -x "^TSharedRef<.+>$" -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UETSharedPtrSummaryProvider -e -x "^TSharedRef<.+>$" -w UEDataFormatters') # debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETWeakPtrSynthProvider -x "^TWeakPtr<.+>$" -w UEDataFormatters') # debugger.HandleCommand('type summary add -F UEDataFormatter.UETWeakPtrSummaryProvider -e -x "^TWeakPtr<.+>$" -w UEDataFormatters') debugger.HandleCommand("type category enable UEDataFormatters") def _patch_codelldb_value(): """Patch codelldb.value.Value with Unreal-friendly navigation: - __getattr__ auto-derefs pointers and falls back to iterating children by GetName() (so base classes like 'UWidget' resolve). This affects every /se and /py expression that uses Value — strictly more forgiving than the stock behavior (paths that used to fail now succeed), but it IS a global behavior change. """ try: from codelldb.value import Value except ImportError: return def __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 Value(child) Value.__getattr__ = __getattr__ _patch_codelldb_value() def fv(expr): """Evaluate a Python-syntax expression against the current frame's variables. Every frame variable is pre-wrapped in a codelldb Value and placed in the eval globals, so e.g. fv("Widget.Object.SCompoundWidget") walks via the (patched) Value.__getattr__, fv("Arr[3].Field") works via Value.__getitem__, and arithmetic/comparisons work via Value's dunders. Usage in Watch: /py fv("Widget.Object.SCompoundWidget.SWidget") """ from codelldb.value import Value frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() eval_globals = {} for var in frame.GetVariables(True, True, True, True): name = var.GetName() if var.IsValid() and name: eval_globals[name] = Value(var) try: result = eval(expr, eval_globals, {}) except Exception as e: return "<%s: %s>" % (type(e).__name__, e) if isinstance(result, Value): return Value.unwrap(result) return result def fv2(): frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() top = frame.FindVariable('Top') if not top.IsValid(): return widget = top.GetChildMemberWithName('Widget') if not widget.IsValid(): return uwidget = FindNamedChild(widget, 'UWidget') if not uwidget.IsValid(): return return uwidget def FindNamedChild(value, name): for i in range(value.GetNumChildren()): child = value.GetChildAtIndex(i) if child.IsValid() and child.GetName() == name: return child return lldb.SBValue() # # The standard repr for SBValue will recurse infinitely if you # provide useful synth providers for smart pointers. This won't. # 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 import builtins builtins.fv = fv builtins.fv2 = fv2