[Lldb-commits] [lldb] [lldb] Fix empty backtraces for scripted threads with no artificial frames (PR #193387)

via lldb-commits lldb-commits at lists.llvm.org
Tue Apr 21 18:46:19 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Med Ismail Bennani (medismailben)

<details>
<summary>Changes</summary>

Following 86aa43999bec, `ScriptedThread::LoadArtificialStackFrames` unconditionally calls `SetAllFramesFetched` even when the scripted thread provided zero artificial stack frames via `get_stackframes`.

This prevents the normal unwinder from creating frames from the register context, leaving `m_frames` empty. When GetFrameAtIndex(0) is subsequently called, it triggered the follow on assertion builds:

```
  assert(!m_thread.IsValid() && "A valid thread has no frames.")
```

This affects scripted threads that provide register context via `get_register_context` but rely on the unwinder for frame creation rather than overriding `get_stackframes`.

This patch fixes the issue in two parts:
- Only call `SetAllFramesFetched` when artificial frames were actually added, so the normal unwinder can still create frames from the register context when none were provided.
- Replace the assertion with a log message, since a debugger should not crash due to unexpected scripted thread state.

rdar://175019759

---
Full diff: https://github.com/llvm/llvm-project/pull/193387.diff


2 Files Affected:

- (modified) lldb/source/Plugins/Process/scripted/ScriptedThread.cpp (+7-4) 
- (modified) lldb/source/Target/StackFrameList.cpp (+6-3) 


``````````diff
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index eafd4b90ce296..a576df6221514 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -286,10 +286,13 @@ bool ScriptedThread::LoadArtificialStackFrames() {
         synth_frame_sp, /*cfa=*/LLDB_INVALID_ADDRESS);
   }
 
-  // Mark the stack as fully unwound so the regular unwinder doesn't try to
-  // extend it beyond the artificial frames (e.g. by reading lr/fp from the
-  // register context).
-  frames->SetAllFramesFetched();
+  // If artificial frames were provided, mark the stack as fully unwound so the
+  // regular unwinder doesn't try to extend it beyond the artificial frames
+  // (e.g. by reading lr/fp from the register context). If no artificial frames
+  // were provided, let the regular unwinder create frames from the register
+  // context.
+  if (frame_list_idx > 0)
+    frames->SetAllFramesFetched();
 
   return true;
 }
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index 5423785c2fd51..889cbf77b5c54 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -728,9 +728,12 @@ StackFrameSP StackFrameList::GetFrameAtIndex(uint32_t idx) {
       // we thought taking that into account, then reset the current inlined
       // depth and return the real zeroth frame.
       if (m_frames.empty()) {
-        // Why do we have a thread with zero frames, that should not ever
-        // happen...
-        assert(!m_thread.IsValid() && "A valid thread has no frames.");
+        // This can happen with scripted threads that provide register
+        // context but no stack frames. Log it instead of crashing.
+        if (m_thread.IsValid()) {
+          LLDB_LOG(GetLog(LLDBLog::Thread), "valid thread {0} has no frames",
+                   m_thread.GetID());
+        }
       } else {
         ResetCurrentInlinedDepth();
         frame_sp = m_frames[original_idx];

``````````

</details>


https://github.com/llvm/llvm-project/pull/193387


More information about the lldb-commits mailing list