## Summary: This change introduces a `DAPSessionManager` to enable multiple DAP sessions to share debugger instances when needed, for things like child process debugging and some scripting hooks that create dynamically new targets. Changes include: - Add `DAPSessionManager` singleton to track and coordinate all active DAP sessions - Support attaching to an existing target via its globally unique target ID (targetId parameter) - Share debugger instances across sessions when new targets are created dynamically - Refactor event thread management to allow sharing event threads between sessions and move event thread and event thread handlers to `EventHelpers` - Add `eBroadcastBitNewTargetCreated` event to notify when new targets are created - Extract session names from target creation events - Defer debugger initialization from 'initialize' request to 'launch'/'attach' requests. The only time the debugger is used currently in between its creation in `InitializeRequestHandler` and the `Launch` or `Attach` requests is during the `TelemetryDispatcher` destruction call at the end of the `DAP::HandleObject` call, so this is safe. This enables scenarios when new targets are created dynamically so that the debug adapter can automatically start a new debug session for the spawned target while sharing the debugger instance. ## Tests: The refactoring maintains backward compatibility. All existing DAP test cases pass. Also added a few basic unit tests for DAPSessionManager ``` >> ninja DAPTests >> ./tools/lldb/unittests/DAP/DAPTests >>./bin/llvm-lit -v ../llvm-project/lldb/test/API/tools/lldb-dap/ ```
90 lines
3.1 KiB
Python
90 lines
3.1 KiB
Python
"""
|
|
Test lldb-dap start-debugging reverse requests.
|
|
"""
|
|
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
import lldbdap_testcase
|
|
|
|
|
|
class TestDAP_startDebugging(lldbdap_testcase.DAPTestCaseBase):
|
|
def test_startDebugging(self):
|
|
"""
|
|
Tests the "startDebugging" reverse request. It makes sure that the IDE can
|
|
start a child debug session.
|
|
"""
|
|
program = self.getBuildArtifact("a.out")
|
|
source = "main.c"
|
|
self.build_and_launch(program)
|
|
|
|
breakpoint_line = line_number(source, "// breakpoint")
|
|
|
|
self.set_source_breakpoints(source, [breakpoint_line])
|
|
self.continue_to_next_stop()
|
|
self.dap_server.request_evaluate(
|
|
"`lldb-dap start-debugging attach '{\"pid\":321}'", context="repl"
|
|
)
|
|
|
|
self.continue_to_exit()
|
|
|
|
self.assertEqual(
|
|
len(self.dap_server.reverse_requests),
|
|
1,
|
|
"make sure we got a reverse request",
|
|
)
|
|
|
|
request = self.dap_server.reverse_requests[0]
|
|
self.assertEqual(request["arguments"]["configuration"]["pid"], 321)
|
|
self.assertEqual(request["arguments"]["request"], "attach")
|
|
|
|
def test_startDebugging_debugger_reuse(self):
|
|
"""
|
|
Tests that debugger and target IDs can be passed through startDebugging
|
|
for debugger reuse. This verifies the infrastructure for child DAP
|
|
sessions to reuse the parent's debugger and attach to an existing target.
|
|
"""
|
|
program = self.getBuildArtifact("a.out")
|
|
source = "main.c"
|
|
self.build_and_launch(program)
|
|
|
|
breakpoint_line = line_number(source, "// breakpoint")
|
|
self.set_source_breakpoints(source, [breakpoint_line])
|
|
self.continue_to_next_stop()
|
|
|
|
# Use mock IDs to test the infrastructure
|
|
# In a real scenario, these would come from the parent session
|
|
test_debugger_id = 1
|
|
test_target_id = 100
|
|
|
|
# Send a startDebugging request with debuggerId and targetId
|
|
# This simulates creating a child DAP session that reuses the debugger
|
|
self.dap_server.request_evaluate(
|
|
f'`lldb-dap start-debugging attach \'{{"debuggerId":{test_debugger_id},"targetId":{test_target_id}}}\'',
|
|
context="repl",
|
|
)
|
|
|
|
self.continue_to_exit()
|
|
|
|
# Verify the reverse request was sent with the correct IDs
|
|
self.assertEqual(
|
|
len(self.dap_server.reverse_requests),
|
|
1,
|
|
"Should have received one startDebugging reverse request",
|
|
)
|
|
|
|
request = self.dap_server.reverse_requests[0]
|
|
self.assertEqual(request["command"], "startDebugging")
|
|
self.assertEqual(request["arguments"]["request"], "attach")
|
|
|
|
config = request["arguments"]["configuration"]
|
|
self.assertEqual(
|
|
config["debuggerId"],
|
|
test_debugger_id,
|
|
"Reverse request should include debugger ID",
|
|
)
|
|
self.assertEqual(
|
|
config["targetId"],
|
|
test_target_id,
|
|
"Reverse request should include target ID",
|
|
)
|