1 //===-- DAP.cpp -------------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
16 #include "LLDBUtils.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/Support/FormatVariadic.h"
27 using namespace lldb_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");
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
57 int result
= _setmode(fileno(stdout
), _O_BINARY
);
59 result
= _setmode(fileno(stdin
), _O_BINARY
);
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
)
77 ExceptionBreakpoint
*DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id
) {
78 for (auto &bp
: exception_breakpoints
) {
79 if (bp
.bp
.GetID() == bp_id
)
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
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
97 void DAP::SendJSON(const llvm::json::Value
&json
) {
99 llvm::raw_string_ostream
strm(s
);
101 static std::mutex mutex
;
102 std::lock_guard
<std::mutex
> locker(mutex
);
103 std::string json_str
= strm
.str();
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
;
119 if (!input
.read_expected(log
.get(), "Content-Length: "))
122 if (!input
.read_line(log
.get(), length_str
))
125 if (!llvm::to_integer(length_str
, length
))
128 if (!input
.read_expected(log
.get(), "\r\n"))
131 if (!input
.read_full(log
.get(), length
, json_str
))
135 *log
<< "--> " << std::endl
<< "Content-Length: " << length
<< "\r\n\r\n";
141 // "allOf": [ { "$ref": "#/definitions/Event" }, {
143 // "description": "Event message for 'output' event type. The event
144 // indicates that the target has produced some output.",
148 // "enum": [ "output" ]
155 // "description": "The output category. If not specified,
156 // 'console' is assumed.",
157 // "_enum": [ "console", "stdout", "stderr", "telemetry" ]
161 // "description": "The output to report."
163 // "variablesReference": {
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."
171 // "$ref": "#/definitions/Source",
172 // "description": "An optional source location where the output
176 // "type": "integer",
177 // "description": "An optional source location line where the
178 // output was produced."
181 // "type": "integer",
182 // "description": "An optional source location column where the
183 // output was produced."
186 // "type":["array","boolean","integer","null","number","object",
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
194 // "required": ["output"]
197 // "required": [ "event", "body" ]
200 void DAP::SendOutput(OutputType o
, const llvm::StringRef output
) {
204 llvm::json::Object
event(CreateEventObject("output"));
205 llvm::json::Object body
;
206 const char *category
= nullptr;
208 case OutputType::Console
:
209 category
= "console";
211 case OutputType::Stdout
:
214 case OutputType::Stderr
:
217 case OutputType::Telemetry
:
218 category
= "telemetry";
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';
232 // * An ID that must be used in subsequent 'progressUpdate' and
234 // * events to make them refer to the same progress reporting.
235 // * IDs must be unique within a debug session.
237 // progressId: string;
240 // * Mandatory (short) title of the progress reporting. Shown in the UI to
241 // * describe the long running operation.
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
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.
254 // requestId?: number;
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
261 // * supports cancellation.
262 // * Clients that don't support cancellation are allowed to ignore the
265 // cancellable?: boolean;
268 // * Optional, more detailed progress message.
273 // * Optional progress percentage to display (value range: 0 to 100). If
274 // * omitted no percentage will be shown.
276 // percentage?: number;
280 // interface ProgressUpdateEvent extends Event {
281 // event: 'progressUpdate';
285 // * The ID that was introduced in the initial 'progressStart' event.
287 // progressId: string;
290 // * Optional, more detailed progress message. If omitted, the previous
291 // * message (if any) is used.
296 // * Optional progress percentage to display (value range: 0 to 100). If
297 // * omitted no percentage will be shown.
299 // percentage?: number;
303 // interface ProgressEndEvent extends Event {
304 // event: 'progressEnd';
308 // * The ID that was introduced in the initial 'ProgressStartEvent'.
310 // progressId: string;
313 // * Optional, more detailed progress message. If omitted, the previous
314 // * message (if any) is used.
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
, ...) {
329 va_start(args
, format
);
330 int actual_length
= vsnprintf(buffer
, sizeof(buffer
), format
, args
);
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)
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
,
385 // Include the escape hatch prefix.
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
;
393 case ReplMode::Variable
:
394 return ExpressionContext::Variable
;
395 case ReplMode::Command
:
396 return ExpressionContext::Command
;
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 "
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
);
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.
485 // Update message if there was an error.
486 error
.SetErrorStringWithFormat(
487 "Could not create a target for a program '%s': %s.", program
.data(),
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();
511 return PacketStatus::EndOfFile
;
513 llvm::StringRef
json_sref(json
);
514 llvm::Expected
<llvm::json::Value
> json_value
= llvm::json::parse(json_sref
);
516 auto error
= json_value
.takeError();
518 std::string error_str
;
519 llvm::raw_string_ostream
strm(error_str
);
522 *log
<< "error: failed to parse JSON: " << error_str
<< std::endl
523 << json
<< std::endl
;
525 return PacketStatus::JSONMalformed
;
529 *log
<< llvm::formatv("{0:2}", *json_value
).str() << std::endl
;
532 llvm::json::Object
*object_ptr
= json_value
->getAsObject();
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
552 *log
<< "error: unhandled command \"" << command
.data() << "\""
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
);
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
));
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
) {
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(),
615 return llvm::Error::success();
618 void DAP::SendReverseRequest(llvm::StringRef command
,
619 llvm::json::Value arguments
,
620 ResponseCallback callback
) {
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
{
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
) {
643 lldb::SBProcess process
= target
.GetProcess();
644 if (!process
.IsValid()) {
645 error
.SetErrorString("invalid process");
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();
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
:
661 case lldb::eStateDetached
:
662 error
.SetErrorString("process detached during launch or attach");
664 case lldb::eStateExited
:
665 error
.SetErrorString("process exited during launch or attach");
667 case lldb::eStateUnloaded
:
668 error
.SetErrorString("process unloaded during launch or attach");
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",
681 void Variables::Clear() {
685 expandable_variables
.clear();
688 int64_t Variables::GetNewVariableReference(bool 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())
704 auto pos
= expandable_variables
.find(var_ref
);
705 if (pos
!= expandable_variables
.end())
708 return lldb::SBValue();
711 int64_t Variables::InsertExpandableVariable(lldb::SBValue variable
,
713 int64_t var_ref
= GetNewVariableReference(is_permanent
);
715 expandable_permanent_variables
.insert(std::make_pair(var_ref
, variable
));
717 expandable_variables
.insert(std::make_pair(var_ref
, variable
));
721 bool StartDebuggingRequestHandler::DoExecute(
722 lldb::SBDebugger debugger
, char **command
,
723 lldb::SBCommandReturnObject
&result
) {
724 // Command format like: `startDebugging <launch|attach> <configuration>`
726 result
.SetError("Invalid use of startDebugging");
727 result
.SetStatus(lldb::eReturnStatusFailed
);
731 if (!command
[0] || llvm::StringRef(command
[0]).empty()) {
732 result
.SetError("startDebugging request type missing.");
733 result
.SetStatus(lldb::eReturnStatusFailed
);
737 if (!command
[1] || llvm::StringRef(command
[1]).empty()) {
738 result
.SetError("configuration missing.");
739 result
.SetStatus(lldb::eReturnStatusFailed
);
743 llvm::StringRef request
{command
[0]};
744 std::string raw_configuration
{command
[1]};
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();
757 "Failed to parse json configuration: " + llvm::toString(std::move(err
));
758 result
.SetError(msg
.c_str());
759 result
.SetStatus(lldb::eReturnStatusFailed
);
763 g_dap
.SendReverseRequest(
765 llvm::json::Object
{{"request", request
},
766 {"configuration", std::move(*configuration
)}},
767 [](llvm::Expected
<llvm::json::Value
> 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
);
780 bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger
,
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()) {
787 switch (g_dap
.repl_mode
) {
788 case ReplMode::Variable
:
791 case ReplMode::Command
:
799 result
.Printf("lldb-dap repl-mode %s.\n", mode
.c_str());
800 result
.SetStatus(lldb::eReturnStatusSuccessFinishResult
);
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
;
814 lldb::SBStream error_message
;
815 error_message
.Printf("Invalid repl-mode '%s'. Expected one of 'variable', "
816 "'command' or 'auto'.\n",
818 result
.SetError(error_message
.GetData());
822 result
.Printf("lldb-dap repl-mode %s set.\n", new_mode
.data());
823 result
.SetStatus(lldb::eReturnStatusSuccessFinishNoResult
);
827 } // namespace lldb_dap