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