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.
123 lines
5.0 KiB
Python
123 lines
5.0 KiB
Python
"""
|
|
Test lldb-dap setBreakpoints request
|
|
"""
|
|
|
|
import dap_server
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
from lldbsuite.test import lldbutil
|
|
import lldbdap_testcase
|
|
import re
|
|
|
|
|
|
class TestDAP_module(lldbdap_testcase.DAPTestCaseBase):
|
|
def run_test(self, symbol_basename, expect_debug_info_size):
|
|
program_basename = "a.out.stripped"
|
|
program = self.getBuildArtifact(program_basename)
|
|
self.build_and_launch(program)
|
|
functions = ["foo"]
|
|
breakpoint_ids = self.set_function_breakpoints(functions)
|
|
self.assertEqual(len(breakpoint_ids), len(functions), "expect one breakpoint")
|
|
self.continue_to_breakpoints(breakpoint_ids)
|
|
active_modules = self.dap_server.get_modules()
|
|
program_module = active_modules[program_basename]
|
|
self.assertIn(
|
|
program_basename,
|
|
active_modules,
|
|
"%s module is in active modules" % (program_basename),
|
|
)
|
|
self.assertIn("name", program_module, "make sure name is in module")
|
|
self.assertEqual(program_basename, program_module["name"])
|
|
self.assertIn("path", program_module, "make sure path is in module")
|
|
self.assertEqual(program, program_module["path"])
|
|
self.assertNotIn(
|
|
"symbolFilePath",
|
|
program_module,
|
|
"Make sure a.out.stripped has no debug info",
|
|
)
|
|
symbols_path = self.getBuildArtifact(symbol_basename)
|
|
self.dap_server.request_evaluate(
|
|
"`%s" % ('target symbols add -s "%s" "%s"' % (program, symbols_path)),
|
|
context="repl",
|
|
)
|
|
|
|
def checkSymbolsLoadedWithSize():
|
|
active_modules = self.dap_server.get_modules()
|
|
program_module = active_modules[program_basename]
|
|
self.assertIn("symbolFilePath", program_module)
|
|
self.assertIn(symbols_path, program_module["symbolFilePath"])
|
|
symbol_regex = re.compile(r"[0-9]+(\.[0-9]*)?[KMG]?B")
|
|
return symbol_regex.match(program_module["symbolStatus"])
|
|
|
|
if expect_debug_info_size:
|
|
self.waitUntil(checkSymbolsLoadedWithSize)
|
|
active_modules = self.dap_server.get_modules()
|
|
program_module = active_modules[program_basename]
|
|
self.assertEqual(program_basename, program_module["name"])
|
|
self.assertEqual(program, program_module["path"])
|
|
self.assertIn("addressRange", program_module)
|
|
|
|
# Collect all the module names we saw as events.
|
|
module_new_names = []
|
|
module_changed_names = []
|
|
module_event = self.dap_server.wait_for_event("module", 1)
|
|
while module_event is not None:
|
|
reason = module_event["body"]["reason"]
|
|
if reason == "new":
|
|
module_new_names.append(module_event["body"]["module"]["name"])
|
|
elif reason == "changed":
|
|
module_changed_names.append(module_event["body"]["module"]["name"])
|
|
|
|
module_event = self.dap_server.wait_for_event("module", 1)
|
|
|
|
# Make sure we got an event for every active module.
|
|
self.assertNotEqual(len(module_new_names), 0)
|
|
for module in active_modules:
|
|
self.assertIn(module, module_new_names)
|
|
|
|
# Make sure we got an update event for the program module when the
|
|
# symbols got added.
|
|
self.assertNotEqual(len(module_changed_names), 0)
|
|
self.assertIn(program_module["name"], module_changed_names)
|
|
|
|
@skipIfWindows
|
|
def test_modules(self):
|
|
"""
|
|
Mac or linux.
|
|
|
|
On mac, if we load a.out as our symbol file, we will use DWARF with .o files and we will
|
|
have debug symbols, but we won't see any debug info size because all of the DWARF
|
|
sections are in .o files.
|
|
|
|
On other platforms, we expect a.out to have debug info, so we will expect a size.
|
|
"""
|
|
return self.run_test(
|
|
"a.out", expect_debug_info_size=platform.system() != "Darwin"
|
|
)
|
|
|
|
@skipUnlessDarwin
|
|
def test_modules_dsym(self):
|
|
"""
|
|
Darwin only test with dSYM file.
|
|
|
|
On mac, if we load a.out.dSYM as our symbol file, we will have debug symbols and we
|
|
will have DWARF sections added to the module, so we will expect a size.
|
|
"""
|
|
return self.run_test("a.out.dSYM", expect_debug_info_size=True)
|
|
|
|
@skipIfWindows
|
|
def test_compile_units(self):
|
|
program = self.getBuildArtifact("a.out")
|
|
self.build_and_launch(program)
|
|
source = "main.cpp"
|
|
main_source_path = self.getSourcePath(source)
|
|
breakpoint1_line = line_number(source, "// breakpoint 1")
|
|
lines = [breakpoint1_line]
|
|
breakpoint_ids = self.set_source_breakpoints(source, lines)
|
|
self.continue_to_breakpoints(breakpoint_ids)
|
|
moduleId = self.dap_server.get_modules()["a.out"]["id"]
|
|
response = self.dap_server.request_compileUnits(moduleId)
|
|
self.assertTrue(response["body"])
|
|
cu_paths = [cu["compileUnitPath"] for cu in response["body"]["compileUnits"]]
|
|
self.assertIn(main_source_path, cu_paths, "Real path to main.cpp matches")
|