[Lldb-commits] [lldb] [lldb/crashlog] Fix inlined frames in crashlog scripted process (PR #191132)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Apr 9 01:00:11 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Med Ismail Bennani (medismailben)
<details>
<summary>Changes</summary>
When loading a crashlog using scripted process, inlined frames get lost.
This happens because `ScriptedThread::LoadArtificialStackFrames` creates all frames as concrete frames via `SetFrameAtIndex`, completely bypassing the inline frame synthesis that `StackFrameList::FetchFramesUpTo` normally performs using `GetParentOfInlinedScope`. Since two crashlog frames share the same PC when one is inlined into the other, `CalculateSymbolContext` resolves both to the innermost inlined scope, which causes the containing function to be dropped from the backtrace.
This patch fixes the issue in two parts:
- On the Python side, `resolve_stackframes` now skips frames whose PC matches the next frame's PC. These are inlined frames that LLDB will synthesize from debug info when it processes the concrete frames we provide. Indices are renumbered accordingly, and `len(frames) == 0` is used for first-frame detection.
- On the C++ side, `LoadArtificialStackFrames` now replicates the inline synthesis loop from `FetchFramesUpTo`: after creating each concrete frame, it calls `GetParentOfInlinedScope` in a loop and creates a `StackFrame` for each inlined parent scope.
rdar://154981041
---
Full diff: https://github.com/llvm/llvm-project/pull/191132.diff
5 Files Affected:
- (modified) lldb/examples/python/crashlog_scripted_process.py (+14-3)
- (modified) lldb/source/Plugins/Process/scripted/ScriptedThread.cpp (+45-6)
- (added) lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/a.out.inline.crash (+49)
- (added) lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/inline_test.c (+8)
- (added) lldb/test/Shell/ScriptInterpreter/Python/Crashlog/inline_crashlog.test (+19)
``````````diff
diff --git a/lldb/examples/python/crashlog_scripted_process.py b/lldb/examples/python/crashlog_scripted_process.py
index 6c6eec8d12b96..8c5c6d82829a4 100644
--- a/lldb/examples/python/crashlog_scripted_process.py
+++ b/lldb/examples/python/crashlog_scripted_process.py
@@ -154,14 +154,25 @@ def create_register_ctx(self):
def resolve_stackframes(thread, addr_mask, target):
frames = []
- for frame in thread.frames:
+ for i, frame in enumerate(thread.frames):
frame_pc = frame.pc & addr_mask
- pc = frame_pc if frame.index == 0 or frame_pc == 0 else frame_pc - 1
+ # Skip inlined frames (same PC as the next frame). LLDB will
+ # synthesize inline frames from debug info when it processes the
+ # concrete frames we provide.
+ next_frame = thread.frames[i + 1] if i + 1 < len(thread.frames) else None
+ if next_frame and frame_pc == (next_frame.pc & addr_mask):
+ continue
+ # Don't subtract 1 from the first concrete frame (it's the actual
+ # PC, not a return address) or from null PCs.
+ if len(frames) == 0 or frame_pc == 0:
+ pc = frame_pc
+ else:
+ pc = frame_pc - 1
sym_addr = lldb.SBAddress()
sym_addr.SetLoadAddress(pc, target)
if not sym_addr.IsValid():
continue
- frames.append({"idx": frame.index, "pc": pc})
+ frames.append({"idx": len(frames), "pc": pc})
return frames
def create_stackframes(self):
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index 391137571ab1e..f3eb5ecd21090 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -11,10 +11,12 @@
#include "Plugins/Process/Utility/RegisterContextThreadMemory.h"
#include "Plugins/Process/Utility/StopInfoMachException.h"
+#include "lldb/Symbol/Block.h"
#include "lldb/Target/OperatingSystem.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
#include "lldb/Target/Unwind.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/LLDBLog.h"
@@ -175,7 +177,8 @@ bool ScriptedThread::LoadArtificialStackFrames() {
error, LLDBLog::Thread);
auto create_frame_from_dict =
- [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> {
+ [this, arr_sp](size_t idx,
+ uint32_t frame_list_idx) -> llvm::Expected<StackFrameSP> {
Status error;
std::optional<StructuredData::Dictionary *> maybe_dict =
arr_sp->GetItemAtIndexAsDictionary(idx);
@@ -206,12 +209,12 @@ bool ScriptedThread::LoadArtificialStackFrames() {
lldb::addr_t cfa = LLDB_INVALID_ADDRESS;
bool cfa_is_valid = false;
const bool artificial = false;
- const bool behaves_like_zeroth_frame = false;
+ const bool behaves_like_zeroth_frame = (frame_list_idx == 0);
SymbolContext sc;
symbol_addr.CalculateSymbolContext(&sc);
- return std::make_shared<StackFrame>(shared_from_this(), idx, idx, cfa,
- cfa_is_valid, pc,
+ return std::make_shared<StackFrame>(shared_from_this(), frame_list_idx, idx,
+ cfa, cfa_is_valid, pc,
StackFrame::Kind::Synthetic, artificial,
behaves_like_zeroth_frame, &sc);
};
@@ -247,11 +250,13 @@ bool ScriptedThread::LoadArtificialStackFrames() {
};
StackFrameListSP frames = GetStackFrameList();
+ TargetSP target_sp = CalculateTarget();
+ uint32_t frame_list_idx = 0;
for (size_t idx = 0; idx < arr_size; idx++) {
StackFrameSP synth_frame_sp = nullptr;
- auto frame_from_dict_or_err = create_frame_from_dict(idx);
+ auto frame_from_dict_or_err = create_frame_from_dict(idx, frame_list_idx);
if (!frame_from_dict_or_err) {
auto frame_from_script_obj_or_err = create_frame_from_script_object(idx);
@@ -270,13 +275,47 @@ bool ScriptedThread::LoadArtificialStackFrames() {
synth_frame_sp = *frame_from_dict_or_err;
}
- if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp))
+ if (!frames->SetFrameAtIndex(frame_list_idx, synth_frame_sp))
return ScriptedInterface::ErrorWithMessage<bool>(
LLVM_PRETTY_FUNCTION,
llvm::Twine("Couldn't add frame (" + llvm::Twine(idx) +
llvm::Twine(") to ScriptedThread StackFrameList."))
.str(),
error, LLDBLog::Thread);
+ frame_list_idx++;
+
+ // Synthesize inline frames, mirroring StackFrameList::FetchFramesUpTo().
+ SymbolContext unwind_sc = synth_frame_sp->GetSymbolContext(
+ eSymbolContextBlock | eSymbolContextFunction);
+ if (!unwind_sc.block)
+ continue;
+
+ Address curr_frame_address(
+ synth_frame_sp->GetFrameCodeAddressForSymbolication());
+ SymbolContext next_frame_sc;
+ Address next_frame_address;
+
+ while (unwind_sc.GetParentOfInlinedScope(curr_frame_address, next_frame_sc,
+ next_frame_address)) {
+ next_frame_sc.line_entry.ApplyFileMappings(target_sp);
+ auto inline_frame_sp = std::make_shared<StackFrame>(
+ shared_from_this(), frame_list_idx, idx,
+ synth_frame_sp->GetRegisterContextSP(), /*cfa=*/LLDB_INVALID_ADDRESS,
+ next_frame_address, /*behaves_like_zeroth_frame=*/false,
+ &next_frame_sc);
+
+ if (!frames->SetFrameAtIndex(frame_list_idx, inline_frame_sp))
+ return ScriptedInterface::ErrorWithMessage<bool>(
+ LLVM_PRETTY_FUNCTION,
+ llvm::Twine("Couldn't add inline frame (" +
+ llvm::Twine(frame_list_idx) +
+ llvm::Twine(") to ScriptedThread StackFrameList."))
+ .str(),
+ error, LLDBLog::Thread);
+ frame_list_idx++;
+ unwind_sc = next_frame_sc;
+ curr_frame_address = next_frame_address;
+ }
}
return true;
diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/a.out.inline.crash b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/a.out.inline.crash
new file mode 100644
index 0000000000000..c8579377ca275
--- /dev/null
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/a.out.inline.crash
@@ -0,0 +1,49 @@
+Process: a.out [21606]
+Path: /private/tmp/a.out
+Identifier: a.out
+Version: 0
+Code Type: ARM-64 (Native)
+Parent Process: fish [88883]
+User ID: 501
+
+Date/Time: 2020-11-11 14:47:34.600 -0800
+OS Version: macOS 14.0
+Report Version: 12
+Anonymous UUID: DCEF35CB-68D5-F524-FF13-060901F52EA8
+
+
+Time Awake Since Boot: 400000 seconds
+
+System Integrity Protection: enabled
+
+Crashed Thread: 0 Dispatch queue: com.apple.main-thread
+
+Exception Type: EXC_BAD_ACCESS (SIGSEGV)
+Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000000
+Exception Note: EXC_CORPSE_NOTIFY
+
+Termination Signal: Segmentation fault: 11
+Termination Reason: Namespace SIGNAL, Code 0xb
+Terminating Process: exc handler [21606]
+
+Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
+0 a.out @bar@ foo + 16 (inline_test.c:3)
+1 a.out @bar@ bar + 16 (inline_test.c:6)
+2 a.out @main@ main + 20 (inline_test.c:8)
+3 libdyld.dylib 0x1000000 start + 1
+
+Thread 0 crashed with ARM Thread State (64-bit):
+ x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x0000000000000000
+ x4: 0x0000000000000000 x5: 0x0000000000000000 x6: 0x0000000000000000 x7: 0x0000000000000000
+ x8: 0x0000000000000001 x9: 0x0000000000000000 x10: 0x0000000000000000 x11: 0x0000000000000000
+ x12: 0x0000000000000000 x13: 0x0000000000000000 x14: 0x0000000000000000 x15: 0x0000000000000000
+ x16: 0x0000000000000000 x17: 0x0000000000000000 x18: 0x0000000000000000 x19: 0x0000000000000000
+ x20: 0x0000000000000000 x21: 0x0000000000000000 x22: 0x0000000000000000 x23: 0x0000000000000000
+ x24: 0x0000000000000000 x25: 0x0000000000000000 x26: 0x0000000000000000 x27: 0x0000000000000000
+ x28: 0x0000000000000000 fp: 0x000000016f04ef00 lr: 0x0000000000000000
+ sp: 0x000000016f04eee0 pc: 0x0000000100000354 cpsr: 0x80001000
+ far: 0x0000000000000000 esr: 0x92000046 (Data Abort) byte write Translation fault
+
+Binary Images:
+ 0x100000000 - 0x200000000 +a.out (0) <@UUID@> @EXEC@
+ 0x0 - 0xffffffffffffffff ??? (*) <00000000-0000-0000-0000-000000000000> ???
diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/inline_test.c b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/inline_test.c
new file mode 100644
index 0000000000000..8f2db68a03706
--- /dev/null
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/Inputs/inline_test.c
@@ -0,0 +1,8 @@
+__attribute__((always_inline)) void foo() {
+ int *i = 0;
+ *i = 1;
+}
+
+void bar() { foo(); }
+
+int main(int argc, char **argv) { bar(); }
diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/inline_crashlog.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/inline_crashlog.test
new file mode 100644
index 0000000000000..f588208226e51
--- /dev/null
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/inline_crashlog.test
@@ -0,0 +1,19 @@
+# REQUIRES: python, native && system-darwin
+
+# RUN: %clang_host -g %S/Inputs/inline_test.c -o %t.out
+# RUN: cp %S/Inputs/a.out.inline.crash %t.crash
+# RUN: %python %S/patch-crashlog.py --binary %t.out --crashlog %t.crash --offsets '{"main":20, "bar":16}'
+# RUN: %lldb %t.out \
+# RUN: -o 'command script import lldb.macosx.crashlog' \
+# RUN: -o 'crashlog -a -i %t.crash' \
+# RUN: -o 'thread backtrace' 2>&1 | FileCheck %s
+
+# CHECK: "crashlog" {{.*}} commands have been installed, use the "--help" options on these commands
+
+# Verify that the inlined frame (foo) and its containing function (bar) both
+# appear in the backtrace, even though the crashlog listed them at the same PC.
+# CHECK: (lldb) thread backtrace
+# CHECK-NEXT: * thread #1
+# CHECK-NEXT: * frame #0: {{.*}}out`foo{{.*}} [synthetic] [inlined]
+# CHECK-NEXT: frame #1: {{.*}}out`bar
+# CHECK: frame #{{[0-9]+}}: {{.*}}out`main{{.*}} [synthetic]
``````````
</details>
https://github.com/llvm/llvm-project/pull/191132
More information about the lldb-commits
mailing list