[lldb] Bound a WebAssembly backtrace by the target's maximum depth (#213397)
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.
This commit is contained in:
parent
9466be2c87
commit
56eb9ab867
3 changed files with 55 additions and 3 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<lldb::addr_t> m_frames;
|
||||
bool m_unwind_complete = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Reference in a new issue