blob: 9688608d696e72eeafec0768870975f818ab6494 [file]
"""
Test lldb-dap stack trace when some of the source paths are missing,
under each `stop-disassembly-display` setting.
"""
import os
from lldbsuite.test.lldbtest import line_number
from lldbsuite.test.tools.lldb_dap.types import LaunchArgs
from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase
from lldbsuite.test.tools.lldb_dap.session_helpers import ThreadContext
OTHER_C_SOURCE_CODE = """
int no_source_func(int n) {
return n + 1; // Break here
}
"""
class TestDAP_stackTraceDisassemblyDisplay(DAPTestCaseBase):
def build_and_run_until_other_c_breakpoint(self):
"""Build, launch, stop at the breakpoint in other.c, and return the
thread context for the stopped thread.
"""
other_source_file = self.getBuildArtifact("other.c")
with open(other_source_file, "w") as f:
f.write(OTHER_C_SOURCE_CODE)
breakpoint_line = line_number(other_source_file, "// Break here")
program = self.getBuildArtifact("a.out")
session = self.build_and_create_session()
with session.configure(LaunchArgs(program, commandEscapePrefix="")) as ctx:
breakpoint_ids = session.resolve_source_breakpoints(
other_source_file, [breakpoint_line]
)
stop_event = session.verify_stopped_on_breakpoint(
breakpoint_ids, after=ctx.process_event
)
# Remove other.c so LLDB sees it as "source unavailable" for the
# subsequent stop-disassembly-display checks.
os.remove(other_source_file)
thread_ctx = session.thread_context_from(stop_event)
frames = [f.frame for f in thread_ctx.frames()]
self.assertLessEqual(2, len(frames), "expect at least 2 frames")
frame_0_source = self.expect_not_none(frames[0].source)
frame_1_source = self.expect_not_none(frames[1].source)
self.assertIsNotNone(
frame_0_source.path, "expect source path to always be in frame (other.c)"
)
self.assertIsNotNone(
frame_1_source.path, "expect source path to always be in frame (main.c)"
)
return session, thread_ctx
def verify_frames_source(
self,
thread_ctx: ThreadContext,
main_frame_assembly: bool,
other_frame_assembly: bool,
):
frames = [f.frame for f in thread_ctx.frames(levels=2)]
self.assertLessEqual(2, len(frames), "expect at least 2 frames")
source_0 = self.expect_not_none(
frames[0].source, "expects a source object in frame 0"
)
source_1 = self.expect_not_none(
frames[1].source, "expects a source object in frame 1"
)
# It does not always have a path.
source_0_path = source_0.path or ""
source_1_path = source_1.path or ""
if other_frame_assembly:
self.assertFalse(
source_0_path.endswith("other.c"),
"expect original source path to not be in unavailable source frame (other.c).",
)
self.assertIsNotNone(
source_0.sourceReference,
"expect sourceReference to be in unavailable source frame (other.c).",
)
else:
self.assertTrue(
source_0_path.endswith("other.c"),
"expect original source path to be in normal source frame (other.c).",
)
self.assertIsNone(
source_0.sourceReference,
"expect sourceReference to not be in normal source frame (other.c).",
)
if main_frame_assembly:
self.assertFalse(
source_1_path.endswith("main.c"),
"expect original source path to not be in unavailable source frame (main.c).",
)
self.assertIsNotNone(
source_1.sourceReference,
"expect sourceReference to be in unavailable source frame (main.c).",
)
else:
self.assertTrue(
source_1_path.endswith("main.c"),
"expect original source path to be in normal source frame (main.c).",
)
self.assertIsNone(
source_1.sourceReference,
"expect sourceReference to not be in normal source code frame (main.c).",
)
def test_stopDisassemblyDisplay(self):
"""With each `stop-disassembly-display` setting, the corresponding frames
carry either an original source path or a sourceReference for assembly."""
session, thread_ctx = self.build_and_run_until_other_c_breakpoint()
# Baseline: both source files are present on disk.
baseline_frames = [f.frame for f in thread_ctx.frames(levels=2)]
self.assertLessEqual(2, len(baseline_frames), "expect at least 2 frames")
self.assertIsNotNone(
baseline_frames[0].source and baseline_frames[0].source.path,
"expect source path to always be in frame (other.c)",
)
self.assertIsNotNone(
baseline_frames[1].source and baseline_frames[1].source.path,
"expect source path to always be in frame (main.c)",
)
session.evaluate("settings set stop-disassembly-display never", context="repl")
self.verify_frames_source(
thread_ctx, main_frame_assembly=False, other_frame_assembly=False
)
session.evaluate("settings set stop-disassembly-display always", context="repl")
self.verify_frames_source(
thread_ctx, main_frame_assembly=True, other_frame_assembly=True
)
session.evaluate(
"settings set stop-disassembly-display no-source", context="repl"
)
self.verify_frames_source(
thread_ctx, main_frame_assembly=False, other_frame_assembly=True
)
session.evaluate(
"settings set stop-disassembly-display no-debuginfo", context="repl"
)
self.verify_frames_source(
thread_ctx, main_frame_assembly=False, other_frame_assembly=False
)
session.continue_to_exit()