1 //===-- JSONUtils.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 <algorithm> 10 #include <iomanip> 11 #include <sstream> 12 13 #include "llvm/ADT/Optional.h" 14 #include "llvm/Support/FormatAdapters.h" 15 #include "llvm/Support/Path.h" 16 #include "llvm/Support/ScopedPrinter.h" 17 18 #include "lldb/API/SBBreakpoint.h" 19 #include "lldb/API/SBBreakpointLocation.h" 20 #include "lldb/API/SBValue.h" 21 #include "lldb/Host/PosixApi.h" 22 23 #include "ExceptionBreakpoint.h" 24 #include "JSONUtils.h" 25 #include "LLDBUtils.h" 26 #include "VSCode.h" 27 28 namespace lldb_vscode { 29 30 void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, 31 llvm::StringRef str) { 32 if (LLVM_LIKELY(llvm::json::isUTF8(str))) 33 obj.try_emplace(key, str.str()); 34 else 35 obj.try_emplace(key, llvm::json::fixUTF8(str)); 36 } 37 38 llvm::StringRef GetAsString(const llvm::json::Value &value) { 39 if (auto s = value.getAsString()) 40 return *s; 41 return llvm::StringRef(); 42 } 43 44 // Gets a string from a JSON object using the key, or returns an empty string. 45 llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) { 46 if (llvm::Optional<llvm::StringRef> value = obj.getString(key)) 47 return *value; 48 return llvm::StringRef(); 49 } 50 51 llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) { 52 if (obj == nullptr) 53 return llvm::StringRef(); 54 return GetString(*obj, key); 55 } 56 57 // Gets an unsigned integer from a JSON object using the key, or returns the 58 // specified fail value. 59 uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key, 60 uint64_t fail_value) { 61 if (auto value = obj.getInteger(key)) 62 return (uint64_t)*value; 63 return fail_value; 64 } 65 66 uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key, 67 uint64_t fail_value) { 68 if (obj == nullptr) 69 return fail_value; 70 return GetUnsigned(*obj, key, fail_value); 71 } 72 73 bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key, 74 bool fail_value) { 75 if (auto value = obj.getBoolean(key)) 76 return *value; 77 if (auto value = obj.getInteger(key)) 78 return *value != 0; 79 return fail_value; 80 } 81 82 bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key, 83 bool fail_value) { 84 if (obj == nullptr) 85 return fail_value; 86 return GetBoolean(*obj, key, fail_value); 87 } 88 89 int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key, 90 int64_t fail_value) { 91 if (auto value = obj.getInteger(key)) 92 return *value; 93 return fail_value; 94 } 95 96 int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key, 97 int64_t fail_value) { 98 if (obj == nullptr) 99 return fail_value; 100 return GetSigned(*obj, key, fail_value); 101 } 102 103 bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) { 104 return obj.find(key) != obj.end(); 105 } 106 107 std::vector<std::string> GetStrings(const llvm::json::Object *obj, 108 llvm::StringRef key) { 109 std::vector<std::string> strs; 110 auto json_array = obj->getArray(key); 111 if (!json_array) 112 return strs; 113 for (const auto &value : *json_array) { 114 switch (value.kind()) { 115 case llvm::json::Value::String: 116 strs.push_back(value.getAsString()->str()); 117 break; 118 case llvm::json::Value::Number: 119 case llvm::json::Value::Boolean: 120 strs.push_back(llvm::to_string(value)); 121 break; 122 case llvm::json::Value::Null: 123 case llvm::json::Value::Object: 124 case llvm::json::Value::Array: 125 break; 126 } 127 } 128 return strs; 129 } 130 131 void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object, 132 llvm::StringRef key) { 133 134 llvm::StringRef value = v.GetValue(); 135 llvm::StringRef summary = v.GetSummary(); 136 llvm::StringRef type_name = v.GetType().GetDisplayTypeName(); 137 138 std::string result; 139 llvm::raw_string_ostream strm(result); 140 if (!value.empty()) { 141 strm << value; 142 if (!summary.empty()) 143 strm << ' ' << summary; 144 } else if (!summary.empty()) { 145 strm << ' ' << summary; 146 } else if (!type_name.empty()) { 147 strm << type_name; 148 lldb::addr_t address = v.GetLoadAddress(); 149 if (address != LLDB_INVALID_ADDRESS) 150 strm << " @ " << llvm::format_hex(address, 0); 151 } 152 strm.flush(); 153 EmplaceSafeString(object, key, result); 154 } 155 156 void FillResponse(const llvm::json::Object &request, 157 llvm::json::Object &response) { 158 // Fill in all of the needed response fields to a "request" and set "success" 159 // to true by default. 160 response.try_emplace("type", "response"); 161 response.try_emplace("seq", (int64_t)0); 162 EmplaceSafeString(response, "command", GetString(request, "command")); 163 const int64_t seq = GetSigned(request, "seq", 0); 164 response.try_emplace("request_seq", seq); 165 response.try_emplace("success", true); 166 } 167 168 // "Scope": { 169 // "type": "object", 170 // "description": "A Scope is a named container for variables. Optionally 171 // a scope can map to a source or a range within a source.", 172 // "properties": { 173 // "name": { 174 // "type": "string", 175 // "description": "Name of the scope such as 'Arguments', 'Locals'." 176 // }, 177 // "variablesReference": { 178 // "type": "integer", 179 // "description": "The variables of this scope can be retrieved by 180 // passing the value of variablesReference to the 181 // VariablesRequest." 182 // }, 183 // "namedVariables": { 184 // "type": "integer", 185 // "description": "The number of named variables in this scope. The 186 // client can use this optional information to present 187 // the variables in a paged UI and fetch them in chunks." 188 // }, 189 // "indexedVariables": { 190 // "type": "integer", 191 // "description": "The number of indexed variables in this scope. The 192 // client can use this optional information to present 193 // the variables in a paged UI and fetch them in chunks." 194 // }, 195 // "expensive": { 196 // "type": "boolean", 197 // "description": "If true, the number of variables in this scope is 198 // large or expensive to retrieve." 199 // }, 200 // "source": { 201 // "$ref": "#/definitions/Source", 202 // "description": "Optional source for this scope." 203 // }, 204 // "line": { 205 // "type": "integer", 206 // "description": "Optional start line of the range covered by this 207 // scope." 208 // }, 209 // "column": { 210 // "type": "integer", 211 // "description": "Optional start column of the range covered by this 212 // scope." 213 // }, 214 // "endLine": { 215 // "type": "integer", 216 // "description": "Optional end line of the range covered by this scope." 217 // }, 218 // "endColumn": { 219 // "type": "integer", 220 // "description": "Optional end column of the range covered by this 221 // scope." 222 // } 223 // }, 224 // "required": [ "name", "variablesReference", "expensive" ] 225 // } 226 llvm::json::Value CreateScope(const llvm::StringRef name, 227 int64_t variablesReference, 228 int64_t namedVariables, bool expensive) { 229 llvm::json::Object object; 230 EmplaceSafeString(object, "name", name.str()); 231 object.try_emplace("variablesReference", variablesReference); 232 object.try_emplace("expensive", expensive); 233 object.try_emplace("namedVariables", namedVariables); 234 return llvm::json::Value(std::move(object)); 235 } 236 237 // "Breakpoint": { 238 // "type": "object", 239 // "description": "Information about a Breakpoint created in setBreakpoints 240 // or setFunctionBreakpoints.", 241 // "properties": { 242 // "id": { 243 // "type": "integer", 244 // "description": "An optional unique identifier for the breakpoint." 245 // }, 246 // "verified": { 247 // "type": "boolean", 248 // "description": "If true breakpoint could be set (but not necessarily 249 // at the desired location)." 250 // }, 251 // "message": { 252 // "type": "string", 253 // "description": "An optional message about the state of the breakpoint. 254 // This is shown to the user and can be used to explain 255 // why a breakpoint could not be verified." 256 // }, 257 // "source": { 258 // "$ref": "#/definitions/Source", 259 // "description": "The source where the breakpoint is located." 260 // }, 261 // "line": { 262 // "type": "integer", 263 // "description": "The start line of the actual range covered by the 264 // breakpoint." 265 // }, 266 // "column": { 267 // "type": "integer", 268 // "description": "An optional start column of the actual range covered 269 // by the breakpoint." 270 // }, 271 // "endLine": { 272 // "type": "integer", 273 // "description": "An optional end line of the actual range covered by 274 // the breakpoint." 275 // }, 276 // "endColumn": { 277 // "type": "integer", 278 // "description": "An optional end column of the actual range covered by 279 // the breakpoint. If no end line is given, then the end 280 // column is assumed to be in the start line." 281 // } 282 // }, 283 // "required": [ "verified" ] 284 // } 285 llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp, 286 llvm::Optional<llvm::StringRef> request_path, 287 llvm::Optional<uint32_t> request_line) { 288 // Each breakpoint location is treated as a separate breakpoint for VS code. 289 // They don't have the notion of a single breakpoint with multiple locations. 290 llvm::json::Object object; 291 if (!bp.IsValid()) 292 return llvm::json::Value(std::move(object)); 293 294 object.try_emplace("verified", bp.GetNumResolvedLocations() > 0); 295 object.try_emplace("id", bp.GetID()); 296 // VS Code DAP doesn't currently allow one breakpoint to have multiple 297 // locations so we just report the first one. If we report all locations 298 // then the IDE starts showing the wrong line numbers and locations for 299 // other source file and line breakpoints in the same file. 300 301 // Below we search for the first resolved location in a breakpoint and report 302 // this as the breakpoint location since it will have a complete location 303 // that is at least loaded in the current process. 304 lldb::SBBreakpointLocation bp_loc; 305 const auto num_locs = bp.GetNumLocations(); 306 for (size_t i = 0; i < num_locs; ++i) { 307 bp_loc = bp.GetLocationAtIndex(i); 308 if (bp_loc.IsResolved()) 309 break; 310 } 311 // If not locations are resolved, use the first location. 312 if (!bp_loc.IsResolved()) 313 bp_loc = bp.GetLocationAtIndex(0); 314 auto bp_addr = bp_loc.GetAddress(); 315 316 if (request_path) 317 object.try_emplace("source", CreateSource(*request_path)); 318 319 if (bp_addr.IsValid()) { 320 auto line_entry = bp_addr.GetLineEntry(); 321 const auto line = line_entry.GetLine(); 322 if (line != UINT32_MAX) 323 object.try_emplace("line", line); 324 object.try_emplace("source", CreateSource(line_entry)); 325 } 326 // We try to add request_line as a fallback 327 if (request_line) 328 object.try_emplace("line", *request_line); 329 return llvm::json::Value(std::move(object)); 330 } 331 332 static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) { 333 uint64_t debug_info_size = 0; 334 llvm::StringRef section_name(section.GetName()); 335 if (section_name.startswith(".debug") || section_name.startswith("__debug") || 336 section_name.startswith(".apple") || section_name.startswith("__apple")) 337 debug_info_size += section.GetFileByteSize(); 338 size_t num_sub_sections = section.GetNumSubSections(); 339 for (size_t i = 0; i < num_sub_sections; i++) { 340 debug_info_size += 341 GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i)); 342 } 343 return debug_info_size; 344 } 345 346 static uint64_t GetDebugInfoSize(lldb::SBModule module) { 347 uint64_t debug_info_size = 0; 348 size_t num_sections = module.GetNumSections(); 349 for (size_t i = 0; i < num_sections; i++) { 350 debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i)); 351 } 352 return debug_info_size; 353 } 354 355 static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) { 356 std::ostringstream oss; 357 oss << std::fixed << std::setprecision(1); 358 if (debug_info < 1024) { 359 oss << debug_info << "B"; 360 } else if (debug_info < 1024 * 1024) { 361 double kb = double(debug_info) / 1024.0; 362 oss << kb << "KB"; 363 } else if (debug_info < 1024 * 1024 * 1024) { 364 double mb = double(debug_info) / (1024.0 * 1024.0); 365 oss << mb << "MB"; 366 } else { 367 double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0); 368 oss << gb << "GB"; 369 } 370 return oss.str(); 371 } 372 llvm::json::Value CreateModule(lldb::SBModule &module) { 373 llvm::json::Object object; 374 if (!module.IsValid()) 375 return llvm::json::Value(std::move(object)); 376 const char *uuid = module.GetUUIDString(); 377 object.try_emplace("id", uuid ? std::string(uuid) : std::string("")); 378 object.try_emplace("name", std::string(module.GetFileSpec().GetFilename())); 379 char module_path_arr[PATH_MAX]; 380 module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr)); 381 std::string module_path(module_path_arr); 382 object.try_emplace("path", module_path); 383 if (module.GetNumCompileUnits() > 0) { 384 std::string symbol_str = "Symbols loaded."; 385 std::string debug_info_size; 386 uint64_t debug_info = GetDebugInfoSize(module); 387 if (debug_info > 0) { 388 debug_info_size = ConvertDebugInfoSizeToString(debug_info); 389 } 390 object.try_emplace("symbolStatus", symbol_str); 391 object.try_emplace("debugInfoSize", debug_info_size); 392 char symbol_path_arr[PATH_MAX]; 393 module.GetSymbolFileSpec().GetPath(symbol_path_arr, 394 sizeof(symbol_path_arr)); 395 std::string symbol_path(symbol_path_arr); 396 object.try_emplace("symbolFilePath", symbol_path); 397 } else { 398 object.try_emplace("symbolStatus", "Symbols not found."); 399 } 400 std::string loaded_addr = std::to_string( 401 module.GetObjectFileHeaderAddress().GetLoadAddress(g_vsc.target)); 402 object.try_emplace("addressRange", loaded_addr); 403 std::string version_str; 404 uint32_t version_nums[3]; 405 uint32_t num_versions = 406 module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t)); 407 for (uint32_t i = 0; i < num_versions; ++i) { 408 if (!version_str.empty()) 409 version_str += "."; 410 version_str += std::to_string(version_nums[i]); 411 } 412 if (!version_str.empty()) 413 object.try_emplace("version", version_str); 414 return llvm::json::Value(std::move(object)); 415 } 416 417 void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints, 418 llvm::Optional<llvm::StringRef> request_path, 419 llvm::Optional<uint32_t> request_line) { 420 breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line)); 421 } 422 423 // "Event": { 424 // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, { 425 // "type": "object", 426 // "description": "Server-initiated event.", 427 // "properties": { 428 // "type": { 429 // "type": "string", 430 // "enum": [ "event" ] 431 // }, 432 // "event": { 433 // "type": "string", 434 // "description": "Type of event." 435 // }, 436 // "body": { 437 // "type": [ "array", "boolean", "integer", "null", "number" , 438 // "object", "string" ], 439 // "description": "Event-specific information." 440 // } 441 // }, 442 // "required": [ "type", "event" ] 443 // }] 444 // }, 445 // "ProtocolMessage": { 446 // "type": "object", 447 // "description": "Base class of requests, responses, and events.", 448 // "properties": { 449 // "seq": { 450 // "type": "integer", 451 // "description": "Sequence number." 452 // }, 453 // "type": { 454 // "type": "string", 455 // "description": "Message type.", 456 // "_enum": [ "request", "response", "event" ] 457 // } 458 // }, 459 // "required": [ "seq", "type" ] 460 // } 461 llvm::json::Object CreateEventObject(const llvm::StringRef event_name) { 462 llvm::json::Object event; 463 event.try_emplace("seq", 0); 464 event.try_emplace("type", "event"); 465 EmplaceSafeString(event, "event", event_name); 466 return event; 467 } 468 469 // "ExceptionBreakpointsFilter": { 470 // "type": "object", 471 // "description": "An ExceptionBreakpointsFilter is shown in the UI as an 472 // option for configuring how exceptions are dealt with.", 473 // "properties": { 474 // "filter": { 475 // "type": "string", 476 // "description": "The internal ID of the filter. This value is passed 477 // to the setExceptionBreakpoints request." 478 // }, 479 // "label": { 480 // "type": "string", 481 // "description": "The name of the filter. This will be shown in the UI." 482 // }, 483 // "default": { 484 // "type": "boolean", 485 // "description": "Initial value of the filter. If not specified a value 486 // 'false' is assumed." 487 // } 488 // }, 489 // "required": [ "filter", "label" ] 490 // } 491 llvm::json::Value 492 CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) { 493 llvm::json::Object object; 494 EmplaceSafeString(object, "filter", bp.filter); 495 EmplaceSafeString(object, "label", bp.label); 496 object.try_emplace("default", bp.default_value); 497 return llvm::json::Value(std::move(object)); 498 } 499 500 // "Source": { 501 // "type": "object", 502 // "description": "A Source is a descriptor for source code. It is returned 503 // from the debug adapter as part of a StackFrame and it is 504 // used by clients when specifying breakpoints.", 505 // "properties": { 506 // "name": { 507 // "type": "string", 508 // "description": "The short name of the source. Every source returned 509 // from the debug adapter has a name. When sending a 510 // source to the debug adapter this name is optional." 511 // }, 512 // "path": { 513 // "type": "string", 514 // "description": "The path of the source to be shown in the UI. It is 515 // only used to locate and load the content of the 516 // source if no sourceReference is specified (or its 517 // value is 0)." 518 // }, 519 // "sourceReference": { 520 // "type": "number", 521 // "description": "If sourceReference > 0 the contents of the source must 522 // be retrieved through the SourceRequest (even if a path 523 // is specified). A sourceReference is only valid for a 524 // session, so it must not be used to persist a source." 525 // }, 526 // "presentationHint": { 527 // "type": "string", 528 // "description": "An optional hint for how to present the source in the 529 // UI. A value of 'deemphasize' can be used to indicate 530 // that the source is not available or that it is 531 // skipped on stepping.", 532 // "enum": [ "normal", "emphasize", "deemphasize" ] 533 // }, 534 // "origin": { 535 // "type": "string", 536 // "description": "The (optional) origin of this source: possible values 537 // 'internal module', 'inlined content from source map', 538 // etc." 539 // }, 540 // "sources": { 541 // "type": "array", 542 // "items": { 543 // "$ref": "#/definitions/Source" 544 // }, 545 // "description": "An optional list of sources that are related to this 546 // source. These may be the source that generated this 547 // source." 548 // }, 549 // "adapterData": { 550 // "type":["array","boolean","integer","null","number","object","string"], 551 // "description": "Optional data that a debug adapter might want to loop 552 // through the client. The client should leave the data 553 // intact and persist it across sessions. The client 554 // should not interpret the data." 555 // }, 556 // "checksums": { 557 // "type": "array", 558 // "items": { 559 // "$ref": "#/definitions/Checksum" 560 // }, 561 // "description": "The checksums associated with this file." 562 // } 563 // } 564 // } 565 llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) { 566 llvm::json::Object object; 567 lldb::SBFileSpec file = line_entry.GetFileSpec(); 568 if (file.IsValid()) { 569 const char *name = file.GetFilename(); 570 if (name) 571 EmplaceSafeString(object, "name", name); 572 char path[PATH_MAX] = ""; 573 file.GetPath(path, sizeof(path)); 574 if (path[0]) { 575 EmplaceSafeString(object, "path", std::string(path)); 576 } 577 } 578 return llvm::json::Value(std::move(object)); 579 } 580 581 llvm::json::Value CreateSource(llvm::StringRef source_path) { 582 llvm::json::Object source; 583 llvm::StringRef name = llvm::sys::path::filename(source_path); 584 EmplaceSafeString(source, "name", name); 585 EmplaceSafeString(source, "path", source_path); 586 return llvm::json::Value(std::move(source)); 587 } 588 589 llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) { 590 disasm_line = 0; 591 auto line_entry = frame.GetLineEntry(); 592 if (line_entry.GetFileSpec().IsValid()) 593 return CreateSource(line_entry); 594 595 llvm::json::Object object; 596 const auto pc = frame.GetPC(); 597 598 lldb::SBInstructionList insts; 599 lldb::SBFunction function = frame.GetFunction(); 600 lldb::addr_t low_pc = LLDB_INVALID_ADDRESS; 601 if (function.IsValid()) { 602 low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target); 603 auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc); 604 if (addr_srcref != g_vsc.addr_to_source_ref.end()) { 605 // We have this disassembly cached already, return the existing 606 // sourceReference 607 object.try_emplace("sourceReference", addr_srcref->second); 608 disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc); 609 } else { 610 insts = function.GetInstructions(g_vsc.target); 611 } 612 } else { 613 lldb::SBSymbol symbol = frame.GetSymbol(); 614 if (symbol.IsValid()) { 615 low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target); 616 auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc); 617 if (addr_srcref != g_vsc.addr_to_source_ref.end()) { 618 // We have this disassembly cached already, return the existing 619 // sourceReference 620 object.try_emplace("sourceReference", addr_srcref->second); 621 disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc); 622 } else { 623 insts = symbol.GetInstructions(g_vsc.target); 624 } 625 } 626 } 627 const auto num_insts = insts.GetSize(); 628 if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) { 629 EmplaceSafeString(object, "name", frame.GetFunctionName()); 630 SourceReference source; 631 llvm::raw_string_ostream src_strm(source.content); 632 std::string line; 633 for (size_t i = 0; i < num_insts; ++i) { 634 lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); 635 const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target); 636 const char *m = inst.GetMnemonic(g_vsc.target); 637 const char *o = inst.GetOperands(g_vsc.target); 638 const char *c = inst.GetComment(g_vsc.target); 639 if (pc == inst_addr) 640 disasm_line = i + 1; 641 const auto inst_offset = inst_addr - low_pc; 642 int spaces = 0; 643 if (inst_offset < 10) 644 spaces = 3; 645 else if (inst_offset < 100) 646 spaces = 2; 647 else if (inst_offset < 1000) 648 spaces = 1; 649 line.clear(); 650 llvm::raw_string_ostream line_strm(line); 651 line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr, 652 inst_offset, llvm::fmt_repeat(' ', spaces), m, 653 o); 654 655 // If there is a comment append it starting at column 60 or after one 656 // space past the last char 657 const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60); 658 if (c && c[0]) { 659 if (line.size() < comment_row) 660 line_strm.indent(comment_row - line_strm.str().size()); 661 line_strm << " # " << c; 662 } 663 src_strm << line_strm.str() << "\n"; 664 source.addr_to_line[inst_addr] = i + 1; 665 } 666 // Flush the source stream 667 src_strm.str(); 668 auto sourceReference = VSCode::GetNextSourceReference(); 669 g_vsc.source_map[sourceReference] = std::move(source); 670 g_vsc.addr_to_source_ref[low_pc] = sourceReference; 671 object.try_emplace("sourceReference", sourceReference); 672 } 673 return llvm::json::Value(std::move(object)); 674 } 675 676 // "StackFrame": { 677 // "type": "object", 678 // "description": "A Stackframe contains the source location.", 679 // "properties": { 680 // "id": { 681 // "type": "integer", 682 // "description": "An identifier for the stack frame. It must be unique 683 // across all threads. This id can be used to retrieve 684 // the scopes of the frame with the 'scopesRequest' or 685 // to restart the execution of a stackframe." 686 // }, 687 // "name": { 688 // "type": "string", 689 // "description": "The name of the stack frame, typically a method name." 690 // }, 691 // "source": { 692 // "$ref": "#/definitions/Source", 693 // "description": "The optional source of the frame." 694 // }, 695 // "line": { 696 // "type": "integer", 697 // "description": "The line within the file of the frame. If source is 698 // null or doesn't exist, line is 0 and must be ignored." 699 // }, 700 // "column": { 701 // "type": "integer", 702 // "description": "The column within the line. If source is null or 703 // doesn't exist, column is 0 and must be ignored." 704 // }, 705 // "endLine": { 706 // "type": "integer", 707 // "description": "An optional end line of the range covered by the 708 // stack frame." 709 // }, 710 // "endColumn": { 711 // "type": "integer", 712 // "description": "An optional end column of the range covered by the 713 // stack frame." 714 // }, 715 // "moduleId": { 716 // "type": ["integer", "string"], 717 // "description": "The module associated with this frame, if any." 718 // }, 719 // "presentationHint": { 720 // "type": "string", 721 // "enum": [ "normal", "label", "subtle" ], 722 // "description": "An optional hint for how to present this frame in 723 // the UI. A value of 'label' can be used to indicate 724 // that the frame is an artificial frame that is used 725 // as a visual label or separator. A value of 'subtle' 726 // can be used to change the appearance of a frame in 727 // a 'subtle' way." 728 // } 729 // }, 730 // "required": [ "id", "name", "line", "column" ] 731 // } 732 llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) { 733 llvm::json::Object object; 734 int64_t frame_id = MakeVSCodeFrameID(frame); 735 object.try_emplace("id", frame_id); 736 EmplaceSafeString(object, "name", frame.GetFunctionName()); 737 int64_t disasm_line = 0; 738 object.try_emplace("source", CreateSource(frame, disasm_line)); 739 740 auto line_entry = frame.GetLineEntry(); 741 if (disasm_line > 0) { 742 object.try_emplace("line", disasm_line); 743 } else { 744 auto line = line_entry.GetLine(); 745 if (line == UINT32_MAX) 746 line = 0; 747 object.try_emplace("line", line); 748 } 749 object.try_emplace("column", line_entry.GetColumn()); 750 return llvm::json::Value(std::move(object)); 751 } 752 753 // "Thread": { 754 // "type": "object", 755 // "description": "A Thread", 756 // "properties": { 757 // "id": { 758 // "type": "integer", 759 // "description": "Unique identifier for the thread." 760 // }, 761 // "name": { 762 // "type": "string", 763 // "description": "A name of the thread." 764 // } 765 // }, 766 // "required": [ "id", "name" ] 767 // } 768 llvm::json::Value CreateThread(lldb::SBThread &thread) { 769 llvm::json::Object object; 770 object.try_emplace("id", (int64_t)thread.GetThreadID()); 771 char thread_str[64]; 772 snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID()); 773 const char *name = thread.GetName(); 774 if (name) { 775 std::string thread_with_name(thread_str); 776 thread_with_name += ' '; 777 thread_with_name += name; 778 EmplaceSafeString(object, "name", thread_with_name); 779 } else { 780 EmplaceSafeString(object, "name", std::string(thread_str)); 781 } 782 return llvm::json::Value(std::move(object)); 783 } 784 785 // "StoppedEvent": { 786 // "allOf": [ { "$ref": "#/definitions/Event" }, { 787 // "type": "object", 788 // "description": "Event message for 'stopped' event type. The event 789 // indicates that the execution of the debuggee has stopped 790 // due to some condition. This can be caused by a break 791 // point previously set, a stepping action has completed, 792 // by executing a debugger statement etc.", 793 // "properties": { 794 // "event": { 795 // "type": "string", 796 // "enum": [ "stopped" ] 797 // }, 798 // "body": { 799 // "type": "object", 800 // "properties": { 801 // "reason": { 802 // "type": "string", 803 // "description": "The reason for the event. For backward 804 // compatibility this string is shown in the UI if 805 // the 'description' attribute is missing (but it 806 // must not be translated).", 807 // "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ] 808 // }, 809 // "description": { 810 // "type": "string", 811 // "description": "The full reason for the event, e.g. 'Paused 812 // on exception'. This string is shown in the UI 813 // as is." 814 // }, 815 // "threadId": { 816 // "type": "integer", 817 // "description": "The thread which was stopped." 818 // }, 819 // "text": { 820 // "type": "string", 821 // "description": "Additional information. E.g. if reason is 822 // 'exception', text contains the exception name. 823 // This string is shown in the UI." 824 // }, 825 // "allThreadsStopped": { 826 // "type": "boolean", 827 // "description": "If allThreadsStopped is true, a debug adapter 828 // can announce that all threads have stopped. 829 // The client should use this information to 830 // enable that all threads can be expanded to 831 // access their stacktraces. If the attribute 832 // is missing or false, only the thread with the 833 // given threadId can be expanded." 834 // } 835 // }, 836 // "required": [ "reason" ] 837 // } 838 // }, 839 // "required": [ "event", "body" ] 840 // }] 841 // } 842 llvm::json::Value CreateThreadStopped(lldb::SBThread &thread, 843 uint32_t stop_id) { 844 llvm::json::Object event(CreateEventObject("stopped")); 845 llvm::json::Object body; 846 switch (thread.GetStopReason()) { 847 case lldb::eStopReasonTrace: 848 case lldb::eStopReasonPlanComplete: 849 body.try_emplace("reason", "step"); 850 break; 851 case lldb::eStopReasonBreakpoint: { 852 ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread); 853 if (exc_bp) { 854 body.try_emplace("reason", "exception"); 855 EmplaceSafeString(body, "description", exc_bp->label); 856 } else { 857 body.try_emplace("reason", "breakpoint"); 858 char desc_str[64]; 859 uint64_t bp_id = thread.GetStopReasonDataAtIndex(0); 860 uint64_t bp_loc_id = thread.GetStopReasonDataAtIndex(1); 861 snprintf(desc_str, sizeof(desc_str), "breakpoint %" PRIu64 ".%" PRIu64, 862 bp_id, bp_loc_id); 863 EmplaceSafeString(body, "description", desc_str); 864 } 865 } break; 866 case lldb::eStopReasonWatchpoint: 867 case lldb::eStopReasonInstrumentation: 868 body.try_emplace("reason", "breakpoint"); 869 break; 870 case lldb::eStopReasonSignal: 871 body.try_emplace("reason", "exception"); 872 break; 873 case lldb::eStopReasonException: 874 body.try_emplace("reason", "exception"); 875 break; 876 case lldb::eStopReasonExec: 877 body.try_emplace("reason", "entry"); 878 break; 879 case lldb::eStopReasonThreadExiting: 880 case lldb::eStopReasonInvalid: 881 case lldb::eStopReasonNone: 882 break; 883 } 884 if (stop_id == 0) 885 body.try_emplace("reason", "entry"); 886 const lldb::tid_t tid = thread.GetThreadID(); 887 body.try_emplace("threadId", (int64_t)tid); 888 // If no description has been set, then set it to the default thread stopped 889 // description. If we have breakpoints that get hit and shouldn't be reported 890 // as breakpoints, then they will set the description above. 891 if (ObjectContainsKey(body, "description")) { 892 char description[1024]; 893 if (thread.GetStopDescription(description, sizeof(description))) { 894 EmplaceSafeString(body, "description", std::string(description)); 895 } 896 } 897 if (tid == g_vsc.focus_tid) { 898 body.try_emplace("threadCausedFocus", true); 899 } 900 body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid); 901 body.try_emplace("allThreadsStopped", true); 902 event.try_emplace("body", std::move(body)); 903 return llvm::json::Value(std::move(event)); 904 } 905 906 // "Variable": { 907 // "type": "object", 908 // "description": "A Variable is a name/value pair. Optionally a variable 909 // can have a 'type' that is shown if space permits or when 910 // hovering over the variable's name. An optional 'kind' is 911 // used to render additional properties of the variable, 912 // e.g. different icons can be used to indicate that a 913 // variable is public or private. If the value is 914 // structured (has children), a handle is provided to 915 // retrieve the children with the VariablesRequest. If 916 // the number of named or indexed children is large, the 917 // numbers should be returned via the optional 918 // 'namedVariables' and 'indexedVariables' attributes. The 919 // client can use this optional information to present the 920 // children in a paged UI and fetch them in chunks.", 921 // "properties": { 922 // "name": { 923 // "type": "string", 924 // "description": "The variable's name." 925 // }, 926 // "value": { 927 // "type": "string", 928 // "description": "The variable's value. This can be a multi-line text, 929 // e.g. for a function the body of a function." 930 // }, 931 // "type": { 932 // "type": "string", 933 // "description": "The type of the variable's value. Typically shown in 934 // the UI when hovering over the value." 935 // }, 936 // "presentationHint": { 937 // "$ref": "#/definitions/VariablePresentationHint", 938 // "description": "Properties of a variable that can be used to determine 939 // how to render the variable in the UI." 940 // }, 941 // "evaluateName": { 942 // "type": "string", 943 // "description": "Optional evaluatable name of this variable which can 944 // be passed to the 'EvaluateRequest' to fetch the 945 // variable's value." 946 // }, 947 // "variablesReference": { 948 // "type": "integer", 949 // "description": "If variablesReference is > 0, the variable is 950 // structured and its children can be retrieved by 951 // passing variablesReference to the VariablesRequest." 952 // }, 953 // "namedVariables": { 954 // "type": "integer", 955 // "description": "The number of named child variables. The client can 956 // use this optional information to present the children 957 // in a paged UI and fetch them in chunks." 958 // }, 959 // "indexedVariables": { 960 // "type": "integer", 961 // "description": "The number of indexed child variables. The client 962 // can use this optional information to present the 963 // children in a paged UI and fetch them in chunks." 964 // } 965 // }, 966 // "required": [ "name", "value", "variablesReference" ] 967 // } 968 llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference, 969 int64_t varID, bool format_hex) { 970 llvm::json::Object object; 971 auto name = v.GetName(); 972 EmplaceSafeString(object, "name", name ? name : "<null>"); 973 if (format_hex) 974 v.SetFormat(lldb::eFormatHex); 975 SetValueForKey(v, object, "value"); 976 auto type_cstr = v.GetType().GetDisplayTypeName(); 977 EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME); 978 if (varID != INT64_MAX) 979 object.try_emplace("id", varID); 980 if (v.MightHaveChildren()) 981 object.try_emplace("variablesReference", variablesReference); 982 else 983 object.try_emplace("variablesReference", (int64_t)0); 984 lldb::SBStream evaluateStream; 985 v.GetExpressionPath(evaluateStream); 986 const char *evaluateName = evaluateStream.GetData(); 987 if (evaluateName && evaluateName[0]) 988 EmplaceSafeString(object, "evaluateName", std::string(evaluateName)); 989 return llvm::json::Value(std::move(object)); 990 } 991 992 llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit) { 993 llvm::json::Object object; 994 char unit_path_arr[PATH_MAX]; 995 unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr)); 996 std::string unit_path(unit_path_arr); 997 object.try_emplace("compileUnitPath", unit_path); 998 return llvm::json::Value(std::move(object)); 999 } 1000 1001 /// See 1002 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal 1003 llvm::json::Object 1004 CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request) { 1005 llvm::json::Object reverse_request; 1006 reverse_request.try_emplace("type", "request"); 1007 reverse_request.try_emplace("command", "runInTerminal"); 1008 1009 llvm::json::Object run_in_terminal_args; 1010 // This indicates the IDE to open an embedded terminal, instead of opening the 1011 // terminal in a new window. 1012 run_in_terminal_args.try_emplace("kind", "integrated"); 1013 1014 auto launch_request_arguments = launch_request.getObject("arguments"); 1015 std::vector<std::string> args = GetStrings(launch_request_arguments, "args"); 1016 // The program path must be the first entry in the "args" field 1017 args.insert(args.begin(), 1018 GetString(launch_request_arguments, "program").str()); 1019 run_in_terminal_args.try_emplace("args", args); 1020 1021 const auto cwd = GetString(launch_request_arguments, "cwd"); 1022 if (!cwd.empty()) 1023 run_in_terminal_args.try_emplace("cwd", cwd); 1024 1025 // We need to convert the input list of environments variables into a 1026 // dictionary 1027 std::vector<std::string> envs = GetStrings(launch_request_arguments, "env"); 1028 llvm::json::Object environment; 1029 for (const std::string &env : envs) { 1030 size_t index = env.find("="); 1031 environment.try_emplace(env.substr(0, index), env.substr(index + 1)); 1032 } 1033 run_in_terminal_args.try_emplace("env", 1034 llvm::json::Value(std::move(environment))); 1035 1036 reverse_request.try_emplace( 1037 "arguments", llvm::json::Value(std::move(run_in_terminal_args))); 1038 return reverse_request; 1039 } 1040 1041 } // namespace lldb_vscode 1042