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