8
8
9
9
#include " DAP.h"
10
10
#include " DAPLog.h"
11
+ #include " EventHelper.h"
11
12
#include " Handler/RequestHandler.h"
12
13
#include " Handler/ResponseHandler.h"
13
14
#include " JSONUtils.h"
20
21
#include " lldb/API/SBBreakpoint.h"
21
22
#include " lldb/API/SBCommandInterpreter.h"
22
23
#include " lldb/API/SBCommandReturnObject.h"
24
+ #include " lldb/API/SBEvent.h"
23
25
#include " lldb/API/SBLanguageRuntime.h"
24
26
#include " lldb/API/SBListener.h"
25
27
#include " lldb/API/SBProcess.h"
52
54
#include < mutex>
53
55
#include < optional>
54
56
#include < string>
57
+ #include < thread>
55
58
#include < utility>
56
59
#include < variant>
57
60
@@ -77,6 +80,48 @@ const char DEV_NULL[] = "/dev/null";
77
80
78
81
namespace lldb_dap {
79
82
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
+
80
125
llvm::StringRef DAP::debug_adapter_path = " " ;
81
126
82
127
DAP::DAP (Log *log, const ReplMode default_repl_mode,
@@ -94,13 +139,6 @@ DAP::DAP(Log *log, const ReplMode default_repl_mode,
94
139
95
140
DAP::~DAP () = default ;
96
141
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
-
104
142
void DAP::PopulateExceptionBreakpoints () {
105
143
llvm::call_once (init_exception_breakpoints_flag, [this ]() {
106
144
exception_breakpoints = std::vector<ExceptionBreakpoint>{};
@@ -1387,4 +1425,213 @@ protocol::Capabilities DAP::GetCapabilities() {
1387
1425
return capabilities;
1388
1426
}
1389
1427
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
+
1390
1637
} // namespace lldb_dap
0 commit comments