Skip to content

Commit beae076

Browse files
committed
Moving Event Thread out of DAP
1 parent 5f995ef commit beae076

File tree

7 files changed

+387
-287
lines changed

7 files changed

+387
-287
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/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=9999 and targetId=99999 (both invalid).
102+
# Since debugger ID 9999 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/tools/lldb-dap/DAP.cpp

Lines changed: 0 additions & 275 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,281 +1469,6 @@ void DAP::ProgressEventThread() {
14691469
}
14701470
}
14711471

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

lldb/tools/lldb-dap/DAP.h

Lines changed: 3 additions & 7 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,13 +478,6 @@ struct DAP final : public DAPTransport::MessageHandler {
475478

476479
/// Event threads.
477480
/// @{
478-
void EventThread();
479-
void HandleProcessEvent(const lldb::SBEvent &event, bool &process_exited);
480-
void HandleTargetEvent(const lldb::SBEvent &event);
481-
void HandleNewTargetEvent(const lldb::SBEvent &event);
482-
void HandleBreakpointEvent(const lldb::SBEvent &event);
483-
void HandleThreadEvent(const lldb::SBEvent &event);
484-
void HandleDiagnosticEvent(const lldb::SBEvent &event);
485481
void ProgressEventThread();
486482

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