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 "JSONUtils.h" 10 11 #include "BreakpointBase.h" 12 #include "DAP.h" 13 #include "ExceptionBreakpoint.h" 14 #include "LLDBUtils.h" 15 #include "lldb/API/SBAddress.h" 16 #include "lldb/API/SBCompileUnit.h" 17 #include "lldb/API/SBDeclaration.h" 18 #include "lldb/API/SBEnvironment.h" 19 #include "lldb/API/SBError.h" 20 #include "lldb/API/SBFileSpec.h" 21 #include "lldb/API/SBFrame.h" 22 #include "lldb/API/SBFunction.h" 23 #include "lldb/API/SBLineEntry.h" 24 #include "lldb/API/SBModule.h" 25 #include "lldb/API/SBQueue.h" 26 #include "lldb/API/SBSection.h" 27 #include "lldb/API/SBStream.h" 28 #include "lldb/API/SBStringList.h" 29 #include "lldb/API/SBStructuredData.h" 30 #include "lldb/API/SBTarget.h" 31 #include "lldb/API/SBThread.h" 32 #include "lldb/API/SBType.h" 33 #include "lldb/API/SBValue.h" 34 #include "lldb/Host/PosixApi.h" // IWYU pragma: keep 35 #include "lldb/lldb-defines.h" 36 #include "lldb/lldb-enumerations.h" 37 #include "lldb/lldb-types.h" 38 #include "llvm/ADT/DenseMap.h" 39 #include "llvm/ADT/StringExtras.h" 40 #include "llvm/ADT/StringRef.h" 41 #include "llvm/Support/Compiler.h" 42 #include "llvm/Support/Format.h" 43 #include "llvm/Support/FormatVariadic.h" 44 #include "llvm/Support/Path.h" 45 #include "llvm/Support/ScopedPrinter.h" 46 #include "llvm/Support/raw_ostream.h" 47 #include <chrono> 48 #include <climits> 49 #include <cstddef> 50 #include <iomanip> 51 #include <optional> 52 #include <sstream> 53 #include <string> 54 #include <utility> 55 #include <vector> 56 57 namespace lldb_dap { 58 59 void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, 60 llvm::StringRef str) { 61 if (LLVM_LIKELY(llvm::json::isUTF8(str))) 62 obj.try_emplace(key, str.str()); 63 else 64 obj.try_emplace(key, llvm::json::fixUTF8(str)); 65 } 66 67 llvm::StringRef GetAsString(const llvm::json::Value &value) { 68 if (auto s = value.getAsString()) 69 return *s; 70 return llvm::StringRef(); 71 } 72 73 // Gets a string from a JSON object using the key, or returns an empty string. 74 llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key, 75 llvm::StringRef defaultValue) { 76 if (std::optional<llvm::StringRef> value = obj.getString(key)) 77 return *value; 78 return defaultValue; 79 } 80 81 llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key, 82 llvm::StringRef defaultValue) { 83 if (obj == nullptr) 84 return defaultValue; 85 return GetString(*obj, key, defaultValue); 86 } 87 88 // Gets an unsigned integer from a JSON object using the key, or returns the 89 // specified fail value. 90 uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key, 91 uint64_t fail_value) { 92 if (auto value = obj.getInteger(key)) 93 return (uint64_t)*value; 94 return fail_value; 95 } 96 97 uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key, 98 uint64_t fail_value) { 99 if (obj == nullptr) 100 return fail_value; 101 return GetUnsigned(*obj, key, fail_value); 102 } 103 104 bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key, 105 bool fail_value) { 106 if (auto value = obj.getBoolean(key)) 107 return *value; 108 if (auto value = obj.getInteger(key)) 109 return *value != 0; 110 return fail_value; 111 } 112 113 bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key, 114 bool fail_value) { 115 if (obj == nullptr) 116 return fail_value; 117 return GetBoolean(*obj, key, fail_value); 118 } 119 120 int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key, 121 int64_t fail_value) { 122 if (auto value = obj.getInteger(key)) 123 return *value; 124 return fail_value; 125 } 126 127 int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key, 128 int64_t fail_value) { 129 if (obj == nullptr) 130 return fail_value; 131 return GetSigned(*obj, key, fail_value); 132 } 133 134 bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) { 135 return obj.find(key) != obj.end(); 136 } 137 138 std::string EncodeMemoryReference(lldb::addr_t addr) { 139 return "0x" + llvm::utohexstr(addr); 140 } 141 142 std::optional<lldb::addr_t> 143 DecodeMemoryReference(llvm::StringRef memoryReference) { 144 if (!memoryReference.starts_with("0x")) 145 return std::nullopt; 146 147 lldb::addr_t addr; 148 if (memoryReference.consumeInteger(0, addr)) 149 return std::nullopt; 150 151 return addr; 152 } 153 154 std::vector<std::string> GetStrings(const llvm::json::Object *obj, 155 llvm::StringRef key) { 156 std::vector<std::string> strs; 157 const auto *json_array = obj->getArray(key); 158 if (!json_array) 159 return strs; 160 for (const auto &value : *json_array) { 161 switch (value.kind()) { 162 case llvm::json::Value::String: 163 strs.push_back(value.getAsString()->str()); 164 break; 165 case llvm::json::Value::Number: 166 case llvm::json::Value::Boolean: 167 strs.push_back(llvm::to_string(value)); 168 break; 169 case llvm::json::Value::Null: 170 case llvm::json::Value::Object: 171 case llvm::json::Value::Array: 172 break; 173 } 174 } 175 return strs; 176 } 177 178 std::unordered_map<std::string, std::string> 179 GetStringMap(const llvm::json::Object &obj, llvm::StringRef key) { 180 std::unordered_map<std::string, std::string> strs; 181 const auto *const json_object = obj.getObject(key); 182 if (!json_object) 183 return strs; 184 185 for (const auto &[key, value] : *json_object) { 186 switch (value.kind()) { 187 case llvm::json::Value::String: 188 strs.emplace(key.str(), value.getAsString()->str()); 189 break; 190 case llvm::json::Value::Number: 191 case llvm::json::Value::Boolean: 192 strs.emplace(key.str(), llvm::to_string(value)); 193 break; 194 case llvm::json::Value::Null: 195 case llvm::json::Value::Object: 196 case llvm::json::Value::Array: 197 break; 198 } 199 } 200 return strs; 201 } 202 203 static bool IsClassStructOrUnionType(lldb::SBType t) { 204 return (t.GetTypeClass() & (lldb::eTypeClassUnion | lldb::eTypeClassStruct | 205 lldb::eTypeClassArray)) != 0; 206 } 207 208 /// Create a short summary for a container that contains the summary of its 209 /// first children, so that the user can get a glimpse of its contents at a 210 /// glance. 211 static std::optional<std::string> 212 TryCreateAutoSummaryForContainer(lldb::SBValue &v) { 213 if (!v.MightHaveChildren()) 214 return std::nullopt; 215 /// As this operation can be potentially slow, we limit the total time spent 216 /// fetching children to a few ms. 217 const auto max_evaluation_time = std::chrono::milliseconds(10); 218 /// We don't want to generate a extremely long summary string, so we limit its 219 /// length. 220 const size_t max_length = 32; 221 222 auto start = std::chrono::steady_clock::now(); 223 std::string summary; 224 llvm::raw_string_ostream os(summary); 225 os << "{"; 226 227 llvm::StringRef separator = ""; 228 229 for (size_t i = 0, e = v.GetNumChildren(); i < e; ++i) { 230 // If we reached the time limit or exceeded the number of characters, we 231 // dump `...` to signal that there are more elements in the collection. 232 if (summary.size() > max_length || 233 (std::chrono::steady_clock::now() - start) > max_evaluation_time) { 234 os << separator << "..."; 235 break; 236 } 237 lldb::SBValue child = v.GetChildAtIndex(i); 238 239 if (llvm::StringRef name = child.GetName(); !name.empty()) { 240 llvm::StringRef desc; 241 if (llvm::StringRef summary = child.GetSummary(); !summary.empty()) 242 desc = summary; 243 else if (llvm::StringRef value = child.GetValue(); !value.empty()) 244 desc = value; 245 else if (IsClassStructOrUnionType(child.GetType())) 246 desc = "{...}"; 247 else 248 continue; 249 250 // If the child is an indexed entry, we don't show its index to save 251 // characters. 252 if (name.starts_with("[")) 253 os << separator << desc; 254 else 255 os << separator << name << ":" << desc; 256 separator = ", "; 257 } 258 } 259 os << "}"; 260 261 if (summary == "{...}" || summary == "{}") 262 return std::nullopt; 263 return summary; 264 } 265 266 /// Try to create a summary string for the given value that doesn't have a 267 /// summary of its own. 268 static std::optional<std::string> TryCreateAutoSummary(lldb::SBValue &value) { 269 // We use the dereferenced value for generating the summary. 270 if (value.GetType().IsPointerType() || value.GetType().IsReferenceType()) 271 value = value.Dereference(); 272 273 // We only support auto summaries for containers. 274 return TryCreateAutoSummaryForContainer(value); 275 } 276 277 void FillResponse(const llvm::json::Object &request, 278 llvm::json::Object &response) { 279 // Fill in all of the needed response fields to a "request" and set "success" 280 // to true by default. 281 response.try_emplace("type", "response"); 282 response.try_emplace("seq", (int64_t)0); 283 EmplaceSafeString(response, "command", GetString(request, "command")); 284 const int64_t seq = GetSigned(request, "seq", 0); 285 response.try_emplace("request_seq", seq); 286 response.try_emplace("success", true); 287 } 288 289 // "Scope": { 290 // "type": "object", 291 // "description": "A Scope is a named container for variables. Optionally 292 // a scope can map to a source or a range within a source.", 293 // "properties": { 294 // "name": { 295 // "type": "string", 296 // "description": "Name of the scope such as 'Arguments', 'Locals'." 297 // }, 298 // "presentationHint": { 299 // "type": "string", 300 // "description": "An optional hint for how to present this scope in the 301 // UI. If this attribute is missing, the scope is shown 302 // with a generic UI.", 303 // "_enum": [ "arguments", "locals", "registers" ], 304 // }, 305 // "variablesReference": { 306 // "type": "integer", 307 // "description": "The variables of this scope can be retrieved by 308 // passing the value of variablesReference to the 309 // VariablesRequest." 310 // }, 311 // "namedVariables": { 312 // "type": "integer", 313 // "description": "The number of named variables in this scope. The 314 // client can use this optional information to present 315 // the variables in a paged UI and fetch them in chunks." 316 // }, 317 // "indexedVariables": { 318 // "type": "integer", 319 // "description": "The number of indexed variables in this scope. The 320 // client can use this optional information to present 321 // the variables in a paged UI and fetch them in chunks." 322 // }, 323 // "expensive": { 324 // "type": "boolean", 325 // "description": "If true, the number of variables in this scope is 326 // large or expensive to retrieve." 327 // }, 328 // "source": { 329 // "$ref": "#/definitions/Source", 330 // "description": "Optional source for this scope." 331 // }, 332 // "line": { 333 // "type": "integer", 334 // "description": "Optional start line of the range covered by this 335 // scope." 336 // }, 337 // "column": { 338 // "type": "integer", 339 // "description": "Optional start column of the range covered by this 340 // scope." 341 // }, 342 // "endLine": { 343 // "type": "integer", 344 // "description": "Optional end line of the range covered by this scope." 345 // }, 346 // "endColumn": { 347 // "type": "integer", 348 // "description": "Optional end column of the range covered by this 349 // scope." 350 // } 351 // }, 352 // "required": [ "name", "variablesReference", "expensive" ] 353 // } 354 llvm::json::Value CreateScope(const llvm::StringRef name, 355 int64_t variablesReference, 356 int64_t namedVariables, bool expensive) { 357 llvm::json::Object object; 358 EmplaceSafeString(object, "name", name.str()); 359 360 // TODO: Support "arguments" scope. At the moment lldb-dap includes the 361 // arguments into the "locals" scope. 362 if (variablesReference == VARREF_LOCALS) { 363 object.try_emplace("presentationHint", "locals"); 364 } else if (variablesReference == VARREF_REGS) { 365 object.try_emplace("presentationHint", "registers"); 366 } 367 368 object.try_emplace("variablesReference", variablesReference); 369 object.try_emplace("expensive", expensive); 370 object.try_emplace("namedVariables", namedVariables); 371 return llvm::json::Value(std::move(object)); 372 } 373 374 // "Breakpoint": { 375 // "type": "object", 376 // "description": "Information about a Breakpoint created in setBreakpoints 377 // or setFunctionBreakpoints.", 378 // "properties": { 379 // "id": { 380 // "type": "integer", 381 // "description": "An optional unique identifier for the breakpoint." 382 // }, 383 // "verified": { 384 // "type": "boolean", 385 // "description": "If true breakpoint could be set (but not necessarily 386 // at the desired location)." 387 // }, 388 // "message": { 389 // "type": "string", 390 // "description": "An optional message about the state of the breakpoint. 391 // This is shown to the user and can be used to explain 392 // why a breakpoint could not be verified." 393 // }, 394 // "source": { 395 // "$ref": "#/definitions/Source", 396 // "description": "The source where the breakpoint is located." 397 // }, 398 // "line": { 399 // "type": "integer", 400 // "description": "The start line of the actual range covered by the 401 // breakpoint." 402 // }, 403 // "column": { 404 // "type": "integer", 405 // "description": "An optional start column of the actual range covered 406 // by the breakpoint." 407 // }, 408 // "endLine": { 409 // "type": "integer", 410 // "description": "An optional end line of the actual range covered by 411 // the breakpoint." 412 // }, 413 // "endColumn": { 414 // "type": "integer", 415 // "description": "An optional end column of the actual range covered by 416 // the breakpoint. If no end line is given, then the end 417 // column is assumed to be in the start line." 418 // } 419 // }, 420 // "required": [ "verified" ] 421 // } 422 llvm::json::Value CreateBreakpoint(BreakpointBase *bp, 423 std::optional<llvm::StringRef> request_path, 424 std::optional<uint32_t> request_line, 425 std::optional<uint32_t> request_column) { 426 llvm::json::Object object; 427 if (request_path) 428 object.try_emplace("source", CreateSource(*request_path)); 429 bp->CreateJsonObject(object); 430 // We try to add request_line as a fallback 431 if (request_line) 432 object.try_emplace("line", *request_line); 433 if (request_column) 434 object.try_emplace("column", *request_column); 435 return llvm::json::Value(std::move(object)); 436 } 437 438 static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) { 439 uint64_t debug_info_size = 0; 440 llvm::StringRef section_name(section.GetName()); 441 if (section_name.starts_with(".debug") || 442 section_name.starts_with("__debug") || 443 section_name.starts_with(".apple") || section_name.starts_with("__apple")) 444 debug_info_size += section.GetFileByteSize(); 445 size_t num_sub_sections = section.GetNumSubSections(); 446 for (size_t i = 0; i < num_sub_sections; i++) { 447 debug_info_size += 448 GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i)); 449 } 450 return debug_info_size; 451 } 452 453 static uint64_t GetDebugInfoSize(lldb::SBModule module) { 454 uint64_t debug_info_size = 0; 455 size_t num_sections = module.GetNumSections(); 456 for (size_t i = 0; i < num_sections; i++) { 457 debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i)); 458 } 459 return debug_info_size; 460 } 461 462 static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) { 463 std::ostringstream oss; 464 oss << std::fixed << std::setprecision(1); 465 if (debug_info < 1024) { 466 oss << debug_info << "B"; 467 } else if (debug_info < 1024 * 1024) { 468 double kb = double(debug_info) / 1024.0; 469 oss << kb << "KB"; 470 } else if (debug_info < 1024 * 1024 * 1024) { 471 double mb = double(debug_info) / (1024.0 * 1024.0); 472 oss << mb << "MB"; 473 } else { 474 double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0); 475 oss << gb << "GB"; 476 } 477 return oss.str(); 478 } 479 480 llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) { 481 llvm::json::Object object; 482 if (!target.IsValid() || !module.IsValid()) 483 return llvm::json::Value(std::move(object)); 484 485 const char *uuid = module.GetUUIDString(); 486 object.try_emplace("id", uuid ? std::string(uuid) : std::string("")); 487 object.try_emplace("name", std::string(module.GetFileSpec().GetFilename())); 488 char module_path_arr[PATH_MAX]; 489 module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr)); 490 std::string module_path(module_path_arr); 491 object.try_emplace("path", module_path); 492 if (module.GetNumCompileUnits() > 0) { 493 std::string symbol_str = "Symbols loaded."; 494 std::string debug_info_size; 495 uint64_t debug_info = GetDebugInfoSize(module); 496 if (debug_info > 0) { 497 debug_info_size = ConvertDebugInfoSizeToString(debug_info); 498 } 499 object.try_emplace("symbolStatus", symbol_str); 500 object.try_emplace("debugInfoSize", debug_info_size); 501 char symbol_path_arr[PATH_MAX]; 502 module.GetSymbolFileSpec().GetPath(symbol_path_arr, 503 sizeof(symbol_path_arr)); 504 std::string symbol_path(symbol_path_arr); 505 object.try_emplace("symbolFilePath", symbol_path); 506 } else { 507 object.try_emplace("symbolStatus", "Symbols not found."); 508 } 509 std::string loaded_addr = std::to_string( 510 module.GetObjectFileHeaderAddress().GetLoadAddress(target)); 511 object.try_emplace("addressRange", loaded_addr); 512 std::string version_str; 513 uint32_t version_nums[3]; 514 uint32_t num_versions = 515 module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t)); 516 for (uint32_t i = 0; i < num_versions; ++i) { 517 if (!version_str.empty()) 518 version_str += "."; 519 version_str += std::to_string(version_nums[i]); 520 } 521 if (!version_str.empty()) 522 object.try_emplace("version", version_str); 523 return llvm::json::Value(std::move(object)); 524 } 525 526 void AppendBreakpoint(BreakpointBase *bp, llvm::json::Array &breakpoints, 527 std::optional<llvm::StringRef> request_path, 528 std::optional<uint32_t> request_line) { 529 breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line)); 530 } 531 532 // "Event": { 533 // "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, { 534 // "type": "object", 535 // "description": "Server-initiated event.", 536 // "properties": { 537 // "type": { 538 // "type": "string", 539 // "enum": [ "event" ] 540 // }, 541 // "event": { 542 // "type": "string", 543 // "description": "Type of event." 544 // }, 545 // "body": { 546 // "type": [ "array", "boolean", "integer", "null", "number" , 547 // "object", "string" ], 548 // "description": "Event-specific information." 549 // } 550 // }, 551 // "required": [ "type", "event" ] 552 // }] 553 // }, 554 // "ProtocolMessage": { 555 // "type": "object", 556 // "description": "Base class of requests, responses, and events.", 557 // "properties": { 558 // "seq": { 559 // "type": "integer", 560 // "description": "Sequence number." 561 // }, 562 // "type": { 563 // "type": "string", 564 // "description": "Message type.", 565 // "_enum": [ "request", "response", "event" ] 566 // } 567 // }, 568 // "required": [ "seq", "type" ] 569 // } 570 llvm::json::Object CreateEventObject(const llvm::StringRef event_name) { 571 llvm::json::Object event; 572 event.try_emplace("seq", 0); 573 event.try_emplace("type", "event"); 574 EmplaceSafeString(event, "event", event_name); 575 return event; 576 } 577 578 // "ExceptionBreakpointsFilter": { 579 // "type": "object", 580 // "description": "An ExceptionBreakpointsFilter is shown in the UI as an 581 // option for configuring how exceptions are dealt with.", 582 // "properties": { 583 // "filter": { 584 // "type": "string", 585 // "description": "The internal ID of the filter. This value is passed 586 // to the setExceptionBreakpoints request." 587 // }, 588 // "label": { 589 // "type": "string", 590 // "description": "The name of the filter. This will be shown in the UI." 591 // }, 592 // "default": { 593 // "type": "boolean", 594 // "description": "Initial value of the filter. If not specified a value 595 // 'false' is assumed." 596 // } 597 // }, 598 // "required": [ "filter", "label" ] 599 // } 600 llvm::json::Value 601 CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) { 602 llvm::json::Object object; 603 EmplaceSafeString(object, "filter", bp.filter); 604 EmplaceSafeString(object, "label", bp.label); 605 object.try_emplace("default", bp.default_value); 606 return llvm::json::Value(std::move(object)); 607 } 608 609 // "Source": { 610 // "type": "object", 611 // "description": "A Source is a descriptor for source code. It is returned 612 // from the debug adapter as part of a StackFrame and it is 613 // used by clients when specifying breakpoints.", 614 // "properties": { 615 // "name": { 616 // "type": "string", 617 // "description": "The short name of the source. Every source returned 618 // from the debug adapter has a name. When sending a 619 // source to the debug adapter this name is optional." 620 // }, 621 // "path": { 622 // "type": "string", 623 // "description": "The path of the source to be shown in the UI. It is 624 // only used to locate and load the content of the 625 // source if no sourceReference is specified (or its 626 // value is 0)." 627 // }, 628 // "sourceReference": { 629 // "type": "number", 630 // "description": "If sourceReference > 0 the contents of the source must 631 // be retrieved through the SourceRequest (even if a path 632 // is specified). A sourceReference is only valid for a 633 // session, so it must not be used to persist a source." 634 // }, 635 // "presentationHint": { 636 // "type": "string", 637 // "description": "An optional hint for how to present the source in the 638 // UI. A value of 'deemphasize' can be used to indicate 639 // that the source is not available or that it is 640 // skipped on stepping.", 641 // "enum": [ "normal", "emphasize", "deemphasize" ] 642 // }, 643 // "origin": { 644 // "type": "string", 645 // "description": "The (optional) origin of this source: possible values 646 // 'internal module', 'inlined content from source map', 647 // etc." 648 // }, 649 // "sources": { 650 // "type": "array", 651 // "items": { 652 // "$ref": "#/definitions/Source" 653 // }, 654 // "description": "An optional list of sources that are related to this 655 // source. These may be the source that generated this 656 // source." 657 // }, 658 // "adapterData": { 659 // "type":["array","boolean","integer","null","number","object","string"], 660 // "description": "Optional data that a debug adapter might want to loop 661 // through the client. The client should leave the data 662 // intact and persist it across sessions. The client 663 // should not interpret the data." 664 // }, 665 // "checksums": { 666 // "type": "array", 667 // "items": { 668 // "$ref": "#/definitions/Checksum" 669 // }, 670 // "description": "The checksums associated with this file." 671 // } 672 // } 673 // } 674 llvm::json::Value CreateSource(const lldb::SBFileSpec &file) { 675 llvm::json::Object object; 676 if (file.IsValid()) { 677 const char *name = file.GetFilename(); 678 if (name) 679 EmplaceSafeString(object, "name", name); 680 char path[PATH_MAX] = ""; 681 if (file.GetPath(path, sizeof(path)) && 682 lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) { 683 EmplaceSafeString(object, "path", std::string(path)); 684 } 685 } 686 return llvm::json::Value(std::move(object)); 687 } 688 689 llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry) { 690 return CreateSource(line_entry.GetFileSpec()); 691 } 692 693 llvm::json::Value CreateSource(llvm::StringRef source_path) { 694 llvm::json::Object source; 695 llvm::StringRef name = llvm::sys::path::filename(source_path); 696 EmplaceSafeString(source, "name", name); 697 EmplaceSafeString(source, "path", source_path); 698 return llvm::json::Value(std::move(source)); 699 } 700 701 static std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) { 702 auto line_entry = frame.GetLineEntry(); 703 // A line entry of 0 indicates the line is compiler generated i.e. no source 704 // file is associated with the frame. 705 if (line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0) 706 return CreateSource(line_entry); 707 708 return {}; 709 } 710 711 // "StackFrame": { 712 // "type": "object", 713 // "description": "A Stackframe contains the source location.", 714 // "properties": { 715 // "id": { 716 // "type": "integer", 717 // "description": "An identifier for the stack frame. It must be unique 718 // across all threads. This id can be used to retrieve 719 // the scopes of the frame with the 'scopesRequest' or 720 // to restart the execution of a stackframe." 721 // }, 722 // "name": { 723 // "type": "string", 724 // "description": "The name of the stack frame, typically a method name." 725 // }, 726 // "source": { 727 // "$ref": "#/definitions/Source", 728 // "description": "The optional source of the frame." 729 // }, 730 // "line": { 731 // "type": "integer", 732 // "description": "The line within the file of the frame. If source is 733 // null or doesn't exist, line is 0 and must be ignored." 734 // }, 735 // "column": { 736 // "type": "integer", 737 // "description": "The column within the line. If source is null or 738 // doesn't exist, column is 0 and must be ignored." 739 // }, 740 // "endLine": { 741 // "type": "integer", 742 // "description": "An optional end line of the range covered by the 743 // stack frame." 744 // }, 745 // "endColumn": { 746 // "type": "integer", 747 // "description": "An optional end column of the range covered by the 748 // stack frame." 749 // }, 750 // "instructionPointerReference": { 751 // "type": "string", 752 // "description": "A memory reference for the current instruction 753 // pointer in this frame." 754 // }, 755 // "moduleId": { 756 // "type": ["integer", "string"], 757 // "description": "The module associated with this frame, if any." 758 // }, 759 // "presentationHint": { 760 // "type": "string", 761 // "enum": [ "normal", "label", "subtle" ], 762 // "description": "An optional hint for how to present this frame in 763 // the UI. A value of 'label' can be used to indicate 764 // that the frame is an artificial frame that is used 765 // as a visual label or separator. A value of 'subtle' 766 // can be used to change the appearance of a frame in 767 // a 'subtle' way." 768 // } 769 // }, 770 // "required": [ "id", "name", "line", "column" ] 771 // } 772 llvm::json::Value CreateStackFrame(lldb::SBFrame &frame, 773 lldb::SBFormat &format) { 774 llvm::json::Object object; 775 int64_t frame_id = MakeDAPFrameID(frame); 776 object.try_emplace("id", frame_id); 777 778 std::string frame_name; 779 lldb::SBStream stream; 780 if (format && frame.GetDescriptionWithFormat(format, stream).Success()) { 781 frame_name = stream.GetData(); 782 783 // `function_name` can be a nullptr, which throws an error when assigned to 784 // an `std::string`. 785 } else if (const char *name = frame.GetDisplayFunctionName()) { 786 frame_name = name; 787 } 788 789 if (frame_name.empty()) { 790 // If the function name is unavailable, display the pc address as a 16-digit 791 // hex string, e.g. "0x0000000000012345" 792 llvm::raw_string_ostream os(frame_name); 793 os << llvm::format_hex(frame.GetPC(), 18); 794 } 795 796 // We only include `[opt]` if a custom frame format is not specified. 797 if (!format && frame.GetFunction().GetIsOptimized()) 798 frame_name += " [opt]"; 799 800 EmplaceSafeString(object, "name", frame_name); 801 802 auto source = CreateSource(frame); 803 804 if (source) { 805 object.try_emplace("source", *source); 806 auto line_entry = frame.GetLineEntry(); 807 auto line = line_entry.GetLine(); 808 if (line && line != LLDB_INVALID_LINE_NUMBER) 809 object.try_emplace("line", line); 810 else 811 object.try_emplace("line", 0); 812 auto column = line_entry.GetColumn(); 813 object.try_emplace("column", column); 814 } else { 815 object.try_emplace("line", 0); 816 object.try_emplace("column", 0); 817 } 818 819 const auto pc = frame.GetPC(); 820 if (pc != LLDB_INVALID_ADDRESS) { 821 std::string formatted_addr = "0x" + llvm::utohexstr(pc); 822 object.try_emplace("instructionPointerReference", formatted_addr); 823 } 824 825 if (frame.IsArtificial() || frame.IsHidden()) 826 object.try_emplace("presentationHint", "subtle"); 827 828 return llvm::json::Value(std::move(object)); 829 } 830 831 llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread, 832 lldb::SBFormat &format) { 833 std::string name; 834 lldb::SBStream stream; 835 if (format && thread.GetDescriptionWithFormat(format, stream).Success()) { 836 name = stream.GetData(); 837 } else { 838 const uint32_t thread_idx = thread.GetExtendedBacktraceOriginatingIndexID(); 839 const char *queue_name = thread.GetQueueName(); 840 if (queue_name != nullptr) { 841 name = llvm::formatv("Enqueued from {0} (Thread {1})", queue_name, 842 thread_idx); 843 } else { 844 name = llvm::formatv("Thread {0}", thread_idx); 845 } 846 } 847 848 return llvm::json::Value(llvm::json::Object{{"id", thread.GetThreadID() + 1}, 849 {"name", name}, 850 {"presentationHint", "label"}}); 851 } 852 853 // "Thread": { 854 // "type": "object", 855 // "description": "A Thread", 856 // "properties": { 857 // "id": { 858 // "type": "integer", 859 // "description": "Unique identifier for the thread." 860 // }, 861 // "name": { 862 // "type": "string", 863 // "description": "A name of the thread." 864 // } 865 // }, 866 // "required": [ "id", "name" ] 867 // } 868 llvm::json::Value CreateThread(lldb::SBThread &thread, lldb::SBFormat &format) { 869 llvm::json::Object object; 870 object.try_emplace("id", (int64_t)thread.GetThreadID()); 871 std::string thread_str; 872 lldb::SBStream stream; 873 if (format && thread.GetDescriptionWithFormat(format, stream).Success()) { 874 thread_str = stream.GetData(); 875 } else { 876 const char *thread_name = thread.GetName(); 877 const char *queue_name = thread.GetQueueName(); 878 879 if (thread_name) { 880 thread_str = std::string(thread_name); 881 } else if (queue_name) { 882 auto kind = thread.GetQueue().GetKind(); 883 std::string queue_kind_label = ""; 884 if (kind == lldb::eQueueKindSerial) { 885 queue_kind_label = " (serial)"; 886 } else if (kind == lldb::eQueueKindConcurrent) { 887 queue_kind_label = " (concurrent)"; 888 } 889 890 thread_str = 891 llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(), 892 queue_name, queue_kind_label) 893 .str(); 894 } else { 895 thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str(); 896 } 897 } 898 899 EmplaceSafeString(object, "name", thread_str); 900 901 return llvm::json::Value(std::move(object)); 902 } 903 904 // "StoppedEvent": { 905 // "allOf": [ { "$ref": "#/definitions/Event" }, { 906 // "type": "object", 907 // "description": "Event message for 'stopped' event type. The event 908 // indicates that the execution of the debuggee has stopped 909 // due to some condition. This can be caused by a break 910 // point previously set, a stepping action has completed, 911 // by executing a debugger statement etc.", 912 // "properties": { 913 // "event": { 914 // "type": "string", 915 // "enum": [ "stopped" ] 916 // }, 917 // "body": { 918 // "type": "object", 919 // "properties": { 920 // "reason": { 921 // "type": "string", 922 // "description": "The reason for the event. For backward 923 // compatibility this string is shown in the UI if 924 // the 'description' attribute is missing (but it 925 // must not be translated).", 926 // "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ] 927 // }, 928 // "description": { 929 // "type": "string", 930 // "description": "The full reason for the event, e.g. 'Paused 931 // on exception'. This string is shown in the UI 932 // as is." 933 // }, 934 // "threadId": { 935 // "type": "integer", 936 // "description": "The thread which was stopped." 937 // }, 938 // "text": { 939 // "type": "string", 940 // "description": "Additional information. E.g. if reason is 941 // 'exception', text contains the exception name. 942 // This string is shown in the UI." 943 // }, 944 // "allThreadsStopped": { 945 // "type": "boolean", 946 // "description": "If allThreadsStopped is true, a debug adapter 947 // can announce that all threads have stopped. 948 // The client should use this information to 949 // enable that all threads can be expanded to 950 // access their stacktraces. If the attribute 951 // is missing or false, only the thread with the 952 // given threadId can be expanded." 953 // } 954 // }, 955 // "required": [ "reason" ] 956 // } 957 // }, 958 // "required": [ "event", "body" ] 959 // }] 960 // } 961 llvm::json::Value CreateThreadStopped(DAP &dap, lldb::SBThread &thread, 962 uint32_t stop_id) { 963 llvm::json::Object event(CreateEventObject("stopped")); 964 llvm::json::Object body; 965 switch (thread.GetStopReason()) { 966 case lldb::eStopReasonTrace: 967 case lldb::eStopReasonPlanComplete: 968 body.try_emplace("reason", "step"); 969 break; 970 case lldb::eStopReasonBreakpoint: { 971 ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread); 972 if (exc_bp) { 973 body.try_emplace("reason", "exception"); 974 EmplaceSafeString(body, "description", exc_bp->label); 975 } else { 976 InstructionBreakpoint *inst_bp = 977 dap.GetInstructionBPFromStopReason(thread); 978 if (inst_bp) { 979 body.try_emplace("reason", "instruction breakpoint"); 980 } else { 981 body.try_emplace("reason", "breakpoint"); 982 } 983 lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(0); 984 lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(1); 985 std::string desc_str = 986 llvm::formatv("breakpoint {0}.{1}", bp_id, bp_loc_id); 987 body.try_emplace("hitBreakpointIds", 988 llvm::json::Array{llvm::json::Value(bp_id)}); 989 EmplaceSafeString(body, "description", desc_str); 990 } 991 } break; 992 case lldb::eStopReasonWatchpoint: 993 case lldb::eStopReasonInstrumentation: 994 body.try_emplace("reason", "breakpoint"); 995 break; 996 case lldb::eStopReasonProcessorTrace: 997 body.try_emplace("reason", "processor trace"); 998 break; 999 case lldb::eStopReasonSignal: 1000 case lldb::eStopReasonException: 1001 body.try_emplace("reason", "exception"); 1002 break; 1003 case lldb::eStopReasonExec: 1004 body.try_emplace("reason", "entry"); 1005 break; 1006 case lldb::eStopReasonFork: 1007 body.try_emplace("reason", "fork"); 1008 break; 1009 case lldb::eStopReasonVFork: 1010 body.try_emplace("reason", "vfork"); 1011 break; 1012 case lldb::eStopReasonVForkDone: 1013 body.try_emplace("reason", "vforkdone"); 1014 break; 1015 case lldb::eStopReasonInterrupt: 1016 body.try_emplace("reason", "async interrupt"); 1017 break; 1018 case lldb::eStopReasonThreadExiting: 1019 case lldb::eStopReasonInvalid: 1020 case lldb::eStopReasonNone: 1021 break; 1022 } 1023 if (stop_id == 0) 1024 body.try_emplace("reason", "entry"); 1025 const lldb::tid_t tid = thread.GetThreadID(); 1026 body.try_emplace("threadId", (int64_t)tid); 1027 // If no description has been set, then set it to the default thread stopped 1028 // description. If we have breakpoints that get hit and shouldn't be reported 1029 // as breakpoints, then they will set the description above. 1030 if (!ObjectContainsKey(body, "description")) { 1031 char description[1024]; 1032 if (thread.GetStopDescription(description, sizeof(description))) { 1033 EmplaceSafeString(body, "description", std::string(description)); 1034 } 1035 } 1036 // "threadCausedFocus" is used in tests to validate breaking behavior. 1037 if (tid == dap.focus_tid) { 1038 body.try_emplace("threadCausedFocus", true); 1039 } 1040 body.try_emplace("preserveFocusHint", tid != dap.focus_tid); 1041 body.try_emplace("allThreadsStopped", true); 1042 event.try_emplace("body", std::move(body)); 1043 return llvm::json::Value(std::move(event)); 1044 } 1045 1046 const char *GetNonNullVariableName(lldb::SBValue &v) { 1047 const char *name = v.GetName(); 1048 return name ? name : "<null>"; 1049 } 1050 1051 std::string CreateUniqueVariableNameForDisplay(lldb::SBValue &v, 1052 bool is_name_duplicated) { 1053 lldb::SBStream name_builder; 1054 name_builder.Print(GetNonNullVariableName(v)); 1055 if (is_name_duplicated) { 1056 lldb::SBDeclaration declaration = v.GetDeclaration(); 1057 const char *file_name = declaration.GetFileSpec().GetFilename(); 1058 const uint32_t line = declaration.GetLine(); 1059 1060 if (file_name != nullptr && line > 0) 1061 name_builder.Printf(" @ %s:%u", file_name, line); 1062 else if (const char *location = v.GetLocation()) 1063 name_builder.Printf(" @ %s", location); 1064 } 1065 return name_builder.GetData(); 1066 } 1067 1068 VariableDescription::VariableDescription(lldb::SBValue v, 1069 bool auto_variable_summaries, 1070 bool format_hex, 1071 bool is_name_duplicated, 1072 std::optional<std::string> custom_name) 1073 : v(v) { 1074 name = custom_name 1075 ? *custom_name 1076 : CreateUniqueVariableNameForDisplay(v, is_name_duplicated); 1077 1078 type_obj = v.GetType(); 1079 std::string raw_display_type_name = 1080 llvm::StringRef(type_obj.GetDisplayTypeName()).str(); 1081 display_type_name = 1082 !raw_display_type_name.empty() ? raw_display_type_name : NO_TYPENAME; 1083 1084 // Only format hex/default if there is no existing special format. 1085 if (v.GetFormat() == lldb::eFormatDefault || 1086 v.GetFormat() == lldb::eFormatHex) { 1087 if (format_hex) 1088 v.SetFormat(lldb::eFormatHex); 1089 else 1090 v.SetFormat(lldb::eFormatDefault); 1091 } 1092 1093 llvm::raw_string_ostream os_display_value(display_value); 1094 1095 if (lldb::SBError sb_error = v.GetError(); sb_error.Fail()) { 1096 error = sb_error.GetCString(); 1097 os_display_value << "<error: " << error << ">"; 1098 } else { 1099 value = llvm::StringRef(v.GetValue()).str(); 1100 summary = llvm::StringRef(v.GetSummary()).str(); 1101 if (summary.empty() && auto_variable_summaries) 1102 auto_summary = TryCreateAutoSummary(v); 1103 1104 std::optional<std::string> effective_summary = 1105 !summary.empty() ? summary : auto_summary; 1106 1107 if (!value.empty()) { 1108 os_display_value << value; 1109 if (effective_summary) 1110 os_display_value << " " << *effective_summary; 1111 } else if (effective_summary) { 1112 os_display_value << *effective_summary; 1113 1114 // As last resort, we print its type and address if available. 1115 } else { 1116 if (!raw_display_type_name.empty()) { 1117 os_display_value << raw_display_type_name; 1118 lldb::addr_t address = v.GetLoadAddress(); 1119 if (address != LLDB_INVALID_ADDRESS) 1120 os_display_value << " @ " << llvm::format_hex(address, 0); 1121 } 1122 } 1123 } 1124 1125 lldb::SBStream evaluateStream; 1126 v.GetExpressionPath(evaluateStream); 1127 evaluate_name = llvm::StringRef(evaluateStream.GetData()).str(); 1128 } 1129 1130 llvm::json::Object VariableDescription::GetVariableExtensionsJSON() { 1131 llvm::json::Object extensions; 1132 if (error) 1133 EmplaceSafeString(extensions, "error", *error); 1134 if (!value.empty()) 1135 EmplaceSafeString(extensions, "value", value); 1136 if (!summary.empty()) 1137 EmplaceSafeString(extensions, "summary", summary); 1138 if (auto_summary) 1139 EmplaceSafeString(extensions, "autoSummary", *auto_summary); 1140 1141 if (lldb::SBDeclaration decl = v.GetDeclaration(); decl.IsValid()) { 1142 llvm::json::Object decl_obj; 1143 if (lldb::SBFileSpec file = decl.GetFileSpec(); file.IsValid()) { 1144 char path[PATH_MAX] = ""; 1145 if (file.GetPath(path, sizeof(path)) && 1146 lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) { 1147 decl_obj.try_emplace("path", std::string(path)); 1148 } 1149 } 1150 1151 if (int line = decl.GetLine()) 1152 decl_obj.try_emplace("line", line); 1153 if (int column = decl.GetColumn()) 1154 decl_obj.try_emplace("column", column); 1155 1156 if (!decl_obj.empty()) 1157 extensions.try_emplace("declaration", std::move(decl_obj)); 1158 } 1159 return extensions; 1160 } 1161 1162 std::string VariableDescription::GetResult(llvm::StringRef context) { 1163 // In repl context, the results can be displayed as multiple lines so more 1164 // detailed descriptions can be returned. 1165 if (context != "repl") 1166 return display_value; 1167 1168 if (!v.IsValid()) 1169 return display_value; 1170 1171 // Try the SBValue::GetDescription(), which may call into language runtime 1172 // specific formatters (see ValueObjectPrinter). 1173 lldb::SBStream stream; 1174 v.GetDescription(stream); 1175 llvm::StringRef description = stream.GetData(); 1176 return description.trim().str(); 1177 } 1178 1179 bool ValuePointsToCode(lldb::SBValue v) { 1180 if (!v.GetType().GetPointeeType().IsFunctionType()) 1181 return false; 1182 1183 lldb::addr_t addr = v.GetValueAsAddress(); 1184 lldb::SBLineEntry line_entry = 1185 v.GetTarget().ResolveLoadAddress(addr).GetLineEntry(); 1186 1187 return line_entry.IsValid(); 1188 } 1189 1190 int64_t PackLocation(int64_t var_ref, bool is_value_location) { 1191 return var_ref << 1 | is_value_location; 1192 } 1193 1194 std::pair<int64_t, bool> UnpackLocation(int64_t location_id) { 1195 return std::pair{location_id >> 1, location_id & 1}; 1196 } 1197 1198 // "Variable": { 1199 // "type": "object", 1200 // "description": "A Variable is a name/value pair. Optionally a variable 1201 // can have a 'type' that is shown if space permits or when 1202 // hovering over the variable's name. An optional 'kind' is 1203 // used to render additional properties of the variable, 1204 // e.g. different icons can be used to indicate that a 1205 // variable is public or private. If the value is 1206 // structured (has children), a handle is provided to 1207 // retrieve the children with the VariablesRequest. If 1208 // the number of named or indexed children is large, the 1209 // numbers should be returned via the optional 1210 // 'namedVariables' and 'indexedVariables' attributes. The 1211 // client can use this optional information to present the 1212 // children in a paged UI and fetch them in chunks.", 1213 // "properties": { 1214 // "name": { 1215 // "type": "string", 1216 // "description": "The variable's name." 1217 // }, 1218 // "value": { 1219 // "type": "string", 1220 // "description": "The variable's value. This can be a multi-line text, 1221 // e.g. for a function the body of a function." 1222 // }, 1223 // "type": { 1224 // "type": "string", 1225 // "description": "The type of the variable's value. Typically shown in 1226 // the UI when hovering over the value." 1227 // }, 1228 // "presentationHint": { 1229 // "$ref": "#/definitions/VariablePresentationHint", 1230 // "description": "Properties of a variable that can be used to determine 1231 // how to render the variable in the UI." 1232 // }, 1233 // "evaluateName": { 1234 // "type": "string", 1235 // "description": "Optional evaluatable name of this variable which can 1236 // be passed to the 'EvaluateRequest' to fetch the 1237 // variable's value." 1238 // }, 1239 // "variablesReference": { 1240 // "type": "integer", 1241 // "description": "If variablesReference is > 0, the variable is 1242 // structured and its children can be retrieved by 1243 // passing variablesReference to the VariablesRequest." 1244 // }, 1245 // "namedVariables": { 1246 // "type": "integer", 1247 // "description": "The number of named child variables. The client can 1248 // use this optional information to present the children 1249 // in a paged UI and fetch them in chunks." 1250 // }, 1251 // "indexedVariables": { 1252 // "type": "integer", 1253 // "description": "The number of indexed child variables. The client 1254 // can use this optional information to present the 1255 // children in a paged UI and fetch them in chunks." 1256 // }, 1257 // "memoryReference": { 1258 // "type": "string", 1259 // "description": "A memory reference associated with this variable. 1260 // For pointer type variables, this is generally a 1261 // reference to the memory address contained in the 1262 // pointer. For executable data, this reference may later 1263 // be used in a `disassemble` request. This attribute may 1264 // be returned by a debug adapter if corresponding 1265 // capability `supportsMemoryReferences` is true." 1266 // }, 1267 // "declarationLocationReference": { 1268 // "type": "integer", 1269 // "description": "A reference that allows the client to request the 1270 // location where the variable is declared. This should be 1271 // present only if the adapter is likely to be able to 1272 // resolve the location.\n\nThis reference shares the same 1273 // lifetime as the `variablesReference`. See 'Lifetime of 1274 // Object References' in the Overview section for 1275 // details." 1276 // }, 1277 // "valueLocationReference": { 1278 // "type": "integer", 1279 // "description": "A reference that allows the client to request the 1280 // location where the variable's value is declared. For 1281 // example, if the variable contains a function pointer, 1282 // the adapter may be able to look up the function's 1283 // location. This should be present only if the adapter 1284 // is likely to be able to resolve the location.\n\nThis 1285 // reference shares the same lifetime as the 1286 // `variablesReference`. See 'Lifetime of Object 1287 // References' in the Overview section for details." 1288 // }, 1289 // 1290 // "$__lldb_extensions": { 1291 // "description": "Unofficial extensions to the protocol", 1292 // "properties": { 1293 // "declaration": { 1294 // "type": "object", 1295 // "description": "The source location where the variable was 1296 // declared. This value won't be present if no 1297 // declaration is available. 1298 // Superseded by `declarationLocationReference`", 1299 // "properties": { 1300 // "path": { 1301 // "type": "string", 1302 // "description": "The source file path where the variable was 1303 // declared." 1304 // }, 1305 // "line": { 1306 // "type": "number", 1307 // "description": "The 1-indexed source line where the variable 1308 // was declared." 1309 // }, 1310 // "column": { 1311 // "type": "number", 1312 // "description": "The 1-indexed source column where the variable 1313 // was declared." 1314 // } 1315 // } 1316 // }, 1317 // "value": { 1318 // "type": "string", 1319 // "description": "The internal value of the variable as returned by 1320 // This is effectively SBValue.GetValue(). The other 1321 // `value` entry in the top-level variable response 1322 // is, on the other hand, just a display string for 1323 // the variable." 1324 // }, 1325 // "summary": { 1326 // "type": "string", 1327 // "description": "The summary string of the variable. This is 1328 // effectively SBValue.GetSummary()." 1329 // }, 1330 // "autoSummary": { 1331 // "type": "string", 1332 // "description": "The auto generated summary if using 1333 // `enableAutoVariableSummaries`." 1334 // }, 1335 // "error": { 1336 // "type": "string", 1337 // "description": "An error message generated if LLDB couldn't inspect 1338 // the variable." 1339 // } 1340 // } 1341 // } 1342 // }, 1343 // "required": [ "name", "value", "variablesReference" ] 1344 // } 1345 llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref, 1346 bool format_hex, bool auto_variable_summaries, 1347 bool synthetic_child_debugging, 1348 bool is_name_duplicated, 1349 std::optional<std::string> custom_name) { 1350 VariableDescription desc(v, auto_variable_summaries, format_hex, 1351 is_name_duplicated, custom_name); 1352 llvm::json::Object object; 1353 EmplaceSafeString(object, "name", desc.name); 1354 EmplaceSafeString(object, "value", desc.display_value); 1355 1356 if (!desc.evaluate_name.empty()) 1357 EmplaceSafeString(object, "evaluateName", desc.evaluate_name); 1358 1359 // If we have a type with many children, we would like to be able to 1360 // give a hint to the IDE that the type has indexed children so that the 1361 // request can be broken up in grabbing only a few children at a time. We 1362 // want to be careful and only call "v.GetNumChildren()" if we have an array 1363 // type or if we have a synthetic child provider producing indexed children. 1364 // We don't want to call "v.GetNumChildren()" on all objects as class, struct 1365 // and union types don't need to be completed if they are never expanded. So 1366 // we want to avoid calling this to only cases where we it makes sense to keep 1367 // performance high during normal debugging. 1368 1369 // If we have an array type, say that it is indexed and provide the number 1370 // of children in case we have a huge array. If we don't do this, then we 1371 // might take a while to produce all children at onces which can delay your 1372 // debug session. 1373 if (desc.type_obj.IsArrayType()) { 1374 object.try_emplace("indexedVariables", v.GetNumChildren()); 1375 } else if (v.IsSynthetic()) { 1376 // For a type with a synthetic child provider, the SBType of "v" won't tell 1377 // us anything about what might be displayed. Instead, we check if the first 1378 // child's name is "[0]" and then say it is indexed. We call 1379 // GetNumChildren() only if the child name matches to avoid a potentially 1380 // expensive operation. 1381 if (lldb::SBValue first_child = v.GetChildAtIndex(0)) { 1382 llvm::StringRef first_child_name = first_child.GetName(); 1383 if (first_child_name == "[0]") { 1384 size_t num_children = v.GetNumChildren(); 1385 // If we are creating a "[raw]" fake child for each synthetic type, we 1386 // have to account for it when returning indexed variables. 1387 if (synthetic_child_debugging) 1388 ++num_children; 1389 object.try_emplace("indexedVariables", num_children); 1390 } 1391 } 1392 } 1393 EmplaceSafeString(object, "type", desc.display_type_name); 1394 1395 // A unique variable identifier to help in properly identifying variables with 1396 // the same name. This is an extension to the VS protocol. 1397 object.try_emplace("id", var_ref); 1398 1399 if (v.MightHaveChildren()) 1400 object.try_emplace("variablesReference", var_ref); 1401 else 1402 object.try_emplace("variablesReference", 0); 1403 1404 if (v.GetDeclaration().IsValid()) 1405 object.try_emplace("declarationLocationReference", 1406 PackLocation(var_ref, false)); 1407 1408 if (ValuePointsToCode(v)) 1409 object.try_emplace("valueLocationReference", PackLocation(var_ref, true)); 1410 1411 if (lldb::addr_t addr = v.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS) 1412 object.try_emplace("memoryReference", EncodeMemoryReference(addr)); 1413 1414 object.try_emplace("$__lldb_extensions", desc.GetVariableExtensionsJSON()); 1415 return llvm::json::Value(std::move(object)); 1416 } 1417 1418 llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit) { 1419 llvm::json::Object object; 1420 char unit_path_arr[PATH_MAX]; 1421 unit.GetFileSpec().GetPath(unit_path_arr, sizeof(unit_path_arr)); 1422 std::string unit_path(unit_path_arr); 1423 object.try_emplace("compileUnitPath", unit_path); 1424 return llvm::json::Value(std::move(object)); 1425 } 1426 1427 /// See 1428 /// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal 1429 llvm::json::Object 1430 CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request, 1431 llvm::StringRef debug_adaptor_path, 1432 llvm::StringRef comm_file, 1433 lldb::pid_t debugger_pid) { 1434 llvm::json::Object run_in_terminal_args; 1435 // This indicates the IDE to open an embedded terminal, instead of opening 1436 // the terminal in a new window. 1437 run_in_terminal_args.try_emplace("kind", "integrated"); 1438 1439 const auto *launch_request_arguments = launch_request.getObject("arguments"); 1440 // The program path must be the first entry in the "args" field 1441 std::vector<std::string> args = {debug_adaptor_path.str(), "--comm-file", 1442 comm_file.str()}; 1443 if (debugger_pid != LLDB_INVALID_PROCESS_ID) { 1444 args.push_back("--debugger-pid"); 1445 args.push_back(std::to_string(debugger_pid)); 1446 } 1447 args.push_back("--launch-target"); 1448 args.push_back(GetString(launch_request_arguments, "program").str()); 1449 std::vector<std::string> target_args = 1450 GetStrings(launch_request_arguments, "args"); 1451 args.insert(args.end(), target_args.begin(), target_args.end()); 1452 run_in_terminal_args.try_emplace("args", args); 1453 1454 const auto cwd = GetString(launch_request_arguments, "cwd"); 1455 if (!cwd.empty()) 1456 run_in_terminal_args.try_emplace("cwd", cwd); 1457 1458 auto envs = GetEnvironmentFromArguments(*launch_request_arguments); 1459 llvm::json::Object env_json; 1460 for (size_t index = 0, env_count = envs.GetNumValues(); index < env_count; 1461 index++) { 1462 llvm::StringRef key = envs.GetNameAtIndex(index); 1463 llvm::StringRef value = envs.GetValueAtIndex(index); 1464 1465 if (!key.empty()) 1466 env_json.try_emplace(key, value); 1467 } 1468 run_in_terminal_args.try_emplace("env", 1469 llvm::json::Value(std::move(env_json))); 1470 1471 return run_in_terminal_args; 1472 } 1473 1474 // Keep all the top level items from the statistics dump, except for the 1475 // "modules" array. It can be huge and cause delay 1476 // Array and dictionary value will return as <key, JSON string> pairs 1477 static void FilterAndGetValueForKey(const lldb::SBStructuredData data, 1478 const char *key, llvm::json::Object &out) { 1479 lldb::SBStructuredData value = data.GetValueForKey(key); 1480 std::string key_utf8 = llvm::json::fixUTF8(key); 1481 if (llvm::StringRef(key) == "modules") 1482 return; 1483 switch (value.GetType()) { 1484 case lldb::eStructuredDataTypeFloat: 1485 out.try_emplace(key_utf8, value.GetFloatValue()); 1486 break; 1487 case lldb::eStructuredDataTypeUnsignedInteger: 1488 out.try_emplace(key_utf8, value.GetIntegerValue((uint64_t)0)); 1489 break; 1490 case lldb::eStructuredDataTypeSignedInteger: 1491 out.try_emplace(key_utf8, value.GetIntegerValue((int64_t)0)); 1492 break; 1493 case lldb::eStructuredDataTypeArray: { 1494 lldb::SBStream contents; 1495 value.GetAsJSON(contents); 1496 out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData())); 1497 } break; 1498 case lldb::eStructuredDataTypeBoolean: 1499 out.try_emplace(key_utf8, value.GetBooleanValue()); 1500 break; 1501 case lldb::eStructuredDataTypeString: { 1502 // Get the string size before reading 1503 const size_t str_length = value.GetStringValue(nullptr, 0); 1504 std::string str(str_length + 1, 0); 1505 value.GetStringValue(&str[0], str_length); 1506 out.try_emplace(key_utf8, llvm::json::fixUTF8(str)); 1507 } break; 1508 case lldb::eStructuredDataTypeDictionary: { 1509 lldb::SBStream contents; 1510 value.GetAsJSON(contents); 1511 out.try_emplace(key_utf8, llvm::json::fixUTF8(contents.GetData())); 1512 } break; 1513 case lldb::eStructuredDataTypeNull: 1514 case lldb::eStructuredDataTypeGeneric: 1515 case lldb::eStructuredDataTypeInvalid: 1516 break; 1517 } 1518 } 1519 1520 static void addStatistic(lldb::SBTarget &target, llvm::json::Object &event) { 1521 lldb::SBStructuredData statistics = target.GetStatistics(); 1522 bool is_dictionary = 1523 statistics.GetType() == lldb::eStructuredDataTypeDictionary; 1524 if (!is_dictionary) 1525 return; 1526 llvm::json::Object stats_body; 1527 1528 lldb::SBStringList keys; 1529 if (!statistics.GetKeys(keys)) 1530 return; 1531 for (size_t i = 0; i < keys.GetSize(); i++) { 1532 const char *key = keys.GetStringAtIndex(i); 1533 FilterAndGetValueForKey(statistics, key, stats_body); 1534 } 1535 event.try_emplace("statistics", std::move(stats_body)); 1536 } 1537 1538 llvm::json::Object CreateTerminatedEventObject(lldb::SBTarget &target) { 1539 llvm::json::Object event(CreateEventObject("terminated")); 1540 addStatistic(target, event); 1541 return event; 1542 } 1543 1544 std::string JSONToString(const llvm::json::Value &json) { 1545 std::string data; 1546 llvm::raw_string_ostream os(data); 1547 os << json; 1548 return data; 1549 } 1550 1551 } // namespace lldb_dap 1552