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"
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"
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
7881namespace 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+
80125llvm::StringRef DAP::debug_adapter_path = " " ;
81126
82127DAP::DAP (Log *log, const ReplMode default_repl_mode,
@@ -94,13 +139,6 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode,
94139
95140DAP::~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-
104142void 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
0 commit comments