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 '' 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 ############################################################ # # 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, 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) ############################################################## # # 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")