[Lldb-commits] [lldb] 16f7938 - [lldb] Fix synthetic frame identity loss during incremental fetches (#191903)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Apr 14 06:42:50 PDT 2026
Author: Alexandre Perez
Date: 2026-04-14T06:42:44-07:00
New Revision: 16f793876e0768f6e322918d1cd99f4d85a2e30d
URL: https://github.com/llvm/llvm-project/commit/16f793876e0768f6e322918d1cd99f4d85a2e30d
DIFF: https://github.com/llvm/llvm-project/commit/16f793876e0768f6e322918d1cd99f4d85a2e30d.diff
LOG: [lldb] Fix synthetic frame identity loss during incremental fetches (#191903)
When `SyntheticStackFrameList::FetchFramesUpTo` is called incrementally,
PC-less synthetic frames can end up with identical `StackID` values.
This happens because `num_synthetic_frames` is reset to zero on each
call, handing out duplicate call frame addresses. Since PC-less frames
all share `LLDB_INVALID_ADDRESS` as their PC, the `StackID` equality
check cannot distinguish them, and `ExecutionContextRef` resolves the
wrong frame.
The fix counts existing synthetic frames in `m_frames` before starting
the fetch loop so new frames receive unique call frame addresses.
Added:
Modified:
lldb/source/Target/StackFrameList.cpp
lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
Removed:
################################################################################
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index fb3d0c5f31cb8..5423785c2fd51 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -70,9 +70,19 @@ SyntheticStackFrameList::SyntheticStackFrameList(
bool SyntheticStackFrameList::FetchFramesUpTo(
uint32_t end_idx, InterruptionControl allow_interrupt) {
- size_t num_synthetic_frames = 0;
// Use the provider to generate frames lazily.
if (m_provider) {
+ // Count how many synthetic frames already exist so we assign unique CFAs
+ // to new ones. This must not be a local initialized to zero — when
+ // FetchFramesUpTo is called incrementally (first for a small range, then
+ // for the full stack), a zero-initialized counter would hand out duplicate
+ // CFA values, creating StackID collisions for PC-less synthetic frames.
+ size_t num_synthetic_frames = 0;
+ for (const auto &f : m_frames) {
+ if (f && f->IsSynthetic())
+ num_synthetic_frames++;
+ }
+
// Keep fetching until we reach end_idx or the provider returns an error.
for (uint32_t idx = m_frames.size(); idx <= end_idx; idx++) {
if (allow_interrupt &&
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
index 577d36f5f0799..1ac74152b0964 100644
--- a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
+++ b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
@@ -1233,4 +1233,30 @@ def collect_stack_changed_thread_ids(count):
collect_stack_changed_thread_ids(len(expected_thread_ids)),
expected_thread_ids,
"All threads should broadcast eBroadcastBitStackChanged on clear",
- )
\ No newline at end of file
+ )
+
+ def test_incremental_fetch_synthetic_frame_identity(self):
+ """Test that incrementally fetched PC-less synthetic frames each get a
+ unique StackID so they resolve to the correct frame."""
+ self.build()
+ target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+ self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False
+ )
+
+ script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py")
+ self.runCmd("command script import " + script_path)
+
+ error = lldb.SBError()
+ target.RegisterScriptedFrameProvider(
+ "test_frame_providers.PythonSourceFrameProvider",
+ lldb.SBStructuredData(),
+ error,
+ )
+ self.assertSuccess(error)
+
+ # Fetch frames one at a time to trigger separate FetchFramesUpTo calls.
+ frame0 = thread.GetFrameAtIndex(0)
+ frame1 = thread.GetFrameAtIndex(1)
+
+ self.assertEqual(frame0.GetFunctionName(), "compute_fibonacci")
+ self.assertEqual(frame1.GetFunctionName(), "process_data")
\ No newline at end of file
More information about the lldb-commits
mailing list