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