From 21d8c40005747e98f4534723813ce63de5853eae Mon Sep 17 00:00:00 2001 From: jyelon Date: Mon, 20 Apr 2026 05:42:34 -0400 Subject: [PATCH] Better data formatters in progress. --- Content/Testing/BP_Test.uasset | 4 +- Docs/Better-Debugging-With-LLDB.md | 288 +++-- Integration.code-workspace.tpl.json | 9 +- tools/UEDataFormatter.py | 1665 ++++++++++++++++----------- 4 files changed, 1106 insertions(+), 860 deletions(-) diff --git a/Content/Testing/BP_Test.uasset b/Content/Testing/BP_Test.uasset index 9356c230..3a9e6533 100644 --- a/Content/Testing/BP_Test.uasset +++ b/Content/Testing/BP_Test.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:744dec37bf46eb8ac6169f05663feb3b3abb63137c429061db4334f39739a454 -size 54771 +oid sha256:0c3960b7b58e58a3d68a1c0d15bf8503e4ea54e7206c4934ef6375938e6062f8 +size 55626 diff --git a/Docs/Better-Debugging-With-LLDB.md b/Docs/Better-Debugging-With-LLDB.md index d6ea9602..c15b07de 100644 --- a/Docs/Better-Debugging-With-LLDB.md +++ b/Docs/Better-Debugging-With-LLDB.md @@ -6,183 +6,167 @@ When debugging Unreal with VS Code + CodeLLDB, the **Variables** pane and the **Watch** pane use two completely different evaluation paths: - **Variables pane** walks a tree built by lldb's *synthetic children - providers* (including Unreal's Python formatters for TArray, TMap, FName, - FString, TSharedRef, etc.). Values are looked up by offset/type — no - compilation. Base classes appear as named children ("SCompoundWidget", - "UWidget"), smart-pointer inners get unwrapped, container elements get - indexed. + providers* (Unreal's Python formatters for TArray, TMap, FName, FString, + and now our TObjectPtr/TSharedPtr/TWeakPtr). Values are looked up by + offset/type — no compilation. Base classes appear as named children + ("SCompoundWidget", "UWidget"); smart-pointer inners get unwrapped; + container elements get indexed. -- **Watch pane** (and `p`/`expression`) runs your text through Clang to - compile a real C++ expression against the program's full type system, - then applies formatters to the result. For Unreal, this is slow and - often fails outright — synthetic children don't exist as C++ members. +- **Watch pane** (without intervention) runs text through the `/se` (simple) + or `/nat` (Clang) evaluator. `/nat` fails for most Unreal paths; + `/se` transpiles to Python and walks the SBValue tree, but — as + originally shipped — fails on pointer auto-deref and can't find base + classes by type name. -The practical consequence: a path you see in the Variables pane — like -`Widget.Object.SCompoundWidget.SWidget.bCanSupportFocus` — cannot be -typed into the Watch pane, because the intermediate labels -(`SCompoundWidget`, `SWidget`) aren't real C++ members. Even "Copy as -Expression" in the Variables pane gives you that broken synthetic path. +When you right-click in Variables and **Add to Watch**, CodeLLDB sends +a path like `Top.Widget` (built itself for synthetic children, uniform +`.`, no pointer awareness). Without the fix below, that path fails in +Watch even though it identifies a real value Variables can show. -Hand-writing the equivalent real C++ expression (with explicit -`->` for pointers, `static_cast`s through base classes, formatter-internal -array indexing math, etc.) is impractical. +## What We Did -## The Fix: A Python Helper That Walks Synthetic Children +All changes live in `tools/UEDataFormatter.py`, loaded via `initCommands` +in every launch config in `Integration.code-workspace.tpl.json`. -We added a function `fv(path)` to `tools/UEDataFormatter.py`. It walks a -dotted path through the synthetic tree using the same APIs the Variables -pane uses (`GetChildMemberWithName`, then fallback to iterating children -by name so base classes match). Pointers are auto-dereferenced. +### 1. Patched `codelldb.value.Value.__getattr__` -CodeLLDB's `/py` expression mode evaluates Python in the Watch pane and -renders returned `SBValue`s through the full formatter pipeline — same -expandable tree as Variables. So: +At module load, we monkey-patch CodeLLDB's `Value` class — the Python +wrapper it uses inside `/se` and `/py` evaluation — to: - /py fv("Widget.Object.SCompoundWidget.SWidget") +- **Auto-deref pointers** before descending into a named child. +- **Fall back to iterating children** by `GetName()` when + `GetChildMemberWithName` returns invalid. This catches base classes + (which appear as named children when iterated but can't be looked up + by name). -works in Watch the way you'd originally expect `Widget.Object.SCompoundWidget.SWidget` -to work. +**Result:** plain "Add to Watch" from the Variables pane produces a path +like `Top.Widget.SomeField` and Watch evaluates it correctly — same +expandable tree you'd see in Variables. No `/py fv(...)` wrapper, no +VS Code extension, no manual prefix typing. -`fv` is also injected into Python builtins, so from the Debug Console -you can also just: +Blast radius: every `/se` and `/py` expression that goes through +`Value.__getattr__`. The change is strictly more permissive (paths that +used to fail now succeed), but it is a global behavior change to +CodeLLDB internals. Breaks if CodeLLDB renames `Value` or the +`__sbvalue` slot; re-applied automatically on extension reload since +our patch runs on `command script import`. - script fv("Widget.Object.SCompoundWidget.SWidget") +### 2. Synth + Summary Providers for Smart Pointers -### How `fv` is loaded +UE's engine formatter covers containers and TWeakObjectPtr, but not the +smart pointer family. We added providers for: -The launch configurations in `Integration.code-workspace.tpl.json` run: +- **`TObjectPtr`** — shows `nullptr` / `unresolved` / wrapped object's + summary. Expanding flattens straight to the target's members (no + intermediate `*DebugPtr` click). +- **`TSharedPtr`** and **`TSharedRef`** — shows `nullptr` / target + summary; expands straight to target's members. +- **`TWeakPtr`** — checks + `WeakReferenceCount.ReferenceController.SharedReferenceCount` before + dereferencing. Expired weak refs show `expired` rather than garbage + from a dangling pointer. - command script import /home/jyelon/integration/tools/UEDataFormatter.py +All registration regexes are anchored with `^` — otherwise the greedy +`.+` matches nested occurrences (a `TArray, ...>` would +get dispatched to the TObjectPtr provider instead of TArray). -This both (a) loads all the Unreal data formatters and (b) defines `fv` -and injects it into builtins. +### 3. Dynamic Type Resolution -### Reloading without restarting the debug session +Every launch config now sets: + + settings set target.prefer-dynamic-value no-run-target + +lldb reads the vtable at each polymorphic value and shows the runtime +type's members. A `UObject*` that actually points to a `UUserWidget` +expands to the full `UUserWidget` subtree, not just `UObject`. +`no-run-target` avoids running code in the debuggee, which is important +during synthesis. + +### 4. Universal SIGTRAP Handling + +`process handle SIGTRAP --notify false --pass false --stop false` is now +in every launch config. Unreal raises SIGTRAP internally in a number of +places (soft asserts, ensure-style checks); without this, the debugger +stops constantly. + +## Reloading Without a Session Restart If you edit `tools/UEDataFormatter.py`, reload in the Debug Console: - script import importlib; importlib.reload(UEDataFormatter) + script import importlib; importlib.reload(UEDataFormatter); UEDataFormatter.__lldb_init_module(lldb.debugger, {}) -## The Remaining Gap: "Add to Watch" - -VS Code's "Add to Watch" context menu copies the variable's `evaluateName` -as reported by the debug adapter. CodeLLDB sends the synthetic path — so -"Add to Watch" produces exactly the unusable expression that started this -whole problem. - -The clean solution is a small VS Code extension that registers a new -command **Add to Watch (fv)** on the Variables pane context menu. When -invoked, it reads the variable's evaluateName and adds -`/py fv("")` to the watch expressions. - -### Scaffolding the extension - -Create a folder, e.g. `tools/vscode-fv-watch/`: - -``` -tools/vscode-fv-watch/ - package.json - src/extension.ts - tsconfig.json -``` - -**`package.json`** declares the contribution points — the command, and -its appearance in the Variables pane context menu: - -```json -{ - "name": "fv-watch", - "version": "0.0.1", - "engines": { "vscode": "^1.80.0" }, - "activationEvents": ["onDebug"], - "main": "./out/extension.js", - "contributes": { - "commands": [{ - "command": "fv.addToWatch", - "title": "Add to Watch (fv)" - }], - "menus": { - "debug/variables/context": [{ - "command": "fv.addToWatch", - "group": "3_modification@1" - }] - } - }, - "devDependencies": { - "@types/vscode": "^1.80.0", - "typescript": "^5.0.0" - } -} -``` - -**`src/extension.ts`** implements the command. The variable reference is -passed as the command argument; we pull its `evaluateName` and call the -built-in `debug.addToWatchExpressions` with the wrapped form: - -```ts -import * as vscode from 'vscode'; - -export function activate(ctx: vscode.ExtensionContext) { - ctx.subscriptions.push(vscode.commands.registerCommand( - 'fv.addToWatch', - async (variable: any) => { - const name = variable?.evaluateName ?? variable?.name; - if (!name) return; - const expr = `/py fv(${JSON.stringify(name)})`; - await vscode.commands.executeCommand( - 'debug.addToWatchExpressions', - { variable: { evaluateName: expr } } - ); - })); -} - -export function deactivate() {} -``` - -**`tsconfig.json`** is standard: - -```json -{ - "compilerOptions": { - "module": "commonjs", - "target": "ES2020", - "outDir": "out", - "strict": true, - "esModuleInterop": true - }, - "include": ["src"] -} -``` - -### Building and installing - -``` -cd tools/vscode-fv-watch -npm install -npx tsc -ln -s "$PWD" ~/.vscode/extensions/fv-watch -``` - -Restart VS Code. Right-click any entry in the Variables pane and -**Add to Watch (fv)** appears alongside the built-in **Add to Watch**. +The reload re-executes module code (updating the patch and the provider +classes). The explicit `__lldb_init_module` call re-runs the provider +registrations — lldb only fires `__lldb_init_module` on initial import, +not on reload. ## Summary of Workflow | Action | Where | How | |---|---|---| | Explore a value | Variables pane | Click disclosure triangles | -| Track a value across steps | Watch pane | "Add to Watch (fv)" from Variables context menu, or type `/py fv("...")` manually | -| One-shot inspection | Debug Console | `script fv("path")` | -| Reload formatter edits | Debug Console | `script import importlib; importlib.reload(UEDataFormatter)` | +| Track a value across steps | Watch pane | Right-click variable → Add to Watch (just works) | +| One-shot inspection | Debug Console | `v Widget.Object->SCompoundWidget.SWidget` (`v` = `frame variable`; use `->` for pointers explicitly) | +| Reload formatter edits | Debug Console | `script import importlib; importlib.reload(UEDataFormatter); UEDataFormatter.__lldb_init_module(lldb.debugger, {})` | -## Why Not Make It Automatic? +## Notes on the Design -The cleanest solution would be to change CodeLLDB's default Watch -evaluator to route through `frame variable` / synthetic children instead -of Clang. That's not exposed as a setting — CodeLLDB's `"expressions"` -option only chooses between its own parser, lldb's `expr`, or raw Python. -None of those hit the synthetic-children walk. +### Why this works -A feature request against CodeLLDB to add a "native frame-variable" -expression mode would address this at the source. In the meantime, the -`fv` helper + extension combo reproduces the missing behavior. +CodeLLDB's `/se` evaluator transpiles user expressions into Python that +operates on `Value` objects. `Value.__getattr__` drives every `.field` +access. By making that method auto-deref and iterate for base classes, +every downstream mechanism (Watch, Debug Console, hover, conditional +breakpoints) inherits the fix. + +### Why `fv` is no longer needed + +Earlier we had an `fv(path)` helper plus a plan for a companion VS Code +extension to wrap "Add to Watch" results in `/py fv(...)`. The +`Value.__getattr__` patch makes the default path work, so that whole +layer is obsolete. + +### Why not patch `SBValue` instead + +Tempting, but much larger blast radius — affects every tool, every +adapter, every Python script using lldb. Patching `Value` confines the +change to CodeLLDB's expression pipeline. + +## Ideas for Further Improvement + +### Propose `/fv` mode to CodeLLDB upstream + +Prefix dispatch is in CodeLLDB's Rust binary, so we can't add a new +prefix from Python. A clean feature request would be to add `/fv` → +`SBFrame::GetValueForVariablePath(code)` as a native evaluator. That +would give direct access to lldb's own frame-variable walker without +the Python/Value indirection — though it has its own limitations +(no arithmetic, no casts). + +### More synth providers + +- **`TOptional`** — hide the storage bytes and `bIsSet`; expose the + contained value (or `unset`) directly. +- **`TVariant<...>`** — expose the currently-held alternative as the + single child. +- **`FText`** — show the resolved localized string as the summary. +- **`FSoftObjectPtr` / `FSoftClassPtr`** — show the asset path. + +### Enrich TObjectPtr summary + +When resolved, show both class and name (`UUserWidget 'W_HUD_0'`) +instead of just the name. The class is reachable via +`ClassPrivate->NamePrivate`. + +### A `fdump` helper + +A Debug Console helper that prints an entire subtree as indented text — +useful for grabbing a snapshot of complex state into a log or comment. + +### Get `Copy as Expression` to emit `->` for pointers + +The path CodeLLDB builds for synthetic children uses `.` uniformly, +regardless of whether intermediate values are pointers. That's why +`v Top.Widget` fails but `v Top->Widget` works. A feature request to +have CodeLLDB emit `->` when traversing a pointer would make paths +`frame variable`-compatible out of the box. diff --git a/Integration.code-workspace.tpl.json b/Integration.code-workspace.tpl.json index e7d36a69..db312414 100644 --- a/Integration.code-workspace.tpl.json +++ b/Integration.code-workspace.tpl.json @@ -125,6 +125,7 @@ "initCommands": [ "command script import [INTEGRATION]/tools/UEDataFormatter.py", "settings set target.inline-breakpoint-strategy always", + "settings set target.prefer-dynamic-value no-run-target", "process handle SIGTRAP --notify false --pass false --stop false", "target stop-hook add --one-liner \"p ::UngrabAllInputImpl()\"" ] @@ -147,7 +148,9 @@ "console": "integratedTerminal", "initCommands": [ "command script import [INTEGRATION]/tools/UEDataFormatter.py", - "settings set target.inline-breakpoint-strategy always" + "settings set target.inline-breakpoint-strategy always", + "settings set target.prefer-dynamic-value no-run-target", + "process handle SIGTRAP --notify false --pass false --stop false" ] }, { @@ -160,7 +163,9 @@ "type": "lldb", "console": "integratedTerminal", "initCommands": [ - "settings set target.inline-breakpoint-strategy always" + "settings set target.inline-breakpoint-strategy always", + "settings set target.prefer-dynamic-value no-run-target", + "process handle SIGTRAP --notify false --pass false --stop false" ] } ] diff --git a/tools/UEDataFormatter.py b/tools/UEDataFormatter.py index 98096672..bea6c9a3 100644 --- a/tools/UEDataFormatter.py +++ b/tools/UEDataFormatter.py @@ -6,6 +6,10 @@ 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 @@ -23,734 +27,987 @@ import lldb.formatters.Logger # command script import "/Path/To/Epic/UE/Engine/Extras/LLDBDataFormatters/UEDataFormatter.py" # 3) Restart Xcode -def UETCharSummaryProvider(valobj,dict): - Data = valobj.GetValue() - Val = valobj.GetSummary() - Type = valobj.GetType().GetUnqualifiedType() - if Type.IsPointerType(): - DataVal = valobj.GetValueAsUnsigned(0) - if DataVal == 0: - Val = 'NULL' - else: - Expr = '(char16_t*)(%s)' % DataVal - ValRef = valobj.CreateValueFromExpression('string', Expr) - Val = ValRef.GetSummary() - elif Type.IsReferenceType(): - Expr = '(char16_t&)(%s)' % Data - ValRef = valobj.CreateValueFromExpression('string', Expr) - Val = ValRef.GetSummary() - elif Type.IsArrayType(): - DataVal = valobj.GetChildAtIndex(0).GetValueAsUnsigned(0) - if DataVal == 0: - Val = 'NULL' - else: - Expr = '(char16_t*)(%s)' % valobj.GetAddress() - ValRef = valobj.CreateValueFromExpression('string', Expr) - Val = ValRef.GetSummary() +_FNameEntryType = None +_FNameEntryStride = 2 + +def UEFStringSummaryProvider(valobj, dict): + target = valobj.GetTarget() + sbtype = valobj.GetType().GetUnqualifiedType() + if sbtype.IsPointerType(): + if valobj.GetValueAsUnsigned(0) == 0: + return 'nullptr' + valobj = valobj.Dereference() + elif sbtype.IsReferenceType(): + valobj = valobj.Dereference() + tarray = valobj.GetChildMemberWithName('Data') + array_num = tarray.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) + if array_num < 0: return 'corrupted string' + if array_num == 0: return 'empty string' + data_ptr = tarray.GetChildMemberWithName('AllocatorInstance').GetChildMemberWithName('Data') + buffer_addr = data_ptr.GetValueAsUnsigned(0) + if buffer_addr == 0: return 'corrupted string' + tchar_type = tarray.GetType().GetTemplateArgumentType(0) + array_type = tchar_type.GetArrayType(array_num) + sbaddr = lldb.SBAddress(buffer_addr, target) + summary = target.CreateValueFromAddress('string', sbaddr, array_type).GetSummary() + if summary and summary.startswith('u"'): return summary[1:] + return summary + + +def UEFNameSummaryProvider(valobj, dict): + target = valobj.GetTarget() + process = target.GetProcess() + entry_id = valobj.GetChildMemberWithName('DisplayIndex') + index = entry_id.GetChildMemberWithName('Value').GetValueAsUnsigned(0) + number = valobj.GetChildMemberWithName('Number').GetValueAsUnsigned(0) + + block_idx = index >> 16 + entry_offset = (index & 0xFFFF) * _FNameEntryStride + + gname_var = target.FindFirstGlobalVariable('GNameBlocksDebug') + gname_ptr = gname_var.GetValueAsUnsigned(0) + + error = lldb.SBError() + block_addr = process.ReadPointerFromMemory(gname_ptr + block_idx * target.GetAddressByteSize(), error) + if error.Fail(): return '' + + entry_addr = block_addr + entry_offset + entry = target.CreateValueFromAddress('entry', lldb.SBAddress(entry_addr, target), _FNameEntryType) + + header = entry.GetChildMemberWithName('Header') + length = header.GetChildMemberWithName('Len').GetValueAsUnsigned(0) + is_wide = header.GetChildMemberWithName('bIsWide').GetValueAsUnsigned(0) + + if is_wide: + name_addr = entry.GetChildMemberWithName('WideName').GetLoadAddress() + raw = process.ReadMemory(name_addr, length * 2, error) + if error.Fail(): return '' + name = raw.decode('utf-16-le') else: - DataVal = valobj.GetValueAsUnsigned(0) - Expr = '(char16_t)(%s)' % DataVal - ValRef = valobj.CreateValueFromExpression('string', Expr) - Val = ValRef.GetSummary() - return Val - -def UESignedCharSummaryProvider(valobj,dict): - Data = valobj.GetValue() - Val = valobj.GetSummary() - Type = valobj.GetType().GetUnqualifiedType() - DataVal = valobj.GetValueAsUnsigned(0) - if DataVal == 0: - Val = 'NULL' - else: - Expr = '(char*)(%s)' % Data - ValRef = valobj.CreateValueFromExpression('string', Expr) - Val = ValRef.GetSummary() - return Val + name_addr = entry.GetChildMemberWithName('AnsiName').GetLoadAddress() + raw = process.ReadMemory(name_addr, length, error) + if error.Fail(): return '' + name = raw.decode('utf-8', errors='replace') -def UEFStringSummaryProvider(valobj,dict): - Data = valobj.GetChildMemberWithName('Data') - ArrayNumVal = Data.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) - if ArrayNumVal < 0: - return 'string=Invalid' - elif ArrayNumVal == 0: - return 'string=Empty' - else: - AllocatorInstance = Data.GetChildMemberWithName('AllocatorInstance') - ActualData = AllocatorInstance.GetChildMemberWithName('Data') - Expr = '(char16_t*)(%s)' % ActualData.GetValue() - ValRef = valobj.CreateValueFromExpression('string', Expr) - Val = ValRef.GetSummary() - return 'string=' + Val + if number > 0: return '%s_%d' % (name, number - 1) + return name -def UEFNameIndexToEntry(EntryId): - Index = EntryId.GetChildMemberWithName('Value').GetValueAsUnsigned(0) - # FNameDebugVisualizer::OffsetBits = 16 - # FNameDebugVisualizer::OffsetMask = (1 << OffsetBits) - 1 = 65535 - NameEntryExpr = '(FNameEntry*)(GNameBlocksDebug['+str(Index)+' >> 16]+((alignof(FNameEntry) * ('+str(Index)+' & 65535))))' - NameEntry = EntryId.CreateValueFromExpression('NameEntry', NameEntryExpr) - return NameEntry - -def UEFNameEntrySummaryProvider(valobj,dict): - Header = valobj.GetChildMemberWithName('Header') - Len = min(Header.GetChildMemberWithName('Len').GetValueAsUnsigned(0), 1023) - if Len == 0: - UnderlyingId = valobj.GetValueForExpressionPath('.NumberedName.Id') - Number = valobj.GetValueForExpressionPath('.NumberedName.Number').GetValueAsUnsigned(0) - BaseNameEntry = UEFNameIndexToEntry(UnderlyingId) - BaseName = UEFNameEntrySummaryProvider(BaseNameEntry, dict) - return '%s_%s' % (BaseName, Number-1) # BaseName will already include name= from recursion - else: - DataPtr = valobj.GetChildAtIndex(1).AddressOf().GetValueAsUnsigned(0) - IsWide = Header.GetChildMemberWithName('bIsWide').GetValueAsUnsigned(0) - if IsWide: - DataPtr = valobj.GetValueForExpressionPath(".WideName").AddressOf().GetValueAsUnsigned(0) - SizeOfTChar = valobj.CreateValueFromExpression('size', 'sizeof(TCHAR))').GetValueAsUnsigned(0) - Encoding = "utf-16" if SizeOfTChar == 2 else "utf-32" - NumBytes = Len * SizeOfTChar - Data = valobj.process.ReadMemory(DataPtr,NumBytes,lldb.SBError()) - Name = Data.decode(Encoding).encode("utf-8") - else: - DataPtr = valobj.GetValueForExpressionPath(".AnsiName").AddressOf().GetValueAsUnsigned(0) - Name = valobj.process.ReadMemory(DataPtr,Len,lldb.SBError()) - return 'name=%s' % Name - -def UEFNameSummaryProvider(valobj,dict): - EntryId = valobj.GetChildMemberWithName('DisplayIndex') - if not EntryId.IsValid(): - EntryId = valobj.GetChildMemberWithName('ComparisonIndex') - if not EntryId.IsValid(): - EntryId = valobj.GetChildMemberWithName('Index') - NameEntry = UEFNameIndexToEntry(EntryId) - Number = valobj.GetChildMemberWithName('Number') - if not Number.IsValid(): - return UEFNameEntrySummaryProvider(NameEntry, dict) - else: - NameStr = UEFNameEntrySummaryProvider(NameEntry, dict) - if Number.GetValueAsUnsigned(0) != 0: - return "'%s'_%d" % (NameStr, Number-1) - else: - return "'%s'" % NameStr - -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: +class ForwardingSynthProvider: def __init__(self, valobj, dict): - logger = lldb.formatters.Logger.Logger() 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): - logger = lldb.formatters.Logger.Logger() return 1 - def get_child_index(self,name): - logger = lldb.formatters.Logger.Logger() - return 0 + def has_children(self): + return true - 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) + def get_child_index(self, name): + if name == 'Object': return 0 + return -1 - Expr = 'nullptr' - return self.valobj.CreateValueFromExpression('Object', Expr) + def get_child_at_index(self, index): + if index == 0: return self.object + return lldb.SBValue() 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() + self.object = self.valobj.GetChildMemberWithName('Object') + + +def TSharedPtrSummaryProvider(valobj, dict): + obj = valobj.GetChildMemberWithName('Object') + addr = obj.GetValueAsUnsigned(0) + if addr == 0: return 'nullptr' + return '{...}' + + +# def UEUObjectBaseSummaryProvider(valobj,dict): +# Name = valobj.GetChildMemberWithName('NamePrivate') +# return Name.GetSummary() + +# def UEFFieldClassSummaryProvider(valobj,dict): +# Name = valobj.GetChildMemberWithName('Name') +# return Name.GetSummary() + +# def UEFFieldSummaryProvider(valobj,dict): +# Name = valobj.GetChildMemberWithName('NamePrivate') +# return Name.GetSummary() + +# class UETWeakObjectPtrSynthProvider: + +# def __init__(self, valobj, dict): +# logger = lldb.formatters.Logger.Logger() +# self.valobj = valobj + +# def num_children(self): +# logger = lldb.formatters.Logger.Logger() +# return 1 + +# def get_child_index(self,name): +# logger = lldb.formatters.Logger.Logger() +# return 0 + +# def get_child_at_index(self,index): +# logger = lldb.formatters.Logger.Logger() +# logger >> "Retrieving child %s" % index +# if self.ObjectSerialNumberVal >= 1: +# Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].SerialNumber == %s' % (int(self.ObjectIndexVal/65536), self.ObjectIndexVal%65536, self.ObjectSerialNumberVal) +# Val = self.valobj.CreateValueFromExpression("%s" % self.ObjectIndexVal, Expr) +# Value = Val.GetValueAsUnsigned(0) +# if Value != 0: +# Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].Object' % (int(self.ObjectIndexVal/65536), self.ObjectIndexVal%65536) +# return self.valobj.CreateValueFromExpression('Object', Expr) +# else: +# Expr = '(void*)0xDEADBEEF' +# return self.valobj.CreateValueFromExpression('Object', Expr) + +# Expr = 'nullptr' +# return self.valobj.CreateValueFromExpression('Object', Expr) + +# def update(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# self.ObjectSerialNumber = self.valobj.GetChildMemberWithName('ObjectSerialNumber') +# self.ObjectSerialNumberVal = self.ObjectSerialNumber.GetValueAsSigned(0) +# self.ObjectIndex = self.valobj.GetChildMemberWithName('ObjectIndex') +# self.ObjectIndexVal = self.ObjectIndex.GetValueAsSigned(0) +# except: +# pass + +# def has_children(self): +# return True + +# def UEFWeakObjectPtrSummaryProvider(valobj,dict): +# ObjectSerialNumber = valobj.GetChildMemberWithName('ObjectSerialNumber') +# ObjectSerialNumberVal = ObjectSerialNumber.GetValueAsSigned(0) +# if ObjectSerialNumberVal < 1: +# return 'object=nullptr' +# ObjectIndex = valobj.GetChildMemberWithName('ObjectIndex') +# ObjectIndexVal = ObjectIndex.GetValueAsSigned(0) +# Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].SerialNumber == %s' % (int(ObjectIndexVal/65536), ObjectIndexVal%65536, ObjectSerialNumberVal) +# Val = valobj.CreateValueFromExpression('%s' % ObjectIndexVal, Expr) +# ValRef = Val.GetValueAsUnsigned(0) +# if ValRef == 0: +# return 'object=STALE' +# else: +# Expr = 'GObjectArrayForDebugVisualizers->Objects[%s][%s].Object' % (int(ObjectIndexVal/65536), ObjectIndexVal%65536) +# Val = valobj.CreateValueFromExpression("%s" % ObjectIndexVal, Expr) +# return 'object=' + Val.GetValue() + +# class UEChunkedArraySynthProvider: + +# def __init__(self, valobj, dict): +# logger = lldb.formatters.Logger.Logger() +# self.valobj = valobj + +# def num_children(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# NumElementsVal = self.NumElements.GetValueAsSigned(0) +# return NumElementsVal; +# except: +# return 0; + +# def get_child_index(self,name): +# logger = lldb.formatters.Logger.Logger() +# try: +# return int(name.lstrip('[').rstrip(']')) +# except: +# return None + +# def get_child_at_index(self,index): +# logger = lldb.formatters.Logger.Logger() +# logger >> "Retrieving child %s" % index +# Expr = '(unsigned)sizeof(%s::FChunk)/%s' % (self.valobj.GetType().GetUnqualifiedType().GetName(), self.ElementTypeSize) +# self.ChunkBytes = self.valobj.CreateValueFromExpression('[%s]' % index, Expr) +# self.ChunkBytesSize = self.ChunkBytes.GetValueAsUnsigned(0) +# assert self.ChunkBytesSize != 0 + +# Expr = '*(*(((%s**)%s)+%s)+%s)' % (self.ElementType.GetName(), self.AllocatorData.GetValue(), index / self.ChunkBytesSize, index % self.ChunkBytesSize) +# return self.valobj.CreateValueFromExpression('[%s]' % index, Expr) + +# def extract_type(self): +# logger = lldb.formatters.Logger.Logger() +# ArrayType = self.valobj.GetType().GetUnqualifiedType() +# if ArrayType.IsReferenceType(): +# ArrayType = ArrayType.GetDereferencedType() +# elif ArrayType.IsPointerType(): +# ArrayType = ArrayType.GetPointeeType() +# if ArrayType.GetNumberOfTemplateArguments() > 0: +# ElementType = ArrayType.GetTemplateArgumentType(0) +# else: +# ElementType = None +# return ElementType + +# def update(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# self.ElementType = self.extract_type() +# self.ElementTypeSize = self.ElementType.GetByteSize() +# assert self.ElementTypeSize != 0 +# self.NumElements = self.valobj.GetChildMemberWithName('NumElements') +# self.Chunks = self.valobj.GetChildMemberWithName('Chunks') +# self.ArrayNum = self.Chunks.GetChildMemberWithName('ArrayNum') +# self.AllocatorInstance = self.Chunks.GetChildMemberWithName('AllocatorInstance') +# self.AllocatorData = self.AllocatorInstance.GetChildMemberWithName('Data') +# except: +# pass + +# def has_children(self): +# return True + +# def UEChunkedArraySummaryProvider(valobj,dict): +# return 'size=%s' % valobj.GetNumChildren() + +# class UESparseArraySynthProvider: + +# def __init__(self, valobj, dict): +# logger = lldb.formatters.Logger.Logger() +# self.valobj = valobj + +# def num_children(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# NumBitsVal = self.NumFreeIndices.GetValueAsSigned(0) +# ArrayNumVal = self.Data.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) +# return ArrayNumVal - NumBitsVal; +# except: +# return 0; + +# def get_child_index(self,name): +# logger = lldb.formatters.Logger.Logger() +# try: +# return int(name.lstrip('[').rstrip(']')) +# except: +# return None + +# def get_child_at_index(self,index): +# logger = lldb.formatters.Logger.Logger() +# logger >> "Retrieving child %s" % index +# if index < 0: +# return None; + +# if index >= self.num_children(): +# return None; + +# Val = None +# if self.SecondaryDataDataVal > 0: +# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataData.GetAddress(), index, index) +# Val = self.SecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr) +# else: +# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.InlineData.GetAddress(), index, index) +# Val = self.InlineData.CreateValueFromExpression('[5s]' % index, Expr) + +# if Val.GetValueAsSigned(0) != 0: +# offset = index * self.ElementTypeSize +# return self.AllocatorData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType) +# else: +# return None + +# def extract_type(self): +# logger = lldb.formatters.Logger.Logger() +# ArrayType = self.valobj.GetType().GetUnqualifiedType() +# if ArrayType.IsReferenceType(): +# ArrayType = ArrayType.GetDereferencedType() +# elif ArrayType.IsPointerType(): +# ArrayType = ArrayType.GetPointeeType() +# if ArrayType.GetNumberOfTemplateArguments() > 0: +# ElementType = ArrayType.GetTemplateArgumentType(0) +# else: +# ElementType = None +# return ElementType + +# def update(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# self.ElementType = self.extract_type() +# self.ElementTypeSize = self.ElementType.GetByteSize() +# assert self.ElementTypeSize != 0 +# self.NumFreeIndices = self.valobj.GetChildMemberWithName('NumFreeIndices') +# self.Data = self.valobj.GetChildMemberWithName('Data') +# self.AllocatorInstance = self.Data.GetChildMemberWithName('AllocatorInstance') +# self.AllocatorData = self.AllocatorInstance.GetChildMemberWithName('Data') +# self.AllocationFlags = self.valobj.GetChildMemberWithName('AllocationFlags') +# self.InlineData = self.AllocationFlags.GetChildMemberWithName('InlineData') +# self.SecondaryData = self.AllocationFlags.GetChildMemberWithName('SecondaryData') +# self.SecondaryDataData = self.SecondaryData.GetChildMemberWithName('Data') +# self.SecondaryDataDataVal = self.SecondaryDataData.GetValueAsSigned(0) +# except: +# pass + +# def has_children(self): +# return True + +# def UESparseArraySummaryProvider(valobj,dict): +# return 'size=%s' % valobj.GetNumChildren() + +# class UEBitArraySynthProvider: + +# def __init__(self, valobj, dict): +# logger = lldb.formatters.Logger.Logger() +# self.valobj = valobj + +# def num_children(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# NumBitsVal = self.NumBits.GetValueAsSigned(0) +# return NumBitsVal; +# except: +# return 0; + +# def get_child_index(self,name): +# logger = lldb.formatters.Logger.Logger() +# try: +# return int(name.lstrip('[').rstrip(']')) +# except: +# return None + +# def get_child_at_index(self,index): +# logger = lldb.formatters.Logger.Logger() +# logger >> "Retrieving child %s" % index +# if index < 0: +# return None; + +# if index >= self.num_children(): +# return None; + +# if self.SecondaryDataDataVal > 0: +# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataData.GetAddress(), index, index) +# return self.SecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr) +# else: +# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.InlineData.GetAddress(), index, index) +# return self.InlineData.CreateValueFromExpression('[%s]' % index, Expr) + +# return None + +# def update(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# self.SecondaryDataData = None +# self.SecondaryDataDataVal = 0 +# self.NumBits = self.valobj.GetChildMemberWithName('NumBits') +# self.MaxBits = self.valobj.GetChildMemberWithName('MaxBits') +# self.InlineData = self.valobj.GetChildMemberWithName('InlineData') +# self.SecondaryData = self.valobj.GetChildMemberWithName('SecondaryData') +# if self.SecondaryData != None: +# self.SecondaryDataData = self.SecondaryData.GetChildMemberWithName('Data') +# self.SecondaryDataDataVal = self.SecondaryDataData.GetValueAsUnsigned(0) +# except: +# pass + +# def has_children(self): +# return True + +# def UEBitArraySummaryProvider(valobj,dict): +# return 'size=%s' % valobj.GetNumChildren() + +# class UEArraySynthProvider: + +# def __init__(self, valobj, dict): +# logger = lldb.formatters.Logger.Logger() +# self.valobj = valobj + +# def num_children(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# ArrayNumVal = self.ArrayNum.GetValueAsSigned(0) +# return ArrayNumVal + self.NumChildren; +# except: +# return 0; + +# def get_child_index(self,name): +# logger = lldb.formatters.Logger.Logger() +# try: +# return self.NumChildren + int(name.lstrip('[').rstrip(']')) +# except: +# return self.valobj.GetIndexOfChildWithName(name) + +# def get_child_at_index(self,index): +# logger = lldb.formatters.Logger.Logger() +# logger >> "Retrieving child %s" % index +# if index < 0: +# return None; + +# if index < self.NumChildren: +# return self.valobj.GetChildAtIndex(index) +# else: +# index -= self.NumChildren + +# if index >= self.num_children(): +# return None; +# try: +# offset = index * self.ElementTypeSize +# if self.Data != None: +# return self.Data.CreateChildAtOffset('[%s]' % index, offset, self.ElementType) +# elif self.SecondaryDataDataVal > 0: +# return self.SecondaryDataData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType) +# else: +# return self.InlineData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType) +# except: +# return None + +# def extract_type(self): +# logger = lldb.formatters.Logger.Logger() +# ArrayType = self.valobj.GetType().GetUnqualifiedType() +# if ArrayType.IsReferenceType(): +# ArrayType = ArrayType.GetDereferencedType() +# elif ArrayType.IsPointerType(): +# ArrayType = ArrayType.GetPointeeType() + +# if ArrayType.GetNumberOfTemplateArguments() > 0: +# ElementType = ArrayType.GetTemplateArgumentType(0) +# else: +# ElementType = None +# return ElementType + +# def update(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# self.NumChildren = self.valobj.GetNumChildren() +# self.ArrayNum = self.valobj.GetChildMemberWithName('ArrayNum') +# self.ArrayMax = self.valobj.GetChildMemberWithName('ArrayMax') +# self.AllocatorInstance = self.valobj.GetChildMemberWithName('AllocatorInstance') +# if self.AllocatorInstance.GetType().IsReferenceType(): +# self.AllocatorInstance = self.AllocatorInstance.Dereference() +# self.Data = self.AllocatorInstance.GetChildMemberWithName('Data') +# self.InlineData = self.AllocatorInstance.GetChildMemberWithName('InlineData') +# self.SecondaryData = self.AllocatorInstance.GetChildMemberWithName('SecondaryData') +# if self.SecondaryData != None: +# self.SecondaryDataData = self.SecondaryData.GetChildMemberWithName('Data') +# self.SecondaryDataDataVal = self.SecondaryDataData.GetValueAsUnsigned(0) +# else: +# self.SecondaryDataData = None +# self.SecondaryDataDataVal = 0 +# except: +# logger >> "UEArraySynthProvider::update failed accessing members" +# pass +# try: +# self.ElementType = self.extract_type() +# self.ElementTypeSize = self.ElementType.GetByteSize() +# assert self.ElementTypeSize != 0 +# except: +# logger >> "UEArraySynthProvider::update failed accessing element type" +# pass + +# def has_children(self): +# return True + +# def UEArraySummaryProvider(valobj,dict): +# return 'size=%s' % valobj.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) + +# class UESetSynthProvider: + +# def __init__(self, valobj, dict): +# logger = lldb.formatters.Logger.Logger() +# self.valobj = valobj + +# # Can't cast to TSetElement - the template instantiation won't allow it +# Expr = '(size_t)sizeof(FSetElementId) + sizeof(int32)' +# self.TSetElement = self.valobj.CreateValueFromExpression('TSetElement', Expr) + +# def num_children(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# ArrayNumVal = self.ArrayNum.GetValueAsUnsigned(0) +# NumFreeIndicesVal = self.NumFreeIndices.GetValueAsUnsigned(0) +# return ArrayNumVal - NumFreeIndicesVal; +# except: +# return 0; + +# def get_child_index(self,name): +# logger = lldb.formatters.Logger.Logger() +# try: +# return int(name.lstrip('[').rstrip(']')) +# except: +# return 0 + +# def get_child_at_index(self,index): +# logger = lldb.formatters.Logger.Logger() +# logger >> "Retrieving child %s" % index +# if index < 0: +# return None; + +# if index >= self.num_children(): +# return None; +# try: +# offset = index * self.ElementTypeSize +# HasObject = 0 +# if self.SecondaryDataDataVal > 0: +# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataDataVal, index, index) +# HasObject = 1 ##self.AllocationFlagsSecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0) +# else: +# Expr = '(bool)((((int*)%s)[%s/32] >> %s) & 1)' % (self.AllocationFlagsInlineDataAddr, index, index) +# HasObject = 1 ##self.AllocationFlagsInlineData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0) + +# if HasObject == 1: +# return self.AllocatorInstanceData.CreateChildAtOffset('[%s]' % index, offset, self.ElementType) +# else: +# return self.valobj.CreateValueFromExpression('[%s]' % index, '(void*)0xDEADBEEF') +# except: +# return None + +# def extract_type(self): +# logger = lldb.formatters.Logger.Logger() +# ArrayType = self.valobj.GetType().GetUnqualifiedType() +# if ArrayType.IsReferenceType(): +# ArrayType = ArrayType.GetDereferencedType() +# elif ArrayType.IsPointerType(): +# ArrayType = ArrayType.GetPointeeType() +# if ArrayType.GetNumberOfTemplateArguments() > 0: +# ElementType = ArrayType.GetTemplateArgumentType(0) +# else: +# ElementType = None +# return ElementType + +# def update(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# self.Elements = self.valobj.GetChildMemberWithName('Elements') +# self.ElementsData = self.Elements.GetChildMemberWithName('Data') +# self.ArrayNum = self.ElementsData.GetChildMemberWithName('ArrayNum') +# self.NumFreeIndices = self.Elements.GetChildMemberWithName('NumFreeIndices') +# self.AllocationFlags = self.Elements.GetChildMemberWithName('AllocationFlags') +# self.AllocationFlagsAllocatorInstance = self.AllocationFlags.GetChildMemberWithName('AllocatorInstance') +# self.AllocatorInstance = self.ElementsData.GetChildMemberWithName('AllocatorInstance') +# self.AllocatorInstanceData = self.AllocatorInstance.GetChildMemberWithName('Data') +# self.AllocationFlagsInlineData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('InlineData') +# self.AllocationFlagsInlineDataAddr = self.AllocationFlagsInlineData.AddressOf().GetValueAsUnsigned(0) +# self.AllocationFlagsSecondaryData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('SecondaryData') +# self.AllocationFlagsSecondaryDataData = self.AllocationFlagsSecondaryData.GetChildMemberWithName('Data') +# self.SecondaryDataDataVal = self.AllocationFlagsSecondaryDataData.GetValueAsUnsigned(0) +# self.ElementType = self.extract_type() +# # This may fail due to C++ struct padding - will have to check +# self.ElementTypeSize = self.ElementType.GetByteSize() + self.TSetElement.GetValueAsUnsigned(0) +# assert self.ElementTypeSize != 0 +# except: +# pass + +# def has_children(self): +# return True + +# def UESetSummaryProvider(valobj,dict): +# return 'size=%s' % valobj.GetNumChildren() + +# class UEMapSynthProvider: + +# def __init__(self, valobj, dict): +# logger = lldb.formatters.Logger.Logger() +# self.valobj = valobj + +# # Can't cast to TSetElement - the template instantiation won't allow it +# Expr = '(size_t)sizeof(FSetElementId) + sizeof(int32)' +# self.TSetElement = self.valobj.CreateValueFromExpression('TSetElement', Expr) + +# def num_children(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# ArrayNumVal = self.ArrayNum.GetValueAsUnsigned(0) +# NumFreeIndicesVal = self.NumFreeIndices.GetValueAsUnsigned(0) +# return ArrayNumVal - NumFreeIndicesVal; +# except: +# return 0; + +# def get_child_index(self,name): +# logger = lldb.formatters.Logger.Logger() +# try: +# return int(name.lstrip('[').rstrip(']')) +# except: +# return 0 + +# def get_child_at_index(self,index): +# logger = lldb.formatters.Logger.Logger() +# logger >> "Retrieving child %s" % index +# if index < 0: +# return None; + +# if index >= self.num_children(): +# return None; +# try: +# offset = index * self.ElementTypeSize +# HasObject = 0 +# if self.SecondaryDataDataVal != 0: +# Expr = '(bool)((((unsigned int*)%s)[%s/32] >> %s) & 1)' % (self.SecondaryDataDataVal, index, index) +# HasObject = 1 ##self.AllocationFlagsSecondaryDataData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0) +# else: +# Expr = '(bool)((((unsigned int*)%s)[%s/32] >> %s) & 1)' % (self.AllocationFlagsInlineDataAddr, index, index) +# HasObject = 1 ##self.AllocationFlagsInlineData.CreateValueFromExpression('[%s]' % index, Expr).GetValueAsUnsigned(0) + +# if HasObject == 1: +# return self.AllocatorInstanceData.CreateChildAtOffset('[%s]' % index,offset,self.ElementType) +# else: +# return self.valobj.CreateValueFromExpression('[%s]' % index, '(void*)0xDEADBEEF') +# except: +# return None + +# def extract_type(self): +# logger = lldb.formatters.Logger.Logger() +# ArrayType = self.Pairs.GetType().GetUnqualifiedType() +# if ArrayType.IsReferenceType(): +# ArrayType = ArrayType.GetDereferencedType() +# elif ArrayType.IsPointerType(): +# ArrayType = ArrayType.GetPointeeType() +# if ArrayType.GetNumberOfTemplateArguments() > 0: +# ElementType = ArrayType.GetTemplateArgumentType(0) +# else: +# ElementType = None +# return ElementType + +# def update(self): +# logger = lldb.formatters.Logger.Logger() +# try: +# self.Pairs = self.valobj.GetChildMemberWithName('Pairs') +# self.Elements = self.Pairs.GetChildMemberWithName('Elements') +# self.ElementsData = self.Elements.GetChildMemberWithName('Data') +# self.ArrayNum = self.ElementsData.GetChildMemberWithName('ArrayNum') +# self.NumFreeIndices = self.Elements.GetChildMemberWithName('NumFreeIndices') +# self.AllocationFlags = self.Elements.GetChildMemberWithName('AllocationFlags') +# self.AllocationFlagsAllocatorInstance = self.AllocationFlags.GetChildMemberWithName('AllocatorInstance') +# self.AllocatorInstance = self.ElementsData.GetChildMemberWithName('AllocatorInstance') +# self.AllocatorInstanceData = self.AllocatorInstance.GetChildMemberWithName('Data') +# self.AllocationFlagsInlineData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('InlineData') +# self.AllocationFlagsInlineDataAddr = self.AllocationFlagsInlineData.AddressOf().GetValueAsUnsigned(0) +# self.AllocationFlagsSecondaryData = self.AllocationFlagsAllocatorInstance.GetChildMemberWithName('SecondaryData') +# self.AllocationFlagsSecondaryDataData = self.AllocationFlagsSecondaryData.GetChildMemberWithName('Data') +# self.SecondaryDataDataVal = self.AllocationFlagsSecondaryDataData.GetValueAsUnsigned(0) +# self.ElementType = self.extract_type() +# # This may fail due to C++ struct padding - will have to check +# self.ElementTypeSize = self.ElementType.GetByteSize() + self.TSetElement.GetValueAsUnsigned(0) +# assert self.ElementTypeSize != 0 +# except: +# pass + +# def has_children(self): +# return True + +# def UEMapSummaryProvider(valobj,dict): +# return 'size=%s' % valobj.GetNumChildren() + +# class UETObjectPtrSynthProvider: + +# def __init__(self, valobj, dict): +# self.valobj = valobj + +# def num_children(self): +# return self.target.GetNumChildren() if self.resolved else 0 + +# def get_child_index(self, name): +# if not self.resolved: +# return None +# return self.target.GetIndexOfChildWithName(name) + +# def get_child_at_index(self, index): +# if not self.resolved: +# return None +# return self.target.GetChildAtIndex(index) + +# def update(self): +# try: +# self.DebugPtr = self.valobj.GetChildMemberWithName('DebugPtr') +# addr = self.DebugPtr.GetValueAsUnsigned(0) +# self.resolved = addr != 0 and (addr & 1) == 0 +# self.target = self.DebugPtr.Dereference() if self.resolved else None +# except: +# self.resolved = False +# self.target = None + +# def has_children(self): +# return self.resolved and self.target.GetNumChildren() > 0 + +# def UETObjectPtrSummaryProvider(valobj, dict): +# raw = valobj.GetNonSyntheticValue() +# DebugPtr = raw.GetChildMemberWithName('DebugPtr') +# addr = DebugPtr.GetValueAsUnsigned(0) +# if addr == 0: +# return 'nullptr' +# if addr & 1: +# return 'unresolved' +# return DebugPtr.Dereference().GetSummary() or ('0x%x' % addr) + +# class UETSharedPtrSynthProvider: + +# def __init__(self, valobj, dict): +# self.valobj = valobj + +# def num_children(self): +# return self.target.GetNumChildren() if self.valid else 0 + +# def get_child_index(self, name): +# if not self.valid: +# return None +# return self.target.GetIndexOfChildWithName(name) + +# def get_child_at_index(self, index): +# if not self.valid: +# return None +# return self.target.GetChildAtIndex(index) + +# def update(self): +# try: +# self.Object = self.valobj.GetChildMemberWithName('Object') +# addr = self.Object.GetValueAsUnsigned(0) +# self.valid = addr != 0 +# self.target = self.Object.Dereference() if self.valid else None +# except: +# self.valid = False +# self.target = None + +# def has_children(self): +# return self.valid and self.target.GetNumChildren() > 0 + +# def UETSharedPtrSummaryProvider(valobj, dict): +# raw = valobj.GetNonSyntheticValue() +# Object = raw.GetChildMemberWithName('Object') +# addr = Object.GetValueAsUnsigned(0) +# if addr == 0: +# return 'nullptr' +# return Object.Dereference().GetSummary() or ('0x%x' % addr) + +# def _tweakptr_alive(raw): +# """True if a TWeakPtr's referenced object is still alive. + +# Walks WeakReferenceCount -> ReferenceController -> SharedReferenceCount. +# SharedReferenceCount may be std::atomic, in which case we drill into +# the atomic's inner storage. +# """ +# wrc = raw.GetChildMemberWithName('WeakReferenceCount') +# rc = wrc.GetChildMemberWithName('ReferenceController') +# if rc.GetValueAsUnsigned(0) == 0: +# return False +# src = rc.Dereference().GetChildMemberWithName('SharedReferenceCount') +# if not src.IsValid(): +# return False +# count = src.GetValueAsSigned(-1) +# # std::atomic has no direct value; drill into its inner storage +# while count == -1 and src.GetNumChildren() > 0: +# src = src.GetChildAtIndex(0) +# count = src.GetValueAsSigned(-1) +# return count > 0 + +# class UETWeakPtrSynthProvider: + +# def __init__(self, valobj, dict): +# self.valobj = valobj + +# def num_children(self): +# return self.target.GetNumChildren() if self.alive else 0 + +# def get_child_index(self, name): +# if not self.alive: +# return None +# return self.target.GetIndexOfChildWithName(name) + +# def get_child_at_index(self, index): +# if not self.alive: +# return None +# return self.target.GetChildAtIndex(index) + +# def update(self): +# try: +# self.Object = self.valobj.GetChildMemberWithName('Object') +# self.alive = self.Object.GetValueAsUnsigned(0) != 0 and _tweakptr_alive(self.valobj) +# self.target = self.Object.Dereference() if self.alive else None +# except: +# self.alive = False +# self.target = None + +# def has_children(self): +# return self.alive and self.target.GetNumChildren() > 0 + +# def UETWeakPtrSummaryProvider(valobj, dict): +# raw = valobj.GetNonSyntheticValue() +# Object = raw.GetChildMemberWithName('Object') +# addr = Object.GetValueAsUnsigned(0) +# if addr == 0: +# return 'nullptr' +# if not _tweakptr_alive(raw): +# return 'expired' +# return Object.Dereference().GetSummary() or ('0x%x' % addr) def __lldb_init_module(debugger,dict): - debugger.HandleCommand('type summary add -F UEDataFormatter.UETCharSummaryProvider -e TCHAR -w UEDataFormatters') - debugger.HandleCommand('type summary add -F UEDataFormatter.UESignedCharSummaryProvider -e "signed char *" -w UEDataFormatters') - debugger.HandleCommand('type summary add -F UEDataFormatter.UEFStringSummaryProvider -e -x "FString$" -w UEDataFormatters') - debugger.HandleCommand('type summary add -F UEDataFormatter.UEFNameEntrySummaryProvider -e -x "FNameEntry$" -w UEDataFormatters') - debugger.HandleCommand('type summary add -F UEDataFormatter.UEFNameSummaryProvider -e -x "FName$" -w UEDataFormatters') - debugger.HandleCommand('type summary add -F UEDataFormatter.UEFNameSummaryProvider -e -x "FMinimalName$" -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') + 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 fv(path): - """Walk a dotted path through an SBValue tree, auto-dereffing pointers. +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). - Usage in CodeLLDB Watch pane: /py fv("Widget.Object.SCompoundWidget.SWidget") - - Uses GetChildMemberWithName, same as the Variables pane, so real members, - base classes, and synthetic children all work uniformly. + 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. """ - frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() - parts = path.split('.') - val = frame.FindVariable(parts[0]) - if not val.IsValid(): - return "" % parts[0] - for p in parts[1:]: - if val.GetType().IsPointerType(): - val = val.Dereference() - child = val.GetChildMemberWithName(p) + 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(val.GetNumChildren()): - c = val.GetChildAtIndex(i) - if c.GetName() == p: + for i in range(sbv.GetNumChildren()): + c = sbv.GetChildAtIndex(i) + if c.GetName() == name: child = c break if not child.IsValid(): - return "" % (p, val.GetName(), val.GetType().GetName()) - val = child - return val + 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