1 //===-- lldb-vscode.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 "VSCode.h"
10
11 #include <cassert>
12 #include <climits>
13 #include <cstdarg>
14 #include <cstdio>
15 #include <cstdlib>
16 #include <cstring>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #if defined(_WIN32)
20 // We need to #define NOMINMAX in order to skip `min()` and `max()` macro
21 // definitions that conflict with other system headers.
22 // We also need to #undef GetObject (which is defined to GetObjectW) because
23 // the JSON code we use also has methods named `GetObject()` and we conflict
24 // against these.
25 #define NOMINMAX
26 #include <windows.h>
27 #undef GetObject
28 #include <io.h>
29 #else
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 #include <unistd.h>
33 #endif
34
35 #include <algorithm>
36 #include <chrono>
37 #include <fstream>
38 #include <map>
39 #include <memory>
40 #include <mutex>
41 #include <set>
42 #include <sstream>
43 #include <thread>
44 #include <vector>
45
46 #include "llvm/ADT/ArrayRef.h"
47 #include "llvm/ADT/DenseMap.h"
48 #include "llvm/ADT/ScopeExit.h"
49 #include "llvm/Option/Arg.h"
50 #include "llvm/Option/ArgList.h"
51 #include "llvm/Option/Option.h"
52 #include "llvm/Support/Errno.h"
53 #include "llvm/Support/FileSystem.h"
54 #include "llvm/Support/InitLLVM.h"
55 #include "llvm/Support/Path.h"
56 #include "llvm/Support/PrettyStackTrace.h"
57 #include "llvm/Support/raw_ostream.h"
58
59 #include "JSONUtils.h"
60 #include "LLDBUtils.h"
61 #include "OutputRedirector.h"
62
63 #if defined(_WIN32)
64 #ifndef PATH_MAX
65 #define PATH_MAX MAX_PATH
66 #endif
67 typedef int socklen_t;
68 #endif
69
70 using namespace lldb_vscode;
71
72 namespace {
73 enum ID {
74 OPT_INVALID = 0, // This is not an option ID.
75 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
76 HELPTEXT, METAVAR, VALUES) \
77 OPT_##ID,
78 #include "Options.inc"
79 #undef OPTION
80 };
81
82 #define PREFIX(NAME, VALUE) \
83 static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
84 static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
85 NAME##_init, std::size(NAME##_init) - 1);
86 #include "Options.inc"
87 #undef PREFIX
88
89 static constexpr llvm::opt::OptTable::Info InfoTable[] = {
90 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
91 HELPTEXT, METAVAR, VALUES) \
92 {PREFIX, NAME, HELPTEXT, \
93 METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
94 PARAM, FLAGS, OPT_##GROUP, \
95 OPT_##ALIAS, ALIASARGS, VALUES},
96 #include "Options.inc"
97 #undef OPTION
98 };
99 class LLDBVSCodeOptTable : public llvm::opt::GenericOptTable {
100 public:
LLDBVSCodeOptTable()101 LLDBVSCodeOptTable() : llvm::opt::GenericOptTable(InfoTable, true) {}
102 };
103
104 typedef void (*RequestCallback)(const llvm::json::Object &command);
105
106 enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
107
GetTopLevelScope(int64_t variablesReference)108 lldb::SBValueList *GetTopLevelScope(int64_t variablesReference) {
109 switch (variablesReference) {
110 case VARREF_LOCALS:
111 return &g_vsc.variables.locals;
112 case VARREF_GLOBALS:
113 return &g_vsc.variables.globals;
114 case VARREF_REGS:
115 return &g_vsc.variables.registers;
116 default:
117 return nullptr;
118 }
119 }
120
AcceptConnection(int portno)121 SOCKET AcceptConnection(int portno) {
122 // Accept a socket connection from any host on "portno".
123 SOCKET newsockfd = -1;
124 struct sockaddr_in serv_addr, cli_addr;
125 SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
126 if (sockfd < 0) {
127 if (g_vsc.log)
128 *g_vsc.log << "error: opening socket (" << strerror(errno) << ")"
129 << std::endl;
130 } else {
131 memset((char *)&serv_addr, 0, sizeof(serv_addr));
132 serv_addr.sin_family = AF_INET;
133 // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
134 serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
135 serv_addr.sin_port = htons(portno);
136 if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
137 if (g_vsc.log)
138 *g_vsc.log << "error: binding socket (" << strerror(errno) << ")"
139 << std::endl;
140 } else {
141 listen(sockfd, 5);
142 socklen_t clilen = sizeof(cli_addr);
143 newsockfd =
144 llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, sockfd,
145 (struct sockaddr *)&cli_addr, &clilen);
146 if (newsockfd < 0)
147 if (g_vsc.log)
148 *g_vsc.log << "error: accept (" << strerror(errno) << ")"
149 << std::endl;
150 }
151 #if defined(_WIN32)
152 closesocket(sockfd);
153 #else
154 close(sockfd);
155 #endif
156 }
157 return newsockfd;
158 }
159
MakeArgv(const llvm::ArrayRef<std::string> & strs)160 std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) {
161 // Create and return an array of "const char *", one for each C string in
162 // "strs" and terminate the list with a NULL. This can be used for argument
163 // vectors (argv) or environment vectors (envp) like those passed to the
164 // "main" function in C programs.
165 std::vector<const char *> argv;
166 for (const auto &s : strs)
167 argv.push_back(s.c_str());
168 argv.push_back(nullptr);
169 return argv;
170 }
171
172 // Send a "exited" event to indicate the process has exited.
SendProcessExitedEvent(lldb::SBProcess & process)173 void SendProcessExitedEvent(lldb::SBProcess &process) {
174 llvm::json::Object event(CreateEventObject("exited"));
175 llvm::json::Object body;
176 body.try_emplace("exitCode", (int64_t)process.GetExitStatus());
177 event.try_emplace("body", std::move(body));
178 g_vsc.SendJSON(llvm::json::Value(std::move(event)));
179 }
180
SendThreadExitedEvent(lldb::tid_t tid)181 void SendThreadExitedEvent(lldb::tid_t tid) {
182 llvm::json::Object event(CreateEventObject("thread"));
183 llvm::json::Object body;
184 body.try_emplace("reason", "exited");
185 body.try_emplace("threadId", (int64_t)tid);
186 event.try_emplace("body", std::move(body));
187 g_vsc.SendJSON(llvm::json::Value(std::move(event)));
188 }
189
190 // Send a "terminated" event to indicate the process is done being
191 // debugged.
SendTerminatedEvent()192 void SendTerminatedEvent() {
193 // If an inferior exits prior to the processing of a disconnect request, then
194 // the threads executing EventThreadFunction and request_discontinue
195 // respectively may call SendTerminatedEvent simultaneously. Without any
196 // synchronization, the thread executing EventThreadFunction may set
197 // g_vsc.sent_terminated_event before the thread executing
198 // request_discontinue has had a chance to test it, in which case the latter
199 // would move ahead to issue a response to the disconnect request. Said
200 // response may get dispatched ahead of the terminated event compelling the
201 // client to terminate the debug session without consuming any console output
202 // that might've been generated by the execution of terminateCommands. So,
203 // synchronize simultaneous calls to SendTerminatedEvent.
204 static std::mutex mutex;
205 std::lock_guard<std::mutex> locker(mutex);
206 if (!g_vsc.sent_terminated_event) {
207 g_vsc.sent_terminated_event = true;
208 g_vsc.RunTerminateCommands();
209 // Send a "terminated" event
210 llvm::json::Object event(CreateTerminatedEventObject());
211 g_vsc.SendJSON(llvm::json::Value(std::move(event)));
212 }
213 }
214
215 // Send a thread stopped event for all threads as long as the process
216 // is stopped.
SendThreadStoppedEvent()217 void SendThreadStoppedEvent() {
218 lldb::SBProcess process = g_vsc.target.GetProcess();
219 if (process.IsValid()) {
220 auto state = process.GetState();
221 if (state == lldb::eStateStopped) {
222 llvm::DenseSet<lldb::tid_t> old_thread_ids;
223 old_thread_ids.swap(g_vsc.thread_ids);
224 uint32_t stop_id = process.GetStopID();
225 const uint32_t num_threads = process.GetNumThreads();
226
227 // First make a pass through the threads to see if the focused thread
228 // has a stop reason. In case the focus thread doesn't have a stop
229 // reason, remember the first thread that has a stop reason so we can
230 // set it as the focus thread if below if needed.
231 lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;
232 uint32_t num_threads_with_reason = 0;
233 bool focus_thread_exists = false;
234 for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
235 lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
236 const lldb::tid_t tid = thread.GetThreadID();
237 const bool has_reason = ThreadHasStopReason(thread);
238 // If the focus thread doesn't have a stop reason, clear the thread ID
239 if (tid == g_vsc.focus_tid) {
240 focus_thread_exists = true;
241 if (!has_reason)
242 g_vsc.focus_tid = LLDB_INVALID_THREAD_ID;
243 }
244 if (has_reason) {
245 ++num_threads_with_reason;
246 if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)
247 first_tid_with_reason = tid;
248 }
249 }
250
251 // We will have cleared g_vsc.focus_tid if he focus thread doesn't have
252 // a stop reason, so if it was cleared, or wasn't set, or doesn't exist,
253 // then set the focus thread to the first thread with a stop reason.
254 if (!focus_thread_exists || g_vsc.focus_tid == LLDB_INVALID_THREAD_ID)
255 g_vsc.focus_tid = first_tid_with_reason;
256
257 // If no threads stopped with a reason, then report the first one so
258 // we at least let the UI know we stopped.
259 if (num_threads_with_reason == 0) {
260 lldb::SBThread thread = process.GetThreadAtIndex(0);
261 g_vsc.SendJSON(CreateThreadStopped(thread, stop_id));
262 } else {
263 for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
264 lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
265 g_vsc.thread_ids.insert(thread.GetThreadID());
266 if (ThreadHasStopReason(thread)) {
267 g_vsc.SendJSON(CreateThreadStopped(thread, stop_id));
268 }
269 }
270 }
271
272 for (auto tid : old_thread_ids) {
273 auto end = g_vsc.thread_ids.end();
274 auto pos = g_vsc.thread_ids.find(tid);
275 if (pos == end)
276 SendThreadExitedEvent(tid);
277 }
278 } else {
279 if (g_vsc.log)
280 *g_vsc.log << "error: SendThreadStoppedEvent() when process"
281 " isn't stopped ("
282 << lldb::SBDebugger::StateAsCString(state) << ')'
283 << std::endl;
284 }
285 } else {
286 if (g_vsc.log)
287 *g_vsc.log << "error: SendThreadStoppedEvent() invalid process"
288 << std::endl;
289 }
290 g_vsc.RunStopCommands();
291 }
292
293 // "ProcessEvent": {
294 // "allOf": [
295 // { "$ref": "#/definitions/Event" },
296 // {
297 // "type": "object",
298 // "description": "Event message for 'process' event type. The event
299 // indicates that the debugger has begun debugging a
300 // new process. Either one that it has launched, or one
301 // that it has attached to.",
302 // "properties": {
303 // "event": {
304 // "type": "string",
305 // "enum": [ "process" ]
306 // },
307 // "body": {
308 // "type": "object",
309 // "properties": {
310 // "name": {
311 // "type": "string",
312 // "description": "The logical name of the process. This is
313 // usually the full path to process's executable
314 // file. Example: /home/myproj/program.js."
315 // },
316 // "systemProcessId": {
317 // "type": "integer",
318 // "description": "The system process id of the debugged process.
319 // This property will be missing for non-system
320 // processes."
321 // },
322 // "isLocalProcess": {
323 // "type": "boolean",
324 // "description": "If true, the process is running on the same
325 // computer as the debug adapter."
326 // },
327 // "startMethod": {
328 // "type": "string",
329 // "enum": [ "launch", "attach", "attachForSuspendedLaunch" ],
330 // "description": "Describes how the debug engine started
331 // debugging this process.",
332 // "enumDescriptions": [
333 // "Process was launched under the debugger.",
334 // "Debugger attached to an existing process.",
335 // "A project launcher component has launched a new process in
336 // a suspended state and then asked the debugger to attach."
337 // ]
338 // }
339 // },
340 // "required": [ "name" ]
341 // }
342 // },
343 // "required": [ "event", "body" ]
344 // }
345 // ]
346 // }
SendProcessEvent(LaunchMethod launch_method)347 void SendProcessEvent(LaunchMethod launch_method) {
348 lldb::SBFileSpec exe_fspec = g_vsc.target.GetExecutable();
349 char exe_path[PATH_MAX];
350 exe_fspec.GetPath(exe_path, sizeof(exe_path));
351 llvm::json::Object event(CreateEventObject("process"));
352 llvm::json::Object body;
353 EmplaceSafeString(body, "name", std::string(exe_path));
354 const auto pid = g_vsc.target.GetProcess().GetProcessID();
355 body.try_emplace("systemProcessId", (int64_t)pid);
356 body.try_emplace("isLocalProcess", true);
357 const char *startMethod = nullptr;
358 switch (launch_method) {
359 case Launch:
360 startMethod = "launch";
361 break;
362 case Attach:
363 startMethod = "attach";
364 break;
365 case AttachForSuspendedLaunch:
366 startMethod = "attachForSuspendedLaunch";
367 break;
368 }
369 body.try_emplace("startMethod", startMethod);
370 event.try_emplace("body", std::move(body));
371 g_vsc.SendJSON(llvm::json::Value(std::move(event)));
372 }
373
374 // Grab any STDOUT and STDERR from the process and send it up to VS Code
375 // via an "output" event to the "stdout" and "stderr" categories.
SendStdOutStdErr(lldb::SBProcess & process)376 void SendStdOutStdErr(lldb::SBProcess &process) {
377 char buffer[1024];
378 size_t count;
379 while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
380 g_vsc.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));
381 while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0)
382 g_vsc.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));
383 }
384
ProgressEventThreadFunction()385 void ProgressEventThreadFunction() {
386 lldb::SBListener listener("lldb-vscode.progress.listener");
387 g_vsc.debugger.GetBroadcaster().AddListener(
388 listener, lldb::SBDebugger::eBroadcastBitProgress);
389 g_vsc.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
390 lldb::SBEvent event;
391 bool done = false;
392 while (!done) {
393 if (listener.WaitForEvent(1, event)) {
394 const auto event_mask = event.GetType();
395 if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) {
396 if (event_mask & eBroadcastBitStopProgressThread) {
397 done = true;
398 }
399 } else {
400 uint64_t progress_id = 0;
401 uint64_t completed = 0;
402 uint64_t total = 0;
403 bool is_debugger_specific = false;
404 const char *message = lldb::SBDebugger::GetProgressFromEvent(
405 event, progress_id, completed, total, is_debugger_specific);
406 if (message)
407 g_vsc.SendProgressEvent(progress_id, message, completed, total);
408 }
409 }
410 }
411 }
412
413 // All events from the debugger, target, process, thread and frames are
414 // received in this function that runs in its own thread. We are using a
415 // "FILE *" to output packets back to VS Code and they have mutexes in them
416 // them prevent multiple threads from writing simultaneously so no locking
417 // is required.
EventThreadFunction()418 void EventThreadFunction() {
419 lldb::SBEvent event;
420 lldb::SBListener listener = g_vsc.debugger.GetListener();
421 bool done = false;
422 while (!done) {
423 if (listener.WaitForEvent(1, event)) {
424 const auto event_mask = event.GetType();
425 if (lldb::SBProcess::EventIsProcessEvent(event)) {
426 lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(event);
427 if (event_mask & lldb::SBProcess::eBroadcastBitStateChanged) {
428 auto state = lldb::SBProcess::GetStateFromEvent(event);
429 switch (state) {
430 case lldb::eStateInvalid:
431 // Not a state event
432 break;
433 case lldb::eStateUnloaded:
434 break;
435 case lldb::eStateConnected:
436 break;
437 case lldb::eStateAttaching:
438 break;
439 case lldb::eStateLaunching:
440 break;
441 case lldb::eStateStepping:
442 break;
443 case lldb::eStateCrashed:
444 break;
445 case lldb::eStateDetached:
446 break;
447 case lldb::eStateSuspended:
448 break;
449 case lldb::eStateStopped:
450 // We launch and attach in synchronous mode then the first stop
451 // event will not be delivered. If we use "launchCommands" during a
452 // launch or "attachCommands" during an attach we might some process
453 // stop events which we do not want to send an event for. We will
454 // manually send a stopped event in request_configurationDone(...)
455 // so don't send any before then.
456 if (g_vsc.configuration_done_sent) {
457 // Only report a stopped event if the process was not restarted.
458 if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
459 SendStdOutStdErr(process);
460 SendThreadStoppedEvent();
461 }
462 }
463 break;
464 case lldb::eStateRunning:
465 g_vsc.WillContinue();
466 break;
467 case lldb::eStateExited: {
468 // Run any exit LLDB commands the user specified in the
469 // launch.json
470 g_vsc.RunExitCommands();
471 SendProcessExitedEvent(process);
472 SendTerminatedEvent();
473 done = true;
474 } break;
475 }
476 } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
477 (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
478 SendStdOutStdErr(process);
479 }
480 } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
481 if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
482 auto event_type =
483 lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
484 auto bp = lldb::SBBreakpoint::GetBreakpointFromEvent(event);
485 // If the breakpoint was originated from the IDE, it will have the
486 // BreakpointBase::GetBreakpointLabel() label attached. Regardless
487 // of wether the locations were added or removed, the breakpoint
488 // ins't going away, so we the reason is always "changed".
489 if ((event_type & lldb::eBreakpointEventTypeLocationsAdded ||
490 event_type & lldb::eBreakpointEventTypeLocationsRemoved) &&
491 bp.MatchesName(BreakpointBase::GetBreakpointLabel())) {
492 auto bp_event = CreateEventObject("breakpoint");
493 llvm::json::Object body;
494 // As VSCode already knows the path of this breakpoint, we don't
495 // need to send it back as part of a "changed" event. This
496 // prevent us from sending to VSCode paths that should be source
497 // mapped. Note that CreateBreakpoint doesn't apply source mapping.
498 // Besides, the current implementation of VSCode ignores the
499 // "source" element of breakpoint events.
500 llvm::json::Value source_bp = CreateBreakpoint(bp);
501 source_bp.getAsObject()->erase("source");
502
503 body.try_emplace("breakpoint", source_bp);
504 body.try_emplace("reason", "changed");
505 bp_event.try_emplace("body", std::move(body));
506 g_vsc.SendJSON(llvm::json::Value(std::move(bp_event)));
507 }
508 }
509 } else if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) {
510 if (event_mask & eBroadcastBitStopEventThread) {
511 done = true;
512 }
513 }
514 }
515 }
516 }
517
518 // Both attach and launch take a either a sourcePath or sourceMap
519 // argument (or neither), from which we need to set the target.source-map.
SetSourceMapFromArguments(const llvm::json::Object & arguments)520 void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
521 const char *sourceMapHelp =
522 "source must be be an array of two-element arrays, "
523 "each containing a source and replacement path string.\n";
524
525 std::string sourceMapCommand;
526 llvm::raw_string_ostream strm(sourceMapCommand);
527 strm << "settings set target.source-map ";
528 auto sourcePath = GetString(arguments, "sourcePath");
529
530 // sourceMap is the new, more general form of sourcePath and overrides it.
531 auto sourceMap = arguments.getArray("sourceMap");
532 if (sourceMap) {
533 for (const auto &value : *sourceMap) {
534 auto mapping = value.getAsArray();
535 if (mapping == nullptr || mapping->size() != 2 ||
536 (*mapping)[0].kind() != llvm::json::Value::String ||
537 (*mapping)[1].kind() != llvm::json::Value::String) {
538 g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
539 return;
540 }
541 auto mapFrom = GetAsString((*mapping)[0]);
542 auto mapTo = GetAsString((*mapping)[1]);
543 strm << "\"" << mapFrom << "\" \"" << mapTo << "\" ";
544 }
545 } else {
546 if (ObjectContainsKey(arguments, "sourceMap")) {
547 g_vsc.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
548 return;
549 }
550 if (sourcePath.empty())
551 return;
552 // Do any source remapping needed before we create our targets
553 strm << "\".\" \"" << sourcePath << "\"";
554 }
555 strm.flush();
556 if (!sourceMapCommand.empty()) {
557 g_vsc.RunLLDBCommands("Setting source map:", {sourceMapCommand});
558 }
559 }
560
561 // "AttachRequest": {
562 // "allOf": [ { "$ref": "#/definitions/Request" }, {
563 // "type": "object",
564 // "description": "Attach request; value of command field is 'attach'.",
565 // "properties": {
566 // "command": {
567 // "type": "string",
568 // "enum": [ "attach" ]
569 // },
570 // "arguments": {
571 // "$ref": "#/definitions/AttachRequestArguments"
572 // }
573 // },
574 // "required": [ "command", "arguments" ]
575 // }]
576 // },
577 // "AttachRequestArguments": {
578 // "type": "object",
579 // "description": "Arguments for 'attach' request.\nThe attach request has no
580 // standardized attributes."
581 // },
582 // "AttachResponse": {
583 // "allOf": [ { "$ref": "#/definitions/Response" }, {
584 // "type": "object",
585 // "description": "Response to 'attach' request. This is just an
586 // acknowledgement, so no body field is required."
587 // }]
588 // }
request_attach(const llvm::json::Object & request)589 void request_attach(const llvm::json::Object &request) {
590 g_vsc.is_attach = true;
591 llvm::json::Object response;
592 lldb::SBError error;
593 FillResponse(request, response);
594 lldb::SBAttachInfo attach_info;
595 auto arguments = request.getObject("arguments");
596 const lldb::pid_t pid =
597 GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
598 if (pid != LLDB_INVALID_PROCESS_ID)
599 attach_info.SetProcessID(pid);
600 const auto wait_for = GetBoolean(arguments, "waitFor", false);
601 attach_info.SetWaitForLaunch(wait_for, false /*async*/);
602 g_vsc.init_commands = GetStrings(arguments, "initCommands");
603 g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands");
604 g_vsc.stop_commands = GetStrings(arguments, "stopCommands");
605 g_vsc.exit_commands = GetStrings(arguments, "exitCommands");
606 g_vsc.terminate_commands = GetStrings(arguments, "terminateCommands");
607 auto attachCommands = GetStrings(arguments, "attachCommands");
608 llvm::StringRef core_file = GetString(arguments, "coreFile");
609 const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
610 g_vsc.stop_at_entry =
611 core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true;
612 std::vector<std::string> postRunCommands =
613 GetStrings(arguments, "postRunCommands");
614 const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
615
616 // This is a hack for loading DWARF in .o files on Mac where the .o files
617 // in the debug map of the main executable have relative paths which require
618 // the lldb-vscode binary to have its working directory set to that relative
619 // root for the .o files in order to be able to load debug info.
620 if (!debuggerRoot.empty())
621 llvm::sys::fs::set_current_path(debuggerRoot);
622
623 // Run any initialize LLDB commands the user specified in the launch.json
624 g_vsc.RunInitCommands();
625
626 SetSourceMapFromArguments(*arguments);
627
628 lldb::SBError status;
629 g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status));
630 if (status.Fail()) {
631 response["success"] = llvm::json::Value(false);
632 EmplaceSafeString(response, "message", status.GetCString());
633 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
634 return;
635 }
636
637 // Run any pre run LLDB commands the user specified in the launch.json
638 g_vsc.RunPreRunCommands();
639
640 if (pid == LLDB_INVALID_PROCESS_ID && wait_for) {
641 char attach_msg[256];
642 auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
643 "Waiting to attach to \"%s\"...",
644 g_vsc.target.GetExecutable().GetFilename());
645 g_vsc.SendOutput(OutputType::Console,
646 llvm::StringRef(attach_msg, attach_msg_len));
647 }
648 if (attachCommands.empty()) {
649 // No "attachCommands", just attach normally.
650 // Disable async events so the attach will be successful when we return from
651 // the launch call and the launch will happen synchronously
652 g_vsc.debugger.SetAsync(false);
653 if (core_file.empty())
654 g_vsc.target.Attach(attach_info, error);
655 else
656 g_vsc.target.LoadCore(core_file.data(), error);
657 // Reenable async events
658 g_vsc.debugger.SetAsync(true);
659 } else {
660 // We have "attachCommands" that are a set of commands that are expected
661 // to execute the commands after which a process should be created. If there
662 // is no valid process after running these commands, we have failed.
663 g_vsc.RunLLDBCommands("Running attachCommands:", attachCommands);
664 // The custom commands might have created a new target so we should use the
665 // selected target after these commands are run.
666 g_vsc.target = g_vsc.debugger.GetSelectedTarget();
667
668 // Make sure the process is attached and stopped before proceeding as the
669 // the launch commands are not run using the synchronous mode.
670 error = g_vsc.WaitForProcessToStop(timeout_seconds);
671 }
672
673 if (error.Success() && core_file.empty()) {
674 auto attached_pid = g_vsc.target.GetProcess().GetProcessID();
675 if (attached_pid == LLDB_INVALID_PROCESS_ID) {
676 if (attachCommands.empty())
677 error.SetErrorString("failed to attach to a process");
678 else
679 error.SetErrorString("attachCommands failed to attach to a process");
680 }
681 }
682
683 if (error.Fail()) {
684 response["success"] = llvm::json::Value(false);
685 EmplaceSafeString(response, "message", std::string(error.GetCString()));
686 } else {
687 g_vsc.RunLLDBCommands("Running postRunCommands:", postRunCommands);
688 }
689
690 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
691 if (error.Success()) {
692 SendProcessEvent(Attach);
693 g_vsc.SendJSON(CreateEventObject("initialized"));
694 }
695 }
696
697 // "ContinueRequest": {
698 // "allOf": [ { "$ref": "#/definitions/Request" }, {
699 // "type": "object",
700 // "description": "Continue request; value of command field is 'continue'.
701 // The request starts the debuggee to run again.",
702 // "properties": {
703 // "command": {
704 // "type": "string",
705 // "enum": [ "continue" ]
706 // },
707 // "arguments": {
708 // "$ref": "#/definitions/ContinueArguments"
709 // }
710 // },
711 // "required": [ "command", "arguments" ]
712 // }]
713 // },
714 // "ContinueArguments": {
715 // "type": "object",
716 // "description": "Arguments for 'continue' request.",
717 // "properties": {
718 // "threadId": {
719 // "type": "integer",
720 // "description": "Continue execution for the specified thread (if
721 // possible). If the backend cannot continue on a single
722 // thread but will continue on all threads, it should
723 // set the allThreadsContinued attribute in the response
724 // to true."
725 // }
726 // },
727 // "required": [ "threadId" ]
728 // },
729 // "ContinueResponse": {
730 // "allOf": [ { "$ref": "#/definitions/Response" }, {
731 // "type": "object",
732 // "description": "Response to 'continue' request.",
733 // "properties": {
734 // "body": {
735 // "type": "object",
736 // "properties": {
737 // "allThreadsContinued": {
738 // "type": "boolean",
739 // "description": "If true, the continue request has ignored the
740 // specified thread and continued all threads
741 // instead. If this attribute is missing a value
742 // of 'true' is assumed for backward
743 // compatibility."
744 // }
745 // }
746 // }
747 // },
748 // "required": [ "body" ]
749 // }]
750 // }
request_continue(const llvm::json::Object & request)751 void request_continue(const llvm::json::Object &request) {
752 llvm::json::Object response;
753 FillResponse(request, response);
754 lldb::SBProcess process = g_vsc.target.GetProcess();
755 auto arguments = request.getObject("arguments");
756 // Remember the thread ID that caused the resume so we can set the
757 // "threadCausedFocus" boolean value in the "stopped" events.
758 g_vsc.focus_tid = GetUnsigned(arguments, "threadId", LLDB_INVALID_THREAD_ID);
759 lldb::SBError error = process.Continue();
760 llvm::json::Object body;
761 body.try_emplace("allThreadsContinued", true);
762 response.try_emplace("body", std::move(body));
763 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
764 }
765
766 // "ConfigurationDoneRequest": {
767 // "allOf": [ { "$ref": "#/definitions/Request" }, {
768 // "type": "object",
769 // "description": "ConfigurationDone request; value of command field
770 // is 'configurationDone'.\nThe client of the debug protocol must
771 // send this request at the end of the sequence of configuration
772 // requests (which was started by the InitializedEvent).",
773 // "properties": {
774 // "command": {
775 // "type": "string",
776 // "enum": [ "configurationDone" ]
777 // },
778 // "arguments": {
779 // "$ref": "#/definitions/ConfigurationDoneArguments"
780 // }
781 // },
782 // "required": [ "command" ]
783 // }]
784 // },
785 // "ConfigurationDoneArguments": {
786 // "type": "object",
787 // "description": "Arguments for 'configurationDone' request.\nThe
788 // configurationDone request has no standardized attributes."
789 // },
790 // "ConfigurationDoneResponse": {
791 // "allOf": [ { "$ref": "#/definitions/Response" }, {
792 // "type": "object",
793 // "description": "Response to 'configurationDone' request. This is
794 // just an acknowledgement, so no body field is required."
795 // }]
796 // },
request_configurationDone(const llvm::json::Object & request)797 void request_configurationDone(const llvm::json::Object &request) {
798 llvm::json::Object response;
799 FillResponse(request, response);
800 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
801 g_vsc.configuration_done_sent = true;
802 if (g_vsc.stop_at_entry)
803 SendThreadStoppedEvent();
804 else
805 g_vsc.target.GetProcess().Continue();
806 }
807
808 // "DisconnectRequest": {
809 // "allOf": [ { "$ref": "#/definitions/Request" }, {
810 // "type": "object",
811 // "description": "Disconnect request; value of command field is
812 // 'disconnect'.",
813 // "properties": {
814 // "command": {
815 // "type": "string",
816 // "enum": [ "disconnect" ]
817 // },
818 // "arguments": {
819 // "$ref": "#/definitions/DisconnectArguments"
820 // }
821 // },
822 // "required": [ "command" ]
823 // }]
824 // },
825 // "DisconnectArguments": {
826 // "type": "object",
827 // "description": "Arguments for 'disconnect' request.",
828 // "properties": {
829 // "terminateDebuggee": {
830 // "type": "boolean",
831 // "description": "Indicates whether the debuggee should be terminated
832 // when the debugger is disconnected. If unspecified,
833 // the debug adapter is free to do whatever it thinks
834 // is best. A client can only rely on this attribute
835 // being properly honored if a debug adapter returns
836 // true for the 'supportTerminateDebuggee' capability."
837 // },
838 // "restart": {
839 // "type": "boolean",
840 // "description": "Indicates whether the debuggee should be restart
841 // the process."
842 // }
843 // }
844 // },
845 // "DisconnectResponse": {
846 // "allOf": [ { "$ref": "#/definitions/Response" }, {
847 // "type": "object",
848 // "description": "Response to 'disconnect' request. This is just an
849 // acknowledgement, so no body field is required."
850 // }]
851 // }
request_disconnect(const llvm::json::Object & request)852 void request_disconnect(const llvm::json::Object &request) {
853 llvm::json::Object response;
854 FillResponse(request, response);
855 auto arguments = request.getObject("arguments");
856
857 bool defaultTerminateDebuggee = g_vsc.is_attach ? false : true;
858 bool terminateDebuggee =
859 GetBoolean(arguments, "terminateDebuggee", defaultTerminateDebuggee);
860 lldb::SBProcess process = g_vsc.target.GetProcess();
861 auto state = process.GetState();
862 switch (state) {
863 case lldb::eStateInvalid:
864 case lldb::eStateUnloaded:
865 case lldb::eStateDetached:
866 case lldb::eStateExited:
867 break;
868 case lldb::eStateConnected:
869 case lldb::eStateAttaching:
870 case lldb::eStateLaunching:
871 case lldb::eStateStepping:
872 case lldb::eStateCrashed:
873 case lldb::eStateSuspended:
874 case lldb::eStateStopped:
875 case lldb::eStateRunning:
876 g_vsc.debugger.SetAsync(false);
877 lldb::SBError error = terminateDebuggee ? process.Kill() : process.Detach();
878 if (!error.Success())
879 response.try_emplace("error", error.GetCString());
880 g_vsc.debugger.SetAsync(true);
881 break;
882 }
883 SendTerminatedEvent();
884 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
885 if (g_vsc.event_thread.joinable()) {
886 g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
887 g_vsc.event_thread.join();
888 }
889 if (g_vsc.progress_event_thread.joinable()) {
890 g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);
891 g_vsc.progress_event_thread.join();
892 }
893 }
894
request_exceptionInfo(const llvm::json::Object & request)895 void request_exceptionInfo(const llvm::json::Object &request) {
896 llvm::json::Object response;
897 FillResponse(request, response);
898 auto arguments = request.getObject("arguments");
899 llvm::json::Object body;
900 lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
901 if (thread.IsValid()) {
902 auto stopReason = thread.GetStopReason();
903 if (stopReason == lldb::eStopReasonSignal)
904 body.try_emplace("exceptionId", "signal");
905 else if (stopReason == lldb::eStopReasonBreakpoint) {
906 ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
907 if (exc_bp) {
908 EmplaceSafeString(body, "exceptionId", exc_bp->filter);
909 EmplaceSafeString(body, "description", exc_bp->label);
910 } else {
911 body.try_emplace("exceptionId", "exception");
912 }
913 } else {
914 body.try_emplace("exceptionId", "exception");
915 }
916 if (!ObjectContainsKey(body, "description")) {
917 char description[1024];
918 if (thread.GetStopDescription(description, sizeof(description))) {
919 EmplaceSafeString(body, "description", std::string(description));
920 }
921 }
922 body.try_emplace("breakMode", "always");
923 // auto excInfoCount = thread.GetStopReasonDataCount();
924 // for (auto i=0; i<excInfoCount; ++i) {
925 // uint64_t exc_data = thread.GetStopReasonDataAtIndex(i);
926 // }
927 } else {
928 response["success"] = llvm::json::Value(false);
929 }
930 response.try_emplace("body", std::move(body));
931 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
932 }
933
934 // "CompletionsRequest": {
935 // "allOf": [ { "$ref": "#/definitions/Request" }, {
936 // "type": "object",
937 // "description": "Returns a list of possible completions for a given caret
938 // position and text.\nThe CompletionsRequest may only be called if the
939 // 'supportsCompletionsRequest' capability exists and is true.",
940 // "properties": {
941 // "command": {
942 // "type": "string",
943 // "enum": [ "completions" ]
944 // },
945 // "arguments": {
946 // "$ref": "#/definitions/CompletionsArguments"
947 // }
948 // },
949 // "required": [ "command", "arguments" ]
950 // }]
951 // },
952 // "CompletionsArguments": {
953 // "type": "object",
954 // "description": "Arguments for 'completions' request.",
955 // "properties": {
956 // "frameId": {
957 // "type": "integer",
958 // "description": "Returns completions in the scope of this stack frame.
959 // If not specified, the completions are returned for the global scope."
960 // },
961 // "text": {
962 // "type": "string",
963 // "description": "One or more source lines. Typically this is the text a
964 // user has typed into the debug console before he asked for completion."
965 // },
966 // "column": {
967 // "type": "integer",
968 // "description": "The character position for which to determine the
969 // completion proposals."
970 // },
971 // "line": {
972 // "type": "integer",
973 // "description": "An optional line for which to determine the completion
974 // proposals. If missing the first line of the text is assumed."
975 // }
976 // },
977 // "required": [ "text", "column" ]
978 // },
979 // "CompletionsResponse": {
980 // "allOf": [ { "$ref": "#/definitions/Response" }, {
981 // "type": "object",
982 // "description": "Response to 'completions' request.",
983 // "properties": {
984 // "body": {
985 // "type": "object",
986 // "properties": {
987 // "targets": {
988 // "type": "array",
989 // "items": {
990 // "$ref": "#/definitions/CompletionItem"
991 // },
992 // "description": "The possible completions for ."
993 // }
994 // },
995 // "required": [ "targets" ]
996 // }
997 // },
998 // "required": [ "body" ]
999 // }]
1000 // },
1001 // "CompletionItem": {
1002 // "type": "object",
1003 // "description": "CompletionItems are the suggestions returned from the
1004 // CompletionsRequest.", "properties": {
1005 // "label": {
1006 // "type": "string",
1007 // "description": "The label of this completion item. By default this is
1008 // also the text that is inserted when selecting this completion."
1009 // },
1010 // "text": {
1011 // "type": "string",
1012 // "description": "If text is not falsy then it is inserted instead of the
1013 // label."
1014 // },
1015 // "sortText": {
1016 // "type": "string",
1017 // "description": "A string that should be used when comparing this item
1018 // with other items. When `falsy` the label is used."
1019 // },
1020 // "type": {
1021 // "$ref": "#/definitions/CompletionItemType",
1022 // "description": "The item's type. Typically the client uses this
1023 // information to render the item in the UI with an icon."
1024 // },
1025 // "start": {
1026 // "type": "integer",
1027 // "description": "This value determines the location (in the
1028 // CompletionsRequest's 'text' attribute) where the completion text is
1029 // added.\nIf missing the text is added at the location specified by the
1030 // CompletionsRequest's 'column' attribute."
1031 // },
1032 // "length": {
1033 // "type": "integer",
1034 // "description": "This value determines how many characters are
1035 // overwritten by the completion text.\nIf missing the value 0 is assumed
1036 // which results in the completion text being inserted."
1037 // }
1038 // },
1039 // "required": [ "label" ]
1040 // },
1041 // "CompletionItemType": {
1042 // "type": "string",
1043 // "description": "Some predefined types for the CompletionItem. Please note
1044 // that not all clients have specific icons for all of them.", "enum": [
1045 // "method", "function", "constructor", "field", "variable", "class",
1046 // "interface", "module", "property", "unit", "value", "enum", "keyword",
1047 // "snippet", "text", "color", "file", "reference", "customcolor" ]
1048 // }
request_completions(const llvm::json::Object & request)1049 void request_completions(const llvm::json::Object &request) {
1050 llvm::json::Object response;
1051 FillResponse(request, response);
1052 llvm::json::Object body;
1053 auto arguments = request.getObject("arguments");
1054 std::string text = std::string(GetString(arguments, "text"));
1055 auto original_column = GetSigned(arguments, "column", text.size());
1056 auto actual_column = original_column - 1;
1057 llvm::json::Array targets;
1058 // NOTE: the 'line' argument is not needed, as multiline expressions
1059 // work well already
1060 // TODO: support frameID. Currently
1061 // g_vsc.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions
1062 // is frame-unaware.
1063
1064 if (!text.empty() && text[0] == '`') {
1065 text = text.substr(1);
1066 actual_column--;
1067 } else {
1068 text = "p " + text;
1069 actual_column += 2;
1070 }
1071 lldb::SBStringList matches;
1072 lldb::SBStringList descriptions;
1073 g_vsc.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions(
1074 text.c_str(), actual_column, 0, -1, matches, descriptions);
1075 size_t count = std::min((uint32_t)100, matches.GetSize());
1076 targets.reserve(count);
1077 for (size_t i = 0; i < count; i++) {
1078 std::string match = matches.GetStringAtIndex(i);
1079 std::string description = descriptions.GetStringAtIndex(i);
1080
1081 llvm::json::Object item;
1082
1083 llvm::StringRef match_ref = match;
1084 for (llvm::StringRef commit_point : {".", "->"}) {
1085 if (match_ref.contains(commit_point)) {
1086 match_ref = match_ref.rsplit(commit_point).second;
1087 }
1088 }
1089 EmplaceSafeString(item, "text", match_ref);
1090
1091 if (description.empty())
1092 EmplaceSafeString(item, "label", match);
1093 else
1094 EmplaceSafeString(item, "label", match + " -- " + description);
1095
1096 targets.emplace_back(std::move(item));
1097 }
1098
1099 body.try_emplace("targets", std::move(targets));
1100 response.try_emplace("body", std::move(body));
1101 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1102 }
1103
1104 // "EvaluateRequest": {
1105 // "allOf": [ { "$ref": "#/definitions/Request" }, {
1106 // "type": "object",
1107 // "description": "Evaluate request; value of command field is 'evaluate'.
1108 // Evaluates the given expression in the context of the
1109 // top most stack frame. The expression has access to any
1110 // variables and arguments that are in scope.",
1111 // "properties": {
1112 // "command": {
1113 // "type": "string",
1114 // "enum": [ "evaluate" ]
1115 // },
1116 // "arguments": {
1117 // "$ref": "#/definitions/EvaluateArguments"
1118 // }
1119 // },
1120 // "required": [ "command", "arguments" ]
1121 // }]
1122 // },
1123 // "EvaluateArguments": {
1124 // "type": "object",
1125 // "description": "Arguments for 'evaluate' request.",
1126 // "properties": {
1127 // "expression": {
1128 // "type": "string",
1129 // "description": "The expression to evaluate."
1130 // },
1131 // "frameId": {
1132 // "type": "integer",
1133 // "description": "Evaluate the expression in the scope of this stack
1134 // frame. If not specified, the expression is evaluated
1135 // in the global scope."
1136 // },
1137 // "context": {
1138 // "type": "string",
1139 // "_enum": [ "watch", "repl", "hover" ],
1140 // "enumDescriptions": [
1141 // "evaluate is run in a watch.",
1142 // "evaluate is run from REPL console.",
1143 // "evaluate is run from a data hover."
1144 // ],
1145 // "description": "The context in which the evaluate request is run."
1146 // },
1147 // "format": {
1148 // "$ref": "#/definitions/ValueFormat",
1149 // "description": "Specifies details on how to format the Evaluate
1150 // result."
1151 // }
1152 // },
1153 // "required": [ "expression" ]
1154 // },
1155 // "EvaluateResponse": {
1156 // "allOf": [ { "$ref": "#/definitions/Response" }, {
1157 // "type": "object",
1158 // "description": "Response to 'evaluate' request.",
1159 // "properties": {
1160 // "body": {
1161 // "type": "object",
1162 // "properties": {
1163 // "result": {
1164 // "type": "string",
1165 // "description": "The result of the evaluate request."
1166 // },
1167 // "type": {
1168 // "type": "string",
1169 // "description": "The optional type of the evaluate result."
1170 // },
1171 // "presentationHint": {
1172 // "$ref": "#/definitions/VariablePresentationHint",
1173 // "description": "Properties of a evaluate result that can be
1174 // used to determine how to render the result in
1175 // the UI."
1176 // },
1177 // "variablesReference": {
1178 // "type": "number",
1179 // "description": "If variablesReference is > 0, the evaluate
1180 // result is structured and its children can be
1181 // retrieved by passing variablesReference to the
1182 // VariablesRequest."
1183 // },
1184 // "namedVariables": {
1185 // "type": "number",
1186 // "description": "The number of named child variables. The
1187 // client can use this optional information to
1188 // present the variables in a paged UI and fetch
1189 // them in chunks."
1190 // },
1191 // "indexedVariables": {
1192 // "type": "number",
1193 // "description": "The number of indexed child variables. The
1194 // client can use this optional information to
1195 // present the variables in a paged UI and fetch
1196 // them in chunks."
1197 // }
1198 // },
1199 // "required": [ "result", "variablesReference" ]
1200 // }
1201 // },
1202 // "required": [ "body" ]
1203 // }]
1204 // }
request_evaluate(const llvm::json::Object & request)1205 void request_evaluate(const llvm::json::Object &request) {
1206 llvm::json::Object response;
1207 FillResponse(request, response);
1208 llvm::json::Object body;
1209 auto arguments = request.getObject("arguments");
1210 lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments);
1211 const auto expression = GetString(arguments, "expression");
1212 llvm::StringRef context = GetString(arguments, "context");
1213
1214 if (!expression.empty() && expression[0] == '`') {
1215 auto result =
1216 RunLLDBCommands(llvm::StringRef(), {std::string(expression.substr(1))});
1217 EmplaceSafeString(body, "result", result);
1218 body.try_emplace("variablesReference", (int64_t)0);
1219 } else {
1220 // Always try to get the answer from the local variables if possible. If
1221 // this fails, then if the context is not "hover", actually evaluate an
1222 // expression using the expression parser.
1223 //
1224 // "frame variable" is more reliable than the expression parser in
1225 // many cases and it is faster.
1226 lldb::SBValue value = frame.GetValueForVariablePath(
1227 expression.data(), lldb::eDynamicDontRunTarget);
1228
1229 // Freeze dry the value in case users expand it later in the debug console
1230 if (value.GetError().Success() && context == "repl")
1231 value = value.Persist();
1232
1233 if (value.GetError().Fail() && context != "hover")
1234 value = frame.EvaluateExpression(expression.data());
1235
1236 if (value.GetError().Fail()) {
1237 response["success"] = llvm::json::Value(false);
1238 // This error object must live until we're done with the pointer returned
1239 // by GetCString().
1240 lldb::SBError error = value.GetError();
1241 const char *error_cstr = error.GetCString();
1242 if (error_cstr && error_cstr[0])
1243 EmplaceSafeString(response, "message", std::string(error_cstr));
1244 else
1245 EmplaceSafeString(response, "message", "evaluate failed");
1246 } else {
1247 SetValueForKey(value, body, "result");
1248 auto value_typename = value.GetType().GetDisplayTypeName();
1249 EmplaceSafeString(body, "type",
1250 value_typename ? value_typename : NO_TYPENAME);
1251 if (value.MightHaveChildren()) {
1252 auto variableReference = g_vsc.variables.InsertExpandableVariable(
1253 value, /*is_permanent=*/context == "repl");
1254 body.try_emplace("variablesReference", variableReference);
1255 } else {
1256 body.try_emplace("variablesReference", (int64_t)0);
1257 }
1258 }
1259 }
1260 response.try_emplace("body", std::move(body));
1261 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1262 }
1263
1264 // "compileUnitsRequest": {
1265 // "allOf": [ { "$ref": "#/definitions/Request" }, {
1266 // "type": "object",
1267 // "description": "Compile Unit request; value of command field is
1268 // 'compileUnits'.",
1269 // "properties": {
1270 // "command": {
1271 // "type": "string",
1272 // "enum": [ "compileUnits" ]
1273 // },
1274 // "arguments": {
1275 // "$ref": "#/definitions/compileUnitRequestArguments"
1276 // }
1277 // },
1278 // "required": [ "command", "arguments" ]
1279 // }]
1280 // },
1281 // "compileUnitsRequestArguments": {
1282 // "type": "object",
1283 // "description": "Arguments for 'compileUnits' request.",
1284 // "properties": {
1285 // "moduleId": {
1286 // "type": "string",
1287 // "description": "The ID of the module."
1288 // }
1289 // },
1290 // "required": [ "moduleId" ]
1291 // },
1292 // "compileUnitsResponse": {
1293 // "allOf": [ { "$ref": "#/definitions/Response" }, {
1294 // "type": "object",
1295 // "description": "Response to 'compileUnits' request.",
1296 // "properties": {
1297 // "body": {
1298 // "description": "Response to 'compileUnits' request. Array of
1299 // paths of compile units."
1300 // }
1301 // }
1302 // }]
1303 // }
request_compileUnits(const llvm::json::Object & request)1304 void request_compileUnits(const llvm::json::Object &request) {
1305 llvm::json::Object response;
1306 FillResponse(request, response);
1307 llvm::json::Object body;
1308 llvm::json::Array units;
1309 auto arguments = request.getObject("arguments");
1310 std::string module_id = std::string(GetString(arguments, "moduleId"));
1311 int num_modules = g_vsc.target.GetNumModules();
1312 for (int i = 0; i < num_modules; i++) {
1313 auto curr_module = g_vsc.target.GetModuleAtIndex(i);
1314 if (module_id == curr_module.GetUUIDString()) {
1315 int num_units = curr_module.GetNumCompileUnits();
1316 for (int j = 0; j < num_units; j++) {
1317 auto curr_unit = curr_module.GetCompileUnitAtIndex(j);
1318 units.emplace_back(CreateCompileUnit(curr_unit));
1319 }
1320 body.try_emplace("compileUnits", std::move(units));
1321 break;
1322 }
1323 }
1324 response.try_emplace("body", std::move(body));
1325 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1326 }
1327
1328 // "modulesRequest": {
1329 // "allOf": [ { "$ref": "#/definitions/Request" }, {
1330 // "type": "object",
1331 // "description": "Modules request; value of command field is
1332 // 'modules'.",
1333 // "properties": {
1334 // "command": {
1335 // "type": "string",
1336 // "enum": [ "modules" ]
1337 // },
1338 // },
1339 // "required": [ "command" ]
1340 // }]
1341 // },
1342 // "modulesResponse": {
1343 // "allOf": [ { "$ref": "#/definitions/Response" }, {
1344 // "type": "object",
1345 // "description": "Response to 'modules' request.",
1346 // "properties": {
1347 // "body": {
1348 // "description": "Response to 'modules' request. Array of
1349 // module objects."
1350 // }
1351 // }
1352 // }]
1353 // }
request_modules(const llvm::json::Object & request)1354 void request_modules(const llvm::json::Object &request) {
1355 llvm::json::Object response;
1356 FillResponse(request, response);
1357
1358 llvm::json::Array modules;
1359 for (size_t i = 0; i < g_vsc.target.GetNumModules(); i++) {
1360 lldb::SBModule module = g_vsc.target.GetModuleAtIndex(i);
1361 modules.emplace_back(CreateModule(module));
1362 }
1363
1364 llvm::json::Object body;
1365 body.try_emplace("modules", std::move(modules));
1366 response.try_emplace("body", std::move(body));
1367 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1368 }
1369
1370 // "InitializeRequest": {
1371 // "allOf": [ { "$ref": "#/definitions/Request" }, {
1372 // "type": "object",
1373 // "description": "Initialize request; value of command field is
1374 // 'initialize'.",
1375 // "properties": {
1376 // "command": {
1377 // "type": "string",
1378 // "enum": [ "initialize" ]
1379 // },
1380 // "arguments": {
1381 // "$ref": "#/definitions/InitializeRequestArguments"
1382 // }
1383 // },
1384 // "required": [ "command", "arguments" ]
1385 // }]
1386 // },
1387 // "InitializeRequestArguments": {
1388 // "type": "object",
1389 // "description": "Arguments for 'initialize' request.",
1390 // "properties": {
1391 // "clientID": {
1392 // "type": "string",
1393 // "description": "The ID of the (frontend) client using this adapter."
1394 // },
1395 // "adapterID": {
1396 // "type": "string",
1397 // "description": "The ID of the debug adapter."
1398 // },
1399 // "locale": {
1400 // "type": "string",
1401 // "description": "The ISO-639 locale of the (frontend) client using
1402 // this adapter, e.g. en-US or de-CH."
1403 // },
1404 // "linesStartAt1": {
1405 // "type": "boolean",
1406 // "description": "If true all line numbers are 1-based (default)."
1407 // },
1408 // "columnsStartAt1": {
1409 // "type": "boolean",
1410 // "description": "If true all column numbers are 1-based (default)."
1411 // },
1412 // "pathFormat": {
1413 // "type": "string",
1414 // "_enum": [ "path", "uri" ],
1415 // "description": "Determines in what format paths are specified. The
1416 // default is 'path', which is the native format."
1417 // },
1418 // "supportsVariableType": {
1419 // "type": "boolean",
1420 // "description": "Client supports the optional type attribute for
1421 // variables."
1422 // },
1423 // "supportsVariablePaging": {
1424 // "type": "boolean",
1425 // "description": "Client supports the paging of variables."
1426 // },
1427 // "supportsRunInTerminalRequest": {
1428 // "type": "boolean",
1429 // "description": "Client supports the runInTerminal request."
1430 // }
1431 // },
1432 // "required": [ "adapterID" ]
1433 // },
1434 // "InitializeResponse": {
1435 // "allOf": [ { "$ref": "#/definitions/Response" }, {
1436 // "type": "object",
1437 // "description": "Response to 'initialize' request.",
1438 // "properties": {
1439 // "body": {
1440 // "$ref": "#/definitions/Capabilities",
1441 // "description": "The capabilities of this debug adapter."
1442 // }
1443 // }
1444 // }]
1445 // }
request_initialize(const llvm::json::Object & request)1446 void request_initialize(const llvm::json::Object &request) {
1447 auto log_cb = [](const char *buf, void *baton) -> void {
1448 g_vsc.SendOutput(OutputType::Console, llvm::StringRef{buf});
1449 };
1450
1451 auto arguments = request.getObject("arguments");
1452 // sourceInitFile option is not from formal DAP specification. It is only
1453 // used by unit tests to prevent sourcing .lldbinit files from environment
1454 // which may affect the outcome of tests.
1455 bool source_init_file = GetBoolean(arguments, "sourceInitFile", true);
1456
1457 g_vsc.debugger =
1458 lldb::SBDebugger::Create(source_init_file, log_cb, nullptr);
1459 g_vsc.progress_event_thread = std::thread(ProgressEventThreadFunction);
1460
1461 // Start our event thread so we can receive events from the debugger, target,
1462 // process and more.
1463 g_vsc.event_thread = std::thread(EventThreadFunction);
1464
1465 llvm::json::Object response;
1466 FillResponse(request, response);
1467 llvm::json::Object body;
1468 // The debug adapter supports the configurationDoneRequest.
1469 body.try_emplace("supportsConfigurationDoneRequest", true);
1470 // The debug adapter supports function breakpoints.
1471 body.try_emplace("supportsFunctionBreakpoints", true);
1472 // The debug adapter supports conditional breakpoints.
1473 body.try_emplace("supportsConditionalBreakpoints", true);
1474 // The debug adapter supports breakpoints that break execution after a
1475 // specified number of hits.
1476 body.try_emplace("supportsHitConditionalBreakpoints", true);
1477 // The debug adapter supports a (side effect free) evaluate request for
1478 // data hovers.
1479 body.try_emplace("supportsEvaluateForHovers", true);
1480 // Available filters or options for the setExceptionBreakpoints request.
1481 llvm::json::Array filters;
1482 for (const auto &exc_bp : g_vsc.exception_breakpoints) {
1483 filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
1484 }
1485 body.try_emplace("exceptionBreakpointFilters", std::move(filters));
1486 // The debug adapter supports launching a debugee in intergrated VSCode
1487 // terminal.
1488 body.try_emplace("supportsRunInTerminalRequest", true);
1489 // The debug adapter supports stepping back via the stepBack and
1490 // reverseContinue requests.
1491 body.try_emplace("supportsStepBack", false);
1492 // The debug adapter supports setting a variable to a value.
1493 body.try_emplace("supportsSetVariable", true);
1494 // The debug adapter supports restarting a frame.
1495 body.try_emplace("supportsRestartFrame", false);
1496 // The debug adapter supports the gotoTargetsRequest.
1497 body.try_emplace("supportsGotoTargetsRequest", false);
1498 // The debug adapter supports the stepInTargetsRequest.
1499 body.try_emplace("supportsStepInTargetsRequest", false);
1500 // We need to improve the current implementation of completions in order to
1501 // enable it again. For some context, this is how VSCode works:
1502 // - VSCode sends a completion request whenever chars are added, the user
1503 // triggers completion manually via CTRL-space or similar mechanisms, but
1504 // not when there's a deletion. Besides, VSCode doesn't let us know which
1505 // of these events we are handling. What is more, the use can paste or cut
1506 // sections of the text arbitrarily.
1507 // https://github.com/microsoft/vscode/issues/89531 tracks part of the
1508 // issue just mentioned.
1509 // This behavior causes many problems with the current way completion is
1510 // implemented in lldb-vscode, as these requests could be really expensive,
1511 // blocking the debugger, and there could be many concurrent requests unless
1512 // the user types very slowly... We need to address this specific issue, or
1513 // at least trigger completion only when the user explicitly wants it, which
1514 // is the behavior of LLDB CLI, that expects a TAB.
1515 body.try_emplace("supportsCompletionsRequest", false);
1516 // The debug adapter supports the modules request.
1517 body.try_emplace("supportsModulesRequest", true);
1518 // The set of additional module information exposed by the debug adapter.
1519 // body.try_emplace("additionalModuleColumns"] = ColumnDescriptor
1520 // Checksum algorithms supported by the debug adapter.
1521 // body.try_emplace("supportedChecksumAlgorithms"] = ChecksumAlgorithm
1522 // The debug adapter supports the RestartRequest. In this case a client
1523 // should not implement 'restart' by terminating and relaunching the adapter
1524 // but by calling the RestartRequest.
1525 body.try_emplace("supportsRestartRequest", false);
1526 // The debug adapter supports 'exceptionOptions' on the
1527 // setExceptionBreakpoints request.
1528 body.try_emplace("supportsExceptionOptions", true);
1529 // The debug adapter supports a 'format' attribute on the stackTraceRequest,
1530 // variablesRequest, and evaluateRequest.
1531 body.try_emplace("supportsValueFormattingOptions", true);
1532 // The debug adapter supports the exceptionInfo request.
1533 body.try_emplace("supportsExceptionInfoRequest", true);
1534 // The debug adapter supports the 'terminateDebuggee' attribute on the
1535 // 'disconnect' request.
1536 body.try_emplace("supportTerminateDebuggee", true);
1537 // The debug adapter supports the delayed loading of parts of the stack,
1538 // which requires that both the 'startFrame' and 'levels' arguments and the
1539 // 'totalFrames' result of the 'StackTrace' request are supported.
1540 body.try_emplace("supportsDelayedStackTraceLoading", true);
1541 // The debug adapter supports the 'loadedSources' request.
1542 body.try_emplace("supportsLoadedSourcesRequest", false);
1543 // The debug adapter supports sending progress reporting events.
1544 body.try_emplace("supportsProgressReporting", true);
1545 // The debug adapter supports 'logMessage' in breakpoint.
1546 body.try_emplace("supportsLogPoints", true);
1547
1548 response.try_emplace("body", std::move(body));
1549 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1550 }
1551
request_runInTerminal(const llvm::json::Object & launch_request)1552 llvm::Error request_runInTerminal(const llvm::json::Object &launch_request) {
1553 g_vsc.is_attach = true;
1554 lldb::SBAttachInfo attach_info;
1555
1556 llvm::Expected<std::shared_ptr<FifoFile>> comm_file_or_err =
1557 CreateRunInTerminalCommFile();
1558 if (!comm_file_or_err)
1559 return comm_file_or_err.takeError();
1560 FifoFile &comm_file = *comm_file_or_err.get();
1561
1562 RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path);
1563
1564 llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
1565 launch_request, g_vsc.debug_adaptor_path, comm_file.m_path);
1566 llvm::json::Object reverse_response;
1567 lldb_vscode::PacketStatus status =
1568 g_vsc.SendReverseRequest(reverse_request, reverse_response);
1569 if (status != lldb_vscode::PacketStatus::Success)
1570 return llvm::createStringError(llvm::inconvertibleErrorCode(),
1571 "Process cannot be launched by the IDE. %s",
1572 comm_channel.GetLauncherError().c_str());
1573
1574 if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
1575 attach_info.SetProcessID(*pid);
1576 else
1577 return pid.takeError();
1578
1579 g_vsc.debugger.SetAsync(false);
1580 lldb::SBError error;
1581 g_vsc.target.Attach(attach_info, error);
1582
1583 if (error.Fail())
1584 return llvm::createStringError(llvm::inconvertibleErrorCode(),
1585 "Failed to attach to the target process. %s",
1586 comm_channel.GetLauncherError().c_str());
1587 // This will notify the runInTerminal launcher that we attached.
1588 // We have to make this async, as the function won't return until the launcher
1589 // resumes and reads the data.
1590 std::future<lldb::SBError> did_attach_message_success =
1591 comm_channel.NotifyDidAttach();
1592
1593 // We just attached to the runInTerminal launcher, which was waiting to be
1594 // attached. We now resume it, so it can receive the didAttach notification
1595 // and then perform the exec. Upon continuing, the debugger will stop the
1596 // process right in the middle of the exec. To the user, what we are doing is
1597 // transparent, as they will only be able to see the process since the exec,
1598 // completely unaware of the preparatory work.
1599 g_vsc.target.GetProcess().Continue();
1600
1601 // Now that the actual target is just starting (i.e. exec was just invoked),
1602 // we return the debugger to its async state.
1603 g_vsc.debugger.SetAsync(true);
1604
1605 // If sending the notification failed, the launcher should be dead by now and
1606 // the async didAttach notification should have an error message, so we
1607 // return it. Otherwise, everything was a success.
1608 did_attach_message_success.wait();
1609 error = did_attach_message_success.get();
1610 if (error.Success())
1611 return llvm::Error::success();
1612 return llvm::createStringError(llvm::inconvertibleErrorCode(),
1613 error.GetCString());
1614 }
1615
1616 // "LaunchRequest": {
1617 // "allOf": [ { "$ref": "#/definitions/Request" }, {
1618 // "type": "object",
1619 // "description": "Launch request; value of command field is 'launch'.",
1620 // "properties": {
1621 // "command": {
1622 // "type": "string",
1623 // "enum": [ "launch" ]
1624 // },
1625 // "arguments": {
1626 // "$ref": "#/definitions/LaunchRequestArguments"
1627 // }
1628 // },
1629 // "required": [ "command", "arguments" ]
1630 // }]
1631 // },
1632 // "LaunchRequestArguments": {
1633 // "type": "object",
1634 // "description": "Arguments for 'launch' request.",
1635 // "properties": {
1636 // "noDebug": {
1637 // "type": "boolean",
1638 // "description": "If noDebug is true the launch request should launch
1639 // the program without enabling debugging."
1640 // }
1641 // }
1642 // },
1643 // "LaunchResponse": {
1644 // "allOf": [ { "$ref": "#/definitions/Response" }, {
1645 // "type": "object",
1646 // "description": "Response to 'launch' request. This is just an
1647 // acknowledgement, so no body field is required."
1648 // }]
1649 // }
request_launch(const llvm::json::Object & request)1650 void request_launch(const llvm::json::Object &request) {
1651 g_vsc.is_attach = false;
1652 llvm::json::Object response;
1653 lldb::SBError error;
1654 FillResponse(request, response);
1655 auto arguments = request.getObject("arguments");
1656 g_vsc.init_commands = GetStrings(arguments, "initCommands");
1657 g_vsc.pre_run_commands = GetStrings(arguments, "preRunCommands");
1658 g_vsc.stop_commands = GetStrings(arguments, "stopCommands");
1659 g_vsc.exit_commands = GetStrings(arguments, "exitCommands");
1660 g_vsc.terminate_commands = GetStrings(arguments, "terminateCommands");
1661 auto launchCommands = GetStrings(arguments, "launchCommands");
1662 std::vector<std::string> postRunCommands =
1663 GetStrings(arguments, "postRunCommands");
1664 g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false);
1665 const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
1666 const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
1667
1668 // This is a hack for loading DWARF in .o files on Mac where the .o files
1669 // in the debug map of the main executable have relative paths which require
1670 // the lldb-vscode binary to have its working directory set to that relative
1671 // root for the .o files in order to be able to load debug info.
1672 if (!debuggerRoot.empty())
1673 llvm::sys::fs::set_current_path(debuggerRoot);
1674
1675 // Run any initialize LLDB commands the user specified in the launch.json.
1676 // This is run before target is created, so commands can't do anything with
1677 // the targets - preRunCommands are run with the target.
1678 g_vsc.RunInitCommands();
1679
1680 SetSourceMapFromArguments(*arguments);
1681
1682 lldb::SBError status;
1683 g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status));
1684 if (status.Fail()) {
1685 response["success"] = llvm::json::Value(false);
1686 EmplaceSafeString(response, "message", status.GetCString());
1687 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1688 return;
1689 }
1690
1691 // Instantiate a launch info instance for the target.
1692 auto launch_info = g_vsc.target.GetLaunchInfo();
1693
1694 // Grab the current working directory if there is one and set it in the
1695 // launch info.
1696 const auto cwd = GetString(arguments, "cwd");
1697 if (!cwd.empty())
1698 launch_info.SetWorkingDirectory(cwd.data());
1699
1700 // Extract any extra arguments and append them to our program arguments for
1701 // when we launch
1702 auto args = GetStrings(arguments, "args");
1703 if (!args.empty())
1704 launch_info.SetArguments(MakeArgv(args).data(), true);
1705
1706 // Pass any environment variables along that the user specified.
1707 auto envs = GetStrings(arguments, "env");
1708 if (!envs.empty())
1709 launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true);
1710
1711 auto flags = launch_info.GetLaunchFlags();
1712
1713 if (GetBoolean(arguments, "disableASLR", true))
1714 flags |= lldb::eLaunchFlagDisableASLR;
1715 if (GetBoolean(arguments, "disableSTDIO", false))
1716 flags |= lldb::eLaunchFlagDisableSTDIO;
1717 if (GetBoolean(arguments, "shellExpandArguments", false))
1718 flags |= lldb::eLaunchFlagShellExpandArguments;
1719 const bool detatchOnError = GetBoolean(arguments, "detachOnError", false);
1720 launch_info.SetDetachOnError(detatchOnError);
1721 launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
1722 lldb::eLaunchFlagStopAtEntry);
1723
1724 // Run any pre run LLDB commands the user specified in the launch.json
1725 g_vsc.RunPreRunCommands();
1726
1727 if (GetBoolean(arguments, "runInTerminal", false)) {
1728 if (llvm::Error err = request_runInTerminal(request))
1729 error.SetErrorString(llvm::toString(std::move(err)).c_str());
1730 } else if (launchCommands.empty()) {
1731 // Disable async events so the launch will be successful when we return from
1732 // the launch call and the launch will happen synchronously
1733 g_vsc.debugger.SetAsync(false);
1734 g_vsc.target.Launch(launch_info, error);
1735 g_vsc.debugger.SetAsync(true);
1736 } else {
1737 g_vsc.RunLLDBCommands("Running launchCommands:", launchCommands);
1738 // The custom commands might have created a new target so we should use the
1739 // selected target after these commands are run.
1740 g_vsc.target = g_vsc.debugger.GetSelectedTarget();
1741 // Make sure the process is launched and stopped at the entry point before
1742 // proceeding as the the launch commands are not run using the synchronous
1743 // mode.
1744 error = g_vsc.WaitForProcessToStop(timeout_seconds);
1745 }
1746
1747 if (error.Fail()) {
1748 response["success"] = llvm::json::Value(false);
1749 EmplaceSafeString(response, "message", std::string(error.GetCString()));
1750 } else {
1751 g_vsc.RunLLDBCommands("Running postRunCommands:", postRunCommands);
1752 }
1753
1754 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1755
1756 if (g_vsc.is_attach)
1757 SendProcessEvent(Attach); // this happens when doing runInTerminal
1758 else
1759 SendProcessEvent(Launch);
1760 g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized")));
1761 }
1762
1763 // "NextRequest": {
1764 // "allOf": [ { "$ref": "#/definitions/Request" }, {
1765 // "type": "object",
1766 // "description": "Next request; value of command field is 'next'. The
1767 // request starts the debuggee to run again for one step.
1768 // The debug adapter first sends the NextResponse and then
1769 // a StoppedEvent (event type 'step') after the step has
1770 // completed.",
1771 // "properties": {
1772 // "command": {
1773 // "type": "string",
1774 // "enum": [ "next" ]
1775 // },
1776 // "arguments": {
1777 // "$ref": "#/definitions/NextArguments"
1778 // }
1779 // },
1780 // "required": [ "command", "arguments" ]
1781 // }]
1782 // },
1783 // "NextArguments": {
1784 // "type": "object",
1785 // "description": "Arguments for 'next' request.",
1786 // "properties": {
1787 // "threadId": {
1788 // "type": "integer",
1789 // "description": "Execute 'next' for this thread."
1790 // }
1791 // },
1792 // "required": [ "threadId" ]
1793 // },
1794 // "NextResponse": {
1795 // "allOf": [ { "$ref": "#/definitions/Response" }, {
1796 // "type": "object",
1797 // "description": "Response to 'next' request. This is just an
1798 // acknowledgement, so no body field is required."
1799 // }]
1800 // }
request_next(const llvm::json::Object & request)1801 void request_next(const llvm::json::Object &request) {
1802 llvm::json::Object response;
1803 FillResponse(request, response);
1804 auto arguments = request.getObject("arguments");
1805 lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
1806 if (thread.IsValid()) {
1807 // Remember the thread ID that caused the resume so we can set the
1808 // "threadCausedFocus" boolean value in the "stopped" events.
1809 g_vsc.focus_tid = thread.GetThreadID();
1810 thread.StepOver();
1811 } else {
1812 response["success"] = llvm::json::Value(false);
1813 }
1814 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1815 }
1816
1817 // "PauseRequest": {
1818 // "allOf": [ { "$ref": "#/definitions/Request" }, {
1819 // "type": "object",
1820 // "description": "Pause request; value of command field is 'pause'. The
1821 // request suspenses the debuggee. The debug adapter first sends the
1822 // PauseResponse and then a StoppedEvent (event type 'pause') after the
1823 // thread has been paused successfully.", "properties": {
1824 // "command": {
1825 // "type": "string",
1826 // "enum": [ "pause" ]
1827 // },
1828 // "arguments": {
1829 // "$ref": "#/definitions/PauseArguments"
1830 // }
1831 // },
1832 // "required": [ "command", "arguments" ]
1833 // }]
1834 // },
1835 // "PauseArguments": {
1836 // "type": "object",
1837 // "description": "Arguments for 'pause' request.",
1838 // "properties": {
1839 // "threadId": {
1840 // "type": "integer",
1841 // "description": "Pause execution for this thread."
1842 // }
1843 // },
1844 // "required": [ "threadId" ]
1845 // },
1846 // "PauseResponse": {
1847 // "allOf": [ { "$ref": "#/definitions/Response" }, {
1848 // "type": "object",
1849 // "description": "Response to 'pause' request. This is just an
1850 // acknowledgement, so no body field is required."
1851 // }]
1852 // }
request_pause(const llvm::json::Object & request)1853 void request_pause(const llvm::json::Object &request) {
1854 llvm::json::Object response;
1855 FillResponse(request, response);
1856 lldb::SBProcess process = g_vsc.target.GetProcess();
1857 lldb::SBError error = process.Stop();
1858 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1859 }
1860
1861 // "ScopesRequest": {
1862 // "allOf": [ { "$ref": "#/definitions/Request" }, {
1863 // "type": "object",
1864 // "description": "Scopes request; value of command field is 'scopes'. The
1865 // request returns the variable scopes for a given stackframe ID.",
1866 // "properties": {
1867 // "command": {
1868 // "type": "string",
1869 // "enum": [ "scopes" ]
1870 // },
1871 // "arguments": {
1872 // "$ref": "#/definitions/ScopesArguments"
1873 // }
1874 // },
1875 // "required": [ "command", "arguments" ]
1876 // }]
1877 // },
1878 // "ScopesArguments": {
1879 // "type": "object",
1880 // "description": "Arguments for 'scopes' request.",
1881 // "properties": {
1882 // "frameId": {
1883 // "type": "integer",
1884 // "description": "Retrieve the scopes for this stackframe."
1885 // }
1886 // },
1887 // "required": [ "frameId" ]
1888 // },
1889 // "ScopesResponse": {
1890 // "allOf": [ { "$ref": "#/definitions/Response" }, {
1891 // "type": "object",
1892 // "description": "Response to 'scopes' request.",
1893 // "properties": {
1894 // "body": {
1895 // "type": "object",
1896 // "properties": {
1897 // "scopes": {
1898 // "type": "array",
1899 // "items": {
1900 // "$ref": "#/definitions/Scope"
1901 // },
1902 // "description": "The scopes of the stackframe. If the array has
1903 // length zero, there are no scopes available."
1904 // }
1905 // },
1906 // "required": [ "scopes" ]
1907 // }
1908 // },
1909 // "required": [ "body" ]
1910 // }]
1911 // }
request_scopes(const llvm::json::Object & request)1912 void request_scopes(const llvm::json::Object &request) {
1913 llvm::json::Object response;
1914 FillResponse(request, response);
1915 llvm::json::Object body;
1916 auto arguments = request.getObject("arguments");
1917 lldb::SBFrame frame = g_vsc.GetLLDBFrame(*arguments);
1918 // As the user selects different stack frames in the GUI, a "scopes" request
1919 // will be sent to the DAP. This is the only way we know that the user has
1920 // selected a frame in a thread. There are no other notifications that are
1921 // sent and VS code doesn't allow multiple frames to show variables
1922 // concurrently. If we select the thread and frame as the "scopes" requests
1923 // are sent, this allows users to type commands in the debugger console
1924 // with a backtick character to run lldb commands and these lldb commands
1925 // will now have the right context selected as they are run. If the user
1926 // types "`bt" into the debugger console and we had another thread selected
1927 // in the LLDB library, we would show the wrong thing to the user. If the
1928 // users switches threads with a lldb command like "`thread select 14", the
1929 // GUI will not update as there are no "event" notification packets that
1930 // allow us to change the currently selected thread or frame in the GUI that
1931 // I am aware of.
1932 if (frame.IsValid()) {
1933 frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
1934 frame.GetThread().SetSelectedFrame(frame.GetFrameID());
1935 }
1936 g_vsc.variables.locals = frame.GetVariables(/*arguments=*/true,
1937 /*locals=*/true,
1938 /*statics=*/false,
1939 /*in_scope_only=*/true);
1940 g_vsc.variables.globals = frame.GetVariables(/*arguments=*/false,
1941 /*locals=*/false,
1942 /*statics=*/true,
1943 /*in_scope_only=*/true);
1944 g_vsc.variables.registers = frame.GetRegisters();
1945 body.try_emplace("scopes", g_vsc.CreateTopLevelScopes());
1946 response.try_emplace("body", std::move(body));
1947 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
1948 }
1949
1950 // "SetBreakpointsRequest": {
1951 // "allOf": [ { "$ref": "#/definitions/Request" }, {
1952 // "type": "object",
1953 // "description": "SetBreakpoints request; value of command field is
1954 // 'setBreakpoints'. Sets multiple breakpoints for a single source and
1955 // clears all previous breakpoints in that source. To clear all breakpoint
1956 // for a source, specify an empty array. When a breakpoint is hit, a
1957 // StoppedEvent (event type 'breakpoint') is generated.", "properties": {
1958 // "command": {
1959 // "type": "string",
1960 // "enum": [ "setBreakpoints" ]
1961 // },
1962 // "arguments": {
1963 // "$ref": "#/definitions/SetBreakpointsArguments"
1964 // }
1965 // },
1966 // "required": [ "command", "arguments" ]
1967 // }]
1968 // },
1969 // "SetBreakpointsArguments": {
1970 // "type": "object",
1971 // "description": "Arguments for 'setBreakpoints' request.",
1972 // "properties": {
1973 // "source": {
1974 // "$ref": "#/definitions/Source",
1975 // "description": "The source location of the breakpoints; either
1976 // source.path or source.reference must be specified."
1977 // },
1978 // "breakpoints": {
1979 // "type": "array",
1980 // "items": {
1981 // "$ref": "#/definitions/SourceBreakpoint"
1982 // },
1983 // "description": "The code locations of the breakpoints."
1984 // },
1985 // "lines": {
1986 // "type": "array",
1987 // "items": {
1988 // "type": "integer"
1989 // },
1990 // "description": "Deprecated: The code locations of the breakpoints."
1991 // },
1992 // "sourceModified": {
1993 // "type": "boolean",
1994 // "description": "A value of true indicates that the underlying source
1995 // has been modified which results in new breakpoint locations."
1996 // }
1997 // },
1998 // "required": [ "source" ]
1999 // },
2000 // "SetBreakpointsResponse": {
2001 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2002 // "type": "object",
2003 // "description": "Response to 'setBreakpoints' request. Returned is
2004 // information about each breakpoint created by this request. This includes
2005 // the actual code location and whether the breakpoint could be verified.
2006 // The breakpoints returned are in the same order as the elements of the
2007 // 'breakpoints' (or the deprecated 'lines') in the
2008 // SetBreakpointsArguments.", "properties": {
2009 // "body": {
2010 // "type": "object",
2011 // "properties": {
2012 // "breakpoints": {
2013 // "type": "array",
2014 // "items": {
2015 // "$ref": "#/definitions/Breakpoint"
2016 // },
2017 // "description": "Information about the breakpoints. The array
2018 // elements are in the same order as the elements of the
2019 // 'breakpoints' (or the deprecated 'lines') in the
2020 // SetBreakpointsArguments."
2021 // }
2022 // },
2023 // "required": [ "breakpoints" ]
2024 // }
2025 // },
2026 // "required": [ "body" ]
2027 // }]
2028 // },
2029 // "SourceBreakpoint": {
2030 // "type": "object",
2031 // "description": "Properties of a breakpoint or logpoint passed to the
2032 // setBreakpoints request.", "properties": {
2033 // "line": {
2034 // "type": "integer",
2035 // "description": "The source line of the breakpoint or logpoint."
2036 // },
2037 // "column": {
2038 // "type": "integer",
2039 // "description": "An optional source column of the breakpoint."
2040 // },
2041 // "condition": {
2042 // "type": "string",
2043 // "description": "An optional expression for conditional breakpoints."
2044 // },
2045 // "hitCondition": {
2046 // "type": "string",
2047 // "description": "An optional expression that controls how many hits of
2048 // the breakpoint are ignored. The backend is expected to interpret the
2049 // expression as needed."
2050 // },
2051 // "logMessage": {
2052 // "type": "string",
2053 // "description": "If this attribute exists and is non-empty, the backend
2054 // must not 'break' (stop) but log the message instead. Expressions within
2055 // {} are interpolated."
2056 // }
2057 // },
2058 // "required": [ "line" ]
2059 // }
request_setBreakpoints(const llvm::json::Object & request)2060 void request_setBreakpoints(const llvm::json::Object &request) {
2061 llvm::json::Object response;
2062 lldb::SBError error;
2063 FillResponse(request, response);
2064 auto arguments = request.getObject("arguments");
2065 auto source = arguments->getObject("source");
2066 const auto path = GetString(source, "path");
2067 auto breakpoints = arguments->getArray("breakpoints");
2068 llvm::json::Array response_breakpoints;
2069
2070 // Decode the source breakpoint infos for this "setBreakpoints" request
2071 SourceBreakpointMap request_bps;
2072 // "breakpoints" may be unset, in which case we treat it the same as being set
2073 // to an empty array.
2074 if (breakpoints) {
2075 for (const auto &bp : *breakpoints) {
2076 auto bp_obj = bp.getAsObject();
2077 if (bp_obj) {
2078 SourceBreakpoint src_bp(*bp_obj);
2079 request_bps[src_bp.line] = src_bp;
2080
2081 // We check if this breakpoint already exists to update it
2082 auto existing_source_bps = g_vsc.source_breakpoints.find(path);
2083 if (existing_source_bps != g_vsc.source_breakpoints.end()) {
2084 const auto &existing_bp =
2085 existing_source_bps->second.find(src_bp.line);
2086 if (existing_bp != existing_source_bps->second.end()) {
2087 existing_bp->second.UpdateBreakpoint(src_bp);
2088 AppendBreakpoint(existing_bp->second.bp, response_breakpoints, path,
2089 src_bp.line);
2090 continue;
2091 }
2092 }
2093 // At this point the breakpoint is new
2094 g_vsc.source_breakpoints[path][src_bp.line] = src_bp;
2095 SourceBreakpoint &new_bp = g_vsc.source_breakpoints[path][src_bp.line];
2096 new_bp.SetBreakpoint(path.data());
2097 AppendBreakpoint(new_bp.bp, response_breakpoints, path, new_bp.line);
2098 }
2099 }
2100 }
2101
2102 // Delete any breakpoints in this source file that aren't in the
2103 // request_bps set. There is no call to remove breakpoints other than
2104 // calling this function with a smaller or empty "breakpoints" list.
2105 auto old_src_bp_pos = g_vsc.source_breakpoints.find(path);
2106 if (old_src_bp_pos != g_vsc.source_breakpoints.end()) {
2107 for (auto &old_bp : old_src_bp_pos->second) {
2108 auto request_pos = request_bps.find(old_bp.first);
2109 if (request_pos == request_bps.end()) {
2110 // This breakpoint no longer exists in this source file, delete it
2111 g_vsc.target.BreakpointDelete(old_bp.second.bp.GetID());
2112 old_src_bp_pos->second.erase(old_bp.first);
2113 }
2114 }
2115 }
2116
2117 llvm::json::Object body;
2118 body.try_emplace("breakpoints", std::move(response_breakpoints));
2119 response.try_emplace("body", std::move(body));
2120 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2121 }
2122
2123 // "SetExceptionBreakpointsRequest": {
2124 // "allOf": [ { "$ref": "#/definitions/Request" }, {
2125 // "type": "object",
2126 // "description": "SetExceptionBreakpoints request; value of command field
2127 // is 'setExceptionBreakpoints'. The request configures the debuggers
2128 // response to thrown exceptions. If an exception is configured to break, a
2129 // StoppedEvent is fired (event type 'exception').", "properties": {
2130 // "command": {
2131 // "type": "string",
2132 // "enum": [ "setExceptionBreakpoints" ]
2133 // },
2134 // "arguments": {
2135 // "$ref": "#/definitions/SetExceptionBreakpointsArguments"
2136 // }
2137 // },
2138 // "required": [ "command", "arguments" ]
2139 // }]
2140 // },
2141 // "SetExceptionBreakpointsArguments": {
2142 // "type": "object",
2143 // "description": "Arguments for 'setExceptionBreakpoints' request.",
2144 // "properties": {
2145 // "filters": {
2146 // "type": "array",
2147 // "items": {
2148 // "type": "string"
2149 // },
2150 // "description": "IDs of checked exception options. The set of IDs is
2151 // returned via the 'exceptionBreakpointFilters' capability."
2152 // },
2153 // "exceptionOptions": {
2154 // "type": "array",
2155 // "items": {
2156 // "$ref": "#/definitions/ExceptionOptions"
2157 // },
2158 // "description": "Configuration options for selected exceptions."
2159 // }
2160 // },
2161 // "required": [ "filters" ]
2162 // },
2163 // "SetExceptionBreakpointsResponse": {
2164 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2165 // "type": "object",
2166 // "description": "Response to 'setExceptionBreakpoints' request. This is
2167 // just an acknowledgement, so no body field is required."
2168 // }]
2169 // }
request_setExceptionBreakpoints(const llvm::json::Object & request)2170 void request_setExceptionBreakpoints(const llvm::json::Object &request) {
2171 llvm::json::Object response;
2172 lldb::SBError error;
2173 FillResponse(request, response);
2174 auto arguments = request.getObject("arguments");
2175 auto filters = arguments->getArray("filters");
2176 // Keep a list of any exception breakpoint filter names that weren't set
2177 // so we can clear any exception breakpoints if needed.
2178 std::set<std::string> unset_filters;
2179 for (const auto &bp : g_vsc.exception_breakpoints)
2180 unset_filters.insert(bp.filter);
2181
2182 for (const auto &value : *filters) {
2183 const auto filter = GetAsString(value);
2184 auto exc_bp = g_vsc.GetExceptionBreakpoint(std::string(filter));
2185 if (exc_bp) {
2186 exc_bp->SetBreakpoint();
2187 unset_filters.erase(std::string(filter));
2188 }
2189 }
2190 for (const auto &filter : unset_filters) {
2191 auto exc_bp = g_vsc.GetExceptionBreakpoint(filter);
2192 if (exc_bp)
2193 exc_bp->ClearBreakpoint();
2194 }
2195 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2196 }
2197
2198 // "SetFunctionBreakpointsRequest": {
2199 // "allOf": [ { "$ref": "#/definitions/Request" }, {
2200 // "type": "object",
2201 // "description": "SetFunctionBreakpoints request; value of command field is
2202 // 'setFunctionBreakpoints'. Sets multiple function breakpoints and clears
2203 // all previous function breakpoints. To clear all function breakpoint,
2204 // specify an empty array. When a function breakpoint is hit, a StoppedEvent
2205 // (event type 'function breakpoint') is generated.", "properties": {
2206 // "command": {
2207 // "type": "string",
2208 // "enum": [ "setFunctionBreakpoints" ]
2209 // },
2210 // "arguments": {
2211 // "$ref": "#/definitions/SetFunctionBreakpointsArguments"
2212 // }
2213 // },
2214 // "required": [ "command", "arguments" ]
2215 // }]
2216 // },
2217 // "SetFunctionBreakpointsArguments": {
2218 // "type": "object",
2219 // "description": "Arguments for 'setFunctionBreakpoints' request.",
2220 // "properties": {
2221 // "breakpoints": {
2222 // "type": "array",
2223 // "items": {
2224 // "$ref": "#/definitions/FunctionBreakpoint"
2225 // },
2226 // "description": "The function names of the breakpoints."
2227 // }
2228 // },
2229 // "required": [ "breakpoints" ]
2230 // },
2231 // "FunctionBreakpoint": {
2232 // "type": "object",
2233 // "description": "Properties of a breakpoint passed to the
2234 // setFunctionBreakpoints request.", "properties": {
2235 // "name": {
2236 // "type": "string",
2237 // "description": "The name of the function."
2238 // },
2239 // "condition": {
2240 // "type": "string",
2241 // "description": "An optional expression for conditional breakpoints."
2242 // },
2243 // "hitCondition": {
2244 // "type": "string",
2245 // "description": "An optional expression that controls how many hits of
2246 // the breakpoint are ignored. The backend is expected to interpret the
2247 // expression as needed."
2248 // }
2249 // },
2250 // "required": [ "name" ]
2251 // },
2252 // "SetFunctionBreakpointsResponse": {
2253 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2254 // "type": "object",
2255 // "description": "Response to 'setFunctionBreakpoints' request. Returned is
2256 // information about each breakpoint created by this request.",
2257 // "properties": {
2258 // "body": {
2259 // "type": "object",
2260 // "properties": {
2261 // "breakpoints": {
2262 // "type": "array",
2263 // "items": {
2264 // "$ref": "#/definitions/Breakpoint"
2265 // },
2266 // "description": "Information about the breakpoints. The array
2267 // elements correspond to the elements of the 'breakpoints' array."
2268 // }
2269 // },
2270 // "required": [ "breakpoints" ]
2271 // }
2272 // },
2273 // "required": [ "body" ]
2274 // }]
2275 // }
request_setFunctionBreakpoints(const llvm::json::Object & request)2276 void request_setFunctionBreakpoints(const llvm::json::Object &request) {
2277 llvm::json::Object response;
2278 lldb::SBError error;
2279 FillResponse(request, response);
2280 auto arguments = request.getObject("arguments");
2281 auto breakpoints = arguments->getArray("breakpoints");
2282 FunctionBreakpointMap request_bps;
2283 llvm::json::Array response_breakpoints;
2284 for (const auto &value : *breakpoints) {
2285 auto bp_obj = value.getAsObject();
2286 if (bp_obj == nullptr)
2287 continue;
2288 FunctionBreakpoint func_bp(*bp_obj);
2289 request_bps[func_bp.functionName] = std::move(func_bp);
2290 }
2291
2292 std::vector<llvm::StringRef> remove_names;
2293 // Disable any function breakpoints that aren't in the request_bps.
2294 // There is no call to remove function breakpoints other than calling this
2295 // function with a smaller or empty "breakpoints" list.
2296 for (auto &pair : g_vsc.function_breakpoints) {
2297 auto request_pos = request_bps.find(pair.first());
2298 if (request_pos == request_bps.end()) {
2299 // This function breakpoint no longer exists delete it from LLDB
2300 g_vsc.target.BreakpointDelete(pair.second.bp.GetID());
2301 remove_names.push_back(pair.first());
2302 } else {
2303 // Update the existing breakpoint as any setting withing the function
2304 // breakpoint might have changed.
2305 pair.second.UpdateBreakpoint(request_pos->second);
2306 // Remove this breakpoint from the request breakpoints since we have
2307 // handled it here and we don't need to set a new breakpoint below.
2308 request_bps.erase(request_pos);
2309 // Add this breakpoint info to the response
2310 AppendBreakpoint(pair.second.bp, response_breakpoints);
2311 }
2312 }
2313 // Remove any breakpoints that are no longer in our list
2314 for (const auto &name : remove_names)
2315 g_vsc.function_breakpoints.erase(name);
2316
2317 // Any breakpoints that are left in "request_bps" are breakpoints that
2318 // need to be set.
2319 for (auto &pair : request_bps) {
2320 // Add this breakpoint info to the response
2321 g_vsc.function_breakpoints[pair.first()] = std::move(pair.second);
2322 FunctionBreakpoint &new_bp = g_vsc.function_breakpoints[pair.first()];
2323 new_bp.SetBreakpoint();
2324 AppendBreakpoint(new_bp.bp, response_breakpoints);
2325 }
2326
2327 llvm::json::Object body;
2328 body.try_emplace("breakpoints", std::move(response_breakpoints));
2329 response.try_emplace("body", std::move(body));
2330 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2331 }
2332
2333 // "SourceRequest": {
2334 // "allOf": [ { "$ref": "#/definitions/Request" }, {
2335 // "type": "object",
2336 // "description": "Source request; value of command field is 'source'. The
2337 // request retrieves the source code for a given source reference.",
2338 // "properties": {
2339 // "command": {
2340 // "type": "string",
2341 // "enum": [ "source" ]
2342 // },
2343 // "arguments": {
2344 // "$ref": "#/definitions/SourceArguments"
2345 // }
2346 // },
2347 // "required": [ "command", "arguments" ]
2348 // }]
2349 // },
2350 // "SourceArguments": {
2351 // "type": "object",
2352 // "description": "Arguments for 'source' request.",
2353 // "properties": {
2354 // "source": {
2355 // "$ref": "#/definitions/Source",
2356 // "description": "Specifies the source content to load. Either
2357 // source.path or source.sourceReference must be specified."
2358 // },
2359 // "sourceReference": {
2360 // "type": "integer",
2361 // "description": "The reference to the source. This is the same as
2362 // source.sourceReference. This is provided for backward compatibility
2363 // since old backends do not understand the 'source' attribute."
2364 // }
2365 // },
2366 // "required": [ "sourceReference" ]
2367 // },
2368 // "SourceResponse": {
2369 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2370 // "type": "object",
2371 // "description": "Response to 'source' request.",
2372 // "properties": {
2373 // "body": {
2374 // "type": "object",
2375 // "properties": {
2376 // "content": {
2377 // "type": "string",
2378 // "description": "Content of the source reference."
2379 // },
2380 // "mimeType": {
2381 // "type": "string",
2382 // "description": "Optional content type (mime type) of the source."
2383 // }
2384 // },
2385 // "required": [ "content" ]
2386 // }
2387 // },
2388 // "required": [ "body" ]
2389 // }]
2390 // }
request_source(const llvm::json::Object & request)2391 void request_source(const llvm::json::Object &request) {
2392 llvm::json::Object response;
2393 FillResponse(request, response);
2394 llvm::json::Object body;
2395
2396 auto arguments = request.getObject("arguments");
2397 auto source = arguments->getObject("source");
2398 auto sourceReference = GetSigned(source, "sourceReference", -1);
2399 auto pos = g_vsc.source_map.find((lldb::addr_t)sourceReference);
2400 if (pos != g_vsc.source_map.end()) {
2401 EmplaceSafeString(body, "content", pos->second.content);
2402 } else {
2403 response["success"] = llvm::json::Value(false);
2404 }
2405 EmplaceSafeString(body, "mimeType", "text/x-lldb.disassembly");
2406 response.try_emplace("body", std::move(body));
2407 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2408 }
2409
2410 // "StackTraceRequest": {
2411 // "allOf": [ { "$ref": "#/definitions/Request" }, {
2412 // "type": "object",
2413 // "description": "StackTrace request; value of command field is
2414 // 'stackTrace'. The request returns a stacktrace from the current execution
2415 // state.", "properties": {
2416 // "command": {
2417 // "type": "string",
2418 // "enum": [ "stackTrace" ]
2419 // },
2420 // "arguments": {
2421 // "$ref": "#/definitions/StackTraceArguments"
2422 // }
2423 // },
2424 // "required": [ "command", "arguments" ]
2425 // }]
2426 // },
2427 // "StackTraceArguments": {
2428 // "type": "object",
2429 // "description": "Arguments for 'stackTrace' request.",
2430 // "properties": {
2431 // "threadId": {
2432 // "type": "integer",
2433 // "description": "Retrieve the stacktrace for this thread."
2434 // },
2435 // "startFrame": {
2436 // "type": "integer",
2437 // "description": "The index of the first frame to return; if omitted
2438 // frames start at 0."
2439 // },
2440 // "levels": {
2441 // "type": "integer",
2442 // "description": "The maximum number of frames to return. If levels is
2443 // not specified or 0, all frames are returned."
2444 // },
2445 // "format": {
2446 // "$ref": "#/definitions/StackFrameFormat",
2447 // "description": "Specifies details on how to format the stack frames."
2448 // }
2449 // },
2450 // "required": [ "threadId" ]
2451 // },
2452 // "StackTraceResponse": {
2453 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2454 // "type": "object",
2455 // "description": "Response to 'stackTrace' request.",
2456 // "properties": {
2457 // "body": {
2458 // "type": "object",
2459 // "properties": {
2460 // "stackFrames": {
2461 // "type": "array",
2462 // "items": {
2463 // "$ref": "#/definitions/StackFrame"
2464 // },
2465 // "description": "The frames of the stackframe. If the array has
2466 // length zero, there are no stackframes available. This means that
2467 // there is no location information available."
2468 // },
2469 // "totalFrames": {
2470 // "type": "integer",
2471 // "description": "The total number of frames available."
2472 // }
2473 // },
2474 // "required": [ "stackFrames" ]
2475 // }
2476 // },
2477 // "required": [ "body" ]
2478 // }]
2479 // }
request_stackTrace(const llvm::json::Object & request)2480 void request_stackTrace(const llvm::json::Object &request) {
2481 llvm::json::Object response;
2482 FillResponse(request, response);
2483 lldb::SBError error;
2484 auto arguments = request.getObject("arguments");
2485 lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
2486 llvm::json::Array stackFrames;
2487 llvm::json::Object body;
2488
2489 if (thread.IsValid()) {
2490 const auto startFrame = GetUnsigned(arguments, "startFrame", 0);
2491 const auto levels = GetUnsigned(arguments, "levels", 0);
2492 const auto endFrame = (levels == 0) ? INT64_MAX : (startFrame + levels);
2493 for (uint32_t i = startFrame; i < endFrame; ++i) {
2494 auto frame = thread.GetFrameAtIndex(i);
2495 if (!frame.IsValid())
2496 break;
2497 stackFrames.emplace_back(CreateStackFrame(frame));
2498 }
2499 const auto totalFrames = thread.GetNumFrames();
2500 body.try_emplace("totalFrames", totalFrames);
2501 }
2502 body.try_emplace("stackFrames", std::move(stackFrames));
2503 response.try_emplace("body", std::move(body));
2504 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2505 }
2506
2507 // "StepInRequest": {
2508 // "allOf": [ { "$ref": "#/definitions/Request" }, {
2509 // "type": "object",
2510 // "description": "StepIn request; value of command field is 'stepIn'. The
2511 // request starts the debuggee to step into a function/method if possible.
2512 // If it cannot step into a target, 'stepIn' behaves like 'next'. The debug
2513 // adapter first sends the StepInResponse and then a StoppedEvent (event
2514 // type 'step') after the step has completed. If there are multiple
2515 // function/method calls (or other targets) on the source line, the optional
2516 // argument 'targetId' can be used to control into which target the 'stepIn'
2517 // should occur. The list of possible targets for a given source line can be
2518 // retrieved via the 'stepInTargets' request.", "properties": {
2519 // "command": {
2520 // "type": "string",
2521 // "enum": [ "stepIn" ]
2522 // },
2523 // "arguments": {
2524 // "$ref": "#/definitions/StepInArguments"
2525 // }
2526 // },
2527 // "required": [ "command", "arguments" ]
2528 // }]
2529 // },
2530 // "StepInArguments": {
2531 // "type": "object",
2532 // "description": "Arguments for 'stepIn' request.",
2533 // "properties": {
2534 // "threadId": {
2535 // "type": "integer",
2536 // "description": "Execute 'stepIn' for this thread."
2537 // },
2538 // "targetId": {
2539 // "type": "integer",
2540 // "description": "Optional id of the target to step into."
2541 // }
2542 // },
2543 // "required": [ "threadId" ]
2544 // },
2545 // "StepInResponse": {
2546 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2547 // "type": "object",
2548 // "description": "Response to 'stepIn' request. This is just an
2549 // acknowledgement, so no body field is required."
2550 // }]
2551 // }
request_stepIn(const llvm::json::Object & request)2552 void request_stepIn(const llvm::json::Object &request) {
2553 llvm::json::Object response;
2554 FillResponse(request, response);
2555 auto arguments = request.getObject("arguments");
2556 lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
2557 if (thread.IsValid()) {
2558 // Remember the thread ID that caused the resume so we can set the
2559 // "threadCausedFocus" boolean value in the "stopped" events.
2560 g_vsc.focus_tid = thread.GetThreadID();
2561 thread.StepInto();
2562 } else {
2563 response["success"] = llvm::json::Value(false);
2564 }
2565 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2566 }
2567
2568 // "StepOutRequest": {
2569 // "allOf": [ { "$ref": "#/definitions/Request" }, {
2570 // "type": "object",
2571 // "description": "StepOut request; value of command field is 'stepOut'. The
2572 // request starts the debuggee to run again for one step. The debug adapter
2573 // first sends the StepOutResponse and then a StoppedEvent (event type
2574 // 'step') after the step has completed.", "properties": {
2575 // "command": {
2576 // "type": "string",
2577 // "enum": [ "stepOut" ]
2578 // },
2579 // "arguments": {
2580 // "$ref": "#/definitions/StepOutArguments"
2581 // }
2582 // },
2583 // "required": [ "command", "arguments" ]
2584 // }]
2585 // },
2586 // "StepOutArguments": {
2587 // "type": "object",
2588 // "description": "Arguments for 'stepOut' request.",
2589 // "properties": {
2590 // "threadId": {
2591 // "type": "integer",
2592 // "description": "Execute 'stepOut' for this thread."
2593 // }
2594 // },
2595 // "required": [ "threadId" ]
2596 // },
2597 // "StepOutResponse": {
2598 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2599 // "type": "object",
2600 // "description": "Response to 'stepOut' request. This is just an
2601 // acknowledgement, so no body field is required."
2602 // }]
2603 // }
request_stepOut(const llvm::json::Object & request)2604 void request_stepOut(const llvm::json::Object &request) {
2605 llvm::json::Object response;
2606 FillResponse(request, response);
2607 auto arguments = request.getObject("arguments");
2608 lldb::SBThread thread = g_vsc.GetLLDBThread(*arguments);
2609 if (thread.IsValid()) {
2610 // Remember the thread ID that caused the resume so we can set the
2611 // "threadCausedFocus" boolean value in the "stopped" events.
2612 g_vsc.focus_tid = thread.GetThreadID();
2613 thread.StepOut();
2614 } else {
2615 response["success"] = llvm::json::Value(false);
2616 }
2617 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2618 }
2619
2620 // "ThreadsRequest": {
2621 // "allOf": [ { "$ref": "#/definitions/Request" }, {
2622 // "type": "object",
2623 // "description": "Thread request; value of command field is 'threads'. The
2624 // request retrieves a list of all threads.", "properties": {
2625 // "command": {
2626 // "type": "string",
2627 // "enum": [ "threads" ]
2628 // }
2629 // },
2630 // "required": [ "command" ]
2631 // }]
2632 // },
2633 // "ThreadsResponse": {
2634 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2635 // "type": "object",
2636 // "description": "Response to 'threads' request.",
2637 // "properties": {
2638 // "body": {
2639 // "type": "object",
2640 // "properties": {
2641 // "threads": {
2642 // "type": "array",
2643 // "items": {
2644 // "$ref": "#/definitions/Thread"
2645 // },
2646 // "description": "All threads."
2647 // }
2648 // },
2649 // "required": [ "threads" ]
2650 // }
2651 // },
2652 // "required": [ "body" ]
2653 // }]
2654 // }
request_threads(const llvm::json::Object & request)2655 void request_threads(const llvm::json::Object &request) {
2656
2657 lldb::SBProcess process = g_vsc.target.GetProcess();
2658 llvm::json::Object response;
2659 FillResponse(request, response);
2660
2661 const uint32_t num_threads = process.GetNumThreads();
2662 llvm::json::Array threads;
2663 for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
2664 lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
2665 threads.emplace_back(CreateThread(thread));
2666 }
2667 if (threads.size() == 0) {
2668 response["success"] = llvm::json::Value(false);
2669 }
2670 llvm::json::Object body;
2671 body.try_emplace("threads", std::move(threads));
2672 response.try_emplace("body", std::move(body));
2673 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2674 }
2675
2676 // "SetVariableRequest": {
2677 // "allOf": [ { "$ref": "#/definitions/Request" }, {
2678 // "type": "object",
2679 // "description": "setVariable request; value of command field is
2680 // 'setVariable'. Set the variable with the given name in the variable
2681 // container to a new value.", "properties": {
2682 // "command": {
2683 // "type": "string",
2684 // "enum": [ "setVariable" ]
2685 // },
2686 // "arguments": {
2687 // "$ref": "#/definitions/SetVariableArguments"
2688 // }
2689 // },
2690 // "required": [ "command", "arguments" ]
2691 // }]
2692 // },
2693 // "SetVariableArguments": {
2694 // "type": "object",
2695 // "description": "Arguments for 'setVariable' request.",
2696 // "properties": {
2697 // "variablesReference": {
2698 // "type": "integer",
2699 // "description": "The reference of the variable container."
2700 // },
2701 // "name": {
2702 // "type": "string",
2703 // "description": "The name of the variable."
2704 // },
2705 // "value": {
2706 // "type": "string",
2707 // "description": "The value of the variable."
2708 // },
2709 // "format": {
2710 // "$ref": "#/definitions/ValueFormat",
2711 // "description": "Specifies details on how to format the response value."
2712 // }
2713 // },
2714 // "required": [ "variablesReference", "name", "value" ]
2715 // },
2716 // "SetVariableResponse": {
2717 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2718 // "type": "object",
2719 // "description": "Response to 'setVariable' request.",
2720 // "properties": {
2721 // "body": {
2722 // "type": "object",
2723 // "properties": {
2724 // "value": {
2725 // "type": "string",
2726 // "description": "The new value of the variable."
2727 // },
2728 // "type": {
2729 // "type": "string",
2730 // "description": "The type of the new value. Typically shown in the
2731 // UI when hovering over the value."
2732 // },
2733 // "variablesReference": {
2734 // "type": "number",
2735 // "description": "If variablesReference is > 0, the new value is
2736 // structured and its children can be retrieved by passing
2737 // variablesReference to the VariablesRequest."
2738 // },
2739 // "namedVariables": {
2740 // "type": "number",
2741 // "description": "The number of named child variables. The client
2742 // can use this optional information to present the variables in a
2743 // paged UI and fetch them in chunks."
2744 // },
2745 // "indexedVariables": {
2746 // "type": "number",
2747 // "description": "The number of indexed child variables. The client
2748 // can use this optional information to present the variables in a
2749 // paged UI and fetch them in chunks."
2750 // }
2751 // },
2752 // "required": [ "value" ]
2753 // }
2754 // },
2755 // "required": [ "body" ]
2756 // }]
2757 // }
request_setVariable(const llvm::json::Object & request)2758 void request_setVariable(const llvm::json::Object &request) {
2759 llvm::json::Object response;
2760 FillResponse(request, response);
2761 llvm::json::Array variables;
2762 llvm::json::Object body;
2763 auto arguments = request.getObject("arguments");
2764 // This is a reference to the containing variable/scope
2765 const auto variablesReference =
2766 GetUnsigned(arguments, "variablesReference", 0);
2767 llvm::StringRef name = GetString(arguments, "name");
2768 bool is_duplicated_variable_name = name.contains(" @");
2769
2770 const auto value = GetString(arguments, "value");
2771 // Set success to false just in case we don't find the variable by name
2772 response.try_emplace("success", false);
2773
2774 lldb::SBValue variable;
2775 int64_t newVariablesReference = 0;
2776
2777 // The "id" is the unique integer ID that is unique within the enclosing
2778 // variablesReference. It is optionally added to any "interface Variable"
2779 // objects to uniquely identify a variable within an enclosing
2780 // variablesReference. It helps to disambiguate between two variables that
2781 // have the same name within the same scope since the "setVariables" request
2782 // only specifies the variable reference of the enclosing scope/variable, and
2783 // the name of the variable. We could have two shadowed variables with the
2784 // same name in "Locals" or "Globals". In our case the "id" absolute index
2785 // of the variable within the g_vsc.variables list.
2786 const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX);
2787 if (id_value != UINT64_MAX) {
2788 variable = g_vsc.variables.GetVariable(id_value);
2789 } else if (lldb::SBValueList *top_scope =
2790 GetTopLevelScope(variablesReference)) {
2791 // variablesReference is one of our scopes, not an actual variable it is
2792 // asking for a variable in locals or globals or registers
2793 int64_t end_idx = top_scope->GetSize();
2794 // Searching backward so that we choose the variable in closest scope
2795 // among variables of the same name.
2796 for (int64_t i = end_idx - 1; i >= 0; --i) {
2797 lldb::SBValue curr_variable = top_scope->GetValueAtIndex(i);
2798 std::string variable_name = CreateUniqueVariableNameForDisplay(
2799 curr_variable, is_duplicated_variable_name);
2800 if (variable_name == name) {
2801 variable = curr_variable;
2802 break;
2803 }
2804 }
2805 } else {
2806 // This is not under the globals or locals scope, so there are no duplicated
2807 // names.
2808
2809 // We have a named item within an actual variable so we need to find it
2810 // withing the container variable by name.
2811 lldb::SBValue container = g_vsc.variables.GetVariable(variablesReference);
2812 variable = container.GetChildMemberWithName(name.data());
2813 if (!variable.IsValid()) {
2814 if (name.startswith("[")) {
2815 llvm::StringRef index_str(name.drop_front(1));
2816 uint64_t index = 0;
2817 if (!index_str.consumeInteger(0, index)) {
2818 if (index_str == "]")
2819 variable = container.GetChildAtIndex(index);
2820 }
2821 }
2822 }
2823 }
2824
2825 if (variable.IsValid()) {
2826 lldb::SBError error;
2827 bool success = variable.SetValueFromCString(value.data(), error);
2828 if (success) {
2829 SetValueForKey(variable, body, "value");
2830 EmplaceSafeString(body, "type", variable.GetType().GetDisplayTypeName());
2831
2832 // We don't know the index of the variable in our g_vsc.variables
2833 // so always insert a new one to get its variablesReference.
2834 // is_permanent is false because debug console does not support
2835 // setVariable request.
2836 if (variable.MightHaveChildren())
2837 newVariablesReference = g_vsc.variables.InsertExpandableVariable(
2838 variable, /*is_permanent=*/false);
2839
2840 body.try_emplace("variablesReference", newVariablesReference);
2841 } else {
2842 EmplaceSafeString(body, "message", std::string(error.GetCString()));
2843 }
2844 response["success"] = llvm::json::Value(success);
2845 } else {
2846 response["success"] = llvm::json::Value(false);
2847 }
2848
2849 response.try_emplace("body", std::move(body));
2850 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
2851 }
2852
2853 // "VariablesRequest": {
2854 // "allOf": [ { "$ref": "#/definitions/Request" }, {
2855 // "type": "object",
2856 // "description": "Variables request; value of command field is 'variables'.
2857 // Retrieves all child variables for the given variable reference. An
2858 // optional filter can be used to limit the fetched children to either named
2859 // or indexed children.", "properties": {
2860 // "command": {
2861 // "type": "string",
2862 // "enum": [ "variables" ]
2863 // },
2864 // "arguments": {
2865 // "$ref": "#/definitions/VariablesArguments"
2866 // }
2867 // },
2868 // "required": [ "command", "arguments" ]
2869 // }]
2870 // },
2871 // "VariablesArguments": {
2872 // "type": "object",
2873 // "description": "Arguments for 'variables' request.",
2874 // "properties": {
2875 // "variablesReference": {
2876 // "type": "integer",
2877 // "description": "The Variable reference."
2878 // },
2879 // "filter": {
2880 // "type": "string",
2881 // "enum": [ "indexed", "named" ],
2882 // "description": "Optional filter to limit the child variables to either
2883 // named or indexed. If ommited, both types are fetched."
2884 // },
2885 // "start": {
2886 // "type": "integer",
2887 // "description": "The index of the first variable to return; if omitted
2888 // children start at 0."
2889 // },
2890 // "count": {
2891 // "type": "integer",
2892 // "description": "The number of variables to return. If count is missing
2893 // or 0, all variables are returned."
2894 // },
2895 // "format": {
2896 // "$ref": "#/definitions/ValueFormat",
2897 // "description": "Specifies details on how to format the Variable
2898 // values."
2899 // }
2900 // },
2901 // "required": [ "variablesReference" ]
2902 // },
2903 // "VariablesResponse": {
2904 // "allOf": [ { "$ref": "#/definitions/Response" }, {
2905 // "type": "object",
2906 // "description": "Response to 'variables' request.",
2907 // "properties": {
2908 // "body": {
2909 // "type": "object",
2910 // "properties": {
2911 // "variables": {
2912 // "type": "array",
2913 // "items": {
2914 // "$ref": "#/definitions/Variable"
2915 // },
2916 // "description": "All (or a range) of variables for the given
2917 // variable reference."
2918 // }
2919 // },
2920 // "required": [ "variables" ]
2921 // }
2922 // },
2923 // "required": [ "body" ]
2924 // }]
2925 // }
request_variables(const llvm::json::Object & request)2926 void request_variables(const llvm::json::Object &request) {
2927 llvm::json::Object response;
2928 FillResponse(request, response);
2929 llvm::json::Array variables;
2930 auto arguments = request.getObject("arguments");
2931 const auto variablesReference =
2932 GetUnsigned(arguments, "variablesReference", 0);
2933 const int64_t start = GetSigned(arguments, "start", 0);
2934 const int64_t count = GetSigned(arguments, "count", 0);
2935 bool hex = false;
2936 auto format = arguments->getObject("format");
2937 if (format)
2938 hex = GetBoolean(format, "hex", false);
2939
2940 if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
2941 // variablesReference is one of our scopes, not an actual variable it is
2942 // asking for the list of args, locals or globals.
2943 int64_t start_idx = 0;
2944 int64_t num_children = 0;
2945
2946 if (variablesReference == VARREF_REGS) {
2947 // Change the default format of any pointer sized registers in the first
2948 // register set to be the lldb::eFormatAddressInfo so we show the pointer
2949 // and resolve what the pointer resolves to. Only change the format if the
2950 // format was set to the default format or if it was hex as some registers
2951 // have formats set for them.
2952 const uint32_t addr_size = g_vsc.target.GetProcess().GetAddressByteSize();
2953 lldb::SBValue reg_set = g_vsc.variables.registers.GetValueAtIndex(0);
2954 const uint32_t num_regs = reg_set.GetNumChildren();
2955 for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
2956 lldb::SBValue reg = reg_set.GetChildAtIndex(reg_idx);
2957 const lldb::Format format = reg.GetFormat();
2958 if (format == lldb::eFormatDefault || format == lldb::eFormatHex) {
2959 if (reg.GetByteSize() == addr_size)
2960 reg.SetFormat(lldb::eFormatAddressInfo);
2961 }
2962 }
2963 }
2964
2965 num_children = top_scope->GetSize();
2966 if (num_children == 0 && variablesReference == VARREF_LOCALS) {
2967 // Check for an error in the SBValueList that might explain why we don't
2968 // have locals. If we have an error display it as the sole value in the
2969 // the locals.
2970
2971 // "error" owns the error string so we must keep it alive as long as we
2972 // want to use the returns "const char *"
2973 lldb::SBError error = top_scope->GetError();
2974 const char *var_err = error.GetCString();
2975 if (var_err) {
2976 // Create a fake variable named "error" to explain why variables were
2977 // not available. This new error will help let users know when there was
2978 // a problem that kept variables from being available for display and
2979 // allow users to fix this issue instead of seeing no variables. The
2980 // errors are only set when there is a problem that the user could
2981 // fix, so no error will show up when you have no debug info, only when
2982 // we do have debug info and something that is fixable can be done.
2983 llvm::json::Object object;
2984 EmplaceSafeString(object, "name", "<error>");
2985 EmplaceSafeString(object, "type", "const char *");
2986 EmplaceSafeString(object, "value", var_err);
2987 object.try_emplace("variablesReference", (int64_t)0);
2988 variables.emplace_back(std::move(object));
2989 }
2990 }
2991 const int64_t end_idx = start_idx + ((count == 0) ? num_children : count);
2992
2993 // We first find out which variable names are duplicated
2994 std::map<std::string, int> variable_name_counts;
2995 for (auto i = start_idx; i < end_idx; ++i) {
2996 lldb::SBValue variable = top_scope->GetValueAtIndex(i);
2997 if (!variable.IsValid())
2998 break;
2999 variable_name_counts[GetNonNullVariableName(variable)]++;
3000 }
3001
3002 // Now we construct the result with unique display variable names
3003 for (auto i = start_idx; i < end_idx; ++i) {
3004 lldb::SBValue variable = top_scope->GetValueAtIndex(i);
3005
3006 if (!variable.IsValid())
3007 break;
3008
3009 int64_t var_ref = 0;
3010 if (variable.MightHaveChildren()) {
3011 var_ref = g_vsc.variables.InsertExpandableVariable(
3012 variable, /*is_permanent=*/false);
3013 }
3014 variables.emplace_back(CreateVariable(
3015 variable, var_ref, var_ref != 0 ? var_ref : UINT64_MAX, hex,
3016 variable_name_counts[GetNonNullVariableName(variable)] > 1));
3017 }
3018 } else {
3019 // We are expanding a variable that has children, so we will return its
3020 // children.
3021 lldb::SBValue variable = g_vsc.variables.GetVariable(variablesReference);
3022 if (variable.IsValid()) {
3023 const auto num_children = variable.GetNumChildren();
3024 const int64_t end_idx = start + ((count == 0) ? num_children : count);
3025 for (auto i = start; i < end_idx; ++i) {
3026 lldb::SBValue child = variable.GetChildAtIndex(i);
3027 if (!child.IsValid())
3028 break;
3029 if (child.MightHaveChildren()) {
3030 auto is_permanent =
3031 g_vsc.variables.IsPermanentVariableReference(variablesReference);
3032 auto childVariablesReferences =
3033 g_vsc.variables.InsertExpandableVariable(child, is_permanent);
3034 variables.emplace_back(CreateVariable(child, childVariablesReferences,
3035 childVariablesReferences, hex));
3036 } else {
3037 variables.emplace_back(CreateVariable(child, 0, INT64_MAX, hex));
3038 }
3039 }
3040 }
3041 }
3042 llvm::json::Object body;
3043 body.try_emplace("variables", std::move(variables));
3044 response.try_emplace("body", std::move(body));
3045 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
3046 }
3047
3048 // A request used in testing to get the details on all breakpoints that are
3049 // currently set in the target. This helps us to test "setBreakpoints" and
3050 // "setFunctionBreakpoints" requests to verify we have the correct set of
3051 // breakpoints currently set in LLDB.
request__testGetTargetBreakpoints(const llvm::json::Object & request)3052 void request__testGetTargetBreakpoints(const llvm::json::Object &request) {
3053 llvm::json::Object response;
3054 FillResponse(request, response);
3055 llvm::json::Array response_breakpoints;
3056 for (uint32_t i = 0; g_vsc.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
3057 auto bp = g_vsc.target.GetBreakpointAtIndex(i);
3058 AppendBreakpoint(bp, response_breakpoints);
3059 }
3060 llvm::json::Object body;
3061 body.try_emplace("breakpoints", std::move(response_breakpoints));
3062 response.try_emplace("body", std::move(body));
3063 g_vsc.SendJSON(llvm::json::Value(std::move(response)));
3064 }
3065
RegisterRequestCallbacks()3066 void RegisterRequestCallbacks() {
3067 g_vsc.RegisterRequestCallback("attach", request_attach);
3068 g_vsc.RegisterRequestCallback("completions", request_completions);
3069 g_vsc.RegisterRequestCallback("continue", request_continue);
3070 g_vsc.RegisterRequestCallback("configurationDone", request_configurationDone);
3071 g_vsc.RegisterRequestCallback("disconnect", request_disconnect);
3072 g_vsc.RegisterRequestCallback("evaluate", request_evaluate);
3073 g_vsc.RegisterRequestCallback("exceptionInfo", request_exceptionInfo);
3074 g_vsc.RegisterRequestCallback("initialize", request_initialize);
3075 g_vsc.RegisterRequestCallback("launch", request_launch);
3076 g_vsc.RegisterRequestCallback("next", request_next);
3077 g_vsc.RegisterRequestCallback("pause", request_pause);
3078 g_vsc.RegisterRequestCallback("scopes", request_scopes);
3079 g_vsc.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
3080 g_vsc.RegisterRequestCallback("setExceptionBreakpoints",
3081 request_setExceptionBreakpoints);
3082 g_vsc.RegisterRequestCallback("setFunctionBreakpoints",
3083 request_setFunctionBreakpoints);
3084 g_vsc.RegisterRequestCallback("setVariable", request_setVariable);
3085 g_vsc.RegisterRequestCallback("source", request_source);
3086 g_vsc.RegisterRequestCallback("stackTrace", request_stackTrace);
3087 g_vsc.RegisterRequestCallback("stepIn", request_stepIn);
3088 g_vsc.RegisterRequestCallback("stepOut", request_stepOut);
3089 g_vsc.RegisterRequestCallback("threads", request_threads);
3090 g_vsc.RegisterRequestCallback("variables", request_variables);
3091 // Custom requests
3092 g_vsc.RegisterRequestCallback("compileUnits", request_compileUnits);
3093 g_vsc.RegisterRequestCallback("modules", request_modules);
3094 // Testing requests
3095 g_vsc.RegisterRequestCallback("_testGetTargetBreakpoints",
3096 request__testGetTargetBreakpoints);
3097 }
3098
3099 } // anonymous namespace
3100
printHelp(LLDBVSCodeOptTable & table,llvm::StringRef tool_name)3101 static void printHelp(LLDBVSCodeOptTable &table, llvm::StringRef tool_name) {
3102 std::string usage_str = tool_name.str() + " options";
3103 table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB VSCode", false);
3104
3105 std::string examples = R"___(
3106 EXAMPLES:
3107 The debug adapter can be started in two modes.
3108
3109 Running lldb-vscode without any arguments will start communicating with the
3110 parent over stdio. Passing a port number causes lldb-vscode to start listening
3111 for connections on that port.
3112
3113 lldb-vscode -p <port>
3114
3115 Passing --wait-for-debugger will pause the process at startup and wait for a
3116 debugger to attach to the process.
3117
3118 lldb-vscode -g
3119 )___";
3120 llvm::outs() << examples;
3121 }
3122
3123 // If --launch-target is provided, this instance of lldb-vscode becomes a
3124 // runInTerminal launcher. It will ultimately launch the program specified in
3125 // the --launch-target argument, which is the original program the user wanted
3126 // to debug. This is done in such a way that the actual debug adaptor can
3127 // place breakpoints at the beginning of the program.
3128 //
3129 // The launcher will communicate with the debug adaptor using a fifo file in the
3130 // directory specified in the --comm-file argument.
3131 //
3132 // Regarding the actual flow, this launcher will first notify the debug adaptor
3133 // of its pid. Then, the launcher will be in a pending state waiting to be
3134 // attached by the adaptor.
3135 //
3136 // Once attached and resumed, the launcher will exec and become the program
3137 // specified by --launch-target, which is the original target the
3138 // user wanted to run.
3139 //
3140 // In case of errors launching the target, a suitable error message will be
3141 // emitted to the debug adaptor.
LaunchRunInTerminalTarget(llvm::opt::Arg & target_arg,llvm::StringRef comm_file,char * argv[])3142 void LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
3143 llvm::StringRef comm_file, char *argv[]) {
3144 #if defined(_WIN32)
3145 llvm::errs() << "runInTerminal is only supported on POSIX systems\n";
3146 exit(EXIT_FAILURE);
3147 #else
3148 RunInTerminalLauncherCommChannel comm_channel(comm_file);
3149 if (llvm::Error err = comm_channel.NotifyPid()) {
3150 llvm::errs() << llvm::toString(std::move(err)) << "\n";
3151 exit(EXIT_FAILURE);
3152 }
3153
3154 // We will wait to be attached with a timeout. We don't wait indefinitely
3155 // using a signal to prevent being paused forever.
3156
3157 // This env var should be used only for tests.
3158 const char *timeout_env_var = getenv("LLDB_VSCODE_RIT_TIMEOUT_IN_MS");
3159 int timeout_in_ms =
3160 timeout_env_var != nullptr ? atoi(timeout_env_var) : 20000;
3161 if (llvm::Error err = comm_channel.WaitUntilDebugAdaptorAttaches(
3162 std::chrono::milliseconds(timeout_in_ms))) {
3163 llvm::errs() << llvm::toString(std::move(err)) << "\n";
3164 exit(EXIT_FAILURE);
3165 }
3166
3167 const char *target = target_arg.getValue();
3168 execvp(target, argv);
3169
3170 std::string error = std::strerror(errno);
3171 comm_channel.NotifyError(error);
3172 llvm::errs() << error << "\n";
3173 exit(EXIT_FAILURE);
3174 #endif
3175 }
3176
3177 /// used only by TestVSCode_redirection_to_console.py
redirection_test()3178 void redirection_test() {
3179 printf("stdout message\n");
3180 fprintf(stderr, "stderr message\n");
3181 fflush(stdout);
3182 fflush(stderr);
3183 }
3184
3185 /// Redirect stdout and stderr fo the IDE's console output.
3186 ///
3187 /// Errors in this operation will be printed to the log file and the IDE's
3188 /// console output as well.
3189 ///
3190 /// \return
3191 /// A fd pointing to the original stdout.
SetupStdoutStderrRedirection()3192 int SetupStdoutStderrRedirection() {
3193 int stdoutfd = fileno(stdout);
3194 int new_stdout_fd = dup(stdoutfd);
3195 auto output_callback_stderr = [](llvm::StringRef data) {
3196 g_vsc.SendOutput(OutputType::Stderr, data);
3197 };
3198 auto output_callback_stdout = [](llvm::StringRef data) {
3199 g_vsc.SendOutput(OutputType::Stdout, data);
3200 };
3201 if (llvm::Error err = RedirectFd(stdoutfd, output_callback_stdout)) {
3202 std::string error_message = llvm::toString(std::move(err));
3203 if (g_vsc.log)
3204 *g_vsc.log << error_message << std::endl;
3205 output_callback_stderr(error_message);
3206 }
3207 if (llvm::Error err = RedirectFd(fileno(stderr), output_callback_stderr)) {
3208 std::string error_message = llvm::toString(std::move(err));
3209 if (g_vsc.log)
3210 *g_vsc.log << error_message << std::endl;
3211 output_callback_stderr(error_message);
3212 }
3213
3214 /// used only by TestVSCode_redirection_to_console.py
3215 if (getenv("LLDB_VSCODE_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
3216 redirection_test();
3217 return new_stdout_fd;
3218 }
3219
main(int argc,char * argv[])3220 int main(int argc, char *argv[]) {
3221 llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
3222 llvm::PrettyStackTraceProgram X(argc, argv);
3223
3224 llvm::SmallString<256> program_path(argv[0]);
3225 llvm::sys::fs::make_absolute(program_path);
3226 g_vsc.debug_adaptor_path = program_path.str().str();
3227
3228 LLDBVSCodeOptTable T;
3229 unsigned MAI, MAC;
3230 llvm::ArrayRef<const char *> ArgsArr = llvm::ArrayRef(argv + 1, argc);
3231 llvm::opt::InputArgList input_args = T.ParseArgs(ArgsArr, MAI, MAC);
3232
3233 if (input_args.hasArg(OPT_help)) {
3234 printHelp(T, llvm::sys::path::filename(argv[0]));
3235 return EXIT_SUCCESS;
3236 }
3237
3238 if (llvm::opt::Arg *target_arg = input_args.getLastArg(OPT_launch_target)) {
3239 if (llvm::opt::Arg *comm_file = input_args.getLastArg(OPT_comm_file)) {
3240 int target_args_pos = argc;
3241 for (int i = 0; i < argc; i++)
3242 if (strcmp(argv[i], "--launch-target") == 0) {
3243 target_args_pos = i + 1;
3244 break;
3245 }
3246 LaunchRunInTerminalTarget(*target_arg, comm_file->getValue(),
3247 argv + target_args_pos);
3248 } else {
3249 llvm::errs() << "\"--launch-target\" requires \"--comm-file\" to be "
3250 "specified\n";
3251 return EXIT_FAILURE;
3252 }
3253 }
3254
3255 // stdout/stderr redirection to the IDE's console
3256 int new_stdout_fd = SetupStdoutStderrRedirection();
3257
3258 // Initialize LLDB first before we do anything.
3259 lldb::SBDebugger::Initialize();
3260
3261 // Terminate the debugger before the C++ destructor chain kicks in.
3262 auto terminate_debugger =
3263 llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
3264
3265 RegisterRequestCallbacks();
3266
3267 int portno = -1;
3268
3269 if (auto *arg = input_args.getLastArg(OPT_port)) {
3270 auto optarg = arg->getValue();
3271 char *remainder;
3272 portno = strtol(optarg, &remainder, 0);
3273 if (remainder == optarg || *remainder != '\0') {
3274 fprintf(stderr, "'%s' is not a valid port number.\n", optarg);
3275 return EXIT_FAILURE;
3276 }
3277 }
3278
3279 #if !defined(_WIN32)
3280 if (input_args.hasArg(OPT_wait_for_debugger)) {
3281 printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid());
3282 pause();
3283 }
3284 #endif
3285 if (portno != -1) {
3286 printf("Listening on port %i...\n", portno);
3287 SOCKET socket_fd = AcceptConnection(portno);
3288 if (socket_fd >= 0) {
3289 g_vsc.input.descriptor = StreamDescriptor::from_socket(socket_fd, true);
3290 g_vsc.output.descriptor = StreamDescriptor::from_socket(socket_fd, false);
3291 } else {
3292 return EXIT_FAILURE;
3293 }
3294 } else {
3295 g_vsc.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
3296 g_vsc.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
3297 }
3298
3299 while (!g_vsc.sent_terminated_event) {
3300 llvm::json::Object object;
3301 lldb_vscode::PacketStatus status = g_vsc.GetNextObject(object);
3302 if (status == lldb_vscode::PacketStatus::EndOfFile)
3303 break;
3304 if (status != lldb_vscode::PacketStatus::Success)
3305 return 1; // Fatal error
3306
3307 if (!g_vsc.HandleObject(object))
3308 return 1;
3309 }
3310
3311 return EXIT_SUCCESS;
3312 }
3313