Skip to content

Commit de98615

Browse files
committed
[lldb-dap] Move the event and progress event threads into DAP (NFC)
Move the event and progress event threads into the DAP class. Currently both are implemented in the InitializeRequestHandler, but it doesn't really belong there because the threads are not tied to that request, they just launch them.
1 parent b0bf48d commit de98615

File tree

3 files changed

+269
-259
lines changed

3 files changed

+269
-259
lines changed

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 254 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "DAP.h"
1010
#include "DAPLog.h"
11+
#include "EventHelper.h"
1112
#include "Handler/RequestHandler.h"
1213
#include "Handler/ResponseHandler.h"
1314
#include "JSONUtils.h"
@@ -20,6 +21,7 @@
2021
#include "lldb/API/SBBreakpoint.h"
2122
#include "lldb/API/SBCommandInterpreter.h"
2223
#include "lldb/API/SBCommandReturnObject.h"
24+
#include "lldb/API/SBEvent.h"
2325
#include "lldb/API/SBLanguageRuntime.h"
2426
#include "lldb/API/SBListener.h"
2527
#include "lldb/API/SBProcess.h"
@@ -52,6 +54,7 @@
5254
#include <mutex>
5355
#include <optional>
5456
#include <string>
57+
#include <thread>
5558
#include <utility>
5659
#include <variant>
5760

@@ -77,6 +80,48 @@ const char DEV_NULL[] = "/dev/null";
7780

7881
namespace lldb_dap {
7982

83+
static std::string GetStringFromStructuredData(lldb::SBStructuredData &data,
84+
const char *key) {
85+
lldb::SBStructuredData keyValue = data.GetValueForKey(key);
86+
if (!keyValue)
87+
return std::string();
88+
89+
const size_t length = keyValue.GetStringValue(nullptr, 0);
90+
91+
if (length == 0)
92+
return std::string();
93+
94+
std::string str(length + 1, 0);
95+
keyValue.GetStringValue(&str[0], length + 1);
96+
return str;
97+
}
98+
99+
static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data,
100+
const char *key) {
101+
lldb::SBStructuredData keyValue = data.GetValueForKey(key);
102+
103+
if (!keyValue.IsValid())
104+
return 0;
105+
return keyValue.GetUnsignedIntegerValue();
106+
}
107+
108+
static llvm::StringRef GetModuleEventReason(uint32_t event_mask) {
109+
if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded)
110+
return "new";
111+
if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded)
112+
return "removed";
113+
assert(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded ||
114+
event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged);
115+
return "changed";
116+
}
117+
118+
/// Return string with first character capitalized.
119+
static std::string capitalize(llvm::StringRef str) {
120+
if (str.empty())
121+
return "";
122+
return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str();
123+
}
124+
80125
llvm::StringRef DAP::debug_adapter_path = "";
81126

82127
DAP::DAP(Log *log, const ReplMode default_repl_mode,
@@ -94,13 +139,6 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode,
94139

95140
DAP::~DAP() = default;
96141

97-
/// Return string with first character capitalized.
98-
static std::string capitalize(llvm::StringRef str) {
99-
if (str.empty())
100-
return "";
101-
return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str();
102-
}
103-
104142
void DAP::PopulateExceptionBreakpoints() {
105143
llvm::call_once(init_exception_breakpoints_flag, [this]() {
106144
exception_breakpoints = std::vector<ExceptionBreakpoint>{};
@@ -1390,4 +1428,213 @@ protocol::Capabilities DAP::GetCapabilities() {
13901428
return capabilities;
13911429
}
13921430

1431+
void DAP::StartEventThread() {
1432+
event_thread = std::thread(&DAP::EventThread, this);
1433+
}
1434+
1435+
void DAP::StartProgressEventThread() {
1436+
progress_event_thread = std::thread(&DAP::ProgressEventThread, this);
1437+
}
1438+
1439+
void DAP::ProgressEventThread() {
1440+
lldb::SBListener listener("lldb-dap.progress.listener");
1441+
debugger.GetBroadcaster().AddListener(
1442+
listener, lldb::SBDebugger::eBroadcastBitProgress |
1443+
lldb::SBDebugger::eBroadcastBitExternalProgress);
1444+
broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
1445+
lldb::SBEvent event;
1446+
bool done = false;
1447+
while (!done) {
1448+
if (listener.WaitForEvent(1, event)) {
1449+
const auto event_mask = event.GetType();
1450+
if (event.BroadcasterMatchesRef(broadcaster)) {
1451+
if (event_mask & eBroadcastBitStopProgressThread) {
1452+
done = true;
1453+
}
1454+
} else {
1455+
lldb::SBStructuredData data =
1456+
lldb::SBDebugger::GetProgressDataFromEvent(event);
1457+
1458+
const uint64_t progress_id =
1459+
GetUintFromStructuredData(data, "progress_id");
1460+
const uint64_t completed = GetUintFromStructuredData(data, "completed");
1461+
const uint64_t total = GetUintFromStructuredData(data, "total");
1462+
const std::string details =
1463+
GetStringFromStructuredData(data, "details");
1464+
1465+
if (completed == 0) {
1466+
if (total == UINT64_MAX) {
1467+
// This progress is non deterministic and won't get updated until it
1468+
// is completed. Send the "message" which will be the combined title
1469+
// and detail. The only other progress event for thus
1470+
// non-deterministic progress will be the completed event So there
1471+
// will be no need to update the detail.
1472+
const std::string message =
1473+
GetStringFromStructuredData(data, "message");
1474+
SendProgressEvent(progress_id, message.c_str(), completed, total);
1475+
} else {
1476+
// This progress is deterministic and will receive updates,
1477+
// on the progress creation event VSCode will save the message in
1478+
// the create packet and use that as the title, so we send just the
1479+
// title in the progressCreate packet followed immediately by a
1480+
// detail packet, if there is any detail.
1481+
const std::string title =
1482+
GetStringFromStructuredData(data, "title");
1483+
SendProgressEvent(progress_id, title.c_str(), completed, total);
1484+
if (!details.empty())
1485+
SendProgressEvent(progress_id, details.c_str(), completed, total);
1486+
}
1487+
} else {
1488+
// This progress event is either the end of the progress dialog, or an
1489+
// update with possible detail. The "detail" string we send to VS Code
1490+
// will be appended to the progress dialog's initial text from when it
1491+
// was created.
1492+
SendProgressEvent(progress_id, details.c_str(), completed, total);
1493+
}
1494+
}
1495+
}
1496+
}
1497+
}
1498+
1499+
// All events from the debugger, target, process, thread and frames are
1500+
// received in this function that runs in its own thread. We are using a
1501+
// "FILE *" to output packets back to VS Code and they have mutexes in them
1502+
// them prevent multiple threads from writing simultaneously so no locking
1503+
// is required.
1504+
void DAP::EventThread() {
1505+
llvm::set_thread_name(transport.GetClientName() + ".event_handler");
1506+
lldb::SBEvent event;
1507+
lldb::SBListener listener = debugger.GetListener();
1508+
broadcaster.AddListener(listener, eBroadcastBitStopEventThread);
1509+
debugger.GetBroadcaster().AddListener(
1510+
listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning);
1511+
bool done = false;
1512+
while (!done) {
1513+
if (listener.WaitForEvent(1, event)) {
1514+
const auto event_mask = event.GetType();
1515+
if (lldb::SBProcess::EventIsProcessEvent(event)) {
1516+
lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
1517+
if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
1518+
auto state = lldb::SBProcess::GetStateFromEvent(event);
1519+
switch (state) {
1520+
case lldb::eStateConnected:
1521+
case lldb::eStateDetached:
1522+
case lldb::eStateInvalid:
1523+
case lldb::eStateUnloaded:
1524+
break;
1525+
case lldb::eStateAttaching:
1526+
case lldb::eStateCrashed:
1527+
case lldb::eStateLaunching:
1528+
case lldb::eStateStopped:
1529+
case lldb::eStateSuspended:
1530+
// Only report a stopped event if the process was not
1531+
// automatically restarted.
1532+
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
1533+
SendStdOutStdErr(*this, process);
1534+
SendThreadStoppedEvent(*this);
1535+
}
1536+
break;
1537+
case lldb::eStateRunning:
1538+
case lldb::eStateStepping:
1539+
WillContinue();
1540+
SendContinuedEvent(*this);
1541+
break;
1542+
case lldb::eStateExited:
1543+
lldb::SBStream stream;
1544+
process.GetStatus(stream);
1545+
SendOutput(OutputType::Console, stream.GetData());
1546+
1547+
// When restarting, we can get an "exited" event for the process we
1548+
// just killed with the old PID, or even with no PID. In that case
1549+
// we don't have to terminate the session.
1550+
if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID ||
1551+
process.GetProcessID() == restarting_process_id) {
1552+
restarting_process_id = LLDB_INVALID_PROCESS_ID;
1553+
} else {
1554+
// Run any exit LLDB commands the user specified in the
1555+
// launch.json
1556+
RunExitCommands();
1557+
SendProcessExitedEvent(*this, process);
1558+
SendTerminatedEvent();
1559+
done = true;
1560+
}
1561+
break;
1562+
}
1563+
} else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
1564+
(event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
1565+
SendStdOutStdErr(*this, process);
1566+
}
1567+
} else if (lldb::SBTarget::EventIsTargetEvent(event)) {
1568+
if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded ||
1569+
event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded ||
1570+
event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded ||
1571+
event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) {
1572+
llvm::StringRef reason = GetModuleEventReason(event_mask);
1573+
const uint32_t num_modules =
1574+
lldb::SBTarget::GetNumModulesFromEvent(event);
1575+
for (uint32_t i = 0; i < num_modules; ++i) {
1576+
lldb::SBModule module =
1577+
lldb::SBTarget::GetModuleAtIndexFromEvent(i, event);
1578+
if (!module.IsValid())
1579+
continue;
1580+
1581+
llvm::json::Object body;
1582+
body.try_emplace("reason", reason);
1583+
body.try_emplace("module", CreateModule(target, module));
1584+
llvm::json::Object module_event = CreateEventObject("module");
1585+
module_event.try_emplace("body", std::move(body));
1586+
SendJSON(llvm::json::Value(std::move(module_event)));
1587+
}
1588+
}
1589+
} else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
1590+
if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
1591+
auto event_type =
1592+
lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
1593+
auto bp = Breakpoint(
1594+
*this, lldb::SBBreakpoint::GetBreakpointFromEvent(event));
1595+
// If the breakpoint was set through DAP, it will have the
1596+
// BreakpointBase::kDAPBreakpointLabel. Regardless of whether
1597+
// locations were added, removed, or resolved, the breakpoint isn't
1598+
// going away and the reason is always "changed".
1599+
if ((event_type & lldb::eBreakpointEventTypeLocationsAdded ||
1600+
event_type & lldb::eBreakpointEventTypeLocationsRemoved ||
1601+
event_type & lldb::eBreakpointEventTypeLocationsResolved) &&
1602+
bp.MatchesName(BreakpointBase::kDAPBreakpointLabel)) {
1603+
// As the DAP client already knows the path of this breakpoint, we
1604+
// don't need to send it back as part of the "changed" event. This
1605+
// avoids sending paths that should be source mapped. Note that
1606+
// CreateBreakpoint doesn't apply source mapping and certain
1607+
// implementation ignore the source part of this event anyway.
1608+
llvm::json::Value source_bp = CreateBreakpoint(&bp);
1609+
source_bp.getAsObject()->erase("source");
1610+
1611+
llvm::json::Object body;
1612+
body.try_emplace("breakpoint", source_bp);
1613+
body.try_emplace("reason", "changed");
1614+
1615+
llvm::json::Object bp_event = CreateEventObject("breakpoint");
1616+
bp_event.try_emplace("body", std::move(body));
1617+
1618+
SendJSON(llvm::json::Value(std::move(bp_event)));
1619+
}
1620+
}
1621+
} else if (event_mask & lldb::eBroadcastBitError ||
1622+
event_mask & lldb::eBroadcastBitWarning) {
1623+
lldb::SBStructuredData data =
1624+
lldb::SBDebugger::GetDiagnosticFromEvent(event);
1625+
if (!data.IsValid())
1626+
continue;
1627+
std::string type = GetStringValue(data.GetValueForKey("type"));
1628+
std::string message = GetStringValue(data.GetValueForKey("message"));
1629+
SendOutput(OutputType::Important,
1630+
llvm::formatv("{0}: {1}", type, message).str());
1631+
} else if (event.BroadcasterMatchesRef(broadcaster)) {
1632+
if (event_mask & eBroadcastBitStopEventThread) {
1633+
done = true;
1634+
}
1635+
}
1636+
}
1637+
}
1638+
}
1639+
13931640
} // namespace lldb_dap

lldb/tools/lldb-dap/DAP.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,6 @@ struct DAP {
167167
lldb::SBTarget target;
168168
Variables variables;
169169
lldb::SBBroadcaster broadcaster;
170-
std::thread event_thread;
171-
std::thread progress_event_thread;
172170
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
173171
FunctionBreakpointMap function_breakpoints;
174172
InstructionBreakpointMap instruction_breakpoints;
@@ -418,7 +416,19 @@ struct DAP {
418416

419417
lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); }
420418

419+
void StartEventThread();
420+
void StartProgressEventThread();
421+
421422
private:
423+
/// Event threads.
424+
/// @{
425+
void EventThread();
426+
void ProgressEventThread();
427+
428+
std::thread event_thread;
429+
std::thread progress_event_thread;
430+
/// @}
431+
422432
/// Queue for all incoming messages.
423433
std::deque<protocol::Message> m_queue;
424434
std::deque<protocol::Message> m_pending_queue;

0 commit comments

Comments
 (0)