1//===-- DAP.cpp -------------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "DAP.h"
10#include "DAPLog.h"
11#include "EventHelper.h"
12#include "Handler/RequestHandler.h"
13#include "Handler/ResponseHandler.h"
14#include "JSONUtils.h"
15#include "LLDBUtils.h"
16#include "OutputRedirector.h"
17#include "Protocol/ProtocolBase.h"
18#include "Protocol/ProtocolRequests.h"
19#include "Protocol/ProtocolTypes.h"
20#include "Transport.h"
21#include "lldb/API/SBBreakpoint.h"
22#include "lldb/API/SBCommandInterpreter.h"
23#include "lldb/API/SBCommandReturnObject.h"
24#include "lldb/API/SBEvent.h"
25#include "lldb/API/SBLanguageRuntime.h"
26#include "lldb/API/SBListener.h"
27#include "lldb/API/SBProcess.h"
28#include "lldb/API/SBStream.h"
29#include "lldb/Utility/IOObject.h"
30#include "lldb/Utility/Status.h"
31#include "lldb/lldb-defines.h"
32#include "lldb/lldb-enumerations.h"
33#include "lldb/lldb-types.h"
34#include "llvm/ADT/ArrayRef.h"
35#include "llvm/ADT/STLExtras.h"
36#include "llvm/ADT/ScopeExit.h"
37#include "llvm/ADT/StringExtras.h"
38#include "llvm/ADT/StringRef.h"
39#include "llvm/ADT/Twine.h"
40#include "llvm/Support/Chrono.h"
41#include "llvm/Support/Error.h"
42#include "llvm/Support/ErrorHandling.h"
43#include "llvm/Support/FormatVariadic.h"
44#include "llvm/Support/raw_ostream.h"
45#include <algorithm>
46#include <cassert>
47#include <chrono>
48#include <condition_variable>
49#include <cstdarg>
50#include <cstdint>
51#include <cstdio>
52#include <fstream>
53#include <future>
54#include <memory>
55#include <mutex>
56#include <optional>
57#include <string>
58#include <thread>
59#include <utility>
60#include <variant>
61
62#if defined(_WIN32)
63#define NOMINMAX
64#include <fcntl.h>
65#include <io.h>
66#include <windows.h>
67#else
68#include <unistd.h>
69#endif
70
71using namespace lldb_dap;
72using namespace lldb_dap::protocol;
73
74namespace {
75#ifdef _WIN32
76const char DEV_NULL[] = "nul";
77#else
78const char DEV_NULL[] = "/dev/null";
79#endif
80} // namespace
81
82namespace lldb_dap {
83
84static std::string GetStringFromStructuredData(lldb::SBStructuredData &data,
85 const char *key) {
86 lldb::SBStructuredData keyValue = data.GetValueForKey(key);
87 if (!keyValue)
88 return std::string();
89
90 const size_t length = keyValue.GetStringValue(dst: nullptr, dst_len: 0);
91
92 if (length == 0)
93 return std::string();
94
95 std::string str(length + 1, 0);
96 keyValue.GetStringValue(dst: &str[0], dst_len: length + 1);
97 return str;
98}
99
100static uint64_t GetUintFromStructuredData(lldb::SBStructuredData &data,
101 const char *key) {
102 lldb::SBStructuredData keyValue = data.GetValueForKey(key);
103
104 if (!keyValue.IsValid())
105 return 0;
106 return keyValue.GetUnsignedIntegerValue();
107}
108
109/// Return string with first character capitalized.
110static std::string capitalize(llvm::StringRef str) {
111 if (str.empty())
112 return "";
113 return ((llvm::Twine)llvm::toUpper(x: str[0]) + str.drop_front()).str();
114}
115
116llvm::StringRef DAP::debug_adapter_path = "";
117
118DAP::DAP(Log *log, const ReplMode default_repl_mode,
119 std::vector<std::string> pre_init_commands, Transport &transport)
120 : log(log), transport(transport), broadcaster("lldb-dap"),
121 progress_event_reporter(
122 [&](const ProgressEvent &event) { SendJSON(json: event.ToJSON()); }),
123 repl_mode(default_repl_mode) {
124 configuration.preInitCommands = std::move(pre_init_commands);
125 RegisterRequests();
126}
127
128DAP::~DAP() = default;
129
130void DAP::PopulateExceptionBreakpoints() {
131 llvm::call_once(flag&: init_exception_breakpoints_flag, F: [this]() {
132 exception_breakpoints = std::vector<ExceptionBreakpoint>{};
133
134 if (lldb::SBDebugger::SupportsLanguage(language: lldb::eLanguageTypeC_plus_plus)) {
135 exception_breakpoints->emplace_back(args&: *this, args: "cpp_catch", args: "C++ Catch",
136 args: lldb::eLanguageTypeC_plus_plus);
137 exception_breakpoints->emplace_back(args&: *this, args: "cpp_throw", args: "C++ Throw",
138 args: lldb::eLanguageTypeC_plus_plus);
139 }
140 if (lldb::SBDebugger::SupportsLanguage(language: lldb::eLanguageTypeObjC)) {
141 exception_breakpoints->emplace_back(
142 args&: *this, args: "objc_catch", args: "Objective-C Catch", args: lldb::eLanguageTypeObjC);
143 exception_breakpoints->emplace_back(
144 args&: *this, args: "objc_throw", args: "Objective-C Throw", args: lldb::eLanguageTypeObjC);
145 }
146 if (lldb::SBDebugger::SupportsLanguage(language: lldb::eLanguageTypeSwift)) {
147 exception_breakpoints->emplace_back(args&: *this, args: "swift_catch", args: "Swift Catch",
148 args: lldb::eLanguageTypeSwift);
149 exception_breakpoints->emplace_back(args&: *this, args: "swift_throw", args: "Swift Throw",
150 args: lldb::eLanguageTypeSwift);
151 }
152 // Besides handling the hardcoded list of languages from above, we try to
153 // find any other languages that support exception breakpoints using the
154 // SB API.
155 for (int raw_lang = lldb::eLanguageTypeUnknown;
156 raw_lang < lldb::eNumLanguageTypes; ++raw_lang) {
157 lldb::LanguageType lang = static_cast<lldb::LanguageType>(raw_lang);
158
159 // We first discard any languages already handled above.
160 if (lldb::SBLanguageRuntime::LanguageIsCFamily(language: lang) ||
161 lang == lldb::eLanguageTypeSwift)
162 continue;
163
164 if (!lldb::SBDebugger::SupportsLanguage(language: lang))
165 continue;
166
167 const char *name = lldb::SBLanguageRuntime::GetNameForLanguageType(language: lang);
168 if (!name)
169 continue;
170 std::string raw_lang_name = name;
171 std::string capitalized_lang_name = capitalize(str: name);
172
173 if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(language: lang)) {
174 const char *raw_throw_keyword =
175 lldb::SBLanguageRuntime::GetThrowKeywordForLanguage(language: lang);
176 std::string throw_keyword =
177 raw_throw_keyword ? raw_throw_keyword : "throw";
178
179 exception_breakpoints->emplace_back(
180 args&: *this, args: raw_lang_name + "_" + throw_keyword,
181 args: capitalized_lang_name + " " + capitalize(str: throw_keyword), args&: lang);
182 }
183
184 if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(language: lang)) {
185 const char *raw_catch_keyword =
186 lldb::SBLanguageRuntime::GetCatchKeywordForLanguage(language: lang);
187 std::string catch_keyword =
188 raw_catch_keyword ? raw_catch_keyword : "catch";
189
190 exception_breakpoints->emplace_back(
191 args&: *this, args: raw_lang_name + "_" + catch_keyword,
192 args: capitalized_lang_name + " " + capitalize(str: catch_keyword), args&: lang);
193 }
194 }
195 assert(!exception_breakpoints->empty() && "should not be empty");
196 });
197}
198
199ExceptionBreakpoint *DAP::GetExceptionBreakpoint(llvm::StringRef filter) {
200 // PopulateExceptionBreakpoints() is called after g_dap.debugger is created
201 // in a request-initialize.
202 //
203 // But this GetExceptionBreakpoint() method may be called before attaching, in
204 // which case, we may not have populated the filter yet.
205 //
206 // We also cannot call PopulateExceptionBreakpoints() in DAP::DAP() because
207 // we need SBDebugger::Initialize() to have been called before this.
208 //
209 // So just calling PopulateExceptionBreakoints(),which does lazy-populating
210 // seems easiest. Two other options include:
211 // + call g_dap.PopulateExceptionBreakpoints() in lldb-dap.cpp::main()
212 // right after the call to SBDebugger::Initialize()
213 // + Just call PopulateExceptionBreakpoints() to get a fresh list everytime
214 // we query (a bit overkill since it's not likely to change?)
215 PopulateExceptionBreakpoints();
216
217 for (auto &bp : *exception_breakpoints) {
218 if (bp.GetFilter() == filter)
219 return &bp;
220 }
221 return nullptr;
222}
223
224ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
225 // See comment in the other GetExceptionBreakpoint().
226 PopulateExceptionBreakpoints();
227
228 for (auto &bp : *exception_breakpoints) {
229 if (bp.GetID() == bp_id)
230 return &bp;
231 }
232 return nullptr;
233}
234
235llvm::Error DAP::ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr) {
236 in = lldb::SBFile(std::fopen(filename: DEV_NULL, modes: "r"), /*transfer_ownership=*/true);
237
238 if (auto Error = out.RedirectTo(file_override: overrideOut, callback: [this](llvm::StringRef output) {
239 SendOutput(o: OutputType::Console, output);
240 }))
241 return Error;
242
243 if (auto Error = err.RedirectTo(file_override: overrideErr, callback: [this](llvm::StringRef output) {
244 SendOutput(o: OutputType::Console, output);
245 }))
246 return Error;
247
248 return llvm::Error::success();
249}
250
251void DAP::StopEventHandlers() {
252 if (event_thread.joinable()) {
253 broadcaster.BroadcastEventByType(event_type: eBroadcastBitStopEventThread);
254 event_thread.join();
255 }
256 if (progress_event_thread.joinable()) {
257 broadcaster.BroadcastEventByType(event_type: eBroadcastBitStopProgressThread);
258 progress_event_thread.join();
259 }
260}
261
262// Serialize the JSON value into a string and send the JSON packet to
263// the "out" stream.
264void DAP::SendJSON(const llvm::json::Value &json) {
265 // FIXME: Instead of parsing the output message from JSON, pass the `Message`
266 // as parameter to `SendJSON`.
267 Message message;
268 llvm::json::Path::Root root;
269 if (!fromJSON(json, message, root)) {
270 DAP_LOG_ERROR(log, root.getError(), "({1}) encoding failed: {0}",
271 transport.GetClientName());
272 return;
273 }
274 Send(message);
275}
276
277void DAP::Send(const Message &message) {
278 // FIXME: After all the requests have migrated from LegacyRequestHandler >
279 // RequestHandler<> this should be handled in RequestHandler<>::operator().
280 if (auto *resp = std::get_if<Response>(ptr: &message);
281 resp && debugger.InterruptRequested()) {
282 // Clear the interrupt request.
283 debugger.CancelInterruptRequest();
284
285 // If the debugger was interrupted, convert this response into a 'cancelled'
286 // response because we might have a partial result.
287 Response cancelled{/*request_seq=*/resp->request_seq,
288 /*command=*/resp->command,
289 /*success=*/false,
290 /*message=*/eResponseMessageCancelled,
291 /*body=*/std::nullopt};
292 if (llvm::Error err = transport.Write(M: cancelled))
293 DAP_LOG_ERROR(log, std::move(err), "({1}) write failed: {0}",
294 transport.GetClientName());
295 return;
296 }
297
298 if (llvm::Error err = transport.Write(M: message))
299 DAP_LOG_ERROR(log, std::move(err), "({1}) write failed: {0}",
300 transport.GetClientName());
301}
302
303// "OutputEvent": {
304// "allOf": [ { "$ref": "#/definitions/Event" }, {
305// "type": "object",
306// "description": "Event message for 'output' event type. The event
307// indicates that the target has produced some output.",
308// "properties": {
309// "event": {
310// "type": "string",
311// "enum": [ "output" ]
312// },
313// "body": {
314// "type": "object",
315// "properties": {
316// "category": {
317// "type": "string",
318// "description": "The output category. If not specified,
319// 'console' is assumed.",
320// "_enum": [ "console", "stdout", "stderr", "telemetry" ]
321// },
322// "output": {
323// "type": "string",
324// "description": "The output to report."
325// },
326// "variablesReference": {
327// "type": "number",
328// "description": "If an attribute 'variablesReference' exists
329// and its value is > 0, the output contains
330// objects which can be retrieved by passing
331// variablesReference to the VariablesRequest."
332// },
333// "source": {
334// "$ref": "#/definitions/Source",
335// "description": "An optional source location where the output
336// was produced."
337// },
338// "line": {
339// "type": "integer",
340// "description": "An optional source location line where the
341// output was produced."
342// },
343// "column": {
344// "type": "integer",
345// "description": "An optional source location column where the
346// output was produced."
347// },
348// "data": {
349// "type":["array","boolean","integer","null","number","object",
350// "string"],
351// "description": "Optional data to report. For the 'telemetry'
352// category the data will be sent to telemetry, for
353// the other categories the data is shown in JSON
354// format."
355// }
356// },
357// "required": ["output"]
358// }
359// },
360// "required": [ "event", "body" ]
361// }]
362// }
363void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
364 if (output.empty())
365 return;
366
367 const char *category = nullptr;
368 switch (o) {
369 case OutputType::Console:
370 category = "console";
371 break;
372 case OutputType::Important:
373 category = "important";
374 break;
375 case OutputType::Stdout:
376 category = "stdout";
377 break;
378 case OutputType::Stderr:
379 category = "stderr";
380 break;
381 case OutputType::Telemetry:
382 category = "telemetry";
383 break;
384 }
385
386 // Send each line of output as an individual event, including the newline if
387 // present.
388 ::size_t idx = 0;
389 do {
390 ::size_t end = output.find(C: '\n', From: idx);
391 if (end == llvm::StringRef::npos)
392 end = output.size() - 1;
393 llvm::json::Object event(CreateEventObject(event_name: "output"));
394 llvm::json::Object body;
395 body.try_emplace(K: "category", Args&: category);
396 EmplaceSafeString(obj&: body, key: "output", str: output.slice(Start: idx, End: end + 1).str());
397 event.try_emplace(K: "body", Args: std::move(body));
398 SendJSON(json: llvm::json::Value(std::move(event)));
399 idx = end + 1;
400 } while (idx < output.size());
401}
402
403// interface ProgressStartEvent extends Event {
404// event: 'progressStart';
405//
406// body: {
407// /**
408// * An ID that must be used in subsequent 'progressUpdate' and
409// 'progressEnd'
410// * events to make them refer to the same progress reporting.
411// * IDs must be unique within a debug session.
412// */
413// progressId: string;
414//
415// /**
416// * Mandatory (short) title of the progress reporting. Shown in the UI to
417// * describe the long running operation.
418// */
419// title: string;
420//
421// /**
422// * The request ID that this progress report is related to. If specified a
423// * debug adapter is expected to emit
424// * progress events for the long running request until the request has
425// been
426// * either completed or cancelled.
427// * If the request ID is omitted, the progress report is assumed to be
428// * related to some general activity of the debug adapter.
429// */
430// requestId?: number;
431//
432// /**
433// * If true, the request that reports progress may be canceled with a
434// * 'cancel' request.
435// * So this property basically controls whether the client should use UX
436// that
437// * supports cancellation.
438// * Clients that don't support cancellation are allowed to ignore the
439// * setting.
440// */
441// cancellable?: boolean;
442//
443// /**
444// * Optional, more detailed progress message.
445// */
446// message?: string;
447//
448// /**
449// * Optional progress percentage to display (value range: 0 to 100). If
450// * omitted no percentage will be shown.
451// */
452// percentage?: number;
453// };
454// }
455//
456// interface ProgressUpdateEvent extends Event {
457// event: 'progressUpdate';
458//
459// body: {
460// /**
461// * The ID that was introduced in the initial 'progressStart' event.
462// */
463// progressId: string;
464//
465// /**
466// * Optional, more detailed progress message. If omitted, the previous
467// * message (if any) is used.
468// */
469// message?: string;
470//
471// /**
472// * Optional progress percentage to display (value range: 0 to 100). If
473// * omitted no percentage will be shown.
474// */
475// percentage?: number;
476// };
477// }
478//
479// interface ProgressEndEvent extends Event {
480// event: 'progressEnd';
481//
482// body: {
483// /**
484// * The ID that was introduced in the initial 'ProgressStartEvent'.
485// */
486// progressId: string;
487//
488// /**
489// * Optional, more detailed progress message. If omitted, the previous
490// * message (if any) is used.
491// */
492// message?: string;
493// };
494// }
495
496void DAP::SendProgressEvent(uint64_t progress_id, const char *message,
497 uint64_t completed, uint64_t total) {
498 progress_event_reporter.Push(progress_id, message, completed, total);
499}
500
501void __attribute__((format(printf, 3, 4)))
502DAP::SendFormattedOutput(OutputType o, const char *format, ...) {
503 char buffer[1024];
504 va_list args;
505 va_start(args, format);
506 int actual_length = vsnprintf(s: buffer, maxlen: sizeof(buffer), format: format, arg: args);
507 va_end(args);
508 SendOutput(
509 o, output: llvm::StringRef(buffer, std::min<int>(a: actual_length, b: sizeof(buffer))));
510}
511
512ExceptionBreakpoint *DAP::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
513 const auto num = thread.GetStopReasonDataCount();
514 // Check to see if have hit an exception breakpoint and change the
515 // reason to "exception", but only do so if all breakpoints that were
516 // hit are exception breakpoints.
517 ExceptionBreakpoint *exc_bp = nullptr;
518 for (size_t i = 0; i < num; i += 2) {
519 // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
520 // thread.GetStopReasonDataAtIndex(i+1) will return the location
521 // within that breakpoint. We only care about the bp ID so we can
522 // see if this is an exception breakpoint that is getting hit.
523 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(idx: i);
524 exc_bp = GetExceptionBreakpoint(bp_id);
525 // If any breakpoint is not an exception breakpoint, then stop and
526 // report this as a normal breakpoint
527 if (exc_bp == nullptr)
528 return nullptr;
529 }
530 return exc_bp;
531}
532
533lldb::SBThread DAP::GetLLDBThread(lldb::tid_t tid) {
534 return target.GetProcess().GetThreadByID(sb_thread_id: tid);
535}
536
537lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
538 auto tid = GetInteger<int64_t>(obj: arguments, key: "threadId")
539 .value_or(LLDB_INVALID_THREAD_ID);
540 return target.GetProcess().GetThreadByID(sb_thread_id: tid);
541}
542
543lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) {
544 lldb::SBProcess process = target.GetProcess();
545 // Upper 32 bits is the thread index ID
546 lldb::SBThread thread =
547 process.GetThreadByIndexID(index_id: GetLLDBThreadIndexID(dap_frame_id: frame_id));
548 // Lower 32 bits is the frame index
549 return thread.GetFrameAtIndex(idx: GetLLDBFrameID(dap_frame_id: frame_id));
550}
551
552lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
553 const auto frame_id =
554 GetInteger<uint64_t>(obj: arguments, key: "frameId").value_or(UINT64_MAX);
555 return GetLLDBFrame(frame_id);
556}
557
558ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
559 bool partial_expression) {
560 // Check for the escape hatch prefix.
561 if (!expression.empty() &&
562 llvm::StringRef(expression)
563 .starts_with(Prefix: configuration.commandEscapePrefix)) {
564 expression = expression.substr(pos: configuration.commandEscapePrefix.size());
565 return ReplMode::Command;
566 }
567
568 switch (repl_mode) {
569 case ReplMode::Variable:
570 return ReplMode::Variable;
571 case ReplMode::Command:
572 return ReplMode::Command;
573 case ReplMode::Auto:
574 // To determine if the expression is a command or not, check if the first
575 // term is a variable or command. If it's a variable in scope we will prefer
576 // that behavior and give a warning to the user if they meant to invoke the
577 // operation as a command.
578 //
579 // Example use case:
580 // int p and expression "p + 1" > variable
581 // int i and expression "i" > variable
582 // int var and expression "va" > command
583 std::pair<llvm::StringRef, llvm::StringRef> token =
584 llvm::getToken(Source: expression);
585
586 // If the first token is not fully finished yet, we can't
587 // determine whether this will be a variable or a lldb command.
588 if (partial_expression && token.second.empty())
589 return ReplMode::Auto;
590
591 std::string term = token.first.str();
592 lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
593 bool term_is_command = interpreter.CommandExists(cmd: term.c_str()) ||
594 interpreter.UserCommandExists(cmd: term.c_str()) ||
595 interpreter.AliasExists(cmd: term.c_str());
596 bool term_is_variable = frame.FindVariable(var_name: term.c_str()).IsValid();
597
598 // If we have both a variable and command, warn the user about the conflict.
599 if (term_is_command && term_is_variable) {
600 llvm::errs()
601 << "Warning: Expression '" << term
602 << "' is both an LLDB command and variable. It will be evaluated as "
603 "a variable. To evaluate the expression as an LLDB command, use '"
604 << configuration.commandEscapePrefix << "' as a prefix.\n";
605 }
606
607 // Variables take preference to commands in auto, since commands can always
608 // be called using the command_escape_prefix
609 return term_is_variable ? ReplMode::Variable
610 : term_is_command ? ReplMode::Command
611 : ReplMode::Variable;
612 }
613
614 llvm_unreachable("enum cases exhausted.");
615}
616
617bool DAP::RunLLDBCommands(llvm::StringRef prefix,
618 llvm::ArrayRef<std::string> commands) {
619 bool required_command_failed = false;
620 std::string output = ::RunLLDBCommands(
621 debugger, prefix, commands, required_command_failed,
622 /*parse_command_directives*/ true, /*echo_commands*/ true);
623 SendOutput(o: OutputType::Console, output);
624 return !required_command_failed;
625}
626
627static llvm::Error createRunLLDBCommandsErrorMessage(llvm::StringRef category) {
628 return llvm::createStringError(
629 EC: llvm::inconvertibleErrorCode(),
630 S: llvm::formatv(
631 Fmt: "Failed to run {0} commands. See the Debug Console for more details.",
632 Vals&: category)
633 .str()
634 .c_str());
635}
636
637llvm::Error
638DAP::RunAttachCommands(llvm::ArrayRef<std::string> attach_commands) {
639 if (!RunLLDBCommands(prefix: "Running attachCommands:", commands: attach_commands))
640 return createRunLLDBCommandsErrorMessage(category: "attach");
641 return llvm::Error::success();
642}
643
644llvm::Error
645DAP::RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands) {
646 if (!RunLLDBCommands(prefix: "Running launchCommands:", commands: launch_commands))
647 return createRunLLDBCommandsErrorMessage(category: "launch");
648 return llvm::Error::success();
649}
650
651llvm::Error DAP::RunInitCommands() {
652 if (!RunLLDBCommands(prefix: "Running initCommands:", commands: configuration.initCommands))
653 return createRunLLDBCommandsErrorMessage(category: "initCommands");
654 return llvm::Error::success();
655}
656
657llvm::Error DAP::RunPreInitCommands() {
658 if (!RunLLDBCommands(prefix: "Running preInitCommands:",
659 commands: configuration.preInitCommands))
660 return createRunLLDBCommandsErrorMessage(category: "preInitCommands");
661 return llvm::Error::success();
662}
663
664llvm::Error DAP::RunPreRunCommands() {
665 if (!RunLLDBCommands(prefix: "Running preRunCommands:", commands: configuration.preRunCommands))
666 return createRunLLDBCommandsErrorMessage(category: "preRunCommands");
667 return llvm::Error::success();
668}
669
670void DAP::RunPostRunCommands() {
671 RunLLDBCommands(prefix: "Running postRunCommands:", commands: configuration.postRunCommands);
672}
673void DAP::RunStopCommands() {
674 RunLLDBCommands(prefix: "Running stopCommands:", commands: configuration.stopCommands);
675}
676
677void DAP::RunExitCommands() {
678 RunLLDBCommands(prefix: "Running exitCommands:", commands: configuration.exitCommands);
679}
680
681void DAP::RunTerminateCommands() {
682 RunLLDBCommands(prefix: "Running terminateCommands:",
683 commands: configuration.terminateCommands);
684}
685
686lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) {
687 // Grab the name of the program we need to debug and create a target using
688 // the given program as an argument. Executable file can be a source of target
689 // architecture and platform, if they differ from the host. Setting exe path
690 // in launch info is useless because Target.Launch() will not change
691 // architecture and platform, therefore they should be known at the target
692 // creation. We also use target triple and platform from the launch
693 // configuration, if given, since in some cases ELF file doesn't contain
694 // enough information to determine correct arch and platform (or ELF can be
695 // omitted at all), so it is good to leave the user an opportunity to specify
696 // those. Any of those three can be left empty.
697 auto target = this->debugger.CreateTarget(
698 /*filename=*/configuration.program.data(),
699 /*target_triple=*/configuration.targetTriple.data(),
700 /*platform_name=*/configuration.platformName.data(),
701 /*add_dependent_modules=*/true, // Add dependent modules.
702 error);
703
704 return target;
705}
706
707void DAP::SetTarget(const lldb::SBTarget target) {
708 this->target = target;
709
710 if (target.IsValid()) {
711 // Configure breakpoint event listeners for the target.
712 lldb::SBListener listener = this->debugger.GetListener();
713 listener.StartListeningForEvents(
714 broadcaster: this->target.GetBroadcaster(),
715 event_mask: lldb::SBTarget::eBroadcastBitBreakpointChanged |
716 lldb::SBTarget::eBroadcastBitModulesLoaded |
717 lldb::SBTarget::eBroadcastBitModulesUnloaded |
718 lldb::SBTarget::eBroadcastBitSymbolsLoaded |
719 lldb::SBTarget::eBroadcastBitSymbolsChanged);
720 listener.StartListeningForEvents(broadcaster: this->broadcaster,
721 event_mask: eBroadcastBitStopEventThread);
722 }
723}
724
725bool DAP::HandleObject(const Message &M) {
726 TelemetryDispatcher dispatcher(&debugger);
727 dispatcher.Set(key: "client_name", value: transport.GetClientName().str());
728 if (const auto *req = std::get_if<Request>(ptr: &M)) {
729 {
730 std::lock_guard<std::mutex> guard(m_active_request_mutex);
731 m_active_request = req;
732
733 // Clear the interrupt request prior to invoking a handler.
734 if (debugger.InterruptRequested())
735 debugger.CancelInterruptRequest();
736 }
737
738 auto cleanup = llvm::make_scope_exit(F: [&]() {
739 std::scoped_lock<std::mutex> active_request_lock(m_active_request_mutex);
740 m_active_request = nullptr;
741 });
742
743 auto handler_pos = request_handlers.find(Key: req->command);
744 dispatcher.Set(key: "client_data",
745 value: llvm::Twine("request_command:", req->command).str());
746 if (handler_pos != request_handlers.end()) {
747 handler_pos->second->Run(*req);
748 return true; // Success
749 }
750
751 dispatcher.Set(key: "error",
752 value: llvm::Twine("unhandled-command:" + req->command).str());
753 DAP_LOG(log, "({0}) error: unhandled command '{1}'",
754 transport.GetClientName(), req->command);
755 return false; // Fail
756 }
757
758 if (const auto *resp = std::get_if<Response>(ptr: &M)) {
759 std::unique_ptr<ResponseHandler> response_handler;
760 {
761 std::lock_guard<std::mutex> guard(call_mutex);
762 auto inflight = inflight_reverse_requests.find(Val: resp->request_seq);
763 if (inflight != inflight_reverse_requests.end()) {
764 response_handler = std::move(inflight->second);
765 inflight_reverse_requests.erase(I: inflight);
766 }
767 }
768
769 if (!response_handler)
770 response_handler =
771 std::make_unique<UnknownResponseHandler>(args: "", args: resp->request_seq);
772
773 // Result should be given, use null if not.
774 if (resp->success) {
775 (*response_handler)(resp->body);
776 dispatcher.Set(key: "client_data",
777 value: llvm::Twine("response_command:", resp->command).str());
778 } else {
779 llvm::StringRef message = "Unknown error, response failed";
780 if (resp->message) {
781 message =
782 std::visit(visitor: llvm::makeVisitor(
783 Callables: [](const std::string &message) -> llvm::StringRef {
784 return message;
785 },
786 Callables: [](const protocol::ResponseMessage &message)
787 -> llvm::StringRef {
788 switch (message) {
789 case protocol::eResponseMessageCancelled:
790 return "cancelled";
791 case protocol::eResponseMessageNotStopped:
792 return "notStopped";
793 }
794 llvm_unreachable("unknown response message kind.");
795 }),
796 variants: *resp->message);
797 }
798 dispatcher.Set(key: "error", value: message.str());
799
800 (*response_handler)(llvm::createStringError(
801 EC: std::error_code(-1, std::generic_category()), S: message));
802 }
803
804 return true;
805 }
806
807 dispatcher.Set(key: "error", value: "Unsupported protocol message");
808 DAP_LOG(log, "Unsupported protocol message");
809
810 return false;
811}
812
813void DAP::SendTerminatedEvent() {
814 // Prevent races if the process exits while we're being asked to disconnect.
815 llvm::call_once(flag&: terminated_event_flag, F: [&] {
816 RunTerminateCommands();
817 // Send a "terminated" event
818 llvm::json::Object event(CreateTerminatedEventObject(target));
819 SendJSON(json: llvm::json::Value(std::move(event)));
820 });
821}
822
823llvm::Error DAP::Disconnect() { return Disconnect(terminateDebuggee: !is_attach); }
824
825llvm::Error DAP::Disconnect(bool terminateDebuggee) {
826 lldb::SBError error;
827 lldb::SBProcess process = target.GetProcess();
828 auto state = process.GetState();
829 switch (state) {
830 case lldb::eStateInvalid:
831 case lldb::eStateUnloaded:
832 case lldb::eStateDetached:
833 case lldb::eStateExited:
834 break;
835 case lldb::eStateConnected:
836 case lldb::eStateAttaching:
837 case lldb::eStateLaunching:
838 case lldb::eStateStepping:
839 case lldb::eStateCrashed:
840 case lldb::eStateSuspended:
841 case lldb::eStateStopped:
842 case lldb::eStateRunning: {
843 ScopeSyncMode scope_sync_mode(debugger);
844 error = terminateDebuggee ? process.Kill() : process.Detach();
845 break;
846 }
847 }
848
849 SendTerminatedEvent();
850
851 disconnecting = true;
852
853 return ToError(error);
854}
855
856bool DAP::IsCancelled(const protocol::Request &req) {
857 std::lock_guard<std::mutex> guard(m_cancelled_requests_mutex);
858 return m_cancelled_requests.contains(V: req.seq);
859}
860
861void DAP::ClearCancelRequest(const CancelArguments &args) {
862 std::lock_guard<std::mutex> guard(m_cancelled_requests_mutex);
863 if (args.requestId)
864 m_cancelled_requests.erase(V: *args.requestId);
865}
866
867template <typename T>
868static std::optional<T> getArgumentsIfRequest(const Message &pm,
869 llvm::StringLiteral command) {
870 auto *const req = std::get_if<Request>(ptr: &pm);
871 if (!req || req->command != command)
872 return std::nullopt;
873
874 T args;
875 llvm::json::Path::Root root;
876 if (!fromJSON(req->arguments, args, root))
877 return std::nullopt;
878
879 return args;
880}
881
882llvm::Error DAP::Loop() {
883 // Can't use \a std::future<llvm::Error> because it doesn't compile on
884 // Windows.
885 std::future<lldb::SBError> queue_reader =
886 std::async(policy: std::launch::async, fn: [&]() -> lldb::SBError {
887 llvm::set_thread_name(transport.GetClientName() + ".transport_handler");
888 auto cleanup = llvm::make_scope_exit(F: [&]() {
889 // Ensure we're marked as disconnecting when the reader exits.
890 disconnecting = true;
891 m_queue_cv.notify_all();
892 });
893
894 while (!disconnecting) {
895 llvm::Expected<Message> next =
896 transport.Read(timeout: std::chrono::seconds(1));
897 if (next.errorIsA<EndOfFileError>()) {
898 consumeError(Err: next.takeError());
899 break;
900 }
901
902 // If the read timed out, continue to check if we should disconnect.
903 if (next.errorIsA<TimeoutError>()) {
904 consumeError(Err: next.takeError());
905 continue;
906 }
907
908 if (llvm::Error err = next.takeError()) {
909 lldb::SBError errWrapper;
910 errWrapper.SetErrorString(llvm::toString(E: std::move(err)).c_str());
911 return errWrapper;
912 }
913
914 if (const protocol::Request *req =
915 std::get_if<protocol::Request>(ptr: &*next);
916 req && req->arguments == "disconnect")
917 disconnecting = true;
918
919 const std::optional<CancelArguments> cancel_args =
920 getArgumentsIfRequest<CancelArguments>(pm: *next, command: "cancel");
921 if (cancel_args) {
922 {
923 std::lock_guard<std::mutex> guard(m_cancelled_requests_mutex);
924 if (cancel_args->requestId)
925 m_cancelled_requests.insert(V: *cancel_args->requestId);
926 }
927
928 // If a cancel is requested for the active request, make a best
929 // effort attempt to interrupt.
930 std::lock_guard<std::mutex> guard(m_active_request_mutex);
931 if (m_active_request &&
932 cancel_args->requestId == m_active_request->seq) {
933 DAP_LOG(
934 log,
935 "({0}) interrupting inflight request (command={1} seq={2})",
936 transport.GetClientName(), m_active_request->command,
937 m_active_request->seq);
938 debugger.RequestInterrupt();
939 }
940 }
941
942 {
943 std::lock_guard<std::mutex> guard(m_queue_mutex);
944 m_queue.push_back(x: std::move(*next));
945 }
946 m_queue_cv.notify_one();
947 }
948
949 return lldb::SBError();
950 });
951
952 auto cleanup = llvm::make_scope_exit(F: [&]() {
953 out.Stop();
954 err.Stop();
955 StopEventHandlers();
956 });
957
958 while (true) {
959 std::unique_lock<std::mutex> lock(m_queue_mutex);
960 m_queue_cv.wait(lock&: lock, p: [&] { return disconnecting || !m_queue.empty(); });
961
962 if (disconnecting && m_queue.empty())
963 break;
964
965 Message next = m_queue.front();
966 m_queue.pop_front();
967
968 // Unlock while we're processing the event.
969 lock.unlock();
970
971 if (!HandleObject(M: next))
972 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
973 S: "unhandled packet");
974 }
975
976 return ToError(error: queue_reader.get());
977}
978
979lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) {
980 lldb::SBError error;
981 lldb::SBProcess process = target.GetProcess();
982 if (!process.IsValid()) {
983 error.SetErrorString("invalid process");
984 return error;
985 }
986 auto timeout_time =
987 std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
988 while (std::chrono::steady_clock::now() < timeout_time) {
989 const auto state = process.GetState();
990 switch (state) {
991 case lldb::eStateUnloaded:
992 case lldb::eStateAttaching:
993 case lldb::eStateConnected:
994 case lldb::eStateInvalid:
995 case lldb::eStateLaunching:
996 case lldb::eStateRunning:
997 case lldb::eStateStepping:
998 case lldb::eStateSuspended:
999 break;
1000 case lldb::eStateDetached:
1001 error.SetErrorString("process detached during launch or attach");
1002 return error;
1003 case lldb::eStateExited:
1004 error.SetErrorString("process exited during launch or attach");
1005 return error;
1006 case lldb::eStateCrashed:
1007 case lldb::eStateStopped:
1008 return lldb::SBError(); // Success!
1009 }
1010 std::this_thread::sleep_for(rtime: std::chrono::microseconds(250));
1011 }
1012 error.SetErrorString(
1013 llvm::formatv(Fmt: "process failed to stop within {0}", Vals&: seconds)
1014 .str()
1015 .c_str());
1016 return error;
1017}
1018
1019void DAP::ConfigureSourceMaps() {
1020 if (configuration.sourceMap.empty() && configuration.sourcePath.empty())
1021 return;
1022
1023 std::string sourceMapCommand;
1024 llvm::raw_string_ostream strm(sourceMapCommand);
1025 strm << "settings set target.source-map ";
1026
1027 if (!configuration.sourceMap.empty()) {
1028 for (const auto &kv : configuration.sourceMap) {
1029 strm << "\"" << kv.first << "\" \"" << kv.second << "\" ";
1030 }
1031 } else if (!configuration.sourcePath.empty()) {
1032 strm << "\".\" \"" << configuration.sourcePath << "\"";
1033 }
1034
1035 RunLLDBCommands(prefix: "Setting source map:", commands: {sourceMapCommand});
1036}
1037
1038void DAP::SetConfiguration(const protocol::Configuration &config,
1039 bool is_attach) {
1040 configuration = config;
1041 stop_at_entry = config.stopOnEntry;
1042 this->is_attach = is_attach;
1043
1044 if (configuration.customFrameFormat)
1045 SetFrameFormat(*configuration.customFrameFormat);
1046 if (configuration.customThreadFormat)
1047 SetThreadFormat(*configuration.customThreadFormat);
1048}
1049
1050void DAP::SetFrameFormat(llvm::StringRef format) {
1051 lldb::SBError error;
1052 frame_format = lldb::SBFormat(format.str().c_str(), error);
1053 if (error.Fail()) {
1054 SendOutput(o: OutputType::Console,
1055 output: llvm::formatv(
1056 Fmt: "The provided frame format '{0}' couldn't be parsed: {1}\n",
1057 Vals&: format, Vals: error.GetCString())
1058 .str());
1059 }
1060}
1061
1062void DAP::SetThreadFormat(llvm::StringRef format) {
1063 lldb::SBError error;
1064 thread_format = lldb::SBFormat(format.str().c_str(), error);
1065 if (error.Fail()) {
1066 SendOutput(o: OutputType::Console,
1067 output: llvm::formatv(
1068 Fmt: "The provided thread format '{0}' couldn't be parsed: {1}\n",
1069 Vals&: format, Vals: error.GetCString())
1070 .str());
1071 }
1072}
1073
1074InstructionBreakpoint *
1075DAP::GetInstructionBreakpoint(const lldb::break_id_t bp_id) {
1076 for (auto &bp : instruction_breakpoints) {
1077 if (bp.second.GetID() == bp_id)
1078 return &bp.second;
1079 }
1080 return nullptr;
1081}
1082
1083InstructionBreakpoint *
1084DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {
1085 const auto num = thread.GetStopReasonDataCount();
1086 InstructionBreakpoint *inst_bp = nullptr;
1087 for (size_t i = 0; i < num; i += 2) {
1088 // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
1089 // thread.GetStopReasonDataAtIndex(i+1) will return the location
1090 // within that breakpoint. We only care about the bp ID so we can
1091 // see if this is an instruction breakpoint that is getting hit.
1092 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(idx: i);
1093 inst_bp = GetInstructionBreakpoint(bp_id);
1094 // If any breakpoint is not an instruction breakpoint, then stop and
1095 // report this as a normal breakpoint
1096 if (inst_bp == nullptr)
1097 return nullptr;
1098 }
1099 return inst_bp;
1100}
1101
1102protocol::Capabilities DAP::GetCapabilities() {
1103 protocol::Capabilities capabilities;
1104
1105 // Supported capabilities that are not specific to a single request.
1106 capabilities.supportedFeatures = {
1107 protocol::eAdapterFeatureLogPoints,
1108 protocol::eAdapterFeatureSteppingGranularity,
1109 protocol::eAdapterFeatureValueFormattingOptions,
1110 };
1111
1112 // Capabilities associated with specific requests.
1113 for (auto &kv : request_handlers) {
1114 llvm::SmallDenseSet<AdapterFeature, 1> features =
1115 kv.second->GetSupportedFeatures();
1116 capabilities.supportedFeatures.insert(I: features.begin(), E: features.end());
1117 }
1118
1119 // Available filters or options for the setExceptionBreakpoints request.
1120 std::vector<protocol::ExceptionBreakpointsFilter> filters;
1121 for (const auto &exc_bp : *exception_breakpoints)
1122 filters.emplace_back(args: CreateExceptionBreakpointFilter(bp: exc_bp));
1123 capabilities.exceptionBreakpointFilters = std::move(filters);
1124
1125 // FIXME: This should be registered based on the supported languages?
1126 std::vector<std::string> completion_characters;
1127 completion_characters.emplace_back(args: ".");
1128 // FIXME: I wonder if we should remove this key... its very aggressive
1129 // triggering and accepting completions.
1130 completion_characters.emplace_back(args: " ");
1131 completion_characters.emplace_back(args: "\t");
1132 capabilities.completionTriggerCharacters = std::move(completion_characters);
1133
1134 // Put in non-DAP specification lldb specific information.
1135 capabilities.lldbExtVersion = debugger.GetVersionString();
1136
1137 return capabilities;
1138}
1139
1140void DAP::StartEventThread() {
1141 event_thread = std::thread(&DAP::EventThread, this);
1142}
1143
1144void DAP::StartProgressEventThread() {
1145 progress_event_thread = std::thread(&DAP::ProgressEventThread, this);
1146}
1147
1148void DAP::ProgressEventThread() {
1149 lldb::SBListener listener("lldb-dap.progress.listener");
1150 debugger.GetBroadcaster().AddListener(
1151 listener, event_mask: lldb::SBDebugger::eBroadcastBitProgress |
1152 lldb::SBDebugger::eBroadcastBitExternalProgress);
1153 broadcaster.AddListener(listener, event_mask: eBroadcastBitStopProgressThread);
1154 lldb::SBEvent event;
1155 bool done = false;
1156 while (!done) {
1157 if (listener.WaitForEvent(num_seconds: 1, event)) {
1158 const auto event_mask = event.GetType();
1159 if (event.BroadcasterMatchesRef(broadcaster)) {
1160 if (event_mask & eBroadcastBitStopProgressThread) {
1161 done = true;
1162 }
1163 } else {
1164 lldb::SBStructuredData data =
1165 lldb::SBDebugger::GetProgressDataFromEvent(event);
1166
1167 const uint64_t progress_id =
1168 GetUintFromStructuredData(data, key: "progress_id");
1169 const uint64_t completed = GetUintFromStructuredData(data, key: "completed");
1170 const uint64_t total = GetUintFromStructuredData(data, key: "total");
1171 const std::string details =
1172 GetStringFromStructuredData(data, key: "details");
1173
1174 if (completed == 0) {
1175 if (total == UINT64_MAX) {
1176 // This progress is non deterministic and won't get updated until it
1177 // is completed. Send the "message" which will be the combined title
1178 // and detail. The only other progress event for thus
1179 // non-deterministic progress will be the completed event So there
1180 // will be no need to update the detail.
1181 const std::string message =
1182 GetStringFromStructuredData(data, key: "message");
1183 SendProgressEvent(progress_id, message: message.c_str(), completed, total);
1184 } else {
1185 // This progress is deterministic and will receive updates,
1186 // on the progress creation event VSCode will save the message in
1187 // the create packet and use that as the title, so we send just the
1188 // title in the progressCreate packet followed immediately by a
1189 // detail packet, if there is any detail.
1190 const std::string title =
1191 GetStringFromStructuredData(data, key: "title");
1192 SendProgressEvent(progress_id, message: title.c_str(), completed, total);
1193 if (!details.empty())
1194 SendProgressEvent(progress_id, message: details.c_str(), completed, total);
1195 }
1196 } else {
1197 // This progress event is either the end of the progress dialog, or an
1198 // update with possible detail. The "detail" string we send to VS Code
1199 // will be appended to the progress dialog's initial text from when it
1200 // was created.
1201 SendProgressEvent(progress_id, message: details.c_str(), completed, total);
1202 }
1203 }
1204 }
1205 }
1206}
1207
1208// All events from the debugger, target, process, thread and frames are
1209// received in this function that runs in its own thread. We are using a
1210// "FILE *" to output packets back to VS Code and they have mutexes in them
1211// them prevent multiple threads from writing simultaneously so no locking
1212// is required.
1213void DAP::EventThread() {
1214 llvm::set_thread_name(transport.GetClientName() + ".event_handler");
1215 lldb::SBEvent event;
1216 lldb::SBListener listener = debugger.GetListener();
1217 broadcaster.AddListener(listener, event_mask: eBroadcastBitStopEventThread);
1218 debugger.GetBroadcaster().AddListener(
1219 listener, event_mask: lldb::eBroadcastBitError | lldb::eBroadcastBitWarning);
1220 bool done = false;
1221 while (!done) {
1222 if (listener.WaitForEvent(num_seconds: 1, event)) {
1223 const auto event_mask = event.GetType();
1224 if (lldb::SBProcess::EventIsProcessEvent(event)) {
1225 lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
1226 if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
1227 auto state = lldb::SBProcess::GetStateFromEvent(event);
1228 switch (state) {
1229 case lldb::eStateConnected:
1230 case lldb::eStateDetached:
1231 case lldb::eStateInvalid:
1232 case lldb::eStateUnloaded:
1233 break;
1234 case lldb::eStateAttaching:
1235 case lldb::eStateCrashed:
1236 case lldb::eStateLaunching:
1237 case lldb::eStateStopped:
1238 case lldb::eStateSuspended:
1239 // Only report a stopped event if the process was not
1240 // automatically restarted.
1241 if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
1242 SendStdOutStdErr(dap&: *this, process);
1243 if (llvm::Error err = SendThreadStoppedEvent(dap&: *this))
1244 DAP_LOG_ERROR(log, std::move(err),
1245 "({1}) reporting thread stopped: {0}",
1246 transport.GetClientName());
1247 }
1248 break;
1249 case lldb::eStateRunning:
1250 case lldb::eStateStepping:
1251 WillContinue();
1252 SendContinuedEvent(dap&: *this);
1253 break;
1254 case lldb::eStateExited:
1255 lldb::SBStream stream;
1256 process.GetStatus(status&: stream);
1257 SendOutput(o: OutputType::Console, output: stream.GetData());
1258
1259 // When restarting, we can get an "exited" event for the process we
1260 // just killed with the old PID, or even with no PID. In that case
1261 // we don't have to terminate the session.
1262 if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID ||
1263 process.GetProcessID() == restarting_process_id) {
1264 restarting_process_id = LLDB_INVALID_PROCESS_ID;
1265 } else {
1266 // Run any exit LLDB commands the user specified in the
1267 // launch.json
1268 RunExitCommands();
1269 SendProcessExitedEvent(dap&: *this, process);
1270 SendTerminatedEvent();
1271 done = true;
1272 }
1273 break;
1274 }
1275 } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
1276 (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
1277 SendStdOutStdErr(dap&: *this, process);
1278 }
1279 } else if (lldb::SBTarget::EventIsTargetEvent(event)) {
1280 if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded ||
1281 event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded ||
1282 event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded ||
1283 event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) {
1284 const uint32_t num_modules =
1285 lldb::SBTarget::GetNumModulesFromEvent(event);
1286 std::lock_guard<std::mutex> guard(modules_mutex);
1287 for (uint32_t i = 0; i < num_modules; ++i) {
1288 lldb::SBModule module =
1289 lldb::SBTarget::GetModuleAtIndexFromEvent(idx: i, event);
1290 if (!module.IsValid())
1291 continue;
1292 llvm::StringRef module_id = module.GetUUIDString();
1293 if (module_id.empty())
1294 continue;
1295
1296 llvm::StringRef reason;
1297 bool id_only = false;
1298 if (modules.contains(key: module_id)) {
1299 if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) {
1300 modules.erase(Key: module_id);
1301 reason = "removed";
1302 id_only = true;
1303 } else {
1304 reason = "changed";
1305 }
1306 } else {
1307 modules.insert(key: module_id);
1308 reason = "new";
1309 }
1310
1311 llvm::json::Object body;
1312 body.try_emplace(K: "reason", Args&: reason);
1313 body.try_emplace(K: "module", Args: CreateModule(target, module, id_only));
1314 llvm::json::Object module_event = CreateEventObject(event_name: "module");
1315 module_event.try_emplace(K: "body", Args: std::move(body));
1316 SendJSON(json: llvm::json::Value(std::move(module_event)));
1317 }
1318 }
1319 } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
1320 if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
1321 auto event_type =
1322 lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
1323 auto bp = Breakpoint(
1324 *this, lldb::SBBreakpoint::GetBreakpointFromEvent(event));
1325 // If the breakpoint was set through DAP, it will have the
1326 // BreakpointBase::kDAPBreakpointLabel. Regardless of whether
1327 // locations were added, removed, or resolved, the breakpoint isn't
1328 // going away and the reason is always "changed".
1329 if ((event_type & lldb::eBreakpointEventTypeLocationsAdded ||
1330 event_type & lldb::eBreakpointEventTypeLocationsRemoved ||
1331 event_type & lldb::eBreakpointEventTypeLocationsResolved) &&
1332 bp.MatchesName(name: BreakpointBase::kDAPBreakpointLabel)) {
1333 // As the DAP client already knows the path of this breakpoint, we
1334 // don't need to send it back as part of the "changed" event. This
1335 // avoids sending paths that should be source mapped. Note that
1336 // CreateBreakpoint doesn't apply source mapping and certain
1337 // implementation ignore the source part of this event anyway.
1338 llvm::json::Value source_bp = bp.ToProtocolBreakpoint();
1339 source_bp.getAsObject()->erase(K: "source");
1340
1341 llvm::json::Object body;
1342 body.try_emplace(K: "breakpoint", Args&: source_bp);
1343 body.try_emplace(K: "reason", Args: "changed");
1344
1345 llvm::json::Object bp_event = CreateEventObject(event_name: "breakpoint");
1346 bp_event.try_emplace(K: "body", Args: std::move(body));
1347
1348 SendJSON(json: llvm::json::Value(std::move(bp_event)));
1349 }
1350 }
1351 } else if (event_mask & lldb::eBroadcastBitError ||
1352 event_mask & lldb::eBroadcastBitWarning) {
1353 lldb::SBStructuredData data =
1354 lldb::SBDebugger::GetDiagnosticFromEvent(event);
1355 if (!data.IsValid())
1356 continue;
1357 std::string type = GetStringValue(data: data.GetValueForKey(key: "type"));
1358 std::string message = GetStringValue(data: data.GetValueForKey(key: "message"));
1359 SendOutput(o: OutputType::Important,
1360 output: llvm::formatv(Fmt: "{0}: {1}", Vals&: type, Vals&: message).str());
1361 } else if (event.BroadcasterMatchesRef(broadcaster)) {
1362 if (event_mask & eBroadcastBitStopEventThread) {
1363 done = true;
1364 }
1365 }
1366 }
1367 }
1368}
1369
1370std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints(
1371 const protocol::Source &source,
1372 const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints) {
1373 std::vector<protocol::Breakpoint> response_breakpoints;
1374 if (source.sourceReference) {
1375 // Breakpoint set by assembly source.
1376 auto &existing_breakpoints =
1377 m_source_assembly_breakpoints[*source.sourceReference];
1378 response_breakpoints =
1379 SetSourceBreakpoints(source, breakpoints, existing_breakpoints);
1380 } else {
1381 // Breakpoint set by a regular source file.
1382 const auto path = source.path.value_or(u: "");
1383 auto &existing_breakpoints = m_source_breakpoints[path];
1384 response_breakpoints =
1385 SetSourceBreakpoints(source, breakpoints, existing_breakpoints);
1386 }
1387
1388 return response_breakpoints;
1389}
1390
1391std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints(
1392 const protocol::Source &source,
1393 const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints,
1394 SourceBreakpointMap &existing_breakpoints) {
1395 std::vector<protocol::Breakpoint> response_breakpoints;
1396
1397 SourceBreakpointMap request_breakpoints;
1398 if (breakpoints) {
1399 for (const auto &bp : *breakpoints) {
1400 SourceBreakpoint src_bp(*this, bp);
1401 std::pair<uint32_t, uint32_t> bp_pos(src_bp.GetLine(),
1402 src_bp.GetColumn());
1403 request_breakpoints.try_emplace(k: bp_pos, args&: src_bp);
1404
1405 const auto [iv, inserted] =
1406 existing_breakpoints.try_emplace(k: bp_pos, args&: src_bp);
1407 // We check if this breakpoint already exists to update it.
1408 if (inserted) {
1409 if (llvm::Error error = iv->second.SetBreakpoint(source)) {
1410 protocol::Breakpoint invalid_breakpoint;
1411 invalid_breakpoint.message = llvm::toString(E: std::move(error));
1412 invalid_breakpoint.verified = false;
1413 response_breakpoints.push_back(x: std::move(invalid_breakpoint));
1414 existing_breakpoints.erase(position: iv);
1415 continue;
1416 }
1417 } else {
1418 iv->second.UpdateBreakpoint(request_bp: src_bp);
1419 }
1420
1421 protocol::Breakpoint response_breakpoint =
1422 iv->second.ToProtocolBreakpoint();
1423 response_breakpoint.source = source;
1424
1425 if (!response_breakpoint.line &&
1426 src_bp.GetLine() != LLDB_INVALID_LINE_NUMBER)
1427 response_breakpoint.line = src_bp.GetLine();
1428 if (!response_breakpoint.column &&
1429 src_bp.GetColumn() != LLDB_INVALID_COLUMN_NUMBER)
1430 response_breakpoint.column = src_bp.GetColumn();
1431 response_breakpoints.push_back(x: std::move(response_breakpoint));
1432 }
1433 }
1434
1435 // Delete any breakpoints in this source file that aren't in the
1436 // request_bps set. There is no call to remove breakpoints other than
1437 // calling this function with a smaller or empty "breakpoints" list.
1438 for (auto it = existing_breakpoints.begin();
1439 it != existing_breakpoints.end();) {
1440 auto request_pos = request_breakpoints.find(x: it->first);
1441 if (request_pos == request_breakpoints.end()) {
1442 // This breakpoint no longer exists in this source file, delete it
1443 target.BreakpointDelete(break_id: it->second.GetID());
1444 it = existing_breakpoints.erase(position: it);
1445 } else {
1446 ++it;
1447 }
1448 }
1449
1450 return response_breakpoints;
1451}
1452
1453void DAP::RegisterRequests() {
1454 RegisterRequest<AttachRequestHandler>();
1455 RegisterRequest<BreakpointLocationsRequestHandler>();
1456 RegisterRequest<CancelRequestHandler>();
1457 RegisterRequest<CompletionsRequestHandler>();
1458 RegisterRequest<ConfigurationDoneRequestHandler>();
1459 RegisterRequest<ContinueRequestHandler>();
1460 RegisterRequest<DataBreakpointInfoRequestHandler>();
1461 RegisterRequest<DisassembleRequestHandler>();
1462 RegisterRequest<DisconnectRequestHandler>();
1463 RegisterRequest<EvaluateRequestHandler>();
1464 RegisterRequest<ExceptionInfoRequestHandler>();
1465 RegisterRequest<InitializeRequestHandler>();
1466 RegisterRequest<LaunchRequestHandler>();
1467 RegisterRequest<LocationsRequestHandler>();
1468 RegisterRequest<NextRequestHandler>();
1469 RegisterRequest<PauseRequestHandler>();
1470 RegisterRequest<ReadMemoryRequestHandler>();
1471 RegisterRequest<RestartRequestHandler>();
1472 RegisterRequest<ScopesRequestHandler>();
1473 RegisterRequest<SetBreakpointsRequestHandler>();
1474 RegisterRequest<SetDataBreakpointsRequestHandler>();
1475 RegisterRequest<SetExceptionBreakpointsRequestHandler>();
1476 RegisterRequest<SetFunctionBreakpointsRequestHandler>();
1477 RegisterRequest<SetInstructionBreakpointsRequestHandler>();
1478 RegisterRequest<SetVariableRequestHandler>();
1479 RegisterRequest<SourceRequestHandler>();
1480 RegisterRequest<StackTraceRequestHandler>();
1481 RegisterRequest<StepInRequestHandler>();
1482 RegisterRequest<StepInTargetsRequestHandler>();
1483 RegisterRequest<StepOutRequestHandler>();
1484 RegisterRequest<ThreadsRequestHandler>();
1485 RegisterRequest<VariablesRequestHandler>();
1486
1487 // Custom requests
1488 RegisterRequest<CompileUnitsRequestHandler>();
1489 RegisterRequest<ModulesRequestHandler>();
1490
1491 // Testing requests
1492 RegisterRequest<TestGetTargetBreakpointsRequestHandler>();
1493}
1494
1495} // namespace lldb_dap
1496

source code of lldb/tools/lldb-dap/DAP.cpp