1//===-- DAP.cpp -------------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include <chrono>
10#include <cstdarg>
11#include <fstream>
12#include <mutex>
13#include <sstream>
14
15#include "DAP.h"
16#include "LLDBUtils.h"
17#include "llvm/ADT/StringExtras.h"
18#include "llvm/Support/FormatVariadic.h"
19
20#if defined(_WIN32)
21#define NOMINMAX
22#include <fcntl.h>
23#include <io.h>
24#include <windows.h>
25#endif
26
27using namespace lldb_dap;
28
29namespace lldb_dap {
30
31DAP g_dap;
32
33DAP::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(json: event.ToJSON()); }),
50 reverse_request_seq(0), repl_mode(ReplMode::Auto) {
51 const char *log_file_path = getenv(name: "LLDBDAP_LOG");
52#if defined(_WIN32)
53 // Windows opens stdout and stdin in text mode which converts \n to 13,10
54 // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
55 // fixes this.
56 int result = _setmode(fileno(stdout), _O_BINARY);
57 assert(result);
58 result = _setmode(fileno(stdin), _O_BINARY);
59 UNUSED_IF_ASSERT_DISABLED(result);
60 assert(result);
61#endif
62 if (log_file_path)
63 log.reset(p: new std::ofstream(log_file_path));
64}
65
66DAP::~DAP() = default;
67
68ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const std::string &filter) {
69 for (auto &bp : exception_breakpoints) {
70 if (bp.filter == filter)
71 return &bp;
72 }
73 return nullptr;
74}
75
76ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
77 for (auto &bp : exception_breakpoints) {
78 if (bp.bp.GetID() == bp_id)
79 return &bp;
80 }
81 return nullptr;
82}
83
84// Send the JSON in "json_str" to the "out" stream. Correctly send the
85// "Content-Length:" field followed by the length, followed by the raw
86// JSON bytes.
87void DAP::SendJSON(const std::string &json_str) {
88 output.write_full(str: "Content-Length: ");
89 output.write_full(str: llvm::utostr(X: json_str.size()));
90 output.write_full(str: "\r\n\r\n");
91 output.write_full(str: json_str);
92}
93
94// Serialize the JSON value into a string and send the JSON packet to
95// the "out" stream.
96void DAP::SendJSON(const llvm::json::Value &json) {
97 std::string s;
98 llvm::raw_string_ostream strm(s);
99 strm << json;
100 static std::mutex mutex;
101 std::lock_guard<std::mutex> locker(mutex);
102 std::string json_str = strm.str();
103 SendJSON(json_str);
104
105 if (log) {
106 *log << "<-- " << std::endl
107 << "Content-Length: " << json_str.size() << "\r\n\r\n"
108 << llvm::formatv(Fmt: "{0:2}", Vals: json).str() << std::endl;
109 }
110}
111
112// Read a JSON packet from the "in" stream.
113std::string DAP::ReadJSON() {
114 std::string length_str;
115 std::string json_str;
116 int length;
117
118 if (!input.read_expected(log: log.get(), expected: "Content-Length: "))
119 return json_str;
120
121 if (!input.read_line(log: log.get(), line&: length_str))
122 return json_str;
123
124 if (!llvm::to_integer(S: length_str, Num&: length))
125 return json_str;
126
127 if (!input.read_expected(log: log.get(), expected: "\r\n"))
128 return json_str;
129
130 if (!input.read_full(log: log.get(), length, text&: json_str))
131 return json_str;
132
133 if (log)
134 *log << "--> " << std::endl << "Content-Length: " << length << "\r\n\r\n";
135
136 return json_str;
137}
138
139// "OutputEvent": {
140// "allOf": [ { "$ref": "#/definitions/Event" }, {
141// "type": "object",
142// "description": "Event message for 'output' event type. The event
143// indicates that the target has produced some output.",
144// "properties": {
145// "event": {
146// "type": "string",
147// "enum": [ "output" ]
148// },
149// "body": {
150// "type": "object",
151// "properties": {
152// "category": {
153// "type": "string",
154// "description": "The output category. If not specified,
155// 'console' is assumed.",
156// "_enum": [ "console", "stdout", "stderr", "telemetry" ]
157// },
158// "output": {
159// "type": "string",
160// "description": "The output to report."
161// },
162// "variablesReference": {
163// "type": "number",
164// "description": "If an attribute 'variablesReference' exists
165// and its value is > 0, the output contains
166// objects which can be retrieved by passing
167// variablesReference to the VariablesRequest."
168// },
169// "source": {
170// "$ref": "#/definitions/Source",
171// "description": "An optional source location where the output
172// was produced."
173// },
174// "line": {
175// "type": "integer",
176// "description": "An optional source location line where the
177// output was produced."
178// },
179// "column": {
180// "type": "integer",
181// "description": "An optional source location column where the
182// output was produced."
183// },
184// "data": {
185// "type":["array","boolean","integer","null","number","object",
186// "string"],
187// "description": "Optional data to report. For the 'telemetry'
188// category the data will be sent to telemetry, for
189// the other categories the data is shown in JSON
190// format."
191// }
192// },
193// "required": ["output"]
194// }
195// },
196// "required": [ "event", "body" ]
197// }]
198// }
199void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
200 if (output.empty())
201 return;
202
203 llvm::json::Object event(CreateEventObject(event_name: "output"));
204 llvm::json::Object body;
205 const char *category = nullptr;
206 switch (o) {
207 case OutputType::Console:
208 category = "console";
209 break;
210 case OutputType::Stdout:
211 category = "stdout";
212 break;
213 case OutputType::Stderr:
214 category = "stderr";
215 break;
216 case OutputType::Telemetry:
217 category = "telemetry";
218 break;
219 }
220 body.try_emplace(K: "category", Args&: category);
221 EmplaceSafeString(obj&: body, key: "output", str: output.str());
222 event.try_emplace(K: "body", Args: std::move(body));
223 SendJSON(json: llvm::json::Value(std::move(event)));
224}
225
226// interface ProgressStartEvent extends Event {
227// event: 'progressStart';
228//
229// body: {
230// /**
231// * An ID that must be used in subsequent 'progressUpdate' and
232// 'progressEnd'
233// * events to make them refer to the same progress reporting.
234// * IDs must be unique within a debug session.
235// */
236// progressId: string;
237//
238// /**
239// * Mandatory (short) title of the progress reporting. Shown in the UI to
240// * describe the long running operation.
241// */
242// title: string;
243//
244// /**
245// * The request ID that this progress report is related to. If specified a
246// * debug adapter is expected to emit
247// * progress events for the long running request until the request has
248// been
249// * either completed or cancelled.
250// * If the request ID is omitted, the progress report is assumed to be
251// * related to some general activity of the debug adapter.
252// */
253// requestId?: number;
254//
255// /**
256// * If true, the request that reports progress may be canceled with a
257// * 'cancel' request.
258// * So this property basically controls whether the client should use UX
259// that
260// * supports cancellation.
261// * Clients that don't support cancellation are allowed to ignore the
262// * setting.
263// */
264// cancellable?: boolean;
265//
266// /**
267// * Optional, more detailed progress message.
268// */
269// message?: string;
270//
271// /**
272// * Optional progress percentage to display (value range: 0 to 100). If
273// * omitted no percentage will be shown.
274// */
275// percentage?: number;
276// };
277// }
278//
279// interface ProgressUpdateEvent extends Event {
280// event: 'progressUpdate';
281//
282// body: {
283// /**
284// * The ID that was introduced in the initial 'progressStart' event.
285// */
286// progressId: string;
287//
288// /**
289// * Optional, more detailed progress message. If omitted, the previous
290// * message (if any) is used.
291// */
292// message?: string;
293//
294// /**
295// * Optional progress percentage to display (value range: 0 to 100). If
296// * omitted no percentage will be shown.
297// */
298// percentage?: number;
299// };
300// }
301//
302// interface ProgressEndEvent extends Event {
303// event: 'progressEnd';
304//
305// body: {
306// /**
307// * The ID that was introduced in the initial 'ProgressStartEvent'.
308// */
309// progressId: string;
310//
311// /**
312// * Optional, more detailed progress message. If omitted, the previous
313// * message (if any) is used.
314// */
315// message?: string;
316// };
317// }
318
319void DAP::SendProgressEvent(uint64_t progress_id, const char *message,
320 uint64_t completed, uint64_t total) {
321 progress_event_reporter.Push(progress_id, message, completed, total);
322}
323
324void __attribute__((format(printf, 3, 4)))
325DAP::SendFormattedOutput(OutputType o, const char *format, ...) {
326 char buffer[1024];
327 va_list args;
328 va_start(args, format);
329 int actual_length = vsnprintf(s: buffer, maxlen: sizeof(buffer), format: format, arg: args);
330 va_end(args);
331 SendOutput(
332 o, output: llvm::StringRef(buffer, std::min<int>(a: actual_length, b: sizeof(buffer))));
333}
334
335ExceptionBreakpoint *DAP::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
336 const auto num = thread.GetStopReasonDataCount();
337 // Check to see if have hit an exception breakpoint and change the
338 // reason to "exception", but only do so if all breakpoints that were
339 // hit are exception breakpoints.
340 ExceptionBreakpoint *exc_bp = nullptr;
341 for (size_t i = 0; i < num; i += 2) {
342 // thread.GetStopReasonDataAtIndex(i) will return the bp ID and
343 // thread.GetStopReasonDataAtIndex(i+1) will return the location
344 // within that breakpoint. We only care about the bp ID so we can
345 // see if this is an exception breakpoint that is getting hit.
346 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(idx: i);
347 exc_bp = GetExceptionBreakpoint(bp_id);
348 // If any breakpoint is not an exception breakpoint, then stop and
349 // report this as a normal breakpoint
350 if (exc_bp == nullptr)
351 return nullptr;
352 }
353 return exc_bp;
354}
355
356lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
357 auto tid = GetSigned(obj: arguments, key: "threadId", LLDB_INVALID_THREAD_ID);
358 return target.GetProcess().GetThreadByID(sb_thread_id: tid);
359}
360
361lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
362 const uint64_t frame_id = GetUnsigned(obj: arguments, key: "frameId", UINT64_MAX);
363 lldb::SBProcess process = target.GetProcess();
364 // Upper 32 bits is the thread index ID
365 lldb::SBThread thread =
366 process.GetThreadByIndexID(index_id: GetLLDBThreadIndexID(dap_frame_id: frame_id));
367 // Lower 32 bits is the frame index
368 return thread.GetFrameAtIndex(idx: GetLLDBFrameID(dap_frame_id: frame_id));
369}
370
371llvm::json::Value DAP::CreateTopLevelScopes() {
372 llvm::json::Array scopes;
373 scopes.emplace_back(A: CreateScope(name: "Locals", VARREF_LOCALS,
374 namedVariables: g_dap.variables.locals.GetSize(), expensive: false));
375 scopes.emplace_back(A: CreateScope(name: "Globals", VARREF_GLOBALS,
376 namedVariables: g_dap.variables.globals.GetSize(), expensive: false));
377 scopes.emplace_back(A: CreateScope(name: "Registers", VARREF_REGS,
378 namedVariables: g_dap.variables.registers.GetSize(), expensive: false));
379 return llvm::json::Value(std::move(scopes));
380}
381
382ExpressionContext DAP::DetectExpressionContext(lldb::SBFrame frame,
383 std::string &expression) {
384 // Include the escape hatch prefix.
385 if (!expression.empty() &&
386 llvm::StringRef(expression).starts_with(Prefix: g_dap.command_escape_prefix)) {
387 expression = expression.substr(pos: g_dap.command_escape_prefix.size());
388 return ExpressionContext::Command;
389 }
390
391 switch (repl_mode) {
392 case ReplMode::Variable:
393 return ExpressionContext::Variable;
394 case ReplMode::Command:
395 return ExpressionContext::Command;
396 case ReplMode::Auto:
397 // To determine if the expression is a command or not, check if the first
398 // term is a variable or command. If it's a variable in scope we will prefer
399 // that behavior and give a warning to the user if they meant to invoke the
400 // operation as a command.
401 //
402 // Example use case:
403 // int p and expression "p + 1" > variable
404 // int i and expression "i" > variable
405 // int var and expression "va" > command
406 std::pair<llvm::StringRef, llvm::StringRef> token =
407 llvm::getToken(Source: expression);
408 std::string term = token.first.str();
409 lldb::SBCommandReturnObject result;
410 debugger.GetCommandInterpreter().ResolveCommand(command_line: term.c_str(), result);
411 bool term_is_command = result.Succeeded();
412 bool term_is_variable = frame.FindVariable(var_name: term.c_str()).IsValid();
413
414 // If we have both a variable and command, warn the user about the conflict.
415 if (term_is_command && term_is_variable) {
416 llvm::errs()
417 << "Warning: Expression '" << term
418 << "' is both an LLDB command and variable. It will be evaluated as "
419 "a variable. To evaluate the expression as an LLDB command, use '"
420 << g_dap.command_escape_prefix << "' as a prefix.\n";
421 }
422
423 // Variables take preference to commands in auto, since commands can always
424 // be called using the command_escape_prefix
425 return term_is_variable ? ExpressionContext::Variable
426 : term_is_command ? ExpressionContext::Command
427 : ExpressionContext::Variable;
428 }
429
430 llvm_unreachable("enum cases exhausted.");
431}
432
433bool DAP::RunLLDBCommands(llvm::StringRef prefix,
434 llvm::ArrayRef<std::string> commands) {
435 bool required_command_failed = false;
436 std::string output =
437 ::RunLLDBCommands(prefix, commands, required_command_failed);
438 SendOutput(o: OutputType::Console, output);
439 return !required_command_failed;
440}
441
442static llvm::Error createRunLLDBCommandsErrorMessage(llvm::StringRef category) {
443 return llvm::createStringError(
444 EC: llvm::inconvertibleErrorCode(),
445 Msg: llvm::formatv(
446 Fmt: "Failed to run {0} commands. See the Debug Console for more details.",
447 Vals&: category)
448 .str()
449 .c_str());
450}
451
452llvm::Error
453DAP::RunAttachCommands(llvm::ArrayRef<std::string> attach_commands) {
454 if (!RunLLDBCommands(prefix: "Running attachCommands:", commands: attach_commands))
455 return createRunLLDBCommandsErrorMessage(category: "attach");
456 return llvm::Error::success();
457}
458
459llvm::Error
460DAP::RunLaunchCommands(llvm::ArrayRef<std::string> launch_commands) {
461 if (!RunLLDBCommands(prefix: "Running launchCommands:", commands: launch_commands))
462 return createRunLLDBCommandsErrorMessage(category: "launch");
463 return llvm::Error::success();
464}
465
466llvm::Error DAP::RunInitCommands() {
467 if (!RunLLDBCommands(prefix: "Running initCommands:", commands: init_commands))
468 return createRunLLDBCommandsErrorMessage(category: "initCommands");
469 return llvm::Error::success();
470}
471
472llvm::Error DAP::RunPreRunCommands() {
473 if (!RunLLDBCommands(prefix: "Running preRunCommands:", commands: pre_run_commands))
474 return createRunLLDBCommandsErrorMessage(category: "preRunCommands");
475 return llvm::Error::success();
476}
477
478void DAP::RunPostRunCommands() {
479 RunLLDBCommands(prefix: "Running postRunCommands:", commands: post_run_commands);
480}
481void DAP::RunStopCommands() {
482 RunLLDBCommands(prefix: "Running stopCommands:", commands: stop_commands);
483}
484
485void DAP::RunExitCommands() {
486 RunLLDBCommands(prefix: "Running exitCommands:", commands: exit_commands);
487}
488
489void DAP::RunTerminateCommands() {
490 RunLLDBCommands(prefix: "Running terminateCommands:", commands: terminate_commands);
491}
492
493lldb::SBTarget
494DAP::CreateTargetFromArguments(const llvm::json::Object &arguments,
495 lldb::SBError &error) {
496 // Grab the name of the program we need to debug and create a target using
497 // the given program as an argument. Executable file can be a source of target
498 // architecture and platform, if they differ from the host. Setting exe path
499 // in launch info is useless because Target.Launch() will not change
500 // architecture and platform, therefore they should be known at the target
501 // creation. We also use target triple and platform from the launch
502 // configuration, if given, since in some cases ELF file doesn't contain
503 // enough information to determine correct arch and platform (or ELF can be
504 // omitted at all), so it is good to leave the user an apportunity to specify
505 // those. Any of those three can be left empty.
506 llvm::StringRef target_triple = GetString(obj: arguments, key: "targetTriple");
507 llvm::StringRef platform_name = GetString(obj: arguments, key: "platformName");
508 llvm::StringRef program = GetString(obj: arguments, key: "program");
509 auto target = this->debugger.CreateTarget(
510 filename: program.data(), target_triple: target_triple.data(), platform_name: platform_name.data(),
511 add_dependent_modules: true, // Add dependent modules.
512 error);
513
514 if (error.Fail()) {
515 // Update message if there was an error.
516 error.SetErrorStringWithFormat(
517 "Could not create a target for a program '%s': %s.", program.data(),
518 error.GetCString());
519 }
520
521 return target;
522}
523
524void DAP::SetTarget(const lldb::SBTarget target) {
525 this->target = target;
526
527 if (target.IsValid()) {
528 // Configure breakpoint event listeners for the target.
529 lldb::SBListener listener = this->debugger.GetListener();
530 listener.StartListeningForEvents(
531 broadcaster: this->target.GetBroadcaster(),
532 event_mask: lldb::SBTarget::eBroadcastBitBreakpointChanged);
533 listener.StartListeningForEvents(broadcaster: this->broadcaster,
534 event_mask: eBroadcastBitStopEventThread);
535 }
536}
537
538PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
539 std::string json = ReadJSON();
540 if (json.empty())
541 return PacketStatus::EndOfFile;
542
543 llvm::StringRef json_sref(json);
544 llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(JSON: json_sref);
545 if (!json_value) {
546 auto error = json_value.takeError();
547 if (log) {
548 std::string error_str;
549 llvm::raw_string_ostream strm(error_str);
550 strm << error;
551 strm.flush();
552 *log << "error: failed to parse JSON: " << error_str << std::endl
553 << json << std::endl;
554 }
555 return PacketStatus::JSONMalformed;
556 }
557
558 if (log) {
559 *log << llvm::formatv(Fmt: "{0:2}", Vals&: *json_value).str() << std::endl;
560 }
561
562 llvm::json::Object *object_ptr = json_value->getAsObject();
563 if (!object_ptr) {
564 if (log)
565 *log << "error: json packet isn't a object" << std::endl;
566 return PacketStatus::JSONNotObject;
567 }
568 object = *object_ptr;
569 return PacketStatus::Success;
570}
571
572bool DAP::HandleObject(const llvm::json::Object &object) {
573 const auto packet_type = GetString(obj: object, key: "type");
574 if (packet_type == "request") {
575 const auto command = GetString(obj: object, key: "command");
576 auto handler_pos = request_handlers.find(x: std::string(command));
577 if (handler_pos != request_handlers.end()) {
578 handler_pos->second(object);
579 return true; // Success
580 } else {
581 if (log)
582 *log << "error: unhandled command \"" << command.data() << "\""
583 << std::endl;
584 return false; // Fail
585 }
586 }
587
588 if (packet_type == "response") {
589 auto id = GetSigned(obj: object, key: "request_seq", fail_value: 0);
590 ResponseCallback response_handler = [](llvm::Expected<llvm::json::Value>) {
591 llvm::errs() << "Unhandled response\n";
592 };
593
594 {
595 std::lock_guard<std::mutex> locker(call_mutex);
596 auto inflight = inflight_reverse_requests.find(x: id);
597 if (inflight != inflight_reverse_requests.end()) {
598 response_handler = std::move(inflight->second);
599 inflight_reverse_requests.erase(position: inflight);
600 }
601 }
602
603 // Result should be given, use null if not.
604 if (GetBoolean(obj: object, key: "success", fail_value: false)) {
605 llvm::json::Value Result = nullptr;
606 if (auto *B = object.get(K: "body")) {
607 Result = std::move(*B);
608 }
609 response_handler(Result);
610 } else {
611 llvm::StringRef message = GetString(obj: object, key: "message");
612 if (message.empty()) {
613 message = "Unknown error, response failed";
614 }
615 response_handler(llvm::createStringError(
616 EC: std::error_code(-1, std::generic_category()), S: message));
617 }
618
619 return true;
620 }
621
622 return false;
623}
624
625llvm::Error DAP::Loop() {
626 while (!sent_terminated_event) {
627 llvm::json::Object object;
628 lldb_dap::PacketStatus status = GetNextObject(object);
629
630 if (status == lldb_dap::PacketStatus::EndOfFile) {
631 break;
632 }
633
634 if (status != lldb_dap::PacketStatus::Success) {
635 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
636 Msg: "failed to send packet");
637 }
638
639 if (!HandleObject(object)) {
640 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
641 Msg: "unhandled packet");
642 }
643 }
644
645 return llvm::Error::success();
646}
647
648void DAP::SendReverseRequest(llvm::StringRef command,
649 llvm::json::Value arguments,
650 ResponseCallback callback) {
651 int64_t id;
652 {
653 std::lock_guard<std::mutex> locker(call_mutex);
654 id = ++reverse_request_seq;
655 inflight_reverse_requests.emplace(args&: id, args: std::move(callback));
656 }
657
658 SendJSON(json: llvm::json::Object{
659 {.K: "type", .V: "request"},
660 {.K: "seq", .V: id},
661 {.K: "command", .V: command},
662 {.K: "arguments", .V: std::move(arguments)},
663 });
664}
665
666void DAP::RegisterRequestCallback(std::string request,
667 RequestCallback callback) {
668 request_handlers[request] = callback;
669}
670
671lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
672 lldb::SBError error;
673 lldb::SBProcess process = target.GetProcess();
674 if (!process.IsValid()) {
675 error.SetErrorString("invalid process");
676 return error;
677 }
678 auto timeout_time =
679 std::chrono::steady_clock::now() + std::chrono::seconds(seconds);
680 while (std::chrono::steady_clock::now() < timeout_time) {
681 const auto state = process.GetState();
682 switch (state) {
683 case lldb::eStateAttaching:
684 case lldb::eStateConnected:
685 case lldb::eStateInvalid:
686 case lldb::eStateLaunching:
687 case lldb::eStateRunning:
688 case lldb::eStateStepping:
689 case lldb::eStateSuspended:
690 break;
691 case lldb::eStateDetached:
692 error.SetErrorString("process detached during launch or attach");
693 return error;
694 case lldb::eStateExited:
695 error.SetErrorString("process exited during launch or attach");
696 return error;
697 case lldb::eStateUnloaded:
698 error.SetErrorString("process unloaded during launch or attach");
699 return error;
700 case lldb::eStateCrashed:
701 case lldb::eStateStopped:
702 return lldb::SBError(); // Success!
703 }
704 std::this_thread::sleep_for(rtime: std::chrono::microseconds(250));
705 }
706 error.SetErrorStringWithFormat("process failed to stop within %u seconds",
707 seconds);
708 return error;
709}
710
711void Variables::Clear() {
712 locals.Clear();
713 globals.Clear();
714 registers.Clear();
715 expandable_variables.clear();
716}
717
718int64_t Variables::GetNewVariableReference(bool is_permanent) {
719 if (is_permanent)
720 return next_permanent_var_ref++;
721 return next_temporary_var_ref++;
722}
723
724bool Variables::IsPermanentVariableReference(int64_t var_ref) {
725 return var_ref >= PermanentVariableStartIndex;
726}
727
728lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
729 if (IsPermanentVariableReference(var_ref)) {
730 auto pos = expandable_permanent_variables.find(Val: var_ref);
731 if (pos != expandable_permanent_variables.end())
732 return pos->second;
733 } else {
734 auto pos = expandable_variables.find(Val: var_ref);
735 if (pos != expandable_variables.end())
736 return pos->second;
737 }
738 return lldb::SBValue();
739}
740
741int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
742 bool is_permanent) {
743 int64_t var_ref = GetNewVariableReference(is_permanent);
744 if (is_permanent)
745 expandable_permanent_variables.insert(KV: std::make_pair(x&: var_ref, y&: variable));
746 else
747 expandable_variables.insert(KV: std::make_pair(x&: var_ref, y&: variable));
748 return var_ref;
749}
750
751bool StartDebuggingRequestHandler::DoExecute(
752 lldb::SBDebugger debugger, char **command,
753 lldb::SBCommandReturnObject &result) {
754 // Command format like: `startDebugging <launch|attach> <configuration>`
755 if (!command) {
756 result.SetError("Invalid use of startDebugging");
757 result.SetStatus(lldb::eReturnStatusFailed);
758 return false;
759 }
760
761 if (!command[0] || llvm::StringRef(command[0]).empty()) {
762 result.SetError("startDebugging request type missing.");
763 result.SetStatus(lldb::eReturnStatusFailed);
764 return false;
765 }
766
767 if (!command[1] || llvm::StringRef(command[1]).empty()) {
768 result.SetError("configuration missing.");
769 result.SetStatus(lldb::eReturnStatusFailed);
770 return false;
771 }
772
773 llvm::StringRef request{command[0]};
774 std::string raw_configuration{command[1]};
775
776 int i = 2;
777 while (command[i]) {
778 raw_configuration.append(s: " ").append(s: command[i]);
779 }
780
781 llvm::Expected<llvm::json::Value> configuration =
782 llvm::json::parse(JSON: raw_configuration);
783
784 if (!configuration) {
785 llvm::Error err = configuration.takeError();
786 std::string msg =
787 "Failed to parse json configuration: " + llvm::toString(E: std::move(err));
788 result.SetError(msg.c_str());
789 result.SetStatus(lldb::eReturnStatusFailed);
790 return false;
791 }
792
793 g_dap.SendReverseRequest(
794 command: "startDebugging",
795 arguments: llvm::json::Object{{.K: "request", .V: request},
796 {.K: "configuration", .V: std::move(*configuration)}},
797 callback: [](llvm::Expected<llvm::json::Value> value) {
798 if (!value) {
799 llvm::Error err = value.takeError();
800 llvm::errs() << "reverse start debugging request failed: "
801 << llvm::toString(E: std::move(err)) << "\n";
802 }
803 });
804
805 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
806
807 return true;
808}
809
810bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
811 char **command,
812 lldb::SBCommandReturnObject &result) {
813 // Command format like: `repl-mode <variable|command|auto>?`
814 // If a new mode is not specified report the current mode.
815 if (!command || llvm::StringRef(command[0]).empty()) {
816 std::string mode;
817 switch (g_dap.repl_mode) {
818 case ReplMode::Variable:
819 mode = "variable";
820 break;
821 case ReplMode::Command:
822 mode = "command";
823 break;
824 case ReplMode::Auto:
825 mode = "auto";
826 break;
827 }
828
829 result.Printf(format: "lldb-dap repl-mode %s.\n", mode.c_str());
830 result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
831
832 return true;
833 }
834
835 llvm::StringRef new_mode{command[0]};
836
837 if (new_mode == "variable") {
838 g_dap.repl_mode = ReplMode::Variable;
839 } else if (new_mode == "command") {
840 g_dap.repl_mode = ReplMode::Command;
841 } else if (new_mode == "auto") {
842 g_dap.repl_mode = ReplMode::Auto;
843 } else {
844 lldb::SBStream error_message;
845 error_message.Printf(format: "Invalid repl-mode '%s'. Expected one of 'variable', "
846 "'command' or 'auto'.\n",
847 new_mode.data());
848 result.SetError(error_message.GetData());
849 return false;
850 }
851
852 result.Printf(format: "lldb-dap repl-mode %s set.\n", new_mode.data());
853 result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
854 return true;
855}
856
857void DAP::SetFrameFormat(llvm::StringRef format) {
858 if (format.empty())
859 return;
860 lldb::SBError error;
861 g_dap.frame_format = lldb::SBFormat(format.str().c_str(), error);
862 if (error.Fail()) {
863 g_dap.SendOutput(
864 o: OutputType::Console,
865 output: llvm::formatv(
866 Fmt: "The provided frame format '{0}' couldn't be parsed: {1}\n", Vals&: format,
867 Vals: error.GetCString())
868 .str());
869 }
870}
871
872void DAP::SetThreadFormat(llvm::StringRef format) {
873 if (format.empty())
874 return;
875 lldb::SBError error;
876 g_dap.thread_format = lldb::SBFormat(format.str().c_str(), error);
877 if (error.Fail()) {
878 g_dap.SendOutput(
879 o: OutputType::Console,
880 output: llvm::formatv(
881 Fmt: "The provided thread format '{0}' couldn't be parsed: {1}\n",
882 Vals&: format, Vals: error.GetCString())
883 .str());
884 }
885}
886
887} // namespace lldb_dap
888

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