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