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