[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