[Lldb-commits] [lldb] [lldb] Bound a WebAssembly backtrace by the target's maximum depth (PR #213397)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Jul 31 18:43:57 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Jonas Devlieghere (JDevlieghere)
<details>
<summary>Changes</summary>
Only UnwindLLDB honored target.process.thread.max-backtrace-depth, so UnwindWasm reported every frame the stub sent and a stack that recursed without end was walked to its end.
Bound the frame count by the depth on every query rather than dropping the frames that exceed it. A Wasm call stack arrives whole on the first query, so a depth lowered after that has to still apply, which is when a user reaches for it. The synthetic call frame addresses stay derived from the whole stack, so the order of the frames does not depend on the depth.
---
Full diff: https://github.com/llvm/llvm-project/pull/213397.diff
3 Files Affected:
- (modified) lldb/source/Plugins/Process/wasm/UnwindWasm.cpp (+12-3)
- (modified) lldb/source/Plugins/Process/wasm/UnwindWasm.h (+4)
- (modified) lldb/test/API/functionalities/gdb_remote_client/TestWasm.py (+39)
``````````diff
diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
index 39b784315a9c1..df3b146efca14 100644
--- a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
+++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
@@ -41,7 +41,7 @@ UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) {
uint32_t UnwindWasm::DoGetFrameCount() {
if (m_unwind_complete)
- return m_frames.size();
+ return GetVisibleFrameCount();
m_unwind_complete = true;
m_frames.clear();
@@ -57,7 +57,16 @@ uint32_t UnwindWasm::DoGetFrameCount() {
}
m_frames = *call_stack_pcs;
- return m_frames.size();
+ return GetVisibleFrameCount();
+}
+
+uint32_t UnwindWasm::GetVisibleFrameCount() {
+ // A backtrace goes no deeper than the target asks for, which is what bounds
+ // the walk of a stack that recurses without end. The depth bounds what a
+ // caller is told rather than what is kept, because it can be raised after a
+ // stack has been fetched.
+ return std::min<uint64_t>(m_frames.size(),
+ GetThread().GetMaxBacktraceDepth());
}
bool UnwindWasm::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
@@ -66,7 +75,7 @@ bool UnwindWasm::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
if (m_frames.size() == 0)
DoGetFrameCount();
- if (frame_idx >= m_frames.size())
+ if (frame_idx >= GetVisibleFrameCount())
return false;
behaves_like_zeroth_frame = (frame_idx == 0);
diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.h b/lldb/source/Plugins/Process/wasm/UnwindWasm.h
index ff5e06d23d960..03c7f9643f1f4 100644
--- a/lldb/source/Plugins/Process/wasm/UnwindWasm.h
+++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h
@@ -38,6 +38,10 @@ class UnwindWasm : public lldb_private::Unwind {
DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
private:
+ /// The number of fetched frames a caller is told about, which the target's
+ /// maximum backtrace depth bounds.
+ uint32_t GetVisibleFrameCount();
+
std::vector<lldb::addr_t> m_frames;
bool m_unwind_complete = false;
diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py
index 861691f5b3ddc..114ddd7adce5a 100644
--- a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py
@@ -406,6 +406,45 @@ def test_simple_wasm_debugging_session(self):
self.assertTrue(b.IsValid())
self.assertEqual(b.GetValueAsUnsigned(), 2)
+ @skipIfAsan
+ @skipIfXmlSupportMissing
+ def test_max_backtrace_depth(self):
+ """Test that a Wasm backtrace stops at the depth the target sets, so
+ that a stack recursing without end is not walked to its end."""
+
+ yaml_path = "simple.yaml"
+ yaml_base, _ = os.path.splitext(yaml_path)
+ obj_path = self.getBuildArtifact(yaml_base)
+ self.yaml2obj(yaml_path, obj_path)
+
+ call_stacks = [
+ WasmCallStack(
+ [WasmStackFrame(0x019C), WasmStackFrame(0x01E5), WasmStackFrame(0x01FE)]
+ ),
+ ]
+ self.server.responder = MyResponder(obj_path, "test_wasm", call_stacks)
+
+ target = self.dbg.CreateTarget("")
+ process = self.connect(target, "wasm")
+ lldbutil.expect_state_changes(
+ self, self.dbg.GetListener(), process, [lldb.eStateStopped]
+ )
+
+ thread = process.GetThreadAtIndex(0)
+ self.assertTrue(thread.IsValid())
+ self.assertTrue(thread.GetFrameAtIndex(0).IsValid())
+
+ # A depth set after a stop still applies to the frames a backtrace has
+ # not reported yet, which is when a user lowers it.
+ self.runCmd("settings set target.process.thread.max-backtrace-depth 2")
+
+ # The stub reported three frames, and the innermost two are the ones
+ # kept.
+ self.assertEqual(2, thread.GetNumFrames())
+ self.assertEqual(thread.GetFrameAtIndex(0).GetPC(), LOAD_ADDRESS | 0x019C)
+ self.assertEqual(thread.GetFrameAtIndex(1).GetPC(), LOAD_ADDRESS | 0x01E5)
+ self.assertFalse(thread.GetFrameAtIndex(2).IsValid())
+
@skipIfAsan
@skipIfXmlSupportMissing
def test_read_global(self):
``````````
</details>
https://github.com/llvm/llvm-project/pull/213397
More information about the lldb-commits
mailing list