Skip to content

Commit 7d596d7

Browse files
committed
Moving Event Thread out of DAP
1 parent 265cb6e commit 7d596d7

File tree

8 files changed

+333
-243
lines changed

8 files changed

+333
-243
lines changed

lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,37 @@ def test_by_name_waitFor(self):
7373
self.attach(program=program, waitFor=True)
7474
self.continue_and_verify_pid()
7575

76-
def test_attach_with_invalid_targetId(self):
76+
def test_attach_with_missing_debuggerId_or_targetId(self):
7777
"""
78-
Test that attaching with an invalid targetId fails with the expected
79-
error message.
78+
Test that attaching with only one of debuggerId or targetId specified
79+
fails with the expected error message.
8080
"""
8181
self.build_and_create_debug_adapter()
8282

83+
# Test with only targetId specified (no debuggerId)
8384
resp = self.attach(targetId=99999, expectFailure=True)
8485
self.assertFalse(resp["success"])
85-
self.assertIn("invalid target_id 99999 in attach config", resp["body"]["error"]["format"])
86+
self.assertIn(
87+
"Both debuggerId and targetId must be specified together",
88+
resp["body"]["error"]["format"],
89+
)
90+
91+
def test_attach_with_invalid_debuggerId_and_targetId(self):
92+
"""
93+
Test that attaching with both debuggerId and targetId specified but
94+
invalid fails with an appropriate error message.
95+
"""
96+
self.build_and_create_debug_adapter()
97+
98+
# Attach with both debuggerId=1 and targetId=99999 (both invalid).
99+
# Since debugger ID 1 likely doesn't exist in the global registry,
100+
# we expect a validation error.
101+
resp = self.attach(debuggerId=9999, targetId=99999, expectFailure=True)
102+
self.assertFalse(resp["success"])
103+
error_msg = resp["body"]["error"]["format"]
104+
# Either error is acceptable - both indicate the debugger reuse
105+
# validation is working correctly
106+
self.assertTrue(
107+
"Unable to find existing debugger" in error_msg
108+
or f"Expected debugger/target not found error, got: {error_msg}"
109+
)

lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ def test_startDebugging_debugger_reuse(self):
5959
# Send a startDebugging request with debuggerId and targetId
6060
# This simulates creating a child DAP session that reuses the debugger
6161
self.dap_server.request_evaluate(
62-
f"`lldb-dap start-debugging attach '{{\"debuggerId\":{test_debugger_id},\"targetId\":{test_target_id}}}'",
63-
context="repl"
62+
f'`lldb-dap start-debugging attach \'{{"debuggerId":{test_debugger_id},"targetId":{test_target_id}}}\'',
63+
context="repl",
6464
)
6565

6666
self.continue_to_exit()
@@ -77,7 +77,13 @@ def test_startDebugging_debugger_reuse(self):
7777
self.assertEqual(request["arguments"]["request"], "attach")
7878

7979
config = request["arguments"]["configuration"]
80-
self.assertEqual(config["debuggerId"], test_debugger_id,
81-
"Reverse request should include debugger ID")
82-
self.assertEqual(config["targetId"], test_target_id,
83-
"Reverse request should include target ID")
80+
self.assertEqual(
81+
config["debuggerId"],
82+
test_debugger_id,
83+
"Reverse request should include debugger ID",
84+
)
85+
self.assertEqual(
86+
config["targetId"],
87+
test_target_id,
88+
"Reverse request should include target ID",
89+
)

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 0 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,237 +1455,6 @@ void DAP::ProgressEventThread() {
14551455
}
14561456
}
14571457

1458-
// All events from the debugger, target, process, thread and frames are
1459-
// received in this function that runs in its own thread. We are using a
1460-
// "FILE *" to output packets back to VS Code and they have mutexes in them
1461-
// them prevent multiple threads from writing simultaneously so no locking
1462-
// is required.
1463-
void DAP::EventThread() {
1464-
llvm::set_thread_name("lldb.DAP.client." + m_client_name + ".event_handler");
1465-
lldb::SBEvent event;
1466-
lldb::SBListener listener = debugger.GetListener();
1467-
broadcaster.AddListener(listener, eBroadcastBitStopEventThread);
1468-
debugger.GetBroadcaster().AddListener(
1469-
listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning);
1470-
bool done = false;
1471-
while (!done) {
1472-
if (listener.WaitForEvent(1, event)) {
1473-
const auto event_mask = event.GetType();
1474-
if (lldb::SBProcess::EventIsProcessEvent(event)) {
1475-
lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
1476-
// Find the DAP instance that owns this process's target.
1477-
DAP *dap_instance = DAPSessionManager::FindDAP(process.GetTarget());
1478-
if (!dap_instance) {
1479-
DAP_LOG(log, "Unable to find DAP instance for process {0}",
1480-
process.GetProcessID());
1481-
continue;
1482-
}
1483-
1484-
if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
1485-
auto state = lldb::SBProcess::GetStateFromEvent(event);
1486-
switch (state) {
1487-
case lldb::eStateConnected:
1488-
case lldb::eStateDetached:
1489-
case lldb::eStateInvalid:
1490-
case lldb::eStateUnloaded:
1491-
break;
1492-
case lldb::eStateAttaching:
1493-
case lldb::eStateCrashed:
1494-
case lldb::eStateLaunching:
1495-
case lldb::eStateStopped:
1496-
case lldb::eStateSuspended:
1497-
// Only report a stopped event if the process was not
1498-
// automatically restarted.
1499-
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
1500-
SendStdOutStdErr(*dap_instance, process);
1501-
if (llvm::Error err = SendThreadStoppedEvent(*dap_instance))
1502-
DAP_LOG_ERROR(dap_instance->log, std::move(err),
1503-
"({1}) reporting thread stopped: {0}",
1504-
dap_instance->m_client_name);
1505-
}
1506-
break;
1507-
case lldb::eStateRunning:
1508-
case lldb::eStateStepping:
1509-
dap_instance->WillContinue();
1510-
SendContinuedEvent(*dap_instance);
1511-
break;
1512-
case lldb::eStateExited:
1513-
lldb::SBStream stream;
1514-
process.GetStatus(stream);
1515-
dap_instance->SendOutput(OutputType::Console, stream.GetData());
1516-
1517-
// When restarting, we can get an "exited" event for the process we
1518-
// just killed with the old PID, or even with no PID. In that case
1519-
// we don't have to terminate the session.
1520-
if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID ||
1521-
process.GetProcessID() == dap_instance->restarting_process_id) {
1522-
dap_instance->restarting_process_id = LLDB_INVALID_PROCESS_ID;
1523-
} else {
1524-
// Run any exit LLDB commands the user specified in the
1525-
// launch.json
1526-
dap_instance->RunExitCommands();
1527-
SendProcessExitedEvent(*dap_instance, process);
1528-
dap_instance->SendTerminatedEvent();
1529-
done = true;
1530-
}
1531-
break;
1532-
}
1533-
} else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
1534-
(event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
1535-
SendStdOutStdErr(*dap_instance, process);
1536-
}
1537-
} else if (lldb::SBTarget::EventIsTargetEvent(event)) {
1538-
if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded ||
1539-
event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded ||
1540-
event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded ||
1541-
event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) {
1542-
lldb::SBTarget event_target =
1543-
lldb::SBTarget::GetTargetFromEvent(event);
1544-
// Find the DAP instance that owns this target.
1545-
DAP *dap_instance = DAPSessionManager::FindDAP(event_target);
1546-
if (!dap_instance)
1547-
continue;
1548-
1549-
const uint32_t num_modules =
1550-
lldb::SBTarget::GetNumModulesFromEvent(event);
1551-
const bool remove_module =
1552-
event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded;
1553-
1554-
std::lock_guard<std::mutex> guard(dap_instance->modules_mutex);
1555-
for (uint32_t i = 0; i < num_modules; ++i) {
1556-
lldb::SBModule module =
1557-
lldb::SBTarget::GetModuleAtIndexFromEvent(i, event);
1558-
1559-
std::optional<protocol::Module> p_module =
1560-
CreateModule(dap_instance->target, module, remove_module);
1561-
if (!p_module)
1562-
continue;
1563-
1564-
llvm::StringRef module_id = p_module->id;
1565-
1566-
const bool module_exists =
1567-
dap_instance->modules.contains(module_id);
1568-
if (remove_module && module_exists) {
1569-
dap_instance->modules.erase(module_id);
1570-
dap_instance->Send(protocol::Event{
1571-
"module", ModuleEventBody{std::move(p_module).value(),
1572-
ModuleEventBody::eReasonRemoved}});
1573-
} else if (module_exists) {
1574-
dap_instance->Send(protocol::Event{
1575-
"module", ModuleEventBody{std::move(p_module).value(),
1576-
ModuleEventBody::eReasonChanged}});
1577-
} else if (!remove_module) {
1578-
dap_instance->modules.insert(module_id);
1579-
dap_instance->Send(protocol::Event{
1580-
"module", ModuleEventBody{std::move(p_module).value(),
1581-
ModuleEventBody::eReasonNew}});
1582-
}
1583-
}
1584-
} else if (event_mask & lldb::SBTarget::eBroadcastBitNewTargetCreated) {
1585-
auto target = lldb::SBTarget::GetTargetFromEvent(event);
1586-
1587-
lldb::user_id_t target_id = target.GetGloballyUniqueID();
1588-
lldb::SBDebugger target_debugger = target.GetDebugger();
1589-
int debugger_id = target_debugger.GetID();
1590-
1591-
// We create an attach config that contains the debugger ID and target
1592-
// ID. The new DAP instance will use these IDs to find the existing
1593-
// debugger and target via FindDebuggerWithID and
1594-
// FindTargetByGloballyUniqueID.
1595-
llvm::json::Object attach_config;
1596-
1597-
attach_config.try_emplace("type", "lldb");
1598-
attach_config.try_emplace("debuggerId", debugger_id);
1599-
attach_config.try_emplace("targetId", target_id);
1600-
const char *session_name = target.GetTargetSessionName();
1601-
attach_config.try_emplace("name", session_name);
1602-
1603-
// 2. Construct the main 'startDebugging' request arguments.
1604-
llvm::json::Object start_debugging_args{
1605-
{"request", "attach"},
1606-
{"configuration", std::move(attach_config)}};
1607-
1608-
// Send the request. Note that this is a reverse request, so you don't
1609-
// expect a direct response in the same way as a client request.
1610-
SendReverseRequest<LogFailureResponseHandler>(
1611-
"startDebugging", std::move(start_debugging_args));
1612-
}
1613-
} else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
1614-
lldb::SBBreakpoint bp =
1615-
lldb::SBBreakpoint::GetBreakpointFromEvent(event);
1616-
if (!bp.IsValid())
1617-
continue;
1618-
1619-
lldb::SBTarget event_target = bp.GetTarget();
1620-
1621-
// Find the DAP instance that owns this target.
1622-
DAP *dap_instance = DAPSessionManager::FindDAP(event_target);
1623-
if (!dap_instance)
1624-
continue;
1625-
1626-
if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
1627-
auto event_type =
1628-
lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
1629-
auto breakpoint = Breakpoint(*dap_instance, bp);
1630-
// If the breakpoint was set through DAP, it will have the
1631-
// BreakpointBase::kDAPBreakpointLabel. Regardless of whether
1632-
// locations were added, removed, or resolved, the breakpoint isn't
1633-
// going away and the reason is always "changed".
1634-
if ((event_type & lldb::eBreakpointEventTypeLocationsAdded ||
1635-
event_type & lldb::eBreakpointEventTypeLocationsRemoved ||
1636-
event_type & lldb::eBreakpointEventTypeLocationsResolved) &&
1637-
breakpoint.MatchesName(BreakpointBase::kDAPBreakpointLabel)) {
1638-
// As the DAP client already knows the path of this breakpoint, we
1639-
// don't need to send it back as part of the "changed" event. This
1640-
// avoids sending paths that should be source mapped. Note that
1641-
// CreateBreakpoint doesn't apply source mapping and certain
1642-
// implementation ignore the source part of this event anyway.
1643-
protocol::Breakpoint protocol_bp =
1644-
breakpoint.ToProtocolBreakpoint();
1645-
// "source" is not needed here, unless we add adapter data to be
1646-
// saved by the client.
1647-
if (protocol_bp.source && !protocol_bp.source->adapterData)
1648-
protocol_bp.source = std::nullopt;
1649-
1650-
llvm::json::Object body;
1651-
body.try_emplace("breakpoint", protocol_bp);
1652-
body.try_emplace("reason", "changed");
1653-
1654-
llvm::json::Object bp_event = CreateEventObject("breakpoint");
1655-
bp_event.try_emplace("body", std::move(body));
1656-
1657-
dap_instance->SendJSON(llvm::json::Value(std::move(bp_event)));
1658-
}
1659-
}
1660-
} else if (event_mask & lldb::eBroadcastBitError ||
1661-
event_mask & lldb::eBroadcastBitWarning) {
1662-
// Global debugger events - send to all DAP instances.
1663-
std::vector<DAP *> active_instances =
1664-
DAPSessionManager::GetInstance().GetActiveSessions();
1665-
for (DAP *dap_instance : active_instances) {
1666-
if (!dap_instance)
1667-
continue;
1668-
1669-
lldb::SBStructuredData data =
1670-
lldb::SBDebugger::GetDiagnosticFromEvent(event);
1671-
if (!data.IsValid())
1672-
continue;
1673-
1674-
std::string type = GetStringValue(data.GetValueForKey("type"));
1675-
std::string message = GetStringValue(data.GetValueForKey("message"));
1676-
dap_instance->SendOutput(
1677-
OutputType::Important,
1678-
llvm::formatv("{0}: {1}", type, message).str());
1679-
}
1680-
} else if (event.BroadcasterMatchesRef(broadcaster)) {
1681-
if (event_mask & eBroadcastBitStopEventThread) {
1682-
done = true;
1683-
}
1684-
}
1685-
}
1686-
}
1687-
}
1688-
16891458
std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints(
16901459
const protocol::Source &source,
16911460
const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints) {

lldb/tools/lldb-dap/DAP.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,9 @@ struct DAP final : public DAPTransport::MessageHandler {
423423

424424
lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); }
425425

426+
/// Get the client name for this DAP session.
427+
llvm::StringRef GetClientName() const { return m_client_name; }
428+
426429
void StartEventThread();
427430
void StartProgressEventThread();
428431

@@ -481,7 +484,6 @@ struct DAP final : public DAPTransport::MessageHandler {
481484

482485
/// Event threads.
483486
/// @{
484-
void EventThread();
485487
void ProgressEventThread();
486488

487489
/// Event thread is a shared pointer in case we have a multiple

lldb/tools/lldb-dap/DAPForward.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace lldb {
2828
class SBAttachInfo;
2929
class SBBreakpoint;
3030
class SBBreakpointLocation;
31+
class SBBroadcaster;
3132
class SBCommandInterpreter;
3233
class SBCommandReturnObject;
3334
class SBCommunication;

lldb/tools/lldb-dap/DAPSessionManager.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88
#include "DAPSessionManager.h"
99
#include "DAP.h"
10+
#include "EventHelper.h"
1011
#include "lldb/API/SBBroadcaster.h"
1112
#include "lldb/API/SBEvent.h"
1213
#include "lldb/API/SBTarget.h"
@@ -108,7 +109,8 @@ DAPSessionManager::GetEventThreadForDebugger(lldb::SBDebugger debugger,
108109
// Create a new event thread and store it.
109110
auto new_thread_sp = std::make_shared<ManagedEventThread>(
110111
requesting_dap->broadcaster,
111-
std::thread(&DAP::EventThread, requesting_dap));
112+
std::thread(EventThread, debugger, requesting_dap->broadcaster,
113+
requesting_dap->m_client_name, requesting_dap->log));
112114
m_debugger_event_threads[debugger_id] = new_thread_sp;
113115
return new_thread_sp;
114116
}

0 commit comments

Comments
 (0)