Skip to content

Commit 60b62c6

Browse files
authored
[lldb-dap] Move the event and progress event threads into DAP (NFC) (#139167)
1 parent cc28158 commit 60b62c6

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>{};
@@ -1387,4 +1425,213 @@ protocol::Capabilities DAP::GetCapabilities() {
13871425
return capabilities;
13881426
}
13891427

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