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