1014 lines
43 KiB
Python
1014 lines
43 KiB
Python
# Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
# /*=============================================================================
|
|
# LLDB Data Formatters for Unreal Types
|
|
# =============================================================================*/
|
|
|
|
import lldb
|
|
import lldb.formatters.Logger
|
|
import faulthandler
|
|
import signal
|
|
import sys
|
|
import time
|
|
|
|
# Uncomment the line below to have the data formatters emit debug logging
|
|
# lldb.formatters.Logger._lldb_formatters_debug_level=1
|
|
|
|
# What documentation there is for parsing values in LLDB can be found here:
|
|
# https://lldb.llvm.org/python_reference/index.html
|
|
# https://lldb.llvm.org/python_reference/lldb.SBValue-class.html
|
|
|
|
# To install:
|
|
# 1) Open Terminal and run:
|
|
# touch ~/.lldbinit
|
|
# open ~/.lldbinit
|
|
# 2) Add the following text to .lldbini and save - modifying the path as appropriate:
|
|
# settings set target.inline-breakpoint-strategy always
|
|
# command script import "/Path/To/Epic/UE/Engine/Extras/LLDBDataFormatters/UEDataFormatter.py"
|
|
# 3) Restart Xcode
|
|
|
|
_FNameEntryType = None
|
|
_FNameEntryStride = 2
|
|
|
|
def UEFStringSummaryProvider(valobj, dict):
|
|
target = valobj.GetTarget()
|
|
sbtype = valobj.GetType().GetUnqualifiedType()
|
|
if sbtype.IsPointerType():
|
|
if valobj.GetValueAsUnsigned(0) == 0:
|
|
return 'nullptr'
|
|
valobj = valobj.Dereference()
|
|
elif sbtype.IsReferenceType():
|
|
valobj = valobj.Dereference()
|
|
tarray = valobj.GetChildMemberWithName('Data')
|
|
array_num = tarray.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0)
|
|
if array_num < 0: return 'corrupted string'
|
|
if array_num == 0: return 'empty string'
|
|
data_ptr = tarray.GetChildMemberWithName('AllocatorInstance').GetChildMemberWithName('Data')
|
|
buffer_addr = data_ptr.GetValueAsUnsigned(0)
|
|
if buffer_addr == 0: return 'corrupted string'
|
|
tchar_type = tarray.GetType().GetTemplateArgumentType(0)
|
|
array_type = tchar_type.GetArrayType(array_num)
|
|
sbaddr = lldb.SBAddress(buffer_addr, target)
|
|
summary = target.CreateValueFromAddress('string', sbaddr, array_type).GetSummary()
|
|
if summary and summary.startswith('u"'): return summary[1:]
|
|
return summary
|
|
|
|
|
|
def UEFNameSummaryProvider(valobj, dict):
|
|
target = valobj.GetTarget()
|
|
process = target.GetProcess()
|
|
entry_id = valobj.GetChildMemberWithName('DisplayIndex')
|
|
index = entry_id.GetChildMemberWithName('Value').GetValueAsUnsigned(0)
|
|
number = valobj.GetChildMemberWithName('Number').GetValueAsUnsigned(0)
|
|
|
|
block_idx = index >> 16
|
|
entry_offset = (index & 0xFFFF) * _FNameEntryStride
|
|
|
|
gname_var = target.FindFirstGlobalVariable('GNameBlocksDebug')
|
|
gname_ptr = gname_var.GetValueAsUnsigned(0)
|
|
|
|
error = lldb.SBError()
|
|
block_addr = process.ReadPointerFromMemory(gname_ptr + block_idx * target.GetAddressByteSize(), error)
|
|
if error.Fail(): return '<read error>'
|
|
|
|
entry_addr = block_addr + entry_offset
|
|
entry = target.CreateValueFromAddress('entry', lldb.SBAddress(entry_addr, target), _FNameEntryType)
|
|
|
|
header = entry.GetChildMemberWithName('Header')
|
|
length = header.GetChildMemberWithName('Len').GetValueAsUnsigned(0)
|
|
is_wide = header.GetChildMemberWithName('bIsWide').GetValueAsUnsigned(0)
|
|
|
|
if is_wide:
|
|
name_addr = entry.GetChildMemberWithName('WideName').GetLoadAddress()
|
|
raw = process.ReadMemory(name_addr, length * 2, error)
|
|
if error.Fail(): return '<read error>'
|
|
name = raw.decode('utf-16-le')
|
|
else:
|
|
name_addr = entry.GetChildMemberWithName('AnsiName').GetLoadAddress()
|
|
raw = process.ReadMemory(name_addr, length, error)
|
|
if error.Fail(): return '<read error>'
|
|
name = raw.decode('utf-8', errors='replace')
|
|
|
|
if number > 0: return '%s_%d' % (name, number - 1)
|
|
return name
|
|
|
|
|
|
class ForwardingSynthProvider:
|
|
def __init__(self, valobj, dict):
|
|
self.valobj = valobj
|
|
self.update()
|
|
|
|
def num_children(self):
|
|
return self.forward_to.GetNumChildren()
|
|
|
|
def get_child_index(self, name):
|
|
return self.forward_to.GetIndexOfChildWithName(name)
|
|
|
|
def get_child_at_index(self, index):
|
|
return self.forward_to.GetChildAtIndex(index)
|
|
|
|
def update(self):
|
|
self.forward_to = lldb.SBValue()
|
|
|
|
def has_children(self):
|
|
return self.forward_to.MightHaveChildren()
|
|
|
|
|
|
class TObjectPtrSynthProvider(ForwardingSynthProvider):
|
|
def update(self):
|
|
debug_ptr = self.valobj.GetChildMemberWithName('DebugPtr')
|
|
addr = debug_ptr.GetValueAsUnsigned(0)
|
|
if addr == 0 or addr & 1: # null or unresolved handle (odd address)
|
|
self.forward_to = lldb.SBValue()
|
|
else:
|
|
self.forward_to = debug_ptr.Dereference()
|
|
|
|
def TObjectPtrSummaryProvider(valobj, dict):
|
|
debug_ptr = valobj.GetChildMemberWithName('DebugPtr')
|
|
addr = debug_ptr.GetValueAsUnsigned(0)
|
|
if addr == 0: return 'nullptr'
|
|
if addr & 1: return 'unresolved'
|
|
return '{...}'
|
|
|
|
|
|
class TSharedPtrSynthProvider:
|
|
def __init__(self, valobj, dict):
|
|
self.valobj = valobj
|
|
self.update()
|
|
|
|
def num_children(self):
|
|
return 1
|
|
|
|
def has_children(self):
|
|
return true
|
|
|
|
def get_child_index(self, name):
|
|
if name == 'Object': return 0
|
|
return -1
|
|
|
|
def get_child_at_index(self, index):
|
|
if index == 0: return self.object
|
|
return lldb.SBValue()
|
|
|
|
def update(self):
|
|
self.object = self.valobj.GetChildMemberWithName('Object')
|
|
|
|
|
|
def TSharedPtrSummaryProvider(valobj, dict):
|
|
obj = valobj.GetChildMemberWithName('Object')
|
|
addr = obj.GetValueAsUnsigned(0)
|
|
if addr == 0: return 'nullptr'
|
|
return '{...}'
|
|
|
|
|
|
# def UEUObjectBaseSummaryProvider(valobj,dict):
|
|
# Name = valobj.GetChildMemberWithName('NamePrivate')
|
|
# return Name.GetSummary()
|
|
|
|
# def UEFFieldClassSummaryProvider(valobj,dict):
|
|
# Name = valobj.GetChildMemberWithName('Name')
|
|
# return Name.GetSummary()
|
|
|
|
# def UEFFieldSummaryProvider(valobj,dict):
|
|
# Name = valobj.GetChildMemberWithName('NamePrivate')
|
|
# return Name.GetSummary()
|
|
|
|
# class UETWeakObjectPtrSynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# self.valobj = valobj
|
|
|
|
# def num_children(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# return 1
|
|
|
|
# def get_child_index(self,name):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# return 0
|
|
|
|
# def get_child_at_index(self,index):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# logger >> "Retrieving child %s" % index
|
|
# if self.ObjectSerialNumberVal >= 1:
|
|
# Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].SerialNumber == %s' % (int(self.ObjectIndexVal/65536), self.ObjectIndexVal%65536, self.ObjectSerialNumberVal)
|
|
# Val = self.valobj.CreateValueFromExpression("%s" % self.ObjectIndexVal, Expr)
|
|
# Value = Val.GetValueAsUnsigned(0)
|
|
# if Value != 0:
|
|
# Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].Object' % (int(self.ObjectIndexVal/65536), self.ObjectIndexVal%65536)
|
|
# return self.valobj.CreateValueFromExpression('Object', Expr)
|
|
# else:
|
|
# Expr = '(void*)0xDEADBEEF'
|
|
# return self.valobj.CreateValueFromExpression('Object', Expr)
|
|
|
|
# Expr = 'nullptr'
|
|
# return self.valobj.CreateValueFromExpression('Object', Expr)
|
|
|
|
# def update(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# self.ObjectSerialNumber = self.valobj.GetChildMemberWithName('ObjectSerialNumber')
|
|
# self.ObjectSerialNumberVal = self.ObjectSerialNumber.GetValueAsSigned(0)
|
|
# self.ObjectIndex = self.valobj.GetChildMemberWithName('ObjectIndex')
|
|
# self.ObjectIndexVal = self.ObjectIndex.GetValueAsSigned(0)
|
|
# except:
|
|
# pass
|
|
|
|
# def has_children(self):
|
|
# return True
|
|
|
|
# def UEFWeakObjectPtrSummaryProvider(valobj,dict):
|
|
# ObjectSerialNumber = valobj.GetChildMemberWithName('ObjectSerialNumber')
|
|
# ObjectSerialNumberVal = ObjectSerialNumber.GetValueAsSigned(0)
|
|
# if ObjectSerialNumberVal < 1:
|
|
# return 'object=nullptr'
|
|
# ObjectIndex = valobj.GetChildMemberWithName('ObjectIndex')
|
|
# ObjectIndexVal = ObjectIndex.GetValueAsSigned(0)
|
|
# Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].SerialNumber == %s' % (int(ObjectIndexVal/65536), ObjectIndexVal%65536, ObjectSerialNumberVal)
|
|
# Val = valobj.CreateValueFromExpression('%s' % ObjectIndexVal, Expr)
|
|
# ValRef = Val.GetValueAsUnsigned(0)
|
|
# if ValRef == 0:
|
|
# return 'object=STALE'
|
|
# else:
|
|
# Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].Object' % (int(ObjectIndexVal/65536), ObjectIndexVal%65536)
|
|
# Val = valobj.CreateValueFromExpression("%s" % ObjectIndexVal, Expr)
|
|
# return 'object=' + Val.GetValue()
|
|
|
|
# class UEChunkedArraySynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# self.valobj = valobj
|
|
|
|
# def num_children(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# NumElementsVal = self.NumElements.GetValueAsSigned(0)
|
|
# return NumElementsVal;
|
|
# except:
|
|
# return 0;
|
|
|
|
# def get_child_index(self,name):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# return int(name.lstrip('[').rstrip(']'))
|
|
# except:
|
|
# return None
|
|
|
|
# def get_child_at_index(self,index):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# logger >> "Retrieving child %s" % index
|
|
# Expr = '(unsigned)sizeof(%s::FChunk)/%s' % (self.valobj.GetType().GetUnqualifiedType().GetName(), self.ElementTypeSize)
|
|
# self.ChunkBytes = self.valobj.CreateValueFromExpression('[%s]' % index, Expr)
|
|
# self.ChunkBytesSize = self.ChunkBytes.GetValueAsUnsigned(0)
|
|
# assert self.ChunkBytesSize != 0
|
|
|
|
# Expr = '*(*(((%s**)%s)+%s)+%s)' % (self.ElementType.GetName(), self.AllocatorData.GetValue(), index / self.ChunkBytesSize, index % self.ChunkBytesSize)
|
|
# return self.valobj.CreateValueFromExpression('[%s]' % index, Expr)
|
|
|
|
# def extract_type(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# ArrayType = self.valobj.GetType().GetUnqualifiedType()
|
|
# if ArrayType.IsReferenceType():
|
|
# ArrayType = ArrayType.GetDereferencedType()
|
|
# elif ArrayType.IsPointerType():
|
|
# ArrayType = ArrayType.GetPointeeType()
|
|
# if ArrayType.GetNumberOfTemplateArguments() > 0:
|
|
# ElementType = ArrayType.GetTemplateArgumentType(0)
|
|
# else:
|
|
# ElementType = None
|
|
# return ElementType
|
|
|
|
# def update(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# self.ElementType = self.extract_type()
|
|
# self.ElementTypeSize = self.ElementType.GetByteSize()
|
|
# assert self.ElementTypeSize != 0
|
|
# self.NumElements = self.valobj.GetChildMemberWithName('NumElements')
|
|
# self.Chunks = self.valobj.GetChildMemberWithName('Chunks')
|
|
# self.ArrayNum = self.Chunks.GetChildMemberWithName('ArrayNum')
|
|
# self.AllocatorInstance = self.Chunks.GetChildMemberWithName('AllocatorInstance')
|
|
# self.AllocatorData = self.AllocatorInstance.GetChildMemberWithName('Data')
|
|
# except:
|
|
# pass
|
|
|
|
# def has_children(self):
|
|
# return True
|
|
|
|
# def UEChunkedArraySummaryProvider(valobj,dict):
|
|
# return 'size=%s' % valobj.GetNumChildren()
|
|
|
|
# class UESparseArraySynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# self.valobj = valobj
|
|
|
|
# def num_children(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# NumBitsVal = self.NumFreeIndices.GetValueAsSigned(0)
|
|
# ArrayNumVal = self.Data.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0)
|
|
# return ArrayNumVal - NumBitsVal;
|
|
# except:
|
|
# return 0;
|
|
|
|
# def get_child_index(self,name):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# return int(name.lstrip('[').rstrip(']'))
|
|
# except:
|
|
# return None
|
|
|
|
# def get_child_at_index(self,index):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# logger >> "Retrieving child %s" % index
|
|
# if index < 0:
|
|
# return None;
|
|
|
|
# if index >= self.num_children():
|
|
# return None;
|
|
|
|
# Val = None
|
|
# if self.SecondaryDataDataVal > 0:
|
|
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataData.GetAddress(), index, index)
|
|
# Val = self.SecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr)
|
|
# else:
|
|
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.InlineData.GetAddress(), index, index)
|
|
# Val = self.InlineData.CreateValueFromExpression('[5s]' % index, Expr)
|
|
|
|
# if Val.GetValueAsSigned(0) != 0:
|
|
# offset = index * self.ElementTypeSize
|
|
# return self.AllocatorData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
|
|
# else:
|
|
# return None
|
|
|
|
# def extract_type(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# ArrayType = self.valobj.GetType().GetUnqualifiedType()
|
|
# if ArrayType.IsReferenceType():
|
|
# ArrayType = ArrayType.GetDereferencedType()
|
|
# elif ArrayType.IsPointerType():
|
|
# ArrayType = ArrayType.GetPointeeType()
|
|
# if ArrayType.GetNumberOfTemplateArguments() > 0:
|
|
# ElementType = ArrayType.GetTemplateArgumentType(0)
|
|
# else:
|
|
# ElementType = None
|
|
# return ElementType
|
|
|
|
# def update(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# self.ElementType = self.extract_type()
|
|
# self.ElementTypeSize = self.ElementType.GetByteSize()
|
|
# assert self.ElementTypeSize != 0
|
|
# self.NumFreeIndices = self.valobj.GetChildMemberWithName('NumFreeIndices')
|
|
# self.Data = self.valobj.GetChildMemberWithName('Data')
|
|
# self.AllocatorInstance = self.Data.GetChildMemberWithName('AllocatorInstance')
|
|
# self.AllocatorData = self.AllocatorInstance.GetChildMemberWithName('Data')
|
|
# self.AllocationFlags = self.valobj.GetChildMemberWithName('AllocationFlags')
|
|
# self.InlineData = self.AllocationFlags.GetChildMemberWithName('InlineData')
|
|
# self.SecondaryData = self.AllocationFlags.GetChildMemberWithName('SecondaryData')
|
|
# self.SecondaryDataData = self.SecondaryData.GetChildMemberWithName('Data')
|
|
# self.SecondaryDataDataVal = self.SecondaryDataData.GetValueAsSigned(0)
|
|
# except:
|
|
# pass
|
|
|
|
# def has_children(self):
|
|
# return True
|
|
|
|
# def UESparseArraySummaryProvider(valobj,dict):
|
|
# return 'size=%s' % valobj.GetNumChildren()
|
|
|
|
# class UEBitArraySynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# self.valobj = valobj
|
|
|
|
# def num_children(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# NumBitsVal = self.NumBits.GetValueAsSigned(0)
|
|
# return NumBitsVal;
|
|
# except:
|
|
# return 0;
|
|
|
|
# def get_child_index(self,name):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# return int(name.lstrip('[').rstrip(']'))
|
|
# except:
|
|
# return None
|
|
|
|
# def get_child_at_index(self,index):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# logger >> "Retrieving child %s" % index
|
|
# if index < 0:
|
|
# return None;
|
|
|
|
# if index >= self.num_children():
|
|
# return None;
|
|
|
|
# if self.SecondaryDataDataVal > 0:
|
|
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataData.GetAddress(), index, index)
|
|
# return self.SecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr)
|
|
# else:
|
|
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.InlineData.GetAddress(), index, index)
|
|
# return self.InlineData.CreateValueFromExpression('[%s]' % index, Expr)
|
|
|
|
# return None
|
|
|
|
# def update(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# self.SecondaryDataData = None
|
|
# self.SecondaryDataDataVal = 0
|
|
# self.NumBits = self.valobj.GetChildMemberWithName('NumBits')
|
|
# self.MaxBits = self.valobj.GetChildMemberWithName('MaxBits')
|
|
# self.InlineData = self.valobj.GetChildMemberWithName('InlineData')
|
|
# self.SecondaryData = self.valobj.GetChildMemberWithName('SecondaryData')
|
|
# if self.SecondaryData != None:
|
|
# self.SecondaryDataData = self.SecondaryData.GetChildMemberWithName('Data')
|
|
# self.SecondaryDataDataVal = self.SecondaryDataData.GetValueAsUnsigned(0)
|
|
# except:
|
|
# pass
|
|
|
|
# def has_children(self):
|
|
# return True
|
|
|
|
# def UEBitArraySummaryProvider(valobj,dict):
|
|
# return 'size=%s' % valobj.GetNumChildren()
|
|
|
|
# class UEArraySynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# self.valobj = valobj
|
|
|
|
# def num_children(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# ArrayNumVal = self.ArrayNum.GetValueAsSigned(0)
|
|
# return ArrayNumVal + self.NumChildren;
|
|
# except:
|
|
# return 0;
|
|
|
|
# def get_child_index(self,name):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# return self.NumChildren + int(name.lstrip('[').rstrip(']'))
|
|
# except:
|
|
# return self.valobj.GetIndexOfChildWithName(name)
|
|
|
|
# def get_child_at_index(self,index):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# logger >> "Retrieving child %s" % index
|
|
# if index < 0:
|
|
# return None;
|
|
|
|
# if index < self.NumChildren:
|
|
# return self.valobj.GetChildAtIndex(index)
|
|
# else:
|
|
# index -= self.NumChildren
|
|
|
|
# if index >= self.num_children():
|
|
# return None;
|
|
# try:
|
|
# offset = index * self.ElementTypeSize
|
|
# if self.Data != None:
|
|
# return self.Data.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
|
|
# elif self.SecondaryDataDataVal > 0:
|
|
# return self.SecondaryDataData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
|
|
# else:
|
|
# return self.InlineData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
|
|
# except:
|
|
# return None
|
|
|
|
# def extract_type(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# ArrayType = self.valobj.GetType().GetUnqualifiedType()
|
|
# if ArrayType.IsReferenceType():
|
|
# ArrayType = ArrayType.GetDereferencedType()
|
|
# elif ArrayType.IsPointerType():
|
|
# ArrayType = ArrayType.GetPointeeType()
|
|
|
|
# if ArrayType.GetNumberOfTemplateArguments() > 0:
|
|
# ElementType = ArrayType.GetTemplateArgumentType(0)
|
|
# else:
|
|
# ElementType = None
|
|
# return ElementType
|
|
|
|
# def update(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# self.NumChildren = self.valobj.GetNumChildren()
|
|
# self.ArrayNum = self.valobj.GetChildMemberWithName('ArrayNum')
|
|
# self.ArrayMax = self.valobj.GetChildMemberWithName('ArrayMax')
|
|
# self.AllocatorInstance = self.valobj.GetChildMemberWithName('AllocatorInstance')
|
|
# if self.AllocatorInstance.GetType().IsReferenceType():
|
|
# self.AllocatorInstance = self.AllocatorInstance.Dereference()
|
|
# self.Data = self.AllocatorInstance.GetChildMemberWithName('Data')
|
|
# self.InlineData = self.AllocatorInstance.GetChildMemberWithName('InlineData')
|
|
# self.SecondaryData = self.AllocatorInstance.GetChildMemberWithName('SecondaryData')
|
|
# if self.SecondaryData != None:
|
|
# self.SecondaryDataData = self.SecondaryData.GetChildMemberWithName('Data')
|
|
# self.SecondaryDataDataVal = self.SecondaryDataData.GetValueAsUnsigned(0)
|
|
# else:
|
|
# self.SecondaryDataData = None
|
|
# self.SecondaryDataDataVal = 0
|
|
# except:
|
|
# logger >> "UEArraySynthProvider::update failed accessing members"
|
|
# pass
|
|
# try:
|
|
# self.ElementType = self.extract_type()
|
|
# self.ElementTypeSize = self.ElementType.GetByteSize()
|
|
# assert self.ElementTypeSize != 0
|
|
# except:
|
|
# logger >> "UEArraySynthProvider::update failed accessing element type"
|
|
# pass
|
|
|
|
# def has_children(self):
|
|
# return True
|
|
|
|
# def UEArraySummaryProvider(valobj,dict):
|
|
# return 'size=%s' % valobj.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0)
|
|
|
|
# class UESetSynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# self.valobj = valobj
|
|
|
|
# # Can't cast to TSetElement - the template instantiation won't allow it
|
|
# Expr = '(size_t)sizeof(FSetElementId) + sizeof(int32)'
|
|
# self.TSetElement = self.valobj.CreateValueFromExpression('TSetElement', Expr)
|
|
|
|
# def num_children(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# ArrayNumVal = self.ArrayNum.GetValueAsUnsigned(0)
|
|
# NumFreeIndicesVal = self.NumFreeIndices.GetValueAsUnsigned(0)
|
|
# return ArrayNumVal - NumFreeIndicesVal;
|
|
# except:
|
|
# return 0;
|
|
|
|
# def get_child_index(self,name):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# return int(name.lstrip('[').rstrip(']'))
|
|
# except:
|
|
# return 0
|
|
|
|
# def get_child_at_index(self,index):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# logger >> "Retrieving child %s" % index
|
|
# if index < 0:
|
|
# return None;
|
|
|
|
# if index >= self.num_children():
|
|
# return None;
|
|
# try:
|
|
# offset = index * self.ElementTypeSize
|
|
# HasObject = 0
|
|
# if self.SecondaryDataDataVal > 0:
|
|
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataDataVal, index, index)
|
|
# HasObject = 1 ##self.AllocationFlagsSecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0)
|
|
# else:
|
|
# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.AllocationFlagsInlineDataAddr, index, index)
|
|
# HasObject = 1 ##self.AllocationFlagsInlineData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0)
|
|
|
|
# if HasObject == 1:
|
|
# return self.AllocatorInstanceData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType)
|
|
# else:
|
|
# return self.valobj.CreateValueFromExpression('[%s]' % index, '(void*)0xDEADBEEF')
|
|
# except:
|
|
# return None
|
|
|
|
# def extract_type(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# ArrayType = self.valobj.GetType().GetUnqualifiedType()
|
|
# if ArrayType.IsReferenceType():
|
|
# ArrayType = ArrayType.GetDereferencedType()
|
|
# elif ArrayType.IsPointerType():
|
|
# ArrayType = ArrayType.GetPointeeType()
|
|
# if ArrayType.GetNumberOfTemplateArguments() > 0:
|
|
# ElementType = ArrayType.GetTemplateArgumentType(0)
|
|
# else:
|
|
# ElementType = None
|
|
# return ElementType
|
|
|
|
# def update(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# self.Elements = self.valobj.GetChildMemberWithName('Elements')
|
|
# self.ElementsData = self.Elements.GetChildMemberWithName('Data')
|
|
# self.ArrayNum = self.ElementsData.GetChildMemberWithName('ArrayNum')
|
|
# self.NumFreeIndices = self.Elements.GetChildMemberWithName('NumFreeIndices')
|
|
# self.AllocationFlags = self.Elements.GetChildMemberWithName('AllocationFlags')
|
|
# self.AllocationFlagsAllocatorInstance = self.AllocationFlags.GetChildMemberWithName('AllocatorInstance')
|
|
# self.AllocatorInstance = self.ElementsData.GetChildMemberWithName('AllocatorInstance')
|
|
# self.AllocatorInstanceData = self.AllocatorInstance.GetChildMemberWithName('Data')
|
|
# self.AllocationFlagsInlineData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('InlineData')
|
|
# self.AllocationFlagsInlineDataAddr = self.AllocationFlagsInlineData.AddressOf().GetValueAsUnsigned(0)
|
|
# self.AllocationFlagsSecondaryData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('SecondaryData')
|
|
# self.AllocationFlagsSecondaryDataData = self.AllocationFlagsSecondaryData.GetChildMemberWithName('Data')
|
|
# self.SecondaryDataDataVal = self.AllocationFlagsSecondaryDataData.GetValueAsUnsigned(0)
|
|
# self.ElementType = self.extract_type()
|
|
# # This may fail due to C++ struct padding - will have to check
|
|
# self.ElementTypeSize = self.ElementType.GetByteSize() + self.TSetElement.GetValueAsUnsigned(0)
|
|
# assert self.ElementTypeSize != 0
|
|
# except:
|
|
# pass
|
|
|
|
# def has_children(self):
|
|
# return True
|
|
|
|
# def UESetSummaryProvider(valobj,dict):
|
|
# return 'size=%s' % valobj.GetNumChildren()
|
|
|
|
# class UEMapSynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# self.valobj = valobj
|
|
|
|
# # Can't cast to TSetElement - the template instantiation won't allow it
|
|
# Expr = '(size_t)sizeof(FSetElementId) + sizeof(int32)'
|
|
# self.TSetElement = self.valobj.CreateValueFromExpression('TSetElement', Expr)
|
|
|
|
# def num_children(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# ArrayNumVal = self.ArrayNum.GetValueAsUnsigned(0)
|
|
# NumFreeIndicesVal = self.NumFreeIndices.GetValueAsUnsigned(0)
|
|
# return ArrayNumVal - NumFreeIndicesVal;
|
|
# except:
|
|
# return 0;
|
|
|
|
# def get_child_index(self,name):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# return int(name.lstrip('[').rstrip(']'))
|
|
# except:
|
|
# return 0
|
|
|
|
# def get_child_at_index(self,index):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# logger >> "Retrieving child %s" % index
|
|
# if index < 0:
|
|
# return None;
|
|
|
|
# if index >= self.num_children():
|
|
# return None;
|
|
# try:
|
|
# offset = index * self.ElementTypeSize
|
|
# HasObject = 0
|
|
# if self.SecondaryDataDataVal != 0:
|
|
# Expr = '(bool)((((unsigned int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataDataVal, index, index)
|
|
# HasObject = 1 ##self.AllocationFlagsSecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0)
|
|
# else:
|
|
# Expr = '(bool)((((unsigned int*)%s)[%s/32] >> %s) & 1)' % (self.AllocationFlagsInlineDataAddr, index, index)
|
|
# HasObject = 1 ##self.AllocationFlagsInlineData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0)
|
|
|
|
# if HasObject == 1:
|
|
# return self.AllocatorInstanceData.CreateChildAtOffset('[%s]' % index,offset,self.ElementType)
|
|
# else:
|
|
# return self.valobj.CreateValueFromExpression('[%s]' % index, '(void*)0xDEADBEEF')
|
|
# except:
|
|
# return None
|
|
|
|
# def extract_type(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# ArrayType = self.Pairs.GetType().GetUnqualifiedType()
|
|
# if ArrayType.IsReferenceType():
|
|
# ArrayType = ArrayType.GetDereferencedType()
|
|
# elif ArrayType.IsPointerType():
|
|
# ArrayType = ArrayType.GetPointeeType()
|
|
# if ArrayType.GetNumberOfTemplateArguments() > 0:
|
|
# ElementType = ArrayType.GetTemplateArgumentType(0)
|
|
# else:
|
|
# ElementType = None
|
|
# return ElementType
|
|
|
|
# def update(self):
|
|
# logger = lldb.formatters.Logger.Logger()
|
|
# try:
|
|
# self.Pairs = self.valobj.GetChildMemberWithName('Pairs')
|
|
# self.Elements = self.Pairs.GetChildMemberWithName('Elements')
|
|
# self.ElementsData = self.Elements.GetChildMemberWithName('Data')
|
|
# self.ArrayNum = self.ElementsData.GetChildMemberWithName('ArrayNum')
|
|
# self.NumFreeIndices = self.Elements.GetChildMemberWithName('NumFreeIndices')
|
|
# self.AllocationFlags = self.Elements.GetChildMemberWithName('AllocationFlags')
|
|
# self.AllocationFlagsAllocatorInstance = self.AllocationFlags.GetChildMemberWithName('AllocatorInstance')
|
|
# self.AllocatorInstance = self.ElementsData.GetChildMemberWithName('AllocatorInstance')
|
|
# self.AllocatorInstanceData = self.AllocatorInstance.GetChildMemberWithName('Data')
|
|
# self.AllocationFlagsInlineData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('InlineData')
|
|
# self.AllocationFlagsInlineDataAddr = self.AllocationFlagsInlineData.AddressOf().GetValueAsUnsigned(0)
|
|
# self.AllocationFlagsSecondaryData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('SecondaryData')
|
|
# self.AllocationFlagsSecondaryDataData = self.AllocationFlagsSecondaryData.GetChildMemberWithName('Data')
|
|
# self.SecondaryDataDataVal = self.AllocationFlagsSecondaryDataData.GetValueAsUnsigned(0)
|
|
# self.ElementType = self.extract_type()
|
|
# # This may fail due to C++ struct padding - will have to check
|
|
# self.ElementTypeSize = self.ElementType.GetByteSize() + self.TSetElement.GetValueAsUnsigned(0)
|
|
# assert self.ElementTypeSize != 0
|
|
# except:
|
|
# pass
|
|
|
|
# def has_children(self):
|
|
# return True
|
|
|
|
# def UEMapSummaryProvider(valobj,dict):
|
|
# return 'size=%s' % valobj.GetNumChildren()
|
|
|
|
# class UETObjectPtrSynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# self.valobj = valobj
|
|
|
|
# def num_children(self):
|
|
# return self.target.GetNumChildren() if self.resolved else 0
|
|
|
|
# def get_child_index(self, name):
|
|
# if not self.resolved:
|
|
# return None
|
|
# return self.target.GetIndexOfChildWithName(name)
|
|
|
|
# def get_child_at_index(self, index):
|
|
# if not self.resolved:
|
|
# return None
|
|
# return self.target.GetChildAtIndex(index)
|
|
|
|
# def update(self):
|
|
# try:
|
|
# self.DebugPtr = self.valobj.GetChildMemberWithName('DebugPtr')
|
|
# addr = self.DebugPtr.GetValueAsUnsigned(0)
|
|
# self.resolved = addr != 0 and (addr & 1) == 0
|
|
# self.target = self.DebugPtr.Dereference() if self.resolved else None
|
|
# except:
|
|
# self.resolved = False
|
|
# self.target = None
|
|
|
|
# def has_children(self):
|
|
# return self.resolved and self.target.GetNumChildren() > 0
|
|
|
|
# def UETObjectPtrSummaryProvider(valobj, dict):
|
|
# raw = valobj.GetNonSyntheticValue()
|
|
# DebugPtr = raw.GetChildMemberWithName('DebugPtr')
|
|
# addr = DebugPtr.GetValueAsUnsigned(0)
|
|
# if addr == 0:
|
|
# return 'nullptr'
|
|
# if addr & 1:
|
|
# return 'unresolved'
|
|
# return DebugPtr.Dereference().GetSummary() or ('0x%x' % addr)
|
|
|
|
# class UETSharedPtrSynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# self.valobj = valobj
|
|
|
|
# def num_children(self):
|
|
# return self.target.GetNumChildren() if self.valid else 0
|
|
|
|
# def get_child_index(self, name):
|
|
# if not self.valid:
|
|
# return None
|
|
# return self.target.GetIndexOfChildWithName(name)
|
|
|
|
# def get_child_at_index(self, index):
|
|
# if not self.valid:
|
|
# return None
|
|
# return self.target.GetChildAtIndex(index)
|
|
|
|
# def update(self):
|
|
# try:
|
|
# self.Object = self.valobj.GetChildMemberWithName('Object')
|
|
# addr = self.Object.GetValueAsUnsigned(0)
|
|
# self.valid = addr != 0
|
|
# self.target = self.Object.Dereference() if self.valid else None
|
|
# except:
|
|
# self.valid = False
|
|
# self.target = None
|
|
|
|
# def has_children(self):
|
|
# return self.valid and self.target.GetNumChildren() > 0
|
|
|
|
# def UETSharedPtrSummaryProvider(valobj, dict):
|
|
# raw = valobj.GetNonSyntheticValue()
|
|
# Object = raw.GetChildMemberWithName('Object')
|
|
# addr = Object.GetValueAsUnsigned(0)
|
|
# if addr == 0:
|
|
# return 'nullptr'
|
|
# return Object.Dereference().GetSummary() or ('0x%x' % addr)
|
|
|
|
# def _tweakptr_alive(raw):
|
|
# """True if a TWeakPtr's referenced object is still alive.
|
|
|
|
# Walks WeakReferenceCount -> ReferenceController -> SharedReferenceCount.
|
|
# SharedReferenceCount may be std::atomic<int32>, in which case we drill into
|
|
# the atomic's inner storage.
|
|
# """
|
|
# wrc = raw.GetChildMemberWithName('WeakReferenceCount')
|
|
# rc = wrc.GetChildMemberWithName('ReferenceController')
|
|
# if rc.GetValueAsUnsigned(0) == 0:
|
|
# return False
|
|
# src = rc.Dereference().GetChildMemberWithName('SharedReferenceCount')
|
|
# if not src.IsValid():
|
|
# return False
|
|
# count = src.GetValueAsSigned(-1)
|
|
# # std::atomic<int32> has no direct value; drill into its inner storage
|
|
# while count == -1 and src.GetNumChildren() > 0:
|
|
# src = src.GetChildAtIndex(0)
|
|
# count = src.GetValueAsSigned(-1)
|
|
# return count > 0
|
|
|
|
# class UETWeakPtrSynthProvider:
|
|
|
|
# def __init__(self, valobj, dict):
|
|
# self.valobj = valobj
|
|
|
|
# def num_children(self):
|
|
# return self.target.GetNumChildren() if self.alive else 0
|
|
|
|
# def get_child_index(self, name):
|
|
# if not self.alive:
|
|
# return None
|
|
# return self.target.GetIndexOfChildWithName(name)
|
|
|
|
# def get_child_at_index(self, index):
|
|
# if not self.alive:
|
|
# return None
|
|
# return self.target.GetChildAtIndex(index)
|
|
|
|
# def update(self):
|
|
# try:
|
|
# self.Object = self.valobj.GetChildMemberWithName('Object')
|
|
# self.alive = self.Object.GetValueAsUnsigned(0) != 0 and _tweakptr_alive(self.valobj)
|
|
# self.target = self.Object.Dereference() if self.alive else None
|
|
# except:
|
|
# self.alive = False
|
|
# self.target = None
|
|
|
|
# def has_children(self):
|
|
# return self.alive and self.target.GetNumChildren() > 0
|
|
|
|
# def UETWeakPtrSummaryProvider(valobj, dict):
|
|
# raw = valobj.GetNonSyntheticValue()
|
|
# Object = raw.GetChildMemberWithName('Object')
|
|
# addr = Object.GetValueAsUnsigned(0)
|
|
# if addr == 0:
|
|
# return 'nullptr'
|
|
# if not _tweakptr_alive(raw):
|
|
# return 'expired'
|
|
# return Object.Dereference().GetSummary() or ('0x%x' % addr)
|
|
|
|
def __lldb_init_module(debugger,dict):
|
|
global _FNameEntryType, _FNameEntryStride
|
|
_FNameEntryType = debugger.GetSelectedTarget().FindFirstType('FNameEntry')
|
|
_FNameEntryStride = _FNameEntryType.GetByteAlign()
|
|
debugger.HandleCommand('type summary add -F UEDataFormatter.UEFStringSummaryProvider -e FString -w UEDataFormatters')
|
|
debugger.HandleCommand('type summary add -F UEDataFormatter.UEFNameSummaryProvider -e FName -w UEDataFormatters')
|
|
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TObjectPtrSynthProvider -x "^TObjectPtr<.+>$" -w UEDataFormatters')
|
|
debugger.HandleCommand('type summary add -F UEDataFormatter.TObjectPtrSummaryProvider -e -x "^TObjectPtr<.+>$" -w UEDataFormatters')
|
|
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TSharedPtrSynthProvider -x "^TSharedPtr<.+>$" -w UEDataFormatters')
|
|
debugger.HandleCommand('type summary add -F UEDataFormatter.TSharedPtrSummaryProvider -e -x "^TSharedPtr<.+>$" -w UEDataFormatters')
|
|
debugger.HandleCommand('type synthetic add -l UEDataFormatter.TSharedPtrSynthProvider -x "^TSharedRef<.+>$" -w UEDataFormatters')
|
|
debugger.HandleCommand('type summary add -F UEDataFormatter.TSharedPtrSummaryProvider -e -x "^TSharedRef<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEUObjectBaseSummaryProvider -e UObject -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEUObjectBaseSummaryProvider -e UObjectBase -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEUObjectBaseSummaryProvider -e UObjectBaseUtility -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEFFieldClassSummaryProvider -e FFieldClass -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEFFieldSummaryProvider -e FField -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEFWeakObjectPtrSummaryProvider -e FWeakObjectPtr -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETWeakObjectPtrSynthProvider -x "TWeakObjectPtr<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETWeakObjectPtrSynthProvider -x "TAutoWeakObjectPtr<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UEArraySynthProvider -x "TArray<.+,.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEArraySummaryProvider -e -x "TArray<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UEBitArraySynthProvider -x "TBitArray<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEBitArraySummaryProvider -e -x "TBitArray<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UESparseArraySynthProvider -x "TSparseArray<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UESparseArraySummaryProvider -e -x "TSparseArray<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UEChunkedArraySynthProvider -x "TChunkedArray<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEChunkedArraySummaryProvider -e -x "TChunkedArray<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UESetSynthProvider -x "TSet<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UESetSummaryProvider -e -x "TSet<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UEMapSynthProvider -x "TMap<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEMapSummaryProvider -e -x "TMap<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UEMapSynthProvider -x "TMapBase<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UEMapSummaryProvider -e -x "TMapBase<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETObjectPtrSynthProvider -x "^TObjectPtr<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UETObjectPtrSummaryProvider -e -x "^TObjectPtr<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETSharedPtrSynthProvider -x "^TSharedPtr<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UETSharedPtrSummaryProvider -e -x "^TSharedPtr<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETSharedPtrSynthProvider -x "^TSharedRef<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UETSharedPtrSummaryProvider -e -x "^TSharedRef<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type synthetic add -l UEDataFormatter.UETWeakPtrSynthProvider -x "^TWeakPtr<.+>$" -w UEDataFormatters')
|
|
# debugger.HandleCommand('type summary add -F UEDataFormatter.UETWeakPtrSummaryProvider -e -x "^TWeakPtr<.+>$" -w UEDataFormatters')
|
|
debugger.HandleCommand("type category enable UEDataFormatters")
|
|
|
|
def _patch_codelldb_value():
|
|
"""Patch codelldb.value.Value with Unreal-friendly navigation:
|
|
- __getattr__ auto-derefs pointers and falls back to iterating
|
|
children by GetName() (so base classes like 'UWidget' resolve).
|
|
|
|
This affects every /se and /py expression that uses Value — strictly
|
|
more forgiving than the stock behavior (paths that used to fail now
|
|
succeed), but it IS a global behavior change.
|
|
"""
|
|
try:
|
|
from codelldb.value import Value
|
|
except ImportError:
|
|
return
|
|
def __getattr__(self, name):
|
|
sbv = self._Value__sbvalue
|
|
if sbv.GetType().IsPointerType():
|
|
sbv = sbv.Dereference()
|
|
child = sbv.GetChildMemberWithName(name)
|
|
if not child.IsValid():
|
|
for i in range(sbv.GetNumChildren()):
|
|
c = sbv.GetChildAtIndex(i)
|
|
if c.GetName() == name:
|
|
child = c
|
|
break
|
|
if not child.IsValid():
|
|
raise AttributeError("Attribute '%s' is not defined" % name)
|
|
return Value(child)
|
|
Value.__getattr__ = __getattr__
|
|
|
|
_patch_codelldb_value()
|
|
|
|
def fv(expr):
|
|
"""Evaluate a Python-syntax expression against the current frame's variables.
|
|
|
|
Every frame variable is pre-wrapped in a codelldb Value and placed in
|
|
the eval globals, so e.g. fv("Widget.Object.SCompoundWidget") walks via
|
|
the (patched) Value.__getattr__, fv("Arr[3].Field") works via
|
|
Value.__getitem__, and arithmetic/comparisons work via Value's dunders.
|
|
|
|
Usage in Watch: /py fv("Widget.Object.SCompoundWidget.SWidget")
|
|
"""
|
|
from codelldb.value import Value
|
|
frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
|
|
eval_globals = {}
|
|
for var in frame.GetVariables(True, True, True, True):
|
|
name = var.GetName()
|
|
if var.IsValid() and name:
|
|
eval_globals[name] = Value(var)
|
|
try:
|
|
result = eval(expr, eval_globals, {})
|
|
except Exception as e:
|
|
return "<%s: %s>" % (type(e).__name__, e)
|
|
if isinstance(result, Value):
|
|
return Value.unwrap(result)
|
|
return result
|
|
|
|
def fv2():
|
|
frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
|
|
top = frame.FindVariable('Top')
|
|
if not top.IsValid():
|
|
return
|
|
widget = top.GetChildMemberWithName('Widget')
|
|
if not widget.IsValid():
|
|
return
|
|
uwidget = FindNamedChild(widget, 'UWidget')
|
|
if not uwidget.IsValid():
|
|
return
|
|
return uwidget
|
|
|
|
def FindNamedChild(value, name):
|
|
for i in range(value.GetNumChildren()):
|
|
child = value.GetChildAtIndex(i)
|
|
if child.IsValid() and child.GetName() == name:
|
|
return child
|
|
return lldb.SBValue()
|
|
|
|
|
|
#
|
|
# The standard repr for SBValue will recurse infinitely if you
|
|
# provide useful synth providers for smart pointers. This won't.
|
|
#
|
|
def _sbvalue_repr(self):
|
|
n = self.GetNumChildren()
|
|
if n == 0: return _sbvalue_display(self)
|
|
parts = []
|
|
for i in range(n):
|
|
child = self.GetChildAtIndex(i)
|
|
name = child.GetName() or str(i)
|
|
parts.append('%s=%s\n' % (name, _sbvalue_display(child)))
|
|
return ''.join(parts)
|
|
|
|
def _sbvalue_display(v):
|
|
s = v.GetSummary()
|
|
if s: return s
|
|
s = v.GetValue()
|
|
if s: return s
|
|
if v.GetNumChildren() > 0: return '{...}'
|
|
return '?'
|
|
|
|
lldb.SBValue.__repr__ = _sbvalue_repr
|
|
|
|
import builtins
|
|
builtins.fv = fv
|
|
builtins.fv2 = fv2
|