dotfiles/symlinks/config/Epic/GDBPrinters/UE4Printers.py

729 lines
20 KiB
Python
Raw Normal View History

2021-01-07 08:09:41 +00:00
#
# GDB Printers for the Unreal Engine 4
#
# How to install:
# If the file ~/.gdbinit doesn't exist
# touch ~/.gdbinit
# open ~/.gdbinit
#
# and add the following lines:
# python
# import sys
# ...
# sys.path.insert(0, '/Path/To/Epic/UE4/Engine/Extras/GDBPrinters') <--
# ...
# from UE4Printers import register_ue4_printers <--
# register_ue4_printers (None) <--
# ...
# end
import gdb
import itertools
import re
import sys
# ------------------------------------------------------------------------------
# We make our own base of the iterator to prevent issues between Python 2/3.
#
if sys.version_info[0] == 3:
Iterator = object
else:
class Iterator(object):
def next(self):
return type(self).__next__(self)
def default_iterator(val):
for field in val.type.fields():
yield field.name, val[field.name]
# ------------------------------------------------------------------------------
#
# Custom pretty printers.
#
#
# ------------------------------------------------------------------------------
# FBitReference
#
class FBitReferencePrinter:
def __init__(self, typename, val):
self.Value = val
def to_string(self):
self.Mask = self.Value['Mask']
self.Data = self.Value['Data']
return '\'%d\'' % (self.Data & self.Mask)
# ------------------------------------------------------------------------------
# TBitArray
#
class TBitArrayPrinter:
"Print TBitArray"
class _iterator(Iterator):
def __init__(self, val):
self.Value = val
self.Counter = -1
try:
self.NumBits = self.Value['NumBits']
if self.NumBits.is_optimized_out:
self.NumBits = 0
else:
self.AllocatorInstance = self.Value['AllocatorInstance']
self.InlineData = self.AllocatorInstance['InlineData']
self.SecondaryData = self.AllocatorInstance['SecondaryData']
self.SecondaryDataData = self.AllocatorInstance['SecondaryData']['Data']
if self.SecondaryData != None:
self.SecondaryDataData = self.SecondaryData['Data']
except:
raise
def __iter__(self):
return self
def __next__(self):
if self.NumBits == 0:
raise StopIteration
self.Counter = self.Counter + 1
if self.Counter >= self.NumBits:
raise StopIteration
if self.SecondaryDataData > 0:
data = self.SecondaryDataData.cast(gdb.lookup_type("uint32").pointer())
else:
data = self.InlineData.cast(gdb.lookup_type("uint32").pointer())
return ('[%d]' % self.Counter, (data[self.Counter/32] >> self.Counter) & 1)
def __init__(self, typename, val):
self.Value = val
self.NumBits = self.Value['NumBits']
def to_string(self):
if self.NumBits.is_optimized_out:
pass
if self.NumBits == 0:
return 'empty'
pass
def children(self):
return self._iterator(self.Value)
def display_hint(self):
return 'array'
# ------------------------------------------------------------------------------
# TIndirectArray
#
# ------------------------------------------------------------------------------
# TChunkedArray
#
class TChunkedArrayPrinter:
"Print TChunkedArray"
class _iterator(Iterator):
def __init__(self, val, typename):
self.Value = val
self.Typename = typename
self.Counter = -1
self.ElementType = self.Value.type.template_argument(0)
self.ElementTypeSize = self.ElementType.sizeof
try:
self.NumElements = self.Value['NumElements']
if self.NumElements.is_optimized_out:
self.NumElements = 0
else:
self.Chunks = self.Value['Chunks']
self.Array = self.Chunks['Array']
self.ArrayNum = self.Array['ArrayNum']
self.AllocatorInstance = self.Array['AllocatorInstance']
self.AllocatorData = self.AllocatorInstance['Data']
except:
raise
def __iter__(self):
return self
def __next__(self):
return self.next()
def __next__(self):
if self.NumElements == 0:
raise StopIteration
self.Counter = self.Counter + 1
if self.Counter >= self.NumElements:
raise StopIteration()
Expr = '(unsigned)sizeof('+str(self.Typename)+'::FChunk)/'+str(self.ElementTypeSize)
self.ChunkBytes = gdb.parse_and_eval(Expr)
assert self.ChunkBytes != 0
Expr = '*(*((('+str(self.ElementType.name)+'**)'+str(self.AllocatorData)+')+'+str(self.Counter / self.ChunkBytes)+')+'+str(self.Counter % self.ChunkBytes)+')'
Val = gdb.parse_and_eval(Expr)
return ('[%d]' % self.Counter, Val)
def __init__(self, typename, val):
self.Value = val
self.Typename = typename
self.NumElements = self.Value['NumElements']
def to_string(self):
if self.NumElements.is_optimized_out:
pass
if self.NumElements == 0:
return 'empty'
pass
def children(self):
return self._iterator(self.Value, self.Typename)
def display_hint(self):
return 'array'
# ------------------------------------------------------------------------------
# TSparseArray
#
class TSparseArrayPrinter:
"Print TSparseArray"
class _iterator(Iterator):
def __init__(self, val, typename):
self.Value = val
self.Counter = -1
self.Typename = typename
self.ElementType = self.Value.type.template_argument(0)
self.ElementTypeSize = self.ElementType.sizeof
assert self.ElementTypeSize != 0
try:
self.NumFreeIndices = self.Value['NumFreeIndices']
self.Data = self.Value['Data']
self.ArrayNum = self.Data['ArrayNum']
if self.ArrayNum.is_optimized_out:
self.ArrayNum = 0
else:
self.AllocatorInstance = self.Data['AllocatorInstance']
self.AllocatorData = self.AllocatorInstance['Data']
self.AllocationFlags = self.Value['AllocationFlags']
self.AllocationFlagsInstance = self.AllocationFlags['AllocatorInstance']
self.InlineData = self.AllocationFlagsInstance['InlineData']
self.SecondaryData = self.AllocationFlagsInstance['SecondaryData']
self.SecondaryDataData = self.SecondaryData['Data']
except:
raise
def __iter__(self):
return self
def __next__(self):
return self.next()
def __next__(self):
if self.ArrayNum == 0:
raise StopIteration
self.Counter = self.Counter + 1
if self.Counter >= self.ArrayNum:
raise StopIteration
else:
Data = None
if self.SecondaryDataData > 0:
Data = (self.SecondaryDataData.address.cast(gdb.lookup_type("int").pointer())[self.Counter/32] >> self.Counter) & 1
else:
Data = (self.InlineData.address.cast(gdb.lookup_type("int").pointer())[self.Counter/32] >> self.Counter) & 1
Value = None
if Data != 0:
offset = self.Counter * self.ElementTypeSize
Value = (self.AllocatorData + offset).cast(self.ElementType.pointer())
else:
Value = None
return ('[%s]' % self.Counter, Value.dereference())
def __init__(self, typename, val):
self.Value = val
self.Typename = typename
self.ArrayNum = self.Value['Data']['ArrayNum']
def to_string(self):
if self.ArrayNum.is_optimized_out:
pass
if self.ArrayNum == 0:
return 'empty'
pass
def children(self):
return self._iterator(self.Value, self.Typename)
def display_hint(self):
return 'array'
# ------------------------------------------------------------------------------
# TSet
#
class TSetPrinter:
"Print TSet"
class _iterator(Iterator):
def __init__(self, val, typename):
self.Value = val
self.Counter = -1
self.typename = typename
self.ElementType = self.Value.type.template_argument(0)
try:
self.Elements = self.Value["Elements"]
self.ElementsData = self.Elements["Data"]
self.ElementsArrayNum = self.ElementsData['ArrayNum']
self.NumFreeIndices = self.Elements['NumFreeIndices']
self.AllocatorInstance = self.ElementsData['AllocatorInstance']
self.AllocatorInstanceData = self.AllocatorInstance['Data']
self.AllocationFlags = self.Elements['AllocationFlags']
self.AllocationFlagsInstance = self.AllocationFlags['AllocatorInstance']
self.InlineData = self.AllocationFlagsInstance['InlineData']
self.SecondaryData = self.AllocationFlagsInstance['SecondaryData']
self.SecondaryDataData = self.SecondaryData['Data']
except:
self.ElementsArrayNum = 0
Expr = '(size_t)sizeof(FSetElementId) + sizeof(int32)'
TSetElement = gdb.parse_and_eval(Expr)
self.ElementTypeSize = self.ElementType.sizeof + TSetElement
def __iter__(self):
return self
def __next__(self):
self.Counter = self.Counter + 1
if self.Counter >= self.ElementsArrayNum:
raise StopIteration()
else:
Data = None
if self.SecondaryDataData > 0:
Data = (self.SecondaryDataData.address.cast(gdb.lookup_type("int").pointer())[self.Counter/32] >> self.Counter) & 1
else:
Data = (self.InlineData.address.cast(gdb.lookup_type("int").pointer())[self.Counter/32] >> self.Counter) & 1
Value = None
if Data != 0:
offset = self.Counter * self.ElementTypeSize
Value = (self.AllocatorInstanceData + offset).cast(self.ElementType.pointer())
else:
Value = None
return ('[%s]' % self.Counter, Value.dereference())
def __init__(self, typename, val):
self.Value = val
self.typename = typename
self.ArrayNum = self.Value["Elements"]["Data"]['ArrayNum']
def to_string(self):
if self.ArrayNum.is_optimized_out:
pass
if self.ArrayNum == 0:
return 'empty'
pass
def children(self):
return self._iterator(self.Value, self.typename)
def display_hint(self):
return 'array'
# ------------------------------------------------------------------------------
# TSetElementPrinter
#
class TSetElementPrinter:
"Print TSetElement"
def __init__(self, typename, val):
self.Value = val
def to_string(self):
if self.Value.is_optimized_out:
return '<optimized out>'
return self.Value["Value"]
def display_hint(self):
return "string"
# ------------------------------------------------------------------------------
# TMap
#
class TMapPrinter:
"Print TMap"
class _iterator(Iterator):
def __init__(self, val):
self.Value = val
self.Counter = -1
try:
self.Pairs = self.Value['Pairs']
if self.Pairs.is_optimized_out:
self.ArrayNum = 0
else:
self.Elements = self.Pairs['Elements']
self.ElementsData = self.Elements['Data']
self.ArrayNum = self.ElementsData['ArrayNum']
except:
raise
def __iter__(self):
return self
def __next__(self):
if self.ArrayNum == 0:
raise StopIteration
self.Counter = self.Counter + 1
if self.Counter > 0:
raise StopIteration
return ('Pairs', self.Pairs)
def __init__(self, typename, val):
self.Value = val
self.ArrayNum = self.Value['Pairs']['Elements']['Data']['ArrayNum']
def children(self):
return self._iterator(self.Value)
def to_string(self):
if self.ArrayNum.is_optimized_out:
pass
if self.ArrayNum == 0:
return 'empty'
pass
def display_hint(self):
return 'map'
# ------------------------------------------------------------------------------
# TWeakObjectPtr
#
class TWeakObjectPtrPrinter:
"Print TWeakObjectPtr"
class _iterator(Iterator):
def __init__(self, val):
self.Value = val
self.Counter = 0
self.Object = None
self.ObjectSerialNumber = int(self.Value['ObjectSerialNumber'])
if self.ObjectSerialNumber >= 1:
ObjectIndexValue = int(self.Value['ObjectIndex'])
ObjectItemExpr = 'GCoreObjectArrayForDebugVisualizers->Objects['+str(ObjectIndexValue)+'/FChunkedFixedUObjectArray::NumElementsPerChunk]['+str(ObjectIndexValue)+ '% FChunkedFixedUObjectArray::NumElementsPerChunk]'
ObjectItem = gdb.parse_and_eval(ObjectItemExpr);
IsValidObject = int(ObjectItem['SerialNumber']) == self.ObjectSerialNumber
if IsValidObject == True:
ObjectType = self.Value.type.template_argument(0)
self.Object = ObjectItem['Object'].dereference().cast(ObjectType.reference())
def __iter__(self):
return self
def __next__(self):
if self.Counter > 0:
raise StopIteration
self.Counter = self.Counter + 1
if self.Object != None:
return ('Object', self.Object)
elif self.ObjectSerialNumber > 0:
return ('Object', 'STALE')
else:
return ('Object', 'nullptr')
def __init__(self, typename, val):
self.Value = val
def children(self):
return self._iterator(self.Value)
def to_string(self):
ObjectType = self.Value.type.template_argument(0)
return 'TWeakObjectPtr<%s>' % ObjectType.name;
# ------------------------------------------------------------------------------
# FString
#
class FStringPrinter:
"Print FString"
def __init__(self, typename, val):
self.Value = val
def to_string(self):
if self.Value.is_optimized_out:
return '<optimized out>'
ArrayNum = self.Value['Data']['ArrayNum']
if ArrayNum == 0:
return 'empty'
elif ArrayNum < 0:
return "nullptr"
else:
ActualData = self.Value['Data']['AllocatorInstance']['Data']
data = ActualData.cast(gdb.lookup_type("TCHAR").pointer())
return '%s' % (data.string())
def display_hint (self):
return 'string'
# ------------------------------------------------------------------------------
# FNameEntry
#
class FNameEntryPrinter:
"Print FNameEntry"
def __init__(self, typename, val):
self.Value = val
def to_string(self):
self.Header = self.Value['Header']
IsWideString = self.Header['bIsWide'].cast(gdb.lookup_type('bool'))
self.AnsiName = self.Value['AnsiName']
self.WideName = self.Value['WideName']
Len = int(self.Header['Len'].cast(gdb.lookup_type('uint16')))
if IsWideString == True:
WideString = self.WideName.cast(gdb.lookup_type('WIDECHAR').pointer())
return '%s' % WideString.string('','',Len)
else:
AnsiString = self.AnsiName.cast(gdb.lookup_type('ANSICHAR').pointer())
return '%s' % AnsiString.string('','',Len)
def display_hint (self):
return 'string'
# ------------------------------------------------------------------------------
# FName
#
class FNamePrinter:
"Print FName"
def __init__(self, typename, val):
self.Value = val
def to_string(self):
if self.Value.is_optimized_out:
return '<optimized out>'
# ComparisonIndex is an FNameEntryId
Index = self.Value['ComparisonIndex']['Value']
IndexValue = int(Index)
if IndexValue >= 4194304:
return 'invalid'
else:
Expr = '((FNameEntry&)GNameBlocksDebug['+str(IndexValue)+' >> FNameDebugVisualizer::OffsetBits][FNameDebugVisualizer::EntryStride * ('+str(IndexValue)+' & FNameDebugVisualizer::OffsetMask)])'
NameEntry = gdb.parse_and_eval(Expr)
Number = self.Value['Number']
NumberValue = int(Number)
if NumberValue == 0:
return NameEntry
else:
return str([str(NameEntry), 'Number=' + str(NumberValue)])
# ------------------------------------------------------------------------------
# FMinimalName
#
class FMinimalNamePrinter:
"Print FMinimalName"
def __init__(self, typename, val):
self.Value = val
def to_string(self):
Index = self.Value['Index']['Value']
IndexValue = int(Index)
if IndexValue >= 4194304:
return 'invalid'
else:
Expr = '((FNameEntry&)GNameBlocksDebug['+str(IndexValue)+' >> FNameDebugVisualizer::OffsetBits][FNameDebugVisualizer::EntryStride * ('+str(IndexValue)+' & FNameDebugVisualizer::OffsetMask)])'
NameEntry = gdb.parse_and_eval(Expr)
Number = self.Value['Number']
NumberValue = int(Number)
if NumberValue == 0:
return NameEntry
else:
return str([str(NameEntry), 'Number=' + str(NumberValue)])
# ------------------------------------------------------------------------------
# TTuple
#
class TTuplePrinter:
"Print TTuple"
def __init__(self, typename, val):
self.Value = val
try:
self.TKey = self.Value["Key"];
self.TValue = self.Value["Value"];
except:
pass
def to_string(self):
return '(%s, %s)' % (self.TKey, self.TValue)
# ------------------------------------------------------------------------------
# TArray
#
class TArrayPrinter:
"Print TArray"
class _iterator(Iterator):
def __init__(self, val):
self.Value = val
self.Counter = -1
self.TType = self.Value.type.template_argument(0)
try:
self.ArrayNum = self.Value['ArrayNum']
if self.ArrayNum.is_optimized_out:
self.ArrayNum = 0
if self.ArrayNum > 0:
self.AllocatorInstance = self.Value['AllocatorInstance']
self.AllocatorInstanceData = self.AllocatorInstance['Data']
try:
self.InlineData = self.AllocatorInstance['InlineData']
self.SecondaryData = self.AllocatorInstance['SecondaryData']
if self.SecondaryData != None:
self.SecondaryDataData = self.SecondaryData['Data']
else:
self.SecondaryDataData = None
except:
pass
except:
raise
def __iter__(self):
return self
def __next__(self):
if self.ArrayNum == 0:
raise StopIteration
self.Counter = self.Counter + 1
if self.Counter >= self.ArrayNum:
raise StopIteration
try:
if self.AllocatorInstanceData != None:
data = self.AllocatorInstanceData.cast(self.TType.pointer())
elif self.SecondaryDataDataVal > 0:
data = self.SecondaryDataData.cast(self.TType.pointer())
else:
data = self.InlineData.cast(self.TType.pointer())
except:
return ('[%d]' % self.Counter, "optmized")
return ('[%d]' % self.Counter, data[self.Counter])
def __init__(self, typename, val):
self.Value = val;
self.ArrayNum = self.Value['ArrayNum']
def to_string(self):
if self.ArrayNum.is_optimized_out:
pass
if self.ArrayNum == 0:
return 'empty'
pass
def children(self):
return self._iterator(self.Value)
def display_hint(self):
return 'array'
#
# Register our lookup function. If no objfile is passed use all globally.
def register_ue4_printers(objfile):
if objfile == None:
objfile = gdb
objfile.pretty_printers.append(lookup_function)
#
# We need this part which is a definition how pretty printers work.
#
def lookup_function (val):
"Look-up and return a pretty-printer that can print val."
# Get the type and check if it points to a reference. We check for both Object and Pointer type.
type = val.type;
if (type.code == gdb.TYPE_CODE_REF) or (type.code == gdb.TYPE_CODE_PTR):
type = type.target ()
# Get the unqualified type, mean remove const nor volatile and strippe of typedefs.
type = type.unqualified ().strip_typedefs ()
# Get the tag name. The tag name is the name after struct, union, or enum in C and C++
tag = type.tag
if tag == None:
return None
for function in pretty_printers_dict:
if function.search (tag):
return pretty_printers_dict[function] (tag, val)
# Cannot find a pretty printer. Return None.
return None
def build_dictionary ():
pretty_printers_dict[re.compile('^FString$')] = lambda typename, val: FStringPrinter(typename, val)
pretty_printers_dict[re.compile('^FNameEntry$')] = lambda typename, val: FNameEntryPrinter(typename, val)
pretty_printers_dict[re.compile('^FName$')] = lambda typename, val: FNamePrinter(typename, val)
pretty_printers_dict[re.compile('^FMinimalName$')] = lambda typename, val: FMinimalNamePrinter(typename, val)
pretty_printers_dict[re.compile('^TArray<.+,.+>$')] = lambda typename, val: TArrayPrinter(typename, val)
pretty_printers_dict[re.compile('^TBitArray<.+>$')] = lambda typename, val: TBitArrayPrinter(typename, val)
pretty_printers_dict[re.compile('^TChunkedArray<.+>$')] = lambda typename, val: TChunkedArrayPrinter(typename, val)
pretty_printers_dict[re.compile('^TSparseArray<.+>$')] = lambda typename, val: TSparseArrayPrinter(typename, val)
pretty_printers_dict[re.compile('^TSetElement<.+>$')] = lambda typename, val: TSetElementPrinter(typename, val)
pretty_printers_dict[re.compile('^TSet<.+>$')] = lambda typename, val: TSetPrinter(typename, val)
pretty_printers_dict[re.compile('^FBitReference$')] = lambda typename, val: FBitReferencePrinter(typename, val)
pretty_printers_dict[re.compile('^TMap<.+,.+,.+>$')] = lambda typename, val: TMapPrinter(typename, val)
pretty_printers_dict[re.compile('^TPair<.+,.+>$')] = lambda typename, val: TTuplePrinter(typename, val)
pretty_printers_dict[re.compile('^TTuple<.+,.+>$')] = lambda typename, val: TTuplePrinter(typename, val)
pretty_printers_dict[re.compile('^TWeakObjectPtr<.+>$')] = lambda typename, val: TWeakObjectPtrPrinter(typename, val)
pretty_printers_dict = {}
build_dictionary ()