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