Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / lldb / tools / lldb-dap / DAP.cpp
blob6fa0514bfe32bdfd11fefb8546b8eae8290ff177
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 "llvm/ADT/StringExtras.h"
18 #include "llvm/Support/FormatVariadic.h"
20 #if defined(_WIN32)
21 #define NOMINMAX
22 #include <fcntl.h>
23 #include <io.h>
24 #include <windows.h>
25 #endif
27 using namespace lldb_dap;
29 namespace lldb_dap {
31 DAP g_dap;
33 DAP::DAP()
34 : broadcaster("lldb-dap"),
35 exception_breakpoints(
36 {{"cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus},
37 {"cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus},
38 {"objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC},
39 {"objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC},
40 {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift},
41 {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}),
42 focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false),
43 stop_at_entry(false), is_attach(false),
44 enable_auto_variable_summaries(false),
45 enable_synthetic_child_debugging(false),
46 restarting_process_id(LLDB_INVALID_PROCESS_ID),
47 configuration_done_sent(false), waiting_for_run_in_terminal(false),
48 progress_event_reporter(
49 [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
50 reverse_request_seq(0), repl_mode(ReplMode::Auto),
51 auto_repl_mode_collision_warning(false) {
52 const char *log_file_path = getenv("LLDBDAP_LOG");
53 #if defined(_WIN32)
54 // Windows opens stdout and stdin in text mode which converts \n to 13,10
55 // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
56 // fixes this.
57 int result = _setmode(fileno(stdout), _O_BINARY);
58 assert(result);
59 result = _setmode(fileno(stdin), _O_BINARY);
60 (void)result;
61 assert(result);
62 #endif
63 if (log_file_path)
64 log.reset(new std::ofstream(log_file_path));
67 DAP::~DAP() = default;
69 ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const std::string &filter) {
70 for (auto &bp : exception_breakpoints) {
71 if (bp.filter == filter)
72 return &bp;
74 return nullptr;
77 ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
78 for (auto &bp : exception_breakpoints) {
79 if (bp.bp.GetID() == bp_id)
80 return &bp;
82 return nullptr;
85 // Send the JSON in "json_str" to the "out" stream. Correctly send the
86 // "Content-Length:" field followed by the length, followed by the raw
87 // JSON bytes.
88 void DAP::SendJSON(const std::string &json_str) {
89 output.write_full("Content-Length: ");
90 output.write_full(llvm::utostr(json_str.size()));
91 output.write_full("\r\n\r\n");
92 output.write_full(json_str);
95 // Serialize the JSON value into a string and send the JSON packet to
96 // the "out" stream.
97 void DAP::SendJSON(const llvm::json::Value &json) {
98 std::string s;
99 llvm::raw_string_ostream strm(s);
100 strm << json;
101 static std::mutex mutex;
102 std::lock_guard<std::mutex> locker(mutex);
103 std::string json_str = strm.str();
104 SendJSON(json_str);
106 if (log) {
107 *log << "<-- " << std::endl
108 << "Content-Length: " << json_str.size() << "\r\n\r\n"
109 << llvm::formatv("{0:2}", json).str() << std::endl;
113 // Read a JSON packet from the "in" stream.
114 std::string DAP::ReadJSON() {
115 std::string length_str;
116 std::string json_str;
117 int length;
119 if (!input.read_expected(log.get(), "Content-Length: "))
120 return json_str;
122 if (!input.read_line(log.get(), length_str))
123 return json_str;
125 if (!llvm::to_integer(length_str, length))
126 return json_str;
128 if (!input.read_expected(log.get(), "\r\n"))
129 return json_str;
131 if (!input.read_full(log.get(), length, json_str))
132 return json_str;
134 if (log)
135 *log << "--> " << std::endl << "Content-Length: " << length << "\r\n\r\n";
137 return json_str;
140 // "OutputEvent": {
141 // "allOf": [ { "$ref": "#/definitions/Event" }, {
142 // "type": "object",
143 // "description": "Event message for 'output' event type. The event
144 // indicates that the target has produced some output.",
145 // "properties": {
146 // "event": {
147 // "type": "string",
148 // "enum": [ "output" ]
149 // },
150 // "body": {
151 // "type": "object",
152 // "properties": {
153 // "category": {
154 // "type": "string",
155 // "description": "The output category. If not specified,
156 // 'console' is assumed.",
157 // "_enum": [ "console", "stdout", "stderr", "telemetry" ]
158 // },
159 // "output": {
160 // "type": "string",
161 // "description": "The output to report."
162 // },
163 // "variablesReference": {
164 // "type": "number",
165 // "description": "If an attribute 'variablesReference' exists
166 // and its value is > 0, the output contains
167 // objects which can be retrieved by passing
168 // variablesReference to the VariablesRequest."
169 // },
170 // "source": {
171 // "$ref": "#/definitions/Source",
172 // "description": "An optional source location where the output
173 // was produced."
174 // },
175 // "line": {
176 // "type": "integer",
177 // "description": "An optional source location line where the
178 // output was produced."
179 // },
180 // "column": {
181 // "type": "integer",
182 // "description": "An optional source location column where the
183 // output was produced."
184 // },
185 // "data": {
186 // "type":["array","boolean","integer","null","number","object",
187 // "string"],
188 // "description": "Optional data to report. For the 'telemetry'
189 // category the data will be sent to telemetry, for
190 // the other categories the data is shown in JSON
191 // format."
192 // }
193 // },
194 // "required": ["output"]
195 // }
196 // },
197 // "required": [ "event", "body" ]
198 // }]
199 // }
200 void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
201 if (output.empty())
202 return;
204 llvm::json::Object event(CreateEventObject("output"));
205 llvm::json::Object body;
206 const char *category = nullptr;
207 switch (o) {
208 case OutputType::Console:
209 category = "console";
210 break;
211 case OutputType::Stdout:
212 category = "stdout";
213 break;
214 case OutputType::Stderr:
215 category = "stderr";
216 break;
217 case OutputType::Telemetry:
218 category = "telemetry";
219 break;
221 body.try_emplace("category", category);
222 EmplaceSafeString(body, "output", output.str());
223 event.try_emplace("body", std::move(body));
224 SendJSON(llvm::json::Value(std::move(event)));
227 // interface ProgressStartEvent extends Event {
228 // event: 'progressStart';
230 // body: {
231 // /**
232 // * An ID that must be used in subsequent 'progressUpdate' and
233 // 'progressEnd'
234 // * events to make them refer to the same progress reporting.
235 // * IDs must be unique within a debug session.
236 // */
237 // progressId: string;
239 // /**
240 // * Mandatory (short) title of the progress reporting. Shown in the UI to
241 // * describe the long running operation.
242 // */
243 // title: string;
245 // /**
246 // * The request ID that this progress report is related to. If specified a
247 // * debug adapter is expected to emit
248 // * progress events for the long running request until the request has
249 // been
250 // * either completed or cancelled.
251 // * If the request ID is omitted, the progress report is assumed to be
252 // * related to some general activity of the debug adapter.
253 // */
254 // requestId?: number;
256 // /**
257 // * If true, the request that reports progress may be canceled with a
258 // * 'cancel' request.
259 // * So this property basically controls whether the client should use UX
260 // that
261 // * supports cancellation.
262 // * Clients that don't support cancellation are allowed to ignore the
263 // * setting.
264 // */
265 // cancellable?: boolean;
267 // /**
268 // * Optional, more detailed progress message.
269 // */
270 // message?: string;
272 // /**
273 // * Optional progress percentage to display (value range: 0 to 100). If
274 // * omitted no percentage will be shown.
275 // */
276 // percentage?: number;
277 // };
278 // }
280 // interface ProgressUpdateEvent extends Event {
281 // event: 'progressUpdate';
283 // body: {
284 // /**
285 // * The ID that was introduced in the initial 'progressStart' event.
286 // */
287 // progressId: string;
289 // /**
290 // * Optional, more detailed progress message. If omitted, the previous
291 // * message (if any) is used.
292 // */
293 // message?: string;
295 // /**
296 // * Optional progress percentage to display (value range: 0 to 100). If
297 // * omitted no percentage will be shown.
298 // */
299 // percentage?: number;
300 // };
301 // }
303 // interface ProgressEndEvent extends Event {
304 // event: 'progressEnd';
306 // body: {
307 // /**
308 // * The ID that was introduced in the initial 'ProgressStartEvent'.
309 // */
310 // progressId: string;
312 // /**
313 // * Optional, more detailed progress message. If omitted, the previous
314 // * message (if any) is used.
315 // */
316 // message?: string;
317 // };
318 // }
320 void DAP::SendProgressEvent(uint64_t progress_id, const char *message,
321 uint64_t completed, uint64_t total) {
322 progress_event_reporter.Push(progress_id, message, completed, total);
325 void __attribute__((format(printf, 3, 4)))
326 DAP::SendFormattedOutput(OutputType o, const char *format, ...) {
327 char buffer[1024];
328 va_list args;
329 va_start(args, format);
330 int actual_length = vsnprintf(buffer, sizeof(buffer), format, args);
331 va_end(args);
332 SendOutput(
333 o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
336 ExceptionBreakpoint *DAP::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
337 const auto num = thread.GetStopReasonDataCount();
338 // Check to see if have hit an exception breakpoint and change the
339 // reason to "exception", but only do so if all breakpoints that were
340 // hit are exception breakpoints.
341 ExceptionBreakpoint *exc_bp = nullptr;
342 for (size_t i = 0; i < num; i += 2) {
343 // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
344 // thread.GetStopReasonDataAtIndex(i+1) will return the location
345 // within that breakpoint. We only care about the bp ID so we can
346 // see if this is an exception breakpoint that is getting hit.
347 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
348 exc_bp = GetExceptionBreakpoint(bp_id);
349 // If any breakpoint is not an exception breakpoint, then stop and
350 // report this as a normal breakpoint
351 if (exc_bp == nullptr)
352 return nullptr;
354 return exc_bp;
357 lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
358 auto tid = GetSigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
359 return target.GetProcess().GetThreadByID(tid);
362 lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
363 const uint64_t frame_id = GetUnsigned(arguments, "frameId", UINT64_MAX);
364 lldb::SBProcess process = target.GetProcess();
365 // Upper 32 bits is the thread index ID
366 lldb::SBThread thread =
367 process.GetThreadByIndexID(GetLLDBThreadIndexID(frame_id));
368 // Lower 32 bits is the frame index
369 return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
372 llvm::json::Value DAP::CreateTopLevelScopes() {
373 llvm::json::Array scopes;
374 scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS,
375 g_dap.variables.locals.GetSize(), false));
376 scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
377 g_dap.variables.globals.GetSize(), false));
378 scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
379 g_dap.variables.registers.GetSize(), false));
380 return llvm::json::Value(std::move(scopes));
383 ExpressionContext DAP::DetectExpressionContext(lldb::SBFrame &frame,
384 std::string &text) {
385 // Include the escape hatch prefix.
386 if (!text.empty() &&
387 llvm::StringRef(text).starts_with(g_dap.command_escape_prefix)) {
388 text = text.substr(g_dap.command_escape_prefix.size());
389 return ExpressionContext::Command;
392 switch (repl_mode) {
393 case ReplMode::Variable:
394 return ExpressionContext::Variable;
395 case ReplMode::Command:
396 return ExpressionContext::Command;
397 case ReplMode::Auto:
398 // If the frame is invalid then there is no variables to complete, assume
399 // this is an lldb command instead.
400 if (!frame.IsValid()) {
401 return ExpressionContext::Command;
404 lldb::SBCommandReturnObject result;
405 debugger.GetCommandInterpreter().ResolveCommand(text.data(), result);
407 // If this command is a simple expression like `var + 1` check if there is
408 // a local variable name that is in the current expression. If so, ensure
409 // the expression runs in the variable context.
410 lldb::SBValueList variables = frame.GetVariables(true, true, true, true);
411 llvm::StringRef input = text;
412 for (uint32_t i = 0; i < variables.GetSize(); i++) {
413 llvm::StringRef name = variables.GetValueAtIndex(i).GetName();
414 // Check both directions in case the input is a partial of a variable
415 // (e.g. input = `va` and local variable = `var1`).
416 if (input.contains(name) || name.contains(input)) {
417 if (!auto_repl_mode_collision_warning) {
418 llvm::errs() << "Variable expression '" << text
419 << "' is hiding an lldb command, prefix an expression "
420 "with '"
421 << g_dap.command_escape_prefix
422 << "' to ensure it runs as a lldb command.\n";
423 auto_repl_mode_collision_warning = true;
425 return ExpressionContext::Variable;
429 if (result.Succeeded()) {
430 return ExpressionContext::Command;
434 return ExpressionContext::Variable;
437 void DAP::RunLLDBCommands(llvm::StringRef prefix,
438 const std::vector<std::string> &commands) {
439 SendOutput(OutputType::Console,
440 llvm::StringRef(::RunLLDBCommands(prefix, commands)));
443 void DAP::RunInitCommands() {
444 RunLLDBCommands("Running initCommands:", init_commands);
447 void DAP::RunPreRunCommands() {
448 RunLLDBCommands("Running preRunCommands:", pre_run_commands);
451 void DAP::RunStopCommands() {
452 RunLLDBCommands("Running stopCommands:", stop_commands);
455 void DAP::RunExitCommands() {
456 RunLLDBCommands("Running exitCommands:", exit_commands);
459 void DAP::RunTerminateCommands() {
460 RunLLDBCommands("Running terminateCommands:", terminate_commands);
463 lldb::SBTarget
464 DAP::CreateTargetFromArguments(const llvm::json::Object &arguments,
465 lldb::SBError &error) {
466 // Grab the name of the program we need to debug and create a target using
467 // the given program as an argument. Executable file can be a source of target
468 // architecture and platform, if they differ from the host. Setting exe path
469 // in launch info is useless because Target.Launch() will not change
470 // architecture and platform, therefore they should be known at the target
471 // creation. We also use target triple and platform from the launch
472 // configuration, if given, since in some cases ELF file doesn't contain
473 // enough information to determine correct arch and platform (or ELF can be
474 // omitted at all), so it is good to leave the user an apportunity to specify
475 // those. Any of those three can be left empty.
476 llvm::StringRef target_triple = GetString(arguments, "targetTriple");
477 llvm::StringRef platform_name = GetString(arguments, "platformName");
478 llvm::StringRef program = GetString(arguments, "program");
479 auto target = this->debugger.CreateTarget(
480 program.data(), target_triple.data(), platform_name.data(),
481 true, // Add dependent modules.
482 error);
484 if (error.Fail()) {
485 // Update message if there was an error.
486 error.SetErrorStringWithFormat(
487 "Could not create a target for a program '%s': %s.", program.data(),
488 error.GetCString());
491 return target;
494 void DAP::SetTarget(const lldb::SBTarget target) {
495 this->target = target;
497 if (target.IsValid()) {
498 // Configure breakpoint event listeners for the target.
499 lldb::SBListener listener = this->debugger.GetListener();
500 listener.StartListeningForEvents(
501 this->target.GetBroadcaster(),
502 lldb::SBTarget::eBroadcastBitBreakpointChanged);
503 listener.StartListeningForEvents(this->broadcaster,
504 eBroadcastBitStopEventThread);
508 PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
509 std::string json = ReadJSON();
510 if (json.empty())
511 return PacketStatus::EndOfFile;
513 llvm::StringRef json_sref(json);
514 llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
515 if (!json_value) {
516 auto error = json_value.takeError();
517 if (log) {
518 std::string error_str;
519 llvm::raw_string_ostream strm(error_str);
520 strm << error;
521 strm.flush();
522 *log << "error: failed to parse JSON: " << error_str << std::endl
523 << json << std::endl;
525 return PacketStatus::JSONMalformed;
528 if (log) {
529 *log << llvm::formatv("{0:2}", *json_value).str() << std::endl;
532 llvm::json::Object *object_ptr = json_value->getAsObject();
533 if (!object_ptr) {
534 if (log)
535 *log << "error: json packet isn't a object" << std::endl;
536 return PacketStatus::JSONNotObject;
538 object = *object_ptr;
539 return PacketStatus::Success;
542 bool DAP::HandleObject(const llvm::json::Object &object) {
543 const auto packet_type = GetString(object, "type");
544 if (packet_type == "request") {
545 const auto command = GetString(object, "command");
546 auto handler_pos = request_handlers.find(std::string(command));
547 if (handler_pos != request_handlers.end()) {
548 handler_pos->second(object);
549 return true; // Success
550 } else {
551 if (log)
552 *log << "error: unhandled command \"" << command.data() << "\""
553 << std::endl;
554 return false; // Fail
558 if (packet_type == "response") {
559 auto id = GetSigned(object, "request_seq", 0);
560 ResponseCallback response_handler = [](llvm::Expected<llvm::json::Value>) {
561 llvm::errs() << "Unhandled response\n";
565 std::lock_guard<std::mutex> locker(call_mutex);
566 auto inflight = inflight_reverse_requests.find(id);
567 if (inflight != inflight_reverse_requests.end()) {
568 response_handler = std::move(inflight->second);
569 inflight_reverse_requests.erase(inflight);
573 // Result should be given, use null if not.
574 if (GetBoolean(object, "success", false)) {
575 llvm::json::Value Result = nullptr;
576 if (auto *B = object.get("body")) {
577 Result = std::move(*B);
579 response_handler(Result);
580 } else {
581 llvm::StringRef message = GetString(object, "message");
582 if (message.empty()) {
583 message = "Unknown error, response failed";
585 response_handler(llvm::createStringError(
586 std::error_code(-1, std::generic_category()), message));
589 return true;
592 return false;
595 llvm::Error DAP::Loop() {
596 while (!sent_terminated_event) {
597 llvm::json::Object object;
598 lldb_dap::PacketStatus status = GetNextObject(object);
600 if (status == lldb_dap::PacketStatus::EndOfFile) {
601 break;
604 if (status != lldb_dap::PacketStatus::Success) {
605 return llvm::createStringError(llvm::inconvertibleErrorCode(),
606 "failed to send packet");
609 if (!HandleObject(object)) {
610 return llvm::createStringError(llvm::inconvertibleErrorCode(),
611 "unhandled packet");
615 return llvm::Error::success();
618 void DAP::SendReverseRequest(llvm::StringRef command,
619 llvm::json::Value arguments,
620 ResponseCallback callback) {
621 int64_t id;
623 std::lock_guard<std::mutex> locker(call_mutex);
624 id = ++reverse_request_seq;
625 inflight_reverse_requests.emplace(id, std::move(callback));
628 SendJSON(llvm::json::Object{
629 {"type", "request"},
630 {"seq", id},
631 {"command", command},
632 {"arguments", std::move(arguments)},
636 void DAP::RegisterRequestCallback(std::string request,
637 RequestCallback callback) {
638 request_handlers[request] = callback;
641 lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
642 lldb::SBError error;
643 lldb::SBProcess process = target.GetProcess();
644 if (!process.IsValid()) {
645 error.SetErrorString("invalid process");
646 return error;
648 auto timeout_time =
649 std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
650 while (std::chrono::steady_clock::now() < timeout_time) {
651 const auto state = process.GetState();
652 switch (state) {
653 case lldb::eStateAttaching:
654 case lldb::eStateConnected:
655 case lldb::eStateInvalid:
656 case lldb::eStateLaunching:
657 case lldb::eStateRunning:
658 case lldb::eStateStepping:
659 case lldb::eStateSuspended:
660 break;
661 case lldb::eStateDetached:
662 error.SetErrorString("process detached during launch or attach");
663 return error;
664 case lldb::eStateExited:
665 error.SetErrorString("process exited during launch or attach");
666 return error;
667 case lldb::eStateUnloaded:
668 error.SetErrorString("process unloaded during launch or attach");
669 return error;
670 case lldb::eStateCrashed:
671 case lldb::eStateStopped:
672 return lldb::SBError(); // Success!
674 std::this_thread::sleep_for(std::chrono::microseconds(250));
676 error.SetErrorStringWithFormat("process failed to stop within %u seconds",
677 seconds);
678 return error;
681 void Variables::Clear() {
682 locals.Clear();
683 globals.Clear();
684 registers.Clear();
685 expandable_variables.clear();
688 int64_t Variables::GetNewVariableReference(bool is_permanent) {
689 if (is_permanent)
690 return next_permanent_var_ref++;
691 return next_temporary_var_ref++;
694 bool Variables::IsPermanentVariableReference(int64_t var_ref) {
695 return var_ref >= PermanentVariableStartIndex;
698 lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
699 if (IsPermanentVariableReference(var_ref)) {
700 auto pos = expandable_permanent_variables.find(var_ref);
701 if (pos != expandable_permanent_variables.end())
702 return pos->second;
703 } else {
704 auto pos = expandable_variables.find(var_ref);
705 if (pos != expandable_variables.end())
706 return pos->second;
708 return lldb::SBValue();
711 int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
712 bool is_permanent) {
713 int64_t var_ref = GetNewVariableReference(is_permanent);
714 if (is_permanent)
715 expandable_permanent_variables.insert(std::make_pair(var_ref, variable));
716 else
717 expandable_variables.insert(std::make_pair(var_ref, variable));
718 return var_ref;
721 bool StartDebuggingRequestHandler::DoExecute(
722 lldb::SBDebugger debugger, char **command,
723 lldb::SBCommandReturnObject &result) {
724 // Command format like: `startDebugging <launch|attach> <configuration>`
725 if (!command) {
726 result.SetError("Invalid use of startDebugging");
727 result.SetStatus(lldb::eReturnStatusFailed);
728 return false;
731 if (!command[0] || llvm::StringRef(command[0]).empty()) {
732 result.SetError("startDebugging request type missing.");
733 result.SetStatus(lldb::eReturnStatusFailed);
734 return false;
737 if (!command[1] || llvm::StringRef(command[1]).empty()) {
738 result.SetError("configuration missing.");
739 result.SetStatus(lldb::eReturnStatusFailed);
740 return false;
743 llvm::StringRef request{command[0]};
744 std::string raw_configuration{command[1]};
746 int i = 2;
747 while (command[i]) {
748 raw_configuration.append(" ").append(command[i]);
751 llvm::Expected<llvm::json::Value> configuration =
752 llvm::json::parse(raw_configuration);
754 if (!configuration) {
755 llvm::Error err = configuration.takeError();
756 std::string msg =
757 "Failed to parse json configuration: " + llvm::toString(std::move(err));
758 result.SetError(msg.c_str());
759 result.SetStatus(lldb::eReturnStatusFailed);
760 return false;
763 g_dap.SendReverseRequest(
764 "startDebugging",
765 llvm::json::Object{{"request", request},
766 {"configuration", std::move(*configuration)}},
767 [](llvm::Expected<llvm::json::Value> value) {
768 if (!value) {
769 llvm::Error err = value.takeError();
770 llvm::errs() << "reverse start debugging request failed: "
771 << llvm::toString(std::move(err)) << "\n";
775 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
777 return true;
780 bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
781 char **command,
782 lldb::SBCommandReturnObject &result) {
783 // Command format like: `repl-mode <variable|command|auto>?`
784 // If a new mode is not specified report the current mode.
785 if (!command || llvm::StringRef(command[0]).empty()) {
786 std::string mode;
787 switch (g_dap.repl_mode) {
788 case ReplMode::Variable:
789 mode = "variable";
790 break;
791 case ReplMode::Command:
792 mode = "command";
793 break;
794 case ReplMode::Auto:
795 mode = "auto";
796 break;
799 result.Printf("lldb-dap repl-mode %s.\n", mode.c_str());
800 result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
802 return true;
805 llvm::StringRef new_mode{command[0]};
807 if (new_mode == "variable") {
808 g_dap.repl_mode = ReplMode::Variable;
809 } else if (new_mode == "command") {
810 g_dap.repl_mode = ReplMode::Command;
811 } else if (new_mode == "auto") {
812 g_dap.repl_mode = ReplMode::Auto;
813 } else {
814 lldb::SBStream error_message;
815 error_message.Printf("Invalid repl-mode '%s'. Expected one of 'variable', "
816 "'command' or 'auto'.\n",
817 new_mode.data());
818 result.SetError(error_message.GetData());
819 return false;
822 result.Printf("lldb-dap repl-mode %s set.\n", new_mode.data());
823 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
824 return true;
827 } // namespace lldb_dap