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