Bump version to 19.1.0 (final)
[llvm-project.git] / lldb / tools / lldb-dap / DAP.cpp
blobc3c70e9d73984641dd429616b31bd11ba982d40f
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>
13 #include <sstream>
15 #include "DAP.h"
16 #include "LLDBUtils.h"
17 #include "lldb/API/SBCommandInterpreter.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/Support/FormatVariadic.h"
21 #if defined(_WIN32)
22 #define NOMINMAX
23 #include <fcntl.h>
24 #include <io.h>
25 #include <windows.h>
26 #endif
28 using namespace lldb_dap;
30 namespace lldb_dap {
32 DAP g_dap;
34 DAP::DAP()
35 : broadcaster("lldb-dap"), exception_breakpoints(),
36 focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
37 enable_auto_variable_summaries(false),
38 enable_synthetic_child_debugging(false),
39 restarting_process_id(LLDB_INVALID_PROCESS_ID),
40 configuration_done_sent(false), waiting_for_run_in_terminal(false),
41 progress_event_reporter(
42 [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
43 reverse_request_seq(0), repl_mode(ReplMode::Auto) {
44 const char *log_file_path = getenv("LLDBDAP_LOG");
45 #if defined(_WIN32)
46 // Windows opens stdout and stdin in text mode which converts \n to 13,10
47 // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
48 // fixes this.
49 int result = _setmode(fileno(stdout), _O_BINARY);
50 assert(result);
51 result = _setmode(fileno(stdin), _O_BINARY);
52 UNUSED_IF_ASSERT_DISABLED(result);
53 assert(result);
54 #endif
55 if (log_file_path)
56 log.reset(new std::ofstream(log_file_path));
59 DAP::~DAP() = default;
61 /// Return string with first character capitalized.
62 static std::string capitalize(llvm::StringRef str) {
63 if (str.empty())
64 return "";
65 return ((llvm::Twine)llvm::toUpper(str[0]) + str.drop_front()).str();
68 void DAP::PopulateExceptionBreakpoints() {
69 llvm::call_once(init_exception_breakpoints_flag, [this]() {
70 exception_breakpoints = std::vector<ExceptionBreakpoint> {};
72 if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {
73 exception_breakpoints->emplace_back("cpp_catch", "C++ Catch",
74 lldb::eLanguageTypeC_plus_plus);
75 exception_breakpoints->emplace_back("cpp_throw", "C++ Throw",
76 lldb::eLanguageTypeC_plus_plus);
78 if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) {
79 exception_breakpoints->emplace_back("objc_catch", "Objective-C Catch",
80 lldb::eLanguageTypeObjC);
81 exception_breakpoints->emplace_back("objc_throw", "Objective-C Throw",
82 lldb::eLanguageTypeObjC);
84 if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) {
85 exception_breakpoints->emplace_back("swift_catch", "Swift Catch",
86 lldb::eLanguageTypeSwift);
87 exception_breakpoints->emplace_back("swift_throw", "Swift Throw",
88 lldb::eLanguageTypeSwift);
90 // Besides handling the hardcoded list of languages from above, we try to
91 // find any other languages that support exception breakpoints using the
92 // SB API.
93 for (int raw_lang = lldb::eLanguageTypeUnknown;
94 raw_lang < lldb::eNumLanguageTypes; ++raw_lang) {
95 lldb::LanguageType lang = static_cast<lldb::LanguageType>(raw_lang);
97 // We first discard any languages already handled above.
98 if (lldb::SBLanguageRuntime::LanguageIsCFamily(lang) ||
99 lang == lldb::eLanguageTypeSwift)
100 continue;
102 if (!lldb::SBDebugger::SupportsLanguage(lang))
103 continue;
105 const char *name = lldb::SBLanguageRuntime::GetNameForLanguageType(lang);
106 if (!name)
107 continue;
108 std::string raw_lang_name = name;
109 std::string capitalized_lang_name = capitalize(name);
111 if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnThrow(lang)) {
112 const char *raw_throw_keyword =
113 lldb::SBLanguageRuntime::GetThrowKeywordForLanguage(lang);
114 std::string throw_keyword =
115 raw_throw_keyword ? raw_throw_keyword : "throw";
117 exception_breakpoints->emplace_back(
118 raw_lang_name + "_" + throw_keyword,
119 capitalized_lang_name + " " + capitalize(throw_keyword), lang);
122 if (lldb::SBLanguageRuntime::SupportsExceptionBreakpointsOnCatch(lang)) {
123 const char *raw_catch_keyword =
124 lldb::SBLanguageRuntime::GetCatchKeywordForLanguage(lang);
125 std::string catch_keyword =
126 raw_catch_keyword ? raw_catch_keyword : "catch";
128 exception_breakpoints->emplace_back(
129 raw_lang_name + "_" + catch_keyword,
130 capitalized_lang_name + " " + capitalize(catch_keyword), lang);
133 assert(!exception_breakpoints->empty() && "should not be empty");
137 ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const std::string &filter) {
138 // PopulateExceptionBreakpoints() is called after g_dap.debugger is created
139 // in a request-initialize.
141 // But this GetExceptionBreakpoint() method may be called before attaching, in
142 // which case, we may not have populated the filter yet.
144 // We also cannot call PopulateExceptionBreakpoints() in DAP::DAP() because
145 // we need SBDebugger::Initialize() to have been called before this.
147 // So just calling PopulateExceptionBreakoints(),which does lazy-populating
148 // seems easiest. Two other options include:
149 // + call g_dap.PopulateExceptionBreakpoints() in lldb-dap.cpp::main()
150 // right after the call to SBDebugger::Initialize()
151 // + Just call PopulateExceptionBreakpoints() to get a fresh list everytime
152 // we query (a bit overkill since it's not likely to change?)
153 PopulateExceptionBreakpoints();
155 for (auto &bp : *exception_breakpoints) {
156 if (bp.filter == filter)
157 return &bp;
159 return nullptr;
162 ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
163 // See comment in the other GetExceptionBreakpoint().
164 PopulateExceptionBreakpoints();
166 for (auto &bp : *exception_breakpoints) {
167 if (bp.bp.GetID() == bp_id)
168 return &bp;
170 return nullptr;
173 // Send the JSON in "json_str" to the "out" stream. Correctly send the
174 // "Content-Length:" field followed by the length, followed by the raw
175 // JSON bytes.
176 void DAP::SendJSON(const std::string &json_str) {
177 output.write_full("Content-Length: ");
178 output.write_full(llvm::utostr(json_str.size()));
179 output.write_full("\r\n\r\n");
180 output.write_full(json_str);
183 // Serialize the JSON value into a string and send the JSON packet to
184 // the "out" stream.
185 void DAP::SendJSON(const llvm::json::Value &json) {
186 std::string s;
187 llvm::raw_string_ostream strm(s);
188 strm << json;
189 static std::mutex mutex;
190 std::lock_guard<std::mutex> locker(mutex);
191 std::string json_str = strm.str();
192 SendJSON(json_str);
194 if (log) {
195 auto now = std::chrono::duration<double>(
196 std::chrono::system_clock::now().time_since_epoch());
197 *log << llvm::formatv("{0:f9} <-- ", now.count()).str() << std::endl
198 << "Content-Length: " << json_str.size() << "\r\n\r\n"
199 << llvm::formatv("{0:2}", json).str() << std::endl;
203 // Read a JSON packet from the "in" stream.
204 std::string DAP::ReadJSON() {
205 std::string length_str;
206 std::string json_str;
207 int length;
209 if (!input.read_expected(log.get(), "Content-Length: "))
210 return json_str;
212 if (!input.read_line(log.get(), length_str))
213 return json_str;
215 if (!llvm::to_integer(length_str, length))
216 return json_str;
218 if (!input.read_expected(log.get(), "\r\n"))
219 return json_str;
221 if (!input.read_full(log.get(), length, json_str))
222 return json_str;
224 if (log) {
225 auto now = std::chrono::duration<double>(
226 std::chrono::system_clock::now().time_since_epoch());
227 *log << llvm::formatv("{0:f9} --> ", now.count()).str() << std::endl
228 << "Content-Length: " << length << "\r\n\r\n";
230 return json_str;
233 // "OutputEvent": {
234 // "allOf": [ { "$ref": "#/definitions/Event" }, {
235 // "type": "object",
236 // "description": "Event message for 'output' event type. The event
237 // indicates that the target has produced some output.",
238 // "properties": {
239 // "event": {
240 // "type": "string",
241 // "enum": [ "output" ]
242 // },
243 // "body": {
244 // "type": "object",
245 // "properties": {
246 // "category": {
247 // "type": "string",
248 // "description": "The output category. If not specified,
249 // 'console' is assumed.",
250 // "_enum": [ "console", "stdout", "stderr", "telemetry" ]
251 // },
252 // "output": {
253 // "type": "string",
254 // "description": "The output to report."
255 // },
256 // "variablesReference": {
257 // "type": "number",
258 // "description": "If an attribute 'variablesReference' exists
259 // and its value is > 0, the output contains
260 // objects which can be retrieved by passing
261 // variablesReference to the VariablesRequest."
262 // },
263 // "source": {
264 // "$ref": "#/definitions/Source",
265 // "description": "An optional source location where the output
266 // was produced."
267 // },
268 // "line": {
269 // "type": "integer",
270 // "description": "An optional source location line where the
271 // output was produced."
272 // },
273 // "column": {
274 // "type": "integer",
275 // "description": "An optional source location column where the
276 // output was produced."
277 // },
278 // "data": {
279 // "type":["array","boolean","integer","null","number","object",
280 // "string"],
281 // "description": "Optional data to report. For the 'telemetry'
282 // category the data will be sent to telemetry, for
283 // the other categories the data is shown in JSON
284 // format."
285 // }
286 // },
287 // "required": ["output"]
288 // }
289 // },
290 // "required": [ "event", "body" ]
291 // }]
292 // }
293 void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
294 if (output.empty())
295 return;
297 llvm::json::Object event(CreateEventObject("output"));
298 llvm::json::Object body;
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;
314 body.try_emplace("category", category);
315 EmplaceSafeString(body, "output", output.str());
316 event.try_emplace("body", std::move(body));
317 SendJSON(llvm::json::Value(std::move(event)));
320 // interface ProgressStartEvent extends Event {
321 // event: 'progressStart';
323 // body: {
324 // /**
325 // * An ID that must be used in subsequent 'progressUpdate' and
326 // 'progressEnd'
327 // * events to make them refer to the same progress reporting.
328 // * IDs must be unique within a debug session.
329 // */
330 // progressId: string;
332 // /**
333 // * Mandatory (short) title of the progress reporting. Shown in the UI to
334 // * describe the long running operation.
335 // */
336 // title: string;
338 // /**
339 // * The request ID that this progress report is related to. If specified a
340 // * debug adapter is expected to emit
341 // * progress events for the long running request until the request has
342 // been
343 // * either completed or cancelled.
344 // * If the request ID is omitted, the progress report is assumed to be
345 // * related to some general activity of the debug adapter.
346 // */
347 // requestId?: number;
349 // /**
350 // * If true, the request that reports progress may be canceled with a
351 // * 'cancel' request.
352 // * So this property basically controls whether the client should use UX
353 // that
354 // * supports cancellation.
355 // * Clients that don't support cancellation are allowed to ignore the
356 // * setting.
357 // */
358 // cancellable?: boolean;
360 // /**
361 // * Optional, more detailed progress message.
362 // */
363 // message?: string;
365 // /**
366 // * Optional progress percentage to display (value range: 0 to 100). If
367 // * omitted no percentage will be shown.
368 // */
369 // percentage?: number;
370 // };
371 // }
373 // interface ProgressUpdateEvent extends Event {
374 // event: 'progressUpdate';
376 // body: {
377 // /**
378 // * The ID that was introduced in the initial 'progressStart' event.
379 // */
380 // progressId: string;
382 // /**
383 // * Optional, more detailed progress message. If omitted, the previous
384 // * message (if any) is used.
385 // */
386 // message?: string;
388 // /**
389 // * Optional progress percentage to display (value range: 0 to 100). If
390 // * omitted no percentage will be shown.
391 // */
392 // percentage?: number;
393 // };
394 // }
396 // interface ProgressEndEvent extends Event {
397 // event: 'progressEnd';
399 // body: {
400 // /**
401 // * The ID that was introduced in the initial 'ProgressStartEvent'.
402 // */
403 // progressId: string;
405 // /**
406 // * Optional, more detailed progress message. If omitted, the previous
407 // * message (if any) is used.
408 // */
409 // message?: string;
410 // };
411 // }
413 void DAP::SendProgressEvent(uint64_t progress_id, const char *message,
414 uint64_t completed, uint64_t total) {
415 progress_event_reporter.Push(progress_id, message, completed, total);
418 void __attribute__((format(printf, 3, 4)))
419 DAP::SendFormattedOutput(OutputType o, const char *format, ...) {
420 char buffer[1024];
421 va_list args;
422 va_start(args, format);
423 int actual_length = vsnprintf(buffer, sizeof(buffer), format, args);
424 va_end(args);
425 SendOutput(
426 o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
429 ExceptionBreakpoint *DAP::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
430 const auto num = thread.GetStopReasonDataCount();
431 // Check to see if have hit an exception breakpoint and change the
432 // reason to "exception", but only do so if all breakpoints that were
433 // hit are exception breakpoints.
434 ExceptionBreakpoint *exc_bp = nullptr;
435 for (size_t i = 0; i < num; i += 2) {
436 // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
437 // thread.GetStopReasonDataAtIndex(i+1) will return the location
438 // within that breakpoint. We only care about the bp ID so we can
439 // see if this is an exception breakpoint that is getting hit.
440 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
441 exc_bp = GetExceptionBreakpoint(bp_id);
442 // If any breakpoint is not an exception breakpoint, then stop and
443 // report this as a normal breakpoint
444 if (exc_bp == nullptr)
445 return nullptr;
447 return exc_bp;
450 lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
451 auto tid = GetSigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
452 return target.GetProcess().GetThreadByID(tid);
455 lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
456 const uint64_t frame_id = GetUnsigned(arguments, "frameId", UINT64_MAX);
457 lldb::SBProcess process = target.GetProcess();
458 // Upper 32 bits is the thread index ID
459 lldb::SBThread thread =
460 process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id));
461 // Lower 32 bits is the frame index
462 return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
465 llvm::json::Value DAP::CreateTopLevelScopes() {
466 llvm::json::Array scopes;
467 scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS,
468 g_dap.variables.locals.GetSize(), false));
469 scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
470 g_dap.variables.globals.GetSize(), false));
471 scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
472 g_dap.variables.registers.GetSize(), false));
473 return llvm::json::Value(std::move(scopes));
476 ExpressionContext DAP::DetectExpressionContext(lldb::SBFrame frame,
477 std::string &expression) {
478 // Include the escape hatch prefix.
479 if (!expression.empty() &&
480 llvm::StringRef(expression).starts_with(g_dap.command_escape_prefix)) {
481 expression = expression.substr(g_dap.command_escape_prefix.size());
482 return ExpressionContext::Command;
485 switch (repl_mode) {
486 case ReplMode::Variable:
487 return ExpressionContext::Variable;
488 case ReplMode::Command:
489 return ExpressionContext::Command;
490 case ReplMode::Auto:
491 // To determine if the expression is a command or not, check if the first
492 // term is a variable or command. If it's a variable in scope we will prefer
493 // that behavior and give a warning to the user if they meant to invoke the
494 // operation as a command.
496 // Example use case:
497 // int p and expression "p + 1" > variable
498 // int i and expression "i" > variable
499 // int var and expression "va" > command
500 std::pair<llvm::StringRef, llvm::StringRef> token =
501 llvm::getToken(expression);
502 std::string term = token.first.str();
503 lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();
504 bool term_is_command = interpreter.CommandExists(term.c_str()) ||
505 interpreter.UserCommandExists(term.c_str()) ||
506 interpreter.AliasExists(term.c_str());
507 bool term_is_variable = frame.FindVariable(term.c_str()).IsValid();
509 // If we have both a variable and command, warn the user about the conflict.
510 if (term_is_command && term_is_variable) {
511 llvm::errs()
512 << "Warning: Expression '" << term
513 << "' is both an LLDB command and variable. It will be evaluated as "
514 "a variable. To evaluate the expression as an LLDB command, use '"
515 << g_dap.command_escape_prefix << "' as a prefix.\n";
518 // Variables take preference to commands in auto, since commands can always
519 // be called using the command_escape_prefix
520 return term_is_variable ? ExpressionContext::Variable
521 : term_is_command ? ExpressionContext::Command
522 : ExpressionContext::Variable;
525 llvm_unreachable("enum cases exhausted.");
528 bool DAP::RunLLDBCommands(llvm::StringRef prefix,
529 llvm::ArrayRef<std::string> commands) {
530 bool required_command_failed = false;
531 std::string output =
532 ::RunLLDBCommands(prefix, commands, required_command_failed);
533 SendOutput(OutputType::Console, output);
534 return !required_command_failed;
537 static llvm::Error createRunLLDBCommandsErrorMessage(llvm::StringRef category) {
538 return llvm::createStringError(
539 llvm::inconvertibleErrorCode(),
540 llvm::formatv(
541 "Failed to run {0} commands. See the Debug Console for more details.",
542 category)
543 .str()
544 .c_str());
547 llvm::Error
548 DAP::RunAttachCommands(llvm::ArrayRef<std::string> attach_commands) {
549 if (!RunLLDBCommands("Running attachCommands:", attach_commands))
550 return createRunLLDBCommandsErrorMessage("attach");
551 return llvm::Error::success();
554 llvm::Error
555 DAP::RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands) {
556 if (!RunLLDBCommands("Running launchCommands:", launch_commands))
557 return createRunLLDBCommandsErrorMessage("launch");
558 return llvm::Error::success();
561 llvm::Error DAP::RunInitCommands() {
562 if (!RunLLDBCommands("Running initCommands:", init_commands))
563 return createRunLLDBCommandsErrorMessage("initCommands");
564 return llvm::Error::success();
567 llvm::Error DAP::RunPreInitCommands() {
568 if (!RunLLDBCommands("Running preInitCommands:", pre_init_commands))
569 return createRunLLDBCommandsErrorMessage("preInitCommands");
570 return llvm::Error::success();
573 llvm::Error DAP::RunPreRunCommands() {
574 if (!RunLLDBCommands("Running preRunCommands:", pre_run_commands))
575 return createRunLLDBCommandsErrorMessage("preRunCommands");
576 return llvm::Error::success();
579 void DAP::RunPostRunCommands() {
580 RunLLDBCommands("Running postRunCommands:", post_run_commands);
582 void DAP::RunStopCommands() {
583 RunLLDBCommands("Running stopCommands:", stop_commands);
586 void DAP::RunExitCommands() {
587 RunLLDBCommands("Running exitCommands:", exit_commands);
590 void DAP::RunTerminateCommands() {
591 RunLLDBCommands("Running terminateCommands:", terminate_commands);
594 lldb::SBTarget
595 DAP::CreateTargetFromArguments(const llvm::json::Object &arguments,
596 lldb::SBError &error) {
597 // Grab the name of the program we need to debug and create a target using
598 // the given program as an argument. Executable file can be a source of target
599 // architecture and platform, if they differ from the host. Setting exe path
600 // in launch info is useless because Target.Launch() will not change
601 // architecture and platform, therefore they should be known at the target
602 // creation. We also use target triple and platform from the launch
603 // configuration, if given, since in some cases ELF file doesn't contain
604 // enough information to determine correct arch and platform (or ELF can be
605 // omitted at all), so it is good to leave the user an apportunity to specify
606 // those. Any of those three can be left empty.
607 llvm::StringRef target_triple = GetString(arguments, "targetTriple");
608 llvm::StringRef platform_name = GetString(arguments, "platformName");
609 llvm::StringRef program = GetString(arguments, "program");
610 auto target = this->debugger.CreateTarget(
611 program.data(), target_triple.data(), platform_name.data(),
612 true, // Add dependent modules.
613 error);
615 if (error.Fail()) {
616 // Update message if there was an error.
617 error.SetErrorStringWithFormat(
618 "Could not create a target for a program '%s': %s.", program.data(),
619 error.GetCString());
622 return target;
625 void DAP::SetTarget(const lldb::SBTarget target) {
626 this->target = target;
628 if (target.IsValid()) {
629 // Configure breakpoint event listeners for the target.
630 lldb::SBListener listener = this->debugger.GetListener();
631 listener.StartListeningForEvents(
632 this->target.GetBroadcaster(),
633 lldb::SBTarget::eBroadcastBitBreakpointChanged);
634 listener.StartListeningForEvents(this->broadcaster,
635 eBroadcastBitStopEventThread);
639 PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
640 std::string json = ReadJSON();
641 if (json.empty())
642 return PacketStatus::EndOfFile;
644 llvm::StringRef json_sref(json);
645 llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
646 if (!json_value) {
647 auto error = json_value.takeError();
648 if (log) {
649 std::string error_str;
650 llvm::raw_string_ostream strm(error_str);
651 strm << error;
652 strm.flush();
653 *log << "error: failed to parse JSON: " << error_str << std::endl
654 << json << std::endl;
656 return PacketStatus::JSONMalformed;
659 if (log) {
660 *log << llvm::formatv("{0:2}", *json_value).str() << std::endl;
663 llvm::json::Object *object_ptr = json_value->getAsObject();
664 if (!object_ptr) {
665 if (log)
666 *log << "error: json packet isn't a object" << std::endl;
667 return PacketStatus::JSONNotObject;
669 object = *object_ptr;
670 return PacketStatus::Success;
673 bool DAP::HandleObject(const llvm::json::Object &object) {
674 const auto packet_type = GetString(object, "type");
675 if (packet_type == "request") {
676 const auto command = GetString(object, "command");
677 auto handler_pos = request_handlers.find(std::string(command));
678 if (handler_pos != request_handlers.end()) {
679 handler_pos->second(object);
680 return true; // Success
681 } else {
682 if (log)
683 *log << "error: unhandled command \"" << command.data() << "\""
684 << std::endl;
685 return false; // Fail
689 if (packet_type == "response") {
690 auto id = GetSigned(object, "request_seq", 0);
691 ResponseCallback response_handler = [](llvm::Expected<llvm::json::Value>) {
692 llvm::errs() << "Unhandled response\n";
696 std::lock_guard<std::mutex> locker(call_mutex);
697 auto inflight = inflight_reverse_requests.find(id);
698 if (inflight != inflight_reverse_requests.end()) {
699 response_handler = std::move(inflight->second);
700 inflight_reverse_requests.erase(inflight);
704 // Result should be given, use null if not.
705 if (GetBoolean(object, "success", false)) {
706 llvm::json::Value Result = nullptr;
707 if (auto *B = object.get("body")) {
708 Result = std::move(*B);
710 response_handler(Result);
711 } else {
712 llvm::StringRef message = GetString(object, "message");
713 if (message.empty()) {
714 message = "Unknown error, response failed";
716 response_handler(llvm::createStringError(
717 std::error_code(-1, std::generic_category()), message));
720 return true;
723 return false;
726 llvm::Error DAP::Loop() {
727 while (!disconnecting) {
728 llvm::json::Object object;
729 lldb_dap::PacketStatus status = GetNextObject(object);
731 if (status == lldb_dap::PacketStatus::EndOfFile) {
732 break;
735 if (status != lldb_dap::PacketStatus::Success) {
736 return llvm::createStringError(llvm::inconvertibleErrorCode(),
737 "failed to send packet");
740 if (!HandleObject(object)) {
741 return llvm::createStringError(llvm::inconvertibleErrorCode(),
742 "unhandled packet");
746 return llvm::Error::success();
749 void DAP::SendReverseRequest(llvm::StringRef command,
750 llvm::json::Value arguments,
751 ResponseCallback callback) {
752 int64_t id;
754 std::lock_guard<std::mutex> locker(call_mutex);
755 id = ++reverse_request_seq;
756 inflight_reverse_requests.emplace(id, std::move(callback));
759 SendJSON(llvm::json::Object{
760 {"type", "request"},
761 {"seq", id},
762 {"command", command},
763 {"arguments", std::move(arguments)},
767 void DAP::RegisterRequestCallback(std::string request,
768 RequestCallback callback) {
769 request_handlers[request] = callback;
772 lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
773 lldb::SBError error;
774 lldb::SBProcess process = target.GetProcess();
775 if (!process.IsValid()) {
776 error.SetErrorString("invalid process");
777 return error;
779 auto timeout_time =
780 std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
781 while (std::chrono::steady_clock::now() < timeout_time) {
782 const auto state = process.GetState();
783 switch (state) {
784 case lldb::eStateAttaching:
785 case lldb::eStateConnected:
786 case lldb::eStateInvalid:
787 case lldb::eStateLaunching:
788 case lldb::eStateRunning:
789 case lldb::eStateStepping:
790 case lldb::eStateSuspended:
791 break;
792 case lldb::eStateDetached:
793 error.SetErrorString("process detached during launch or attach");
794 return error;
795 case lldb::eStateExited:
796 error.SetErrorString("process exited during launch or attach");
797 return error;
798 case lldb::eStateUnloaded:
799 error.SetErrorString("process unloaded during launch or attach");
800 return error;
801 case lldb::eStateCrashed:
802 case lldb::eStateStopped:
803 return lldb::SBError(); // Success!
805 std::this_thread::sleep_for(std::chrono::microseconds(250));
807 error.SetErrorStringWithFormat("process failed to stop within %u seconds",
808 seconds);
809 return error;
812 void Variables::Clear() {
813 locals.Clear();
814 globals.Clear();
815 registers.Clear();
816 expandable_variables.clear();
819 int64_t Variables::GetNewVariableReference(bool is_permanent) {
820 if (is_permanent)
821 return next_permanent_var_ref++;
822 return next_temporary_var_ref++;
825 bool Variables::IsPermanentVariableReference(int64_t var_ref) {
826 return var_ref >= PermanentVariableStartIndex;
829 lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
830 if (IsPermanentVariableReference(var_ref)) {
831 auto pos = expandable_permanent_variables.find(var_ref);
832 if (pos != expandable_permanent_variables.end())
833 return pos->second;
834 } else {
835 auto pos = expandable_variables.find(var_ref);
836 if (pos != expandable_variables.end())
837 return pos->second;
839 return lldb::SBValue();
842 int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
843 bool is_permanent) {
844 int64_t var_ref = GetNewVariableReference(is_permanent);
845 if (is_permanent)
846 expandable_permanent_variables.insert(std::make_pair(var_ref, variable));
847 else
848 expandable_variables.insert(std::make_pair(var_ref, variable));
849 return var_ref;
852 bool StartDebuggingRequestHandler::DoExecute(
853 lldb::SBDebugger debugger, char **command,
854 lldb::SBCommandReturnObject &result) {
855 // Command format like: `startDebugging <launch|attach> <configuration>`
856 if (!command) {
857 result.SetError("Invalid use of startDebugging");
858 result.SetStatus(lldb::eReturnStatusFailed);
859 return false;
862 if (!command[0] || llvm::StringRef(command[0]).empty()) {
863 result.SetError("startDebugging request type missing.");
864 result.SetStatus(lldb::eReturnStatusFailed);
865 return false;
868 if (!command[1] || llvm::StringRef(command[1]).empty()) {
869 result.SetError("configuration missing.");
870 result.SetStatus(lldb::eReturnStatusFailed);
871 return false;
874 llvm::StringRef request{command[0]};
875 std::string raw_configuration{command[1]};
877 int i = 2;
878 while (command[i]) {
879 raw_configuration.append(" ").append(command[i]);
882 llvm::Expected<llvm::json::Value> configuration =
883 llvm::json::parse(raw_configuration);
885 if (!configuration) {
886 llvm::Error err = configuration.takeError();
887 std::string msg =
888 "Failed to parse json configuration: " + llvm::toString(std::move(err));
889 result.SetError(msg.c_str());
890 result.SetStatus(lldb::eReturnStatusFailed);
891 return false;
894 g_dap.SendReverseRequest(
895 "startDebugging",
896 llvm::json::Object{{"request", request},
897 {"configuration", std::move(*configuration)}},
898 [](llvm::Expected<llvm::json::Value> value) {
899 if (!value) {
900 llvm::Error err = value.takeError();
901 llvm::errs() << "reverse start debugging request failed: "
902 << llvm::toString(std::move(err)) << "\n";
906 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
908 return true;
911 bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
912 char **command,
913 lldb::SBCommandReturnObject &result) {
914 // Command format like: `repl-mode <variable|command|auto>?`
915 // If a new mode is not specified report the current mode.
916 if (!command || llvm::StringRef(command[0]).empty()) {
917 std::string mode;
918 switch (g_dap.repl_mode) {
919 case ReplMode::Variable:
920 mode = "variable";
921 break;
922 case ReplMode::Command:
923 mode = "command";
924 break;
925 case ReplMode::Auto:
926 mode = "auto";
927 break;
930 result.Printf("lldb-dap repl-mode %s.\n", mode.c_str());
931 result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
933 return true;
936 llvm::StringRef new_mode{command[0]};
938 if (new_mode == "variable") {
939 g_dap.repl_mode = ReplMode::Variable;
940 } else if (new_mode == "command") {
941 g_dap.repl_mode = ReplMode::Command;
942 } else if (new_mode == "auto") {
943 g_dap.repl_mode = ReplMode::Auto;
944 } else {
945 lldb::SBStream error_message;
946 error_message.Printf("Invalid repl-mode '%s'. Expected one of 'variable', "
947 "'command' or 'auto'.\n",
948 new_mode.data());
949 result.SetError(error_message.GetData());
950 return false;
953 result.Printf("lldb-dap repl-mode %s set.\n", new_mode.data());
954 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
955 return true;
958 void DAP::SetFrameFormat(llvm::StringRef format) {
959 if (format.empty())
960 return;
961 lldb::SBError error;
962 g_dap.frame_format = lldb::SBFormat(format.str().c_str(), error);
963 if (error.Fail()) {
964 g_dap.SendOutput(
965 OutputType::Console,
966 llvm::formatv(
967 "The provided frame format '{0}' couldn't be parsed: {1}\n", format,
968 error.GetCString())
969 .str());
973 void DAP::SetThreadFormat(llvm::StringRef format) {
974 if (format.empty())
975 return;
976 lldb::SBError error;
977 g_dap.thread_format = lldb::SBFormat(format.str().c_str(), error);
978 if (error.Fail()) {
979 g_dap.SendOutput(
980 OutputType::Console,
981 llvm::formatv(
982 "The provided thread format '{0}' couldn't be parsed: {1}\n",
983 format, error.GetCString())
984 .str());
988 } // namespace lldb_dap