This is more straight forward refactor of the startup sequence that reverts parts ofba29e60f9a. Unlike my previous attempt, I ended up removing the pending request queue and not including an `AsyncReqeustHandler` because I don't think we actually need that at the moment. The key is that during the startup flow there are 2 parallel operations happening in the DAP that have different triggers. * The `initialize` request is sent and once the response is received the `launch` or `attach` is sent. * When the `initialized` event is recieved the `setBreakpionts` and other config requests are made followed by the `configurationDone` event. I moved the `initialized` event back to happen in the `PostRun` of the `launch` or `attach` request handlers. This ensures that we have a valid target by the time the configuration calls are made. I added also added a few extra validations that to the `configurationeDone` handler to ensure we're in an expected state. I've also fixed up the tests to match the new flow. With the other additional test fixes in087a5d2ec7I think we've narrowed down the main source of test instability that motivated the startup sequence change.
172 lines
5.7 KiB
Python
172 lines
5.7 KiB
Python
"""
|
|
Test lldb-dap stack trace when some of the source paths are missing
|
|
"""
|
|
|
|
from lldbsuite.test.lldbtest import line_number
|
|
import lldbdap_testcase
|
|
from contextlib import contextmanager
|
|
import os
|
|
|
|
|
|
OTHER_C_SOURCE_CODE = """
|
|
int no_source_func(int n) {
|
|
return n + 1; // Break here
|
|
}
|
|
"""
|
|
|
|
|
|
@contextmanager
|
|
def delete_file_on_exit(path):
|
|
try:
|
|
yield path
|
|
finally:
|
|
if os.path.exists(path):
|
|
os.remove(path)
|
|
|
|
|
|
class TestDAP_stackTraceMissingSourcePath(lldbdap_testcase.DAPTestCaseBase):
|
|
def build_and_run_until_breakpoint(self):
|
|
"""
|
|
Build the program and run until the breakpoint is hit, and return the stack frames.
|
|
"""
|
|
other_source_file = "other.c"
|
|
with delete_file_on_exit(other_source_file):
|
|
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")
|
|
self.build_and_launch(program, commandEscapePrefix="")
|
|
|
|
breakpoint_ids = self.set_source_breakpoints(
|
|
other_source_file, [breakpoint_line]
|
|
)
|
|
self.assertEqual(
|
|
len(breakpoint_ids), 1, "expect correct number of breakpoints"
|
|
)
|
|
|
|
self.continue_to_breakpoints(breakpoint_ids)
|
|
|
|
frames = self.get_stackFrames()
|
|
self.assertLessEqual(2, len(frames), "expect at least 2 frames")
|
|
|
|
self.assertIn(
|
|
"path",
|
|
frames[0]["source"],
|
|
"Expect source path to always be in frame (other.c)",
|
|
)
|
|
self.assertIn(
|
|
"path",
|
|
frames[1]["source"],
|
|
"Expect source path in always be in frame (main.c)",
|
|
)
|
|
|
|
return frames
|
|
|
|
def verify_frames_source(
|
|
self, frames, main_frame_assembly: bool, other_frame_assembly: bool
|
|
):
|
|
self.assertLessEqual(2, len(frames), "expect at least 2 frames")
|
|
source_0 = frames[0].get("source")
|
|
source_1 = frames[1].get("source")
|
|
self.assertIsNotNone(source_0, "Expects a source object in frame 0")
|
|
self.assertIsNotNone(source_1, "Expects a source object in frame 1")
|
|
|
|
# it does not always have a path.
|
|
source_0_path: str = source_0.get("path", "")
|
|
source_1_path: str = source_1.get("path", "")
|
|
|
|
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.assertIn(
|
|
"sourceReference",
|
|
source_0,
|
|
"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.assertNotIn(
|
|
"sourceReference",
|
|
source_0,
|
|
"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.assertIn(
|
|
"sourceReference",
|
|
source_1,
|
|
"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.assertNotIn(
|
|
"sourceReference",
|
|
source_1,
|
|
"Expect sourceReference to not be in normal source code frame (main.c)",
|
|
)
|
|
|
|
def test_stopDisassemblyDisplay(self):
|
|
"""
|
|
Test that with with all stop-disassembly-display values you get correct assembly / no assembly source code.
|
|
"""
|
|
self.build_and_run_until_breakpoint()
|
|
frames = self.get_stackFrames()
|
|
self.assertLessEqual(2, len(frames), "expect at least 2 frames")
|
|
|
|
self.assertIn(
|
|
"path",
|
|
frames[0]["source"],
|
|
"Expect source path to always be in frame (other.c)",
|
|
)
|
|
self.assertIn(
|
|
"path",
|
|
frames[1]["source"],
|
|
"Expect source path in always be in frame (main.c)",
|
|
)
|
|
|
|
self.dap_server.request_evaluate(
|
|
"settings set stop-disassembly-display never", context="repl"
|
|
)
|
|
frames = self.get_stackFrames()
|
|
self.verify_frames_source(
|
|
frames, main_frame_assembly=False, other_frame_assembly=False
|
|
)
|
|
|
|
self.dap_server.request_evaluate(
|
|
"settings set stop-disassembly-display always", context="repl"
|
|
)
|
|
frames = self.get_stackFrames()
|
|
self.verify_frames_source(
|
|
frames, main_frame_assembly=True, other_frame_assembly=True
|
|
)
|
|
|
|
self.dap_server.request_evaluate(
|
|
"settings set stop-disassembly-display no-source", context="repl"
|
|
)
|
|
frames = self.get_stackFrames()
|
|
self.verify_frames_source(
|
|
frames, main_frame_assembly=False, other_frame_assembly=True
|
|
)
|
|
|
|
self.dap_server.request_evaluate(
|
|
"settings set stop-disassembly-display no-debuginfo", context="repl"
|
|
)
|
|
frames = self.get_stackFrames()
|
|
self.verify_frames_source(
|
|
frames, main_frame_assembly=False, other_frame_assembly=False
|
|
)
|