diff --git a/EnginePatches/EnginePatch b/EnginePatches/EnginePatch index 66b65452..cc99f821 100644 --- a/EnginePatches/EnginePatch +++ b/EnginePatches/EnginePatch @@ -1,26 +1,3 @@ ---- Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py.orig 2026-04-12 22:58:33.989318455 -0400 -+++ Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py 2025-11-10 23:34:18.481538118 -0500 -@@ -32,7 +32,7 @@ - if DataVal == 0: - Val = 'NULL' - else: -- Expr = '(char16_t*)(%s)' % Data -+ Expr = '(char16_t*)(%s)' % DataVal - ValRef = valobj.CreateValueFromExpression('string', Expr) - Val = ValRef.GetSummary() - elif Type.IsReferenceType(): -@@ -47,6 +47,11 @@ - Expr = '(char16_t*)(%s)' % valobj.GetAddress() - ValRef = valobj.CreateValueFromExpression('string', Expr) - Val = ValRef.GetSummary() -+ else: -+ DataVal = valobj.GetValueAsUnsigned(0) -+ Expr = '(char16_t)(%s)' % DataVal -+ ValRef = valobj.CreateValueFromExpression('string', Expr) -+ Val = ValRef.GetSummary() - return Val - - def UESignedCharSummaryProvider(valobj,dict): --- Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp.orig 2026-04-12 22:58:34.075320964 -0400 +++ Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp 2026-04-12 23:03:40.139226303 -0400 @@ -149,7 +149,7 @@ diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Test_TMaps.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Test_TMaps.h new file mode 100644 index 00000000..bc399635 --- /dev/null +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Test_TMaps.h @@ -0,0 +1,57 @@ +#pragma once + +#include "CoreMinimal.h" +#include "WingServer.h" +#include "WingBasics.h" +#include "Containers/BitArray.h" +#include "Containers/SparseArray.h" +#include "Test_TMaps.generated.h" + + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +UCLASS() +class UWing_Test_TMaps : public UWingHandler +{ + GENERATED_BODY() + +public: + virtual void Register() override + { + UWingServer::AddHandler(this, + TEXT("Constructs a small TMap, TBitArray, and TSparseArray so that " + "a developer can set a breakpoint and inspect them with the " + "lldb data formatters.")); + } + + virtual void Handle() override + { + TMap Map; + Map.Add(1, TEXT("one")); + Map.Add(2, TEXT("two")); + Map.Add(3, TEXT("three")); + Map.Add(42, TEXT("forty-two")); + + TBitArray<> Bits; + Bits.Add(true); + Bits.Add(false); + Bits.Add(true); + Bits.Add(true); + Bits.Add(false); + + // Add a few entries, then remove a middle one so the live set is + // non-contiguous. Exercises the sparse-array formatter against a hole. + TSparseArray Sparse; + Sparse.Add(TEXT("alpha")); + int32 BetaIdx = Sparse.Add(TEXT("beta")); + Sparse.Add(TEXT("gamma")); + Sparse.Add(TEXT("delta")); + Sparse.RemoveAt(BetaIdx); + + // Set a breakpoint on the following line to inspect Map, Bits, and Sparse. + WingOut::Stdout.Printf(TEXT("Test_TMaps: Map has %d entries, Bits has %d bits, Sparse has %d entries.\n"), + Map.Num(), Bits.Num(), Sparse.Num()); + } +}; diff --git a/tools/UEDataFormatter.py b/tools/UEDataFormatter.py index b07dcd29..49169fbe 100644 --- a/tools/UEDataFormatter.py +++ b/tools/UEDataFormatter.py @@ -97,6 +97,37 @@ class StoredSynthProvider: def has_children(self): return len(self.children) > 0 +############################################################ +# +# An Indexed synth provider exposes N integer-indexed +# children [0]..[N-1]. Subclasses set self.count in update() +# and override get_child_at_index(index) to return the SBValue +# for a given index (with their own range check). +# +############################################################ + +MAX_ARRAY_CHILDREN = 50 + +class IndexedSynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj + self.update() + + def num_children(self): + return min(self.count, MAX_ARRAY_CHILDREN) + + def get_child_index(self, name): + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def has_children(self): + return self.count > 0 + + def update(self): + self.count = 0 + ############################################################ # # Provider for FString @@ -112,7 +143,7 @@ def UEFStringSummaryProvider(valobj, dict): valobj = valobj.Dereference() elif sbtype.IsReferenceType(): valobj = valobj.Dereference() - tarray = valobj.GetChildMemberWithName('Data') + tarray = valobj.GetChildMemberWithName('Data').GetNonSyntheticValue() array_num = tarray.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) if array_num < 0: return 'corrupted string' if array_num == 0: return 'empty string' @@ -349,638 +380,157 @@ def UObjectSummaryProvider(valobj, dict): class_addr = class_debug_ptr.GetValueAsUnsigned(0) if not class_addr: return 'null' - return UEFNameSummaryProvider( + class_name = 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) - + instance_name = UEFNameSummaryProvider( + raw.GetChildMemberWithName('NamePrivate'), dict) + return 'Class=%s Name=%s' % (class_name, instance_name) + +############################################################ +# +# Providers for TArray +# +############################################################ + +def _resolve_tarray_data_addr(allocator): + """Returns the base address of the element data for any TArray allocator.""" + # Heap allocator (TSizedHeapAllocator): has a Data pointer directly + data = allocator.GetChildMemberWithName('Data') + if data.IsValid(): + return data.GetValueAsUnsigned(0) + # Inline allocator (TSizedInlineAllocator): prefer SecondaryData.Data, fall back to InlineData + secondary = allocator.GetChildMemberWithName('SecondaryData') + if secondary.IsValid(): + sec_data = secondary.GetChildMemberWithName('Data') + if sec_data.IsValid() and sec_data.GetValueAsUnsigned(0) != 0: + return sec_data.GetValueAsUnsigned(0) + # Fixed allocator or inline allocator using inline storage + inline = allocator.GetChildMemberWithName('InlineData') + if inline.IsValid(): + return inline.GetLoadAddress() + return 0 + +def _resolve_tarray(tarray_val): + """Given an SBValue of a TArray, return (array_num, data_sbvalue) where + data_sbvalue is a typed array of the TArray's element type. Returns + (0, None) if empty or unresolvable.""" + tarray_val = tarray_val.GetNonSyntheticValue() + arr_type = tarray_val.GetType().GetUnqualifiedType() + if arr_type.IsReferenceType(): + arr_type = arr_type.GetDereferencedType() + elif arr_type.IsPointerType(): + arr_type = arr_type.GetPointeeType() + if arr_type.GetNumberOfTemplateArguments() == 0: return 0, None + array_num = tarray_val.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) + if array_num <= 0: return 0, None + data_addr = _resolve_tarray_data_addr(tarray_val.GetChildMemberWithName('AllocatorInstance')) + if data_addr == 0: return 0, None + elem_type = arr_type.GetTemplateArgumentType(0) + if elem_type.GetByteSize() == 0: return 0, None + target = tarray_val.GetTarget() + data = target.CreateValueFromAddress( + 'data', lldb.SBAddress(data_addr, target), elem_type.GetArrayType(array_num)) + return array_num, data + +class TArraySynthProvider(IndexedSynthProvider): + def get_child_at_index(self, index): + if index < 0 or index >= self.count: + return lldb.SBValue() + return self.data_array.GetChildAtIndex(index) + + def update(self): + self.count, self.data_array = _resolve_tarray(self.valobj) + +def TArraySummaryProvider(valobj, dict): + raw = valobj.GetNonSyntheticValue() + array_num = raw.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) + if array_num < 0: return 'corrupted' + return 'size=%d' % array_num + +############################################################ +# +# Provider for TBitArray +# +############################################################ + +def _bitarray_words(bit_array_val): + """Given an SBValue of a TBitArray, return its backing uint32[] as an SBValue + (or None if the array is empty/unallocated).""" + bit_array_val = bit_array_val.GetNonSyntheticValue() + num_bits = bit_array_val.GetChildMemberWithName('NumBits').GetValueAsSigned(0) + if num_bits <= 0: return None + allocator = bit_array_val.GetChildMemberWithName('AllocatorInstance') + data_addr = _resolve_tarray_data_addr(allocator) + if data_addr == 0: return None + target = bit_array_val.GetTarget() + uint32_type = target.GetBasicType(lldb.eBasicTypeUnsignedInt) + num_words = (num_bits + 31) // 32 + return target.CreateValueFromAddress( + 'words', lldb.SBAddress(data_addr, target), + uint32_type.GetArrayType(num_words)) + +class TBitArraySynthProvider(IndexedSynthProvider): + def get_child_at_index(self, index): + if index < 0 or index >= self.count: + return lldb.SBValue() + word = self.words.GetChildAtIndex(index) + target = self.valobj.GetTarget() + child = target.CreateValueFromAddress( + '[%d]' % index, lldb.SBAddress(word.GetLoadAddress(), target), + target.GetBasicType(lldb.eBasicTypeUnsignedInt)) + child.SetFormat(lldb.eFormatBinary) + return child + + def update(self): + self.count = 0 + self.words = _bitarray_words(self.valobj) + if self.words is None: return + self.count = self.words.GetNumChildren() + +def TBitArraySummaryProvider(valobj, dict): + num_bits = valobj.GetNonSyntheticValue().GetChildMemberWithName('NumBits').GetValueAsSigned(0) + return 'size=%d' % num_bits + +############################################################ +# +# Provider for TSparseArray +# +############################################################ + +class TSparseArraySynthProvider(IndexedSynthProvider): + def get_child_at_index(self, index): + if index < 0 or index >= self.count: + return lldb.SBValue() + raw_idx = self.live_indices[index] + slot = self.data_array.GetChildAtIndex(raw_idx) + target = self.valobj.GetTarget() + return target.CreateValueFromAddress( + '[%d]' % index, + lldb.SBAddress(slot.GetLoadAddress(), target), + self.element_type) + + def update(self): + self.count = 0 + self.live_indices = [] + self.data_array = None + self.element_type = None + array_num, data = _resolve_tarray(self.valobj.GetChildMemberWithName('Data')) + if data is None: return + words = _bitarray_words(self.valobj.GetChildMemberWithName('AllocationFlags')) + if words is None: return + self.data_array = data + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) + for i in range(array_num): + word = words.GetChildAtIndex(i // 32).GetValueAsUnsigned(0) + if (word >> (i % 32)) & 1: + self.live_indices.append(i) + self.count = len(self.live_indices) + +def TSparseArraySummaryProvider(valobj, dict): + raw = valobj.GetNonSyntheticValue() + array_num = raw.GetChildMemberWithName('Data').GetNonSyntheticValue().GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) + num_free = raw.GetChildMemberWithName('NumFreeIndices').GetValueAsSigned(0) + return 'size=%d' % (array_num - num_free) ############################################################## # @@ -1040,38 +590,48 @@ lldb.SBValue.__repr__ = _sbvalue_repr # ############################################################# +def _register_provider(cat, pattern, summary_fn=None, synth_cls=None): + """Register summary and/or synthetic providers for a type pattern.""" + spec = lldb.SBTypeNameSpecifier(pattern, lldb.eFormatterMatchRegex) + if summary_fn: + cat.AddTypeSummary(spec, lldb.SBTypeSummary.CreateWithFunctionName( + __name__ + '.' + summary_fn, lldb.eTypeOptionCascade)) + if synth_cls: + cat.AddTypeSynthetic(spec, lldb.SBTypeSynthetic.CreateWithClassName( + __name__ + '.' + synth_cls, lldb.eTypeOptionCascade)) + def __lldb_init_module(debugger, dict): print("Running lldb_init_module") _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") + debugger.HandleCommand('type category delete ' + __name__) + cat = debugger.CreateCategory(__name__) + + _register_provider(cat, '^FString$', summary_fn='UEFStringSummaryProvider') + _register_provider(cat, '^FName$', summary_fn='UEFNameSummaryProvider') + _register_provider(cat, '^TObjectPtr<.+>$', summary_fn='TObjectPtrSummaryProvider', synth_cls='TObjectPtrSynthProvider') + _register_provider(cat, '^TStrongObjectPtr<.+>$', summary_fn='TStrongObjectPtrSummaryProvider', synth_cls='TStrongObjectPtrSynthProvider') + _register_provider(cat, '^TSharedPtr<.+>$', summary_fn='TSharedPtrSummaryProvider', synth_cls='TSharedPtrSynthProvider') + _register_provider(cat, '^TSharedRef<.+>$', summary_fn='TSharedPtrSummaryProvider', synth_cls='TSharedPtrSynthProvider') + _register_provider(cat, '^TWeakPtr<.+>$', summary_fn='TWeakPtrSummaryProvider', synth_cls='TWeakPtrSynthProvider') + _register_provider(cat, '^TWeakObjectPtr<.+>$', summary_fn='TWeakObjectPtrSummaryProvider', synth_cls='TWeakObjectPtrSynthProvider') + _register_provider(cat, '^FWeakObjectPtr$', summary_fn='TWeakObjectPtrSummaryProvider', synth_cls='TWeakObjectPtrSynthProvider') + _register_provider(cat, '^TArray<.+>$', summary_fn='TArraySummaryProvider', synth_cls='TArraySynthProvider') + _register_provider(cat, '^TBitArray<.+>$', summary_fn='TBitArraySummaryProvider', synth_cls='TBitArraySynthProvider') + _register_provider(cat, '^TSparseArray<.+>$', summary_fn='TSparseArraySummaryProvider', synth_cls='TSparseArraySynthProvider') + _register_provider(cat, '^UObject$', summary_fn='UObjectSummaryProvider', synth_cls='UObjectSynthProvider') + + for class_name in ( + 'AActor', 'APawn', 'ACharacter', 'APlayerController', 'AGameModeBase', + 'UActorComponent', 'USceneComponent', 'UPrimitiveComponent', + 'UWidget', 'UUserWidget', 'UWorld', 'ULevel', 'UGameInstance'): + _register_provider(cat, '^%s$' % class_name, summary_fn='UObjectSummaryProvider') + + cat.SetEnabled(True) + +def counttypes(): + # Count all class types. + target = lldb.debugger.GetSelectedTarget() + count = 0 + for module in target.module_iter(): + count += module.GetTypes(lldb.eTypeClassClass).GetSize() + print('UEDataFormatter: %d class types found' % count)