diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp index 39b784315a9c..df3b146efca1 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(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 ff5e06d23d96..03c7f9643f1f 100644 --- a/lldb/source/Plugins/Process/wasm/UnwindWasm.h +++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h @@ -38,6 +38,10 @@ protected: 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 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 861691f5b3dd..114ddd7adce5 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 @@ class TestWasm(GDBRemoteTestBase): 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):