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