Skip to content

Commit c5afcf0

Browse files
committed
Moving Event Thread out of DAP
1 parent f1c010f commit c5afcf0

File tree

8 files changed

+354
-267
lines changed

8 files changed

+354
-267
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
@@ -76,13 +76,37 @@ def test_by_name_waitFor(self):
7676
self.attach(program=program, waitFor=True)
7777
self.continue_and_verify_pid()
7878

79-
def test_attach_with_invalid_targetId(self):
79+
def test_attach_with_missing_debuggerId_or_targetId(self):
8080
"""
81-
Test that attaching with an invalid targetId fails with the expected
82-
error message.
81+
Test that attaching with only one of debuggerId or targetId specified
82+
fails with the expected error message.
8383
"""
8484
self.build_and_create_debug_adapter()
8585

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

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 & 254 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,260 +1467,6 @@ void DAP::ProgressEventThread() {
14671467
}
14681468
}
14691469

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

lldb/tools/lldb-dap/DAP.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,9 @@ struct DAP final : public DAPTransport::MessageHandler {
417417

418418
lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); }
419419

420+
/// Get the client name for this DAP session.
421+
llvm::StringRef GetClientName() const { return m_client_name; }
422+
420423
void StartEventThread();
421424
void StartProgressEventThread();
422425

@@ -475,8 +478,6 @@ struct DAP final : public DAPTransport::MessageHandler {
475478

476479
/// Event threads.
477480
/// @{
478-
void EventThread();
479-
void HandleThreadEvent(const lldb::SBEvent &event);
480481
void ProgressEventThread();
481482

482483
/// 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)