[NFC][Coroutines] Use structured binding with llvm::enumerate in CoroSplit (#116879)
[llvm-project.git] / lldb / tools / lldb-dap / DAP.cpp
blob35250d9eef608a347817d21b4e624e7c1a4739fe
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 //===----------------------------------------------------------------------===//
9 #include <chrono>
10 #include <cstdarg>
11 #include <fstream>
12 #include <mutex>
14 #include "DAP.h"
15 #include "JSONUtils.h"
16 #include "LLDBUtils.h"
17 #include "lldb/API/SBCommandInterpreter.h"
18 #include "lldb/API/SBLanguageRuntime.h"
19 #include "lldb/API/SBListener.h"
20 #include "lldb/API/SBStream.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/Support/FormatVariadic.h"
24 #if defined(_WIN32)
25 #define NOMINMAX
26 #include <fcntl.h>
27 #include <io.h>
28 #include <windows.h>
29 #endif
31 using namespace lldb_dap;
33 namespace lldb_dap {
35 DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
36 : debug_adaptor_path(path), broadcaster("lldb-dap"),
37 exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
38 stop_at_entry(false), is_attach(false),
39 enable_auto_variable_summaries(false),
40 enable_synthetic_child_debugging(false),
41 display_extended_backtrace(false),
42 restarting_process_id(LLDB_INVALID_PROCESS_ID),
43 configuration_done_sent(false), waiting_for_run_in_terminal(false),
44 progress_event_reporter(
45 [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
46 reverse_request_seq(0), repl_mode(repl_mode) {
47 const char *log_file_path = getenv("LLDBDAP_LOG");
48 #if defined(_WIN32)
49 // Windows opens stdout and stdin in text mode which converts \n to 13,10
50 // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
51 // fixes this.
52 int result = _setmode(fileno(stdout), _O_BINARY);
53 assert(result);
54 result = _setmode(fileno(stdin), _O_BINARY);
55 UNUSED_IF_ASSERT_DISABLED(result);
56 assert(result);
57 #endif
58 if (log_file_path)
59 log.reset(new std::ofstream(log_file_path));
62 DAP::~DAP() = default;
64 /// Return string with first character capitalized.
65 static std::string capitalize(llvm::StringRef str) {
66 if (str.empty())
67 return "";
68 return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str();
71 void DAP::PopulateExceptionBreakpoints() {
72 llvm::call_once(init_exception_breakpoints_flag, [this]() {
73 exception_breakpoints = std::vector<ExceptionBreakpoint>{};
75 if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {
76 exception_breakpoints->emplace_back(*this, "cpp_catch", "C++ Catch",
77 lldb::eLanguageTypeC_plus_plus);
78 exception_breakpoints->emplace_back(*this, "cpp_throw", "C++ Throw",
79 lldb::eLanguageTypeC_plus_plus);
81 if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) {
82 exception_breakpoints->emplace_back(
83 *this, "objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC);
84 exception_breakpoints->emplace_back(
85 *this, "objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC);
87 if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) {
88 exception_breakpoints->emplace_back(*this, "swift_catch", "Swift Catch",
89 lldb::eLanguageTypeSwift);
90 exception_breakpoints->emplace_back(*this, "swift_throw", "Swift Throw",
91 lldb::eLanguageTypeSwift);
93 // Besides handling the hardcoded list of languages from above, we try to
94 // find any other languages that support exception breakpoints using the
95 // SB API.
96 for (int raw_lang = lldb::eLanguageTypeUnknown;
97 raw_lang < lldb::eNumLanguageTypes; ++raw_lang) {
98 lldb::LanguageType lang = static_cast<lldb::LanguageType>(raw_lang);
100 // We first discard any languages already handled above.
101 if (lldb::SBLanguageRuntime::LanguageIsCFamily(lang) ||
102 lang == lldb::eLanguageTypeSwift)
103 continue;
105 if (!lldb::SBDebugger::SupportsLanguage(lang))
106 continue;
108 const char *name = lldb::SBLanguageRuntime::GetNameForLanguageType(lang);
109 if (!name)
110 continue;
111 std::string raw_lang_name = name;
112 std::string capitalized_lang_name = capitalize(name);
114 if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(lang)) {
115 const char *raw_throw_keyword =
116 lldb::SBLanguageRuntime::GetThrowKeywordForLanguage(lang);
117 std::string throw_keyword =
118 raw_throw_keyword ? raw_throw_keyword : "throw";
120 exception_breakpoints->emplace_back(
121 *this, raw_lang_name + "_" + throw_keyword,
122 capitalized_lang_name + " " + capitalize(throw_keyword), lang);
125 if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(lang)) {
126 const char *raw_catch_keyword =
127 lldb::SBLanguageRuntime::GetCatchKeywordForLanguage(lang);
128 std::string catch_keyword =
129 raw_catch_keyword ? raw_catch_keyword : "catch";
131 exception_breakpoints->emplace_back(
132 *this, raw_lang_name + "_" + catch_keyword,
133 capitalized_lang_name + " " + capitalize(catch_keyword), lang);
136 assert(!exception_breakpoints->empty() && "should not be empty");
140 ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const std::string &filter) {
141 // PopulateExceptionBreakpoints() is called after g_dap.debugger is created
142 // in a request-initialize.
144 // But this GetExceptionBreakpoint() method may be called before attaching, in
145 // which case, we may not have populated the filter yet.
147 // We also cannot call PopulateExceptionBreakpoints() in DAP::DAP() because
148 // we need SBDebugger::Initialize() to have been called before this.
150 // So just calling PopulateExceptionBreakoints(),which does lazy-populating
151 // seems easiest. Two other options include:
152 // + call g_dap.PopulateExceptionBreakpoints() in lldb-dap.cpp::main()
153 // right after the call to SBDebugger::Initialize()
154 // + Just call PopulateExceptionBreakpoints() to get a fresh list everytime
155 // we query (a bit overkill since it's not likely to change?)
156 PopulateExceptionBreakpoints();
158 for (auto &bp : *exception_breakpoints) {
159 if (bp.filter == filter)
160 return &bp;
162 return nullptr;
165 ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
166 // See comment in the other GetExceptionBreakpoint().
167 PopulateExceptionBreakpoints();
169 for (auto &bp : *exception_breakpoints) {
170 if (bp.bp.GetID() == bp_id)
171 return &bp;
173 return nullptr;
176 // Send the JSON in "json_str" to the "out" stream. Correctly send the
177 // "Content-Length:" field followed by the length, followed by the raw
178 // JSON bytes.
179 void DAP::SendJSON(const std::string &json_str) {
180 output.write_full("Content-Length: ");
181 output.write_full(llvm::utostr(json_str.size()));
182 output.write_full("\r\n\r\n");
183 output.write_full(json_str);
186 // Serialize the JSON value into a string and send the JSON packet to
187 // the "out" stream.
188 void DAP::SendJSON(const llvm::json::Value &json) {
189 std::string json_str;
190 llvm::raw_string_ostream strm(json_str);
191 strm << json;
192 static std::mutex mutex;
193 std::lock_guard<std::mutex> locker(mutex);
194 SendJSON(json_str);
196 if (log) {
197 auto now = std::chrono::duration<double>(
198 std::chrono::system_clock::now().time_since_epoch());
199 *log << llvm::formatv("{0:f9} <-- ", now.count()).str() << std::endl
200 << "Content-Length: " << json_str.size() << "\r\n\r\n"
201 << llvm::formatv("{0:2}", json).str() << std::endl;
205 // Read a JSON packet from the "in" stream.
206 std::string DAP::ReadJSON() {
207 std::string length_str;
208 std::string json_str;
209 int length;
211 if (!input.read_expected(log.get(), "Content-Length: "))
212 return json_str;
214 if (!input.read_line(log.get(), length_str))
215 return json_str;
217 if (!llvm::to_integer(length_str, length))
218 return json_str;
220 if (!input.read_expected(log.get(), "\r\n"))
221 return json_str;
223 if (!input.read_full(log.get(), length, json_str))
224 return json_str;
226 if (log) {
227 auto now = std::chrono::duration<double>(
228 std::chrono::system_clock::now().time_since_epoch());
229 *log << llvm::formatv("{0:f9} --> ", now.count()).str() << std::endl
230 << "Content-Length: " << length << "\r\n\r\n";
232 return json_str;
235 // "OutputEvent": {
236 // "allOf": [ { "$ref": "#/definitions/Event" }, {
237 // "type": "object",
238 // "description": "Event message for 'output' event type. The event
239 // indicates that the target has produced some output.",
240 // "properties": {
241 // "event": {
242 // "type": "string",
243 // "enum": [ "output" ]
244 // },
245 // "body": {
246 // "type": "object",
247 // "properties": {
248 // "category": {
249 // "type": "string",
250 // "description": "The output category. If not specified,
251 // 'console' is assumed.",
252 // "_enum": [ "console", "stdout", "stderr", "telemetry" ]
253 // },
254 // "output": {
255 // "type": "string",
256 // "description": "The output to report."
257 // },
258 // "variablesReference": {
259 // "type": "number",
260 // "description": "If an attribute 'variablesReference' exists
261 // and its value is > 0, the output contains
262 // objects which can be retrieved by passing
263 // variablesReference to the VariablesRequest."
264 // },
265 // "source": {
266 // "$ref": "#/definitions/Source",
267 // "description": "An optional source location where the output
268 // was produced."
269 // },
270 // "line": {
271 // "type": "integer",
272 // "description": "An optional source location line where the
273 // output was produced."
274 // },
275 // "column": {
276 // "type": "integer",
277 // "description": "An optional source location column where the
278 // output was produced."
279 // },
280 // "data": {
281 // "type":["array","boolean","integer","null","number","object",
282 // "string"],
283 // "description": "Optional data to report. For the 'telemetry'
284 // category the data will be sent to telemetry, for
285 // the other categories the data is shown in JSON
286 // format."
287 // }
288 // },
289 // "required": ["output"]
290 // }
291 // },
292 // "required": [ "event", "body" ]
293 // }]
294 // }
295 void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
296 if (output.empty())
297 return;
299 const char *category = nullptr;
300 switch (o) {
301 case OutputType::Console:
302 category = "console";
303 break;
304 case OutputType::Stdout:
305 category = "stdout";
306 break;
307 case OutputType::Stderr:
308 category = "stderr";
309 break;
310 case OutputType::Telemetry:
311 category = "telemetry";
312 break;
315 // Send each line of output as an individual event, including the newline if
316 // present.
317 ::size_t idx = 0;
318 do {
319 ::size_t end = output.find('\n', idx);
320 if (end == llvm::StringRef::npos)
321 end = output.size() - 1;
322 llvm::json::Object event(CreateEventObject("output"));
323 llvm::json::Object body;
324 body.try_emplace("category", category);
325 EmplaceSafeString(body, "output", output.slice(idx, end + 1).str());
326 event.try_emplace("body", std::move(body));
327 SendJSON(llvm::json::Value(std::move(event)));
328 idx = end + 1;
329 } while (idx < output.size());
332 // interface ProgressStartEvent extends Event {
333 // event: 'progressStart';
335 // body: {
336 // /**
337 // * An ID that must be used in subsequent 'progressUpdate' and
338 // 'progressEnd'
339 // * events to make them refer to the same progress reporting.
340 // * IDs must be unique within a debug session.
341 // */
342 // progressId: string;
344 // /**
345 // * Mandatory (short) title of the progress reporting. Shown in the UI to
346 // * describe the long running operation.
347 // */
348 // title: string;
350 // /**
351 // * The request ID that this progress report is related to. If specified a
352 // * debug adapter is expected to emit
353 // * progress events for the long running request until the request has
354 // been
355 // * either completed or cancelled.
356 // * If the request ID is omitted, the progress report is assumed to be
357 // * related to some general activity of the debug adapter.
358 // */
359 // requestId?: number;
361 // /**
362 // * If true, the request that reports progress may be canceled with a
363 // * 'cancel' request.
364 // * So this property basically controls whether the client should use UX
365 // that
366 // * supports cancellation.
367 // * Clients that don't support cancellation are allowed to ignore the
368 // * setting.
369 // */
370 // cancellable?: boolean;
372 // /**
373 // * Optional, more detailed progress message.
374 // */
375 // message?: string;
377 // /**
378 // * Optional progress percentage to display (value range: 0 to 100). If
379 // * omitted no percentage will be shown.
380 // */
381 // percentage?: number;
382 // };
383 // }
385 // interface ProgressUpdateEvent extends Event {
386 // event: 'progressUpdate';
388 // body: {
389 // /**
390 // * The ID that was introduced in the initial 'progressStart' event.
391 // */
392 // progressId: string;
394 // /**
395 // * Optional, more detailed progress message. If omitted, the previous
396 // * message (if any) is used.
397 // */
398 // message?: string;
400 // /**
401 // * Optional progress percentage to display (value range: 0 to 100). If
402 // * omitted no percentage will be shown.
403 // */
404 // percentage?: number;
405 // };
406 // }
408 // interface ProgressEndEvent extends Event {
409 // event: 'progressEnd';
411 // body: {
412 // /**
413 // * The ID that was introduced in the initial 'ProgressStartEvent'.
414 // */
415 // progressId: string;
417 // /**
418 // * Optional, more detailed progress message. If omitted, the previous
419 // * message (if any) is used.
420 // */
421 // message?: string;
422 // };
423 // }
425 void DAP::SendProgressEvent(uint64_t progress_id, const char *message,
426 uint64_t completed, uint64_t total) {
427 progress_event_reporter.Push(progress_id, message, completed, total);
430 void __attribute__((format(printf, 3, 4)))
431 DAP::SendFormattedOutput(OutputType o, const char *format, ...) {
432 char buffer[1024];
433 va_list args;
434 va_start(args, format);
435 int actual_length = vsnprintf(buffer, sizeof(buffer), format, args);
436 va_end(args);
437 SendOutput(
438 o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
441 ExceptionBreakpoint *DAP::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
442 const auto num = thread.GetStopReasonDataCount();
443 // Check to see if have hit an exception breakpoint and change the
444 // reason to "exception", but only do so if all breakpoints that were
445 // hit are exception breakpoints.
446 ExceptionBreakpoint *exc_bp = nullptr;
447 for (size_t i = 0; i < num; i += 2) {
448 // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
449 // thread.GetStopReasonDataAtIndex(i+1) will return the location
450 // within that breakpoint. We only care about the bp ID so we can
451 // see if this is an exception breakpoint that is getting hit.
452 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
453 exc_bp = GetExceptionBreakpoint(bp_id);
454 // If any breakpoint is not an exception breakpoint, then stop and
455 // report this as a normal breakpoint
456 if (exc_bp == nullptr)
457 return nullptr;
459 return exc_bp;
462 lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
463 auto tid = GetSigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
464 return target.GetProcess().GetThreadByID(tid);
467 lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
468 const uint64_t frame_id = GetUnsigned(arguments, "frameId", UINT64_MAX);
469 lldb::SBProcess process = target.GetProcess();
470 // Upper 32 bits is the thread index ID
471 lldb::SBThread thread =
472 process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id));
473 // Lower 32 bits is the frame index
474 return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
477 llvm::json::Value DAP::CreateTopLevelScopes() {
478 llvm::json::Array scopes;
479 scopes.emplace_back(
480 CreateScope("Locals", VARREF_LOCALS, variables.locals.GetSize(), false));
481 scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
482 variables.globals.GetSize(), false));
483 scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
484 variables.registers.GetSize(), false));
485 return llvm::json::Value(std::move(scopes));
488 ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
489 bool partial_expression) {
490 // Check for the escape hatch prefix.
491 if (!expression.empty() &&
492 llvm::StringRef(expression).starts_with(command_escape_prefix)) {
493 expression = expression.substr(command_escape_prefix.size());
494 return ReplMode::Command;
497 switch (repl_mode) {
498 case ReplMode::Variable:
499 return ReplMode::Variable;
500 case ReplMode::Command:
501 return ReplMode::Command;
502 case ReplMode::Auto:
503 // To determine if the expression is a command or not, check if the first
504 // term is a variable or command. If it's a variable in scope we will prefer
505 // that behavior and give a warning to the user if they meant to invoke the
506 // operation as a command.
508 // Example use case:
509 // int p and expression "p + 1" > variable
510 // int i and expression "i" > variable
511 // int var and expression "va" > command
512 std::pair<llvm::StringRef, llvm::StringRef> token =
513 llvm::getToken(expression);
515 // If the first token is not fully finished yet, we can't
516 // determine whether this will be a variable or a lldb command.
517 if (partial_expression && token.second.empty())
518 return ReplMode::Auto;
520 std::string term = token.first.str();
521 lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
522 bool term_is_command = interpreter.CommandExists(term.c_str()) ||
523 interpreter.UserCommandExists(term.c_str()) ||
524 interpreter.AliasExists(term.c_str());
525 bool term_is_variable = frame.FindVariable(term.c_str()).IsValid();
527 // If we have both a variable and command, warn the user about the conflict.
528 if (term_is_command && term_is_variable) {
529 llvm::errs()
530 << "Warning: Expression '" << term
531 << "' is both an LLDB command and variable. It will be evaluated as "
532 "a variable. To evaluate the expression as an LLDB command, use '"
533 << command_escape_prefix << "' as a prefix.\n";
536 // Variables take preference to commands in auto, since commands can always
537 // be called using the command_escape_prefix
538 return term_is_variable ? ReplMode::Variable
539 : term_is_command ? ReplMode::Command
540 : ReplMode::Variable;
543 llvm_unreachable("enum cases exhausted.");
546 bool DAP::RunLLDBCommands(llvm::StringRef prefix,
547 llvm::ArrayRef<std::string> commands) {
548 bool required_command_failed = false;
549 std::string output =
550 ::RunLLDBCommands(debugger, prefix, commands, required_command_failed);
551 SendOutput(OutputType::Console, output);
552 return !required_command_failed;
555 static llvm::Error createRunLLDBCommandsErrorMessage(llvm::StringRef category) {
556 return llvm::createStringError(
557 llvm::inconvertibleErrorCode(),
558 llvm::formatv(
559 "Failed to run {0} commands. See the Debug Console for more details.",
560 category)
561 .str()
562 .c_str());
565 llvm::Error
566 DAP::RunAttachCommands(llvm::ArrayRef<std::string> attach_commands) {
567 if (!RunLLDBCommands("Running attachCommands:", attach_commands))
568 return createRunLLDBCommandsErrorMessage("attach");
569 return llvm::Error::success();
572 llvm::Error
573 DAP::RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands) {
574 if (!RunLLDBCommands("Running launchCommands:", launch_commands))
575 return createRunLLDBCommandsErrorMessage("launch");
576 return llvm::Error::success();
579 llvm::Error DAP::RunInitCommands() {
580 if (!RunLLDBCommands("Running initCommands:", init_commands))
581 return createRunLLDBCommandsErrorMessage("initCommands");
582 return llvm::Error::success();
585 llvm::Error DAP::RunPreInitCommands() {
586 if (!RunLLDBCommands("Running preInitCommands:", pre_init_commands))
587 return createRunLLDBCommandsErrorMessage("preInitCommands");
588 return llvm::Error::success();
591 llvm::Error DAP::RunPreRunCommands() {
592 if (!RunLLDBCommands("Running preRunCommands:", pre_run_commands))
593 return createRunLLDBCommandsErrorMessage("preRunCommands");
594 return llvm::Error::success();
597 void DAP::RunPostRunCommands() {
598 RunLLDBCommands("Running postRunCommands:", post_run_commands);
600 void DAP::RunStopCommands() {
601 RunLLDBCommands("Running stopCommands:", stop_commands);
604 void DAP::RunExitCommands() {
605 RunLLDBCommands("Running exitCommands:", exit_commands);
608 void DAP::RunTerminateCommands() {
609 RunLLDBCommands("Running terminateCommands:", terminate_commands);
612 lldb::SBTarget
613 DAP::CreateTargetFromArguments(const llvm::json::Object &arguments,
614 lldb::SBError &error) {
615 // Grab the name of the program we need to debug and create a target using
616 // the given program as an argument. Executable file can be a source of target
617 // architecture and platform, if they differ from the host. Setting exe path
618 // in launch info is useless because Target.Launch() will not change
619 // architecture and platform, therefore they should be known at the target
620 // creation. We also use target triple and platform from the launch
621 // configuration, if given, since in some cases ELF file doesn't contain
622 // enough information to determine correct arch and platform (or ELF can be
623 // omitted at all), so it is good to leave the user an apportunity to specify
624 // those. Any of those three can be left empty.
625 llvm::StringRef target_triple = GetString(arguments, "targetTriple");
626 llvm::StringRef platform_name = GetString(arguments, "platformName");
627 llvm::StringRef program = GetString(arguments, "program");
628 auto target = this->debugger.CreateTarget(
629 program.data(), target_triple.data(), platform_name.data(),
630 true, // Add dependent modules.
631 error);
633 if (error.Fail()) {
634 // Update message if there was an error.
635 error.SetErrorStringWithFormat(
636 "Could not create a target for a program '%s': %s.", program.data(),
637 error.GetCString());
640 return target;
643 void DAP::SetTarget(const lldb::SBTarget target) {
644 this->target = target;
646 if (target.IsValid()) {
647 // Configure breakpoint event listeners for the target.
648 lldb::SBListener listener = this->debugger.GetListener();
649 listener.StartListeningForEvents(
650 this->target.GetBroadcaster(),
651 lldb::SBTarget::eBroadcastBitBreakpointChanged);
652 listener.StartListeningForEvents(this->broadcaster,
653 eBroadcastBitStopEventThread);
657 PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
658 std::string json = ReadJSON();
659 if (json.empty())
660 return PacketStatus::EndOfFile;
662 llvm::StringRef json_sref(json);
663 llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
664 if (!json_value) {
665 auto error = json_value.takeError();
666 if (log) {
667 std::string error_str;
668 llvm::raw_string_ostream strm(error_str);
669 strm << error;
670 *log << "error: failed to parse JSON: " << error_str << std::endl
671 << json << std::endl;
673 return PacketStatus::JSONMalformed;
676 if (log) {
677 *log << llvm::formatv("{0:2}", *json_value).str() << std::endl;
680 llvm::json::Object *object_ptr = json_value->getAsObject();
681 if (!object_ptr) {
682 if (log)
683 *log << "error: json packet isn't a object" << std::endl;
684 return PacketStatus::JSONNotObject;
686 object = *object_ptr;
687 return PacketStatus::Success;
690 bool DAP::HandleObject(const llvm::json::Object &object) {
691 const auto packet_type = GetString(object, "type");
692 if (packet_type == "request") {
693 const auto command = GetString(object, "command");
694 auto handler_pos = request_handlers.find(command);
695 if (handler_pos == request_handlers.end()) {
696 if (log)
697 *log << "error: unhandled command \"" << command.data() << "\""
698 << std::endl;
699 return false; // Fail
702 handler_pos->second(*this, object);
703 return true; // Success
706 if (packet_type == "response") {
707 auto id = GetSigned(object, "request_seq", 0);
708 ResponseCallback response_handler = [](llvm::Expected<llvm::json::Value>) {
709 llvm::errs() << "Unhandled response\n";
713 std::lock_guard<std::mutex> locker(call_mutex);
714 auto inflight = inflight_reverse_requests.find(id);
715 if (inflight != inflight_reverse_requests.end()) {
716 response_handler = std::move(inflight->second);
717 inflight_reverse_requests.erase(inflight);
721 // Result should be given, use null if not.
722 if (GetBoolean(object, "success", false)) {
723 llvm::json::Value Result = nullptr;
724 if (auto *B = object.get("body")) {
725 Result = std::move(*B);
727 response_handler(Result);
728 } else {
729 llvm::StringRef message = GetString(object, "message");
730 if (message.empty()) {
731 message = "Unknown error, response failed";
733 response_handler(llvm::createStringError(
734 std::error_code(-1, std::generic_category()), message));
737 return true;
740 return false;
743 llvm::Error DAP::Loop() {
744 while (!disconnecting) {
745 llvm::json::Object object;
746 lldb_dap::PacketStatus status = GetNextObject(object);
748 if (status == lldb_dap::PacketStatus::EndOfFile) {
749 break;
752 if (status != lldb_dap::PacketStatus::Success) {
753 return llvm::createStringError(llvm::inconvertibleErrorCode(),
754 "failed to send packet");
757 if (!HandleObject(object)) {
758 return llvm::createStringError(llvm::inconvertibleErrorCode(),
759 "unhandled packet");
763 return llvm::Error::success();
766 void DAP::SendReverseRequest(llvm::StringRef command,
767 llvm::json::Value arguments,
768 ResponseCallback callback) {
769 int64_t id;
771 std::lock_guard<std::mutex> locker(call_mutex);
772 id = ++reverse_request_seq;
773 inflight_reverse_requests.emplace(id, std::move(callback));
776 SendJSON(llvm::json::Object{
777 {"type", "request"},
778 {"seq", id},
779 {"command", command},
780 {"arguments", std::move(arguments)},
784 void DAP::RegisterRequestCallback(std::string request,
785 RequestCallback callback) {
786 request_handlers[request] = callback;
789 lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
790 lldb::SBError error;
791 lldb::SBProcess process = target.GetProcess();
792 if (!process.IsValid()) {
793 error.SetErrorString("invalid process");
794 return error;
796 auto timeout_time =
797 std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
798 while (std::chrono::steady_clock::now() < timeout_time) {
799 const auto state = process.GetState();
800 switch (state) {
801 case lldb::eStateAttaching:
802 case lldb::eStateConnected:
803 case lldb::eStateInvalid:
804 case lldb::eStateLaunching:
805 case lldb::eStateRunning:
806 case lldb::eStateStepping:
807 case lldb::eStateSuspended:
808 break;
809 case lldb::eStateDetached:
810 error.SetErrorString("process detached during launch or attach");
811 return error;
812 case lldb::eStateExited:
813 error.SetErrorString("process exited during launch or attach");
814 return error;
815 case lldb::eStateUnloaded:
816 error.SetErrorString("process unloaded during launch or attach");
817 return error;
818 case lldb::eStateCrashed:
819 case lldb::eStateStopped:
820 return lldb::SBError(); // Success!
822 std::this_thread::sleep_for(std::chrono::microseconds(250));
824 error.SetErrorStringWithFormat("process failed to stop within %u seconds",
825 seconds);
826 return error;
829 void Variables::Clear() {
830 locals.Clear();
831 globals.Clear();
832 registers.Clear();
833 referenced_variables.clear();
836 int64_t Variables::GetNewVariableReference(bool is_permanent) {
837 if (is_permanent)
838 return next_permanent_var_ref++;
839 return next_temporary_var_ref++;
842 bool Variables::IsPermanentVariableReference(int64_t var_ref) {
843 return var_ref >= PermanentVariableStartIndex;
846 lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
847 if (IsPermanentVariableReference(var_ref)) {
848 auto pos = referenced_permanent_variables.find(var_ref);
849 if (pos != referenced_permanent_variables.end())
850 return pos->second;
851 } else {
852 auto pos = referenced_variables.find(var_ref);
853 if (pos != referenced_variables.end())
854 return pos->second;
856 return lldb::SBValue();
859 int64_t Variables::InsertVariable(lldb::SBValue variable, bool is_permanent) {
860 int64_t var_ref = GetNewVariableReference(is_permanent);
861 if (is_permanent)
862 referenced_permanent_variables.insert(std::make_pair(var_ref, variable));
863 else
864 referenced_variables.insert(std::make_pair(var_ref, variable));
865 return var_ref;
868 bool StartDebuggingRequestHandler::DoExecute(
869 lldb::SBDebugger debugger, char **command,
870 lldb::SBCommandReturnObject &result) {
871 // Command format like: `start-debugging <launch|attach> <configuration>`
872 if (!command) {
873 result.SetError("Invalid use of start-debugging, expected format "
874 "`start-debugging <launch|attach> <configuration>`.");
875 return false;
878 if (!command[0] || llvm::StringRef(command[0]).empty()) {
879 result.SetError("start-debugging request type missing.");
880 return false;
883 if (!command[1] || llvm::StringRef(command[1]).empty()) {
884 result.SetError("start-debugging debug configuration missing.");
885 return false;
888 llvm::StringRef request{command[0]};
889 std::string raw_configuration{command[1]};
891 llvm::Expected<llvm::json::Value> configuration =
892 llvm::json::parse(raw_configuration);
894 if (!configuration) {
895 llvm::Error err = configuration.takeError();
896 std::string msg = "Failed to parse json configuration: " +
897 llvm::toString(std::move(err)) + "\n\n" +
898 raw_configuration;
899 result.SetError(msg.c_str());
900 return false;
903 dap.SendReverseRequest(
904 "startDebugging",
905 llvm::json::Object{{"request", request},
906 {"configuration", std::move(*configuration)}},
907 [](llvm::Expected<llvm::json::Value> value) {
908 if (!value) {
909 llvm::Error err = value.takeError();
910 llvm::errs() << "reverse start debugging request failed: "
911 << llvm::toString(std::move(err)) << "\n";
915 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
917 return true;
920 bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
921 char **command,
922 lldb::SBCommandReturnObject &result) {
923 // Command format like: `repl-mode <variable|command|auto>?`
924 // If a new mode is not specified report the current mode.
925 if (!command || llvm::StringRef(command[0]).empty()) {
926 std::string mode;
927 switch (dap.repl_mode) {
928 case ReplMode::Variable:
929 mode = "variable";
930 break;
931 case ReplMode::Command:
932 mode = "command";
933 break;
934 case ReplMode::Auto:
935 mode = "auto";
936 break;
939 result.Printf("lldb-dap repl-mode %s.\n", mode.c_str());
940 result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
942 return true;
945 llvm::StringRef new_mode{command[0]};
947 if (new_mode == "variable") {
948 dap.repl_mode = ReplMode::Variable;
949 } else if (new_mode == "command") {
950 dap.repl_mode = ReplMode::Command;
951 } else if (new_mode == "auto") {
952 dap.repl_mode = ReplMode::Auto;
953 } else {
954 lldb::SBStream error_message;
955 error_message.Printf("Invalid repl-mode '%s'. Expected one of 'variable', "
956 "'command' or 'auto'.\n",
957 new_mode.data());
958 result.SetError(error_message.GetData());
959 return false;
962 result.Printf("lldb-dap repl-mode %s set.\n", new_mode.data());
963 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
964 return true;
967 // Sends a DAP event with an optional body.
969 // See
970 // https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent
971 bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger,
972 char **command,
973 lldb::SBCommandReturnObject &result) {
974 // Command format like: `send-event <name> <body>?`
975 if (!command || !command[0] || llvm::StringRef(command[0]).empty()) {
976 result.SetError("Not enough arguments found, expected format "
977 "`lldb-dap send-event <name> <body>?`.");
978 return false;
981 llvm::StringRef name{command[0]};
982 // Events that are stateful and should be handled by lldb-dap internally.
983 const std::array internal_events{"breakpoint", "capabilities", "continued",
984 "exited", "initialize", "loadedSource",
985 "module", "process", "stopped",
986 "terminated", "thread"};
987 if (std::find(internal_events.begin(), internal_events.end(), name) !=
988 std::end(internal_events)) {
989 std::string msg =
990 llvm::formatv("Invalid use of lldb-dap send-event, event \"{0}\" "
991 "should be handled by lldb-dap internally.",
992 name)
993 .str();
994 result.SetError(msg.c_str());
995 return false;
998 llvm::json::Object event(CreateEventObject(name));
1000 if (command[1] && !llvm::StringRef(command[1]).empty()) {
1001 // See if we have unused arguments.
1002 if (command[2]) {
1003 result.SetError(
1004 "Additional arguments found, expected `lldb-dap send-event "
1005 "<name> <body>?`.");
1006 return false;
1009 llvm::StringRef raw_body{command[1]};
1011 llvm::Expected<llvm::json::Value> body = llvm::json::parse(raw_body);
1013 if (!body) {
1014 llvm::Error err = body.takeError();
1015 std::string msg = "Failed to parse custom event body: " +
1016 llvm::toString(std::move(err));
1017 result.SetError(msg.c_str());
1018 return false;
1021 event.try_emplace("body", std::move(*body));
1024 dap.SendJSON(llvm::json::Value(std::move(event)));
1025 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
1026 return true;
1029 void DAP::SetFrameFormat(llvm::StringRef format) {
1030 if (format.empty())
1031 return;
1032 lldb::SBError error;
1033 frame_format = lldb::SBFormat(format.str().c_str(), error);
1034 if (error.Fail()) {
1035 SendOutput(OutputType::Console,
1036 llvm::formatv(
1037 "The provided frame format '{0}' couldn't be parsed: {1}\n",
1038 format, error.GetCString())
1039 .str());
1043 void DAP::SetThreadFormat(llvm::StringRef format) {
1044 if (format.empty())
1045 return;
1046 lldb::SBError error;
1047 thread_format = lldb::SBFormat(format.str().c_str(), error);
1048 if (error.Fail()) {
1049 SendOutput(OutputType::Console,
1050 llvm::formatv(
1051 "The provided thread format '{0}' couldn't be parsed: {1}\n",
1052 format, error.GetCString())
1053 .str());
1057 InstructionBreakpoint *
1058 DAP::GetInstructionBreakpoint(const lldb::break_id_t bp_id) {
1059 for (auto &bp : instruction_breakpoints) {
1060 if (bp.second.bp.GetID() == bp_id)
1061 return &bp.second;
1063 return nullptr;
1066 InstructionBreakpoint *
1067 DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {
1068 const auto num = thread.GetStopReasonDataCount();
1069 InstructionBreakpoint *inst_bp = nullptr;
1070 for (size_t i = 0; i < num; i += 2) {
1071 // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
1072 // thread.GetStopReasonDataAtIndex(i+1) will return the location
1073 // within that breakpoint. We only care about the bp ID so we can
1074 // see if this is an instruction breakpoint that is getting hit.
1075 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
1076 inst_bp = GetInstructionBreakpoint(bp_id);
1077 // If any breakpoint is not an instruction breakpoint, then stop and
1078 // report this as a normal breakpoint
1079 if (inst_bp == nullptr)
1080 return nullptr;
1082 return inst_bp;
1085 } // namespace lldb_dap