diff options
Diffstat (limited to 'tools/lldb-vscode/JSONUtils.cpp')
| -rw-r--r-- | tools/lldb-vscode/JSONUtils.cpp | 892 | 
1 files changed, 892 insertions, 0 deletions
diff --git a/tools/lldb-vscode/JSONUtils.cpp b/tools/lldb-vscode/JSONUtils.cpp new file mode 100644 index 000000000000..76cd44cc72d1 --- /dev/null +++ b/tools/lldb-vscode/JSONUtils.cpp @@ -0,0 +1,892 @@ +//===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <algorithm> + +#include "llvm/Support/FormatAdapters.h" + +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBValue.h" +#include "lldb/Host/PosixApi.h" + +#include "ExceptionBreakpoint.h" +#include "JSONUtils.h" +#include "LLDBUtils.h" +#include "VSCode.h" + +namespace lldb_vscode { + +void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key, +                       llvm::StringRef str) { +  if (LLVM_LIKELY(llvm::json::isUTF8(str))) +    obj.try_emplace(key, str.str()); +  else +    obj.try_emplace(key, llvm::json::fixUTF8(str)); +} + +llvm::StringRef GetAsString(const llvm::json::Value &value) { +  if (auto s = value.getAsString()) +    return *s; +  return llvm::StringRef(); +} + +// Gets a string from a JSON object using the key, or returns an empty string. +llvm::StringRef GetString(const llvm::json::Object &obj, llvm::StringRef key) { +  if (auto value = obj.getString(key)) +    return GetAsString(*value); +  return llvm::StringRef(); +} + +llvm::StringRef GetString(const llvm::json::Object *obj, llvm::StringRef key) { +  if (obj == nullptr) +    return llvm::StringRef(); +  return GetString(*obj, key); +} + +// Gets an unsigned integer from a JSON object using the key, or returns the +// specified fail value. +uint64_t GetUnsigned(const llvm::json::Object &obj, llvm::StringRef key, +                     uint64_t fail_value) { +  if (auto value = obj.getInteger(key)) +    return (uint64_t)*value; +  return fail_value; +} + +uint64_t GetUnsigned(const llvm::json::Object *obj, llvm::StringRef key, +                     uint64_t fail_value) { +  if (obj == nullptr) +    return fail_value; +  return GetUnsigned(*obj, key, fail_value); +} + +bool GetBoolean(const llvm::json::Object &obj, llvm::StringRef key, +                bool fail_value) { +  if (auto value = obj.getBoolean(key)) +    return *value; +  if (auto value = obj.getInteger(key)) +    return *value != 0; +  return fail_value; +} + +bool GetBoolean(const llvm::json::Object *obj, llvm::StringRef key, +                bool fail_value) { +  if (obj == nullptr) +    return fail_value; +  return GetBoolean(*obj, key, fail_value); +} + +int64_t GetSigned(const llvm::json::Object &obj, llvm::StringRef key, +                  int64_t fail_value) { +  if (auto value = obj.getInteger(key)) +    return *value; +  return fail_value; +} + +int64_t GetSigned(const llvm::json::Object *obj, llvm::StringRef key, +                  int64_t fail_value) { +  if (obj == nullptr) +    return fail_value; +  return GetSigned(*obj, key, fail_value); +} + +bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key) { +  return obj.find(key) != obj.end(); +} + +std::vector<std::string> GetStrings(const llvm::json::Object *obj, +                                    llvm::StringRef key) { +  std::vector<std::string> strs; +  auto json_array = obj->getArray(key); +  if (!json_array) +    return strs; +  for (const auto &value : *json_array) { +    switch (value.kind()) { +    case llvm::json::Value::String: +      strs.push_back(value.getAsString()->str()); +      break; +    case llvm::json::Value::Number: +    case llvm::json::Value::Boolean: { +      std::string s; +      llvm::raw_string_ostream strm(s); +      strm << value; +      strs.push_back(strm.str()); +      break; +    } +    case llvm::json::Value::Null: +    case llvm::json::Value::Object: +    case llvm::json::Value::Array: +      break; +    } +  } +  return strs; +} + +void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object, +                    llvm::StringRef key) { + +  llvm::StringRef value = v.GetValue(); +  llvm::StringRef summary = v.GetSummary(); +  llvm::StringRef type_name = v.GetType().GetDisplayTypeName(); + +  std::string result; +  llvm::raw_string_ostream strm(result); +  if (!value.empty()) { +    strm << value; +    if (!summary.empty()) +      strm << ' ' << summary; +  } else if (!summary.empty()) { +    strm << ' ' << summary; +  } else if (!type_name.empty()) { +    strm << type_name; +    lldb::addr_t address = v.GetLoadAddress(); +    if (address != LLDB_INVALID_ADDRESS) +      strm << " @ " << llvm::format_hex(address, 0); +  } +  strm.flush(); +  EmplaceSafeString(object, key, result); +} + +void FillResponse(const llvm::json::Object &request, +                  llvm::json::Object &response) { +  // Fill in all of the needed response fields to a "request" and set "success" +  // to true by default. +  response.try_emplace("type", "response"); +  response.try_emplace("seq", (int64_t)0); +  EmplaceSafeString(response, "command", GetString(request, "command")); +  const int64_t seq = GetSigned(request, "seq", 0); +  response.try_emplace("request_seq", seq); +  response.try_emplace("success", true); +} + +//---------------------------------------------------------------------- +// "Scope": { +//   "type": "object", +//   "description": "A Scope is a named container for variables. Optionally +//                   a scope can map to a source or a range within a source.", +//   "properties": { +//     "name": { +//       "type": "string", +//       "description": "Name of the scope such as 'Arguments', 'Locals'." +//     }, +//     "variablesReference": { +//       "type": "integer", +//       "description": "The variables of this scope can be retrieved by +//                       passing the value of variablesReference to the +//                       VariablesRequest." +//     }, +//     "namedVariables": { +//       "type": "integer", +//       "description": "The number of named variables in this scope. The +//                       client can use this optional information to present +//                       the variables in a paged UI and fetch them in chunks." +//     }, +//     "indexedVariables": { +//       "type": "integer", +//       "description": "The number of indexed variables in this scope. The +//                       client can use this optional information to present +//                       the variables in a paged UI and fetch them in chunks." +//     }, +//     "expensive": { +//       "type": "boolean", +//       "description": "If true, the number of variables in this scope is +//                       large or expensive to retrieve." +//     }, +//     "source": { +//       "$ref": "#/definitions/Source", +//       "description": "Optional source for this scope." +//     }, +//     "line": { +//       "type": "integer", +//       "description": "Optional start line of the range covered by this +//                       scope." +//     }, +//     "column": { +//       "type": "integer", +//       "description": "Optional start column of the range covered by this +//                       scope." +//     }, +//     "endLine": { +//       "type": "integer", +//       "description": "Optional end line of the range covered by this scope." +//     }, +//     "endColumn": { +//       "type": "integer", +//       "description": "Optional end column of the range covered by this +//                       scope." +//     } +//   }, +//   "required": [ "name", "variablesReference", "expensive" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateScope(const llvm::StringRef name, +                              int64_t variablesReference, +                              int64_t namedVariables, bool expensive) { +  llvm::json::Object object; +  EmplaceSafeString(object, "name", name.str()); +  object.try_emplace("variablesReference", variablesReference); +  object.try_emplace("expensive", expensive); +  object.try_emplace("namedVariables", namedVariables); +  return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "Breakpoint": { +//   "type": "object", +//   "description": "Information about a Breakpoint created in setBreakpoints +//                   or setFunctionBreakpoints.", +//   "properties": { +//     "id": { +//       "type": "integer", +//       "description": "An optional unique identifier for the breakpoint." +//     }, +//     "verified": { +//       "type": "boolean", +//       "description": "If true breakpoint could be set (but not necessarily +//                       at the desired location)." +//     }, +//     "message": { +//       "type": "string", +//       "description": "An optional message about the state of the breakpoint. +//                       This is shown to the user and can be used to explain +//                       why a breakpoint could not be verified." +//     }, +//     "source": { +//       "$ref": "#/definitions/Source", +//       "description": "The source where the breakpoint is located." +//     }, +//     "line": { +//       "type": "integer", +//       "description": "The start line of the actual range covered by the +//                       breakpoint." +//     }, +//     "column": { +//       "type": "integer", +//       "description": "An optional start column of the actual range covered +//                       by the breakpoint." +//     }, +//     "endLine": { +//       "type": "integer", +//       "description": "An optional end line of the actual range covered by +//                       the breakpoint." +//     }, +//     "endColumn": { +//       "type": "integer", +//       "description": "An optional end column of the actual range covered by +//                       the breakpoint. If no end line is given, then the end +//                       column is assumed to be in the start line." +//     } +//   }, +//   "required": [ "verified" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateBreakpoint(lldb::SBBreakpointLocation &bp_loc) { +  // Each breakpoint location is treated as a separate breakpoint for VS code. +  // They don't have the notion of a single breakpoint with multiple locations. +  llvm::json::Object object; +  if (!bp_loc.IsValid()) +    return llvm::json::Value(std::move(object)); + +  object.try_emplace("verified", true); +  const auto vs_id = MakeVSCodeBreakpointID(bp_loc); +  object.try_emplace("id", vs_id); +  auto bp_addr = bp_loc.GetAddress(); +  if (bp_addr.IsValid()) { +    auto line_entry = bp_addr.GetLineEntry(); +    const auto line = line_entry.GetLine(); +    if (line != UINT32_MAX) +      object.try_emplace("line", line); +    object.try_emplace("source", CreateSource(line_entry)); +  } +  return llvm::json::Value(std::move(object)); +} + +void AppendBreakpoint(lldb::SBBreakpoint &bp, llvm::json::Array &breakpoints) { +  if (!bp.IsValid()) +    return; +  const auto num_locations = bp.GetNumLocations(); +  if (num_locations == 0) +    return; +  for (size_t i = 0; i < num_locations; ++i) { +    auto bp_loc = bp.GetLocationAtIndex(i); +    breakpoints.emplace_back(CreateBreakpoint(bp_loc)); +  } +} + +//---------------------------------------------------------------------- +// "Event": { +//   "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, { +//     "type": "object", +//     "description": "Server-initiated event.", +//     "properties": { +//       "type": { +//         "type": "string", +//         "enum": [ "event" ] +//       }, +//       "event": { +//         "type": "string", +//         "description": "Type of event." +//       }, +//       "body": { +//         "type": [ "array", "boolean", "integer", "null", "number" , +//                   "object", "string" ], +//         "description": "Event-specific information." +//       } +//     }, +//     "required": [ "type", "event" ] +//   }] +// }, +// "ProtocolMessage": { +//   "type": "object", +//   "description": "Base class of requests, responses, and events.", +//   "properties": { +//         "seq": { +//           "type": "integer", +//           "description": "Sequence number." +//         }, +//         "type": { +//           "type": "string", +//           "description": "Message type.", +//           "_enum": [ "request", "response", "event" ] +//         } +//   }, +//   "required": [ "seq", "type" ] +// } +//---------------------------------------------------------------------- +llvm::json::Object CreateEventObject(const llvm::StringRef event_name) { +  llvm::json::Object event; +  event.try_emplace("seq", 0); +  event.try_emplace("type", "event"); +  EmplaceSafeString(event, "event", event_name); +  return event; +} + +//---------------------------------------------------------------------- +// "ExceptionBreakpointsFilter": { +//   "type": "object", +//   "description": "An ExceptionBreakpointsFilter is shown in the UI as an +//                   option for configuring how exceptions are dealt with.", +//   "properties": { +//     "filter": { +//       "type": "string", +//       "description": "The internal ID of the filter. This value is passed +//                       to the setExceptionBreakpoints request." +//     }, +//     "label": { +//       "type": "string", +//       "description": "The name of the filter. This will be shown in the UI." +//     }, +//     "default": { +//       "type": "boolean", +//       "description": "Initial value of the filter. If not specified a value +//                       'false' is assumed." +//     } +//   }, +//   "required": [ "filter", "label" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value +CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) { +  llvm::json::Object object; +  EmplaceSafeString(object, "filter", bp.filter); +  EmplaceSafeString(object, "label", bp.label); +  object.try_emplace("default", bp.default_value); +  return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "Source": { +//   "type": "object", +//   "description": "A Source is a descriptor for source code. It is returned +//                   from the debug adapter as part of a StackFrame and it is +//                   used by clients when specifying breakpoints.", +//   "properties": { +//     "name": { +//       "type": "string", +//       "description": "The short name of the source. Every source returned +//                       from the debug adapter has a name. When sending a +//                       source to the debug adapter this name is optional." +//     }, +//     "path": { +//       "type": "string", +//       "description": "The path of the source to be shown in the UI. It is +//                       only used to locate and load the content of the +//                       source if no sourceReference is specified (or its +//                       value is 0)." +//     }, +//     "sourceReference": { +//       "type": "number", +//       "description": "If sourceReference > 0 the contents of the source must +//                       be retrieved through the SourceRequest (even if a path +//                       is specified). A sourceReference is only valid for a +//                       session, so it must not be used to persist a source." +//     }, +//     "presentationHint": { +//       "type": "string", +//       "description": "An optional hint for how to present the source in the +//                       UI. A value of 'deemphasize' can be used to indicate +//                       that the source is not available or that it is +//                       skipped on stepping.", +//       "enum": [ "normal", "emphasize", "deemphasize" ] +//     }, +//     "origin": { +//       "type": "string", +//       "description": "The (optional) origin of this source: possible values +//                       'internal module', 'inlined content from source map', +//                       etc." +//     }, +//     "sources": { +//       "type": "array", +//       "items": { +//         "$ref": "#/definitions/Source" +//       }, +//       "description": "An optional list of sources that are related to this +//                       source. These may be the source that generated this +//                       source." +//     }, +//     "adapterData": { +//       "type":["array","boolean","integer","null","number","object","string"], +//       "description": "Optional data that a debug adapter might want to loop +//                       through the client. The client should leave the data +//                       intact and persist it across sessions. The client +//                       should not interpret the data." +//     }, +//     "checksums": { +//       "type": "array", +//       "items": { +//         "$ref": "#/definitions/Checksum" +//       }, +//       "description": "The checksums associated with this file." +//     } +//   } +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) { +  llvm::json::Object object; +  lldb::SBFileSpec file = line_entry.GetFileSpec(); +  if (file.IsValid()) { +    const char *name = file.GetFilename(); +    if (name) +      EmplaceSafeString(object, "name", name); +    char path[PATH_MAX] = ""; +    file.GetPath(path, sizeof(path)); +    if (path[0]) { +      EmplaceSafeString(object, "path", std::string(path)); +    } +  } +  return llvm::json::Value(std::move(object)); +} + +llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) { +  disasm_line = 0; +  auto line_entry = frame.GetLineEntry(); +  if (line_entry.GetFileSpec().IsValid()) +    return CreateSource(line_entry); + +  llvm::json::Object object; +  const auto pc = frame.GetPC(); + +  lldb::SBInstructionList insts; +  lldb::SBFunction function = frame.GetFunction(); +  lldb::addr_t low_pc = LLDB_INVALID_ADDRESS; +  if (function.IsValid()) { +    low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target); +    auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc); +    if (addr_srcref != g_vsc.addr_to_source_ref.end()) { +      // We have this disassembly cached already, return the existing +      // sourceReference +      object.try_emplace("sourceReference", addr_srcref->second); +      disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc); +    } else { +      insts = function.GetInstructions(g_vsc.target); +    } +  } else { +    lldb::SBSymbol symbol = frame.GetSymbol(); +    if (symbol.IsValid()) { +      low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target); +      auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc); +      if (addr_srcref != g_vsc.addr_to_source_ref.end()) { +        // We have this disassembly cached already, return the existing +        // sourceReference +        object.try_emplace("sourceReference", addr_srcref->second); +        disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc); +      } else { +        insts = symbol.GetInstructions(g_vsc.target); +      } +    } +  } +  const auto num_insts = insts.GetSize(); +  if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) { +    EmplaceSafeString(object, "name", frame.GetFunctionName()); +    SourceReference source; +    llvm::raw_string_ostream src_strm(source.content); +    std::string line; +    for (size_t i = 0; i < num_insts; ++i) { +      lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); +      const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target); +      const char *m = inst.GetMnemonic(g_vsc.target); +      const char *o = inst.GetOperands(g_vsc.target); +      const char *c = inst.GetComment(g_vsc.target); +      if (pc == inst_addr) +        disasm_line = i + 1; +      const auto inst_offset = inst_addr - low_pc; +      int spaces = 0; +      if (inst_offset < 10) +        spaces = 3; +      else if (inst_offset < 100) +        spaces = 2; +      else if (inst_offset < 1000) +        spaces = 1; +      line.clear(); +      llvm::raw_string_ostream line_strm(line); +      line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr, +                                 inst_offset, llvm::fmt_repeat(' ', spaces), m, +                                 o); + +      // If there is a comment append it starting at column 60 or after one +      // space past the last char +      const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60); +      if (c && c[0]) { +        if (line.size() < comment_row) +          line_strm.indent(comment_row - line_strm.str().size()); +        line_strm << " # " << c; +      } +      src_strm << line_strm.str() << "\n"; +      source.addr_to_line[inst_addr] = i + 1; +    } +    // Flush the source stream +    src_strm.str(); +    auto sourceReference = VSCode::GetNextSourceReference(); +    g_vsc.source_map[sourceReference] = std::move(source); +    g_vsc.addr_to_source_ref[low_pc] = sourceReference; +    object.try_emplace("sourceReference", sourceReference); +  } +  return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "StackFrame": { +//   "type": "object", +//   "description": "A Stackframe contains the source location.", +//   "properties": { +//     "id": { +//       "type": "integer", +//       "description": "An identifier for the stack frame. It must be unique +//                       across all threads. This id can be used to retrieve +//                       the scopes of the frame with the 'scopesRequest' or +//                       to restart the execution of a stackframe." +//     }, +//     "name": { +//       "type": "string", +//       "description": "The name of the stack frame, typically a method name." +//     }, +//     "source": { +//       "$ref": "#/definitions/Source", +//       "description": "The optional source of the frame." +//     }, +//     "line": { +//       "type": "integer", +//       "description": "The line within the file of the frame. If source is +//                       null or doesn't exist, line is 0 and must be ignored." +//     }, +//     "column": { +//       "type": "integer", +//       "description": "The column within the line. If source is null or +//                       doesn't exist, column is 0 and must be ignored." +//     }, +//     "endLine": { +//       "type": "integer", +//       "description": "An optional end line of the range covered by the +//                       stack frame." +//     }, +//     "endColumn": { +//       "type": "integer", +//       "description": "An optional end column of the range covered by the +//                       stack frame." +//     }, +//     "moduleId": { +//       "type": ["integer", "string"], +//       "description": "The module associated with this frame, if any." +//     }, +//     "presentationHint": { +//       "type": "string", +//       "enum": [ "normal", "label", "subtle" ], +//       "description": "An optional hint for how to present this frame in +//                       the UI. A value of 'label' can be used to indicate +//                       that the frame is an artificial frame that is used +//                       as a visual label or separator. A value of 'subtle' +//                       can be used to change the appearance of a frame in +//                       a 'subtle' way." +//     } +//   }, +//   "required": [ "id", "name", "line", "column" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) { +  llvm::json::Object object; +  int64_t frame_id = MakeVSCodeFrameID(frame); +  object.try_emplace("id", frame_id); +  EmplaceSafeString(object, "name", frame.GetFunctionName()); +  int64_t disasm_line = 0; +  object.try_emplace("source", CreateSource(frame, disasm_line)); + +  auto line_entry = frame.GetLineEntry(); +  if (disasm_line > 0) { +    object.try_emplace("line", disasm_line); +  } else { +    auto line = line_entry.GetLine(); +    if (line == UINT32_MAX) +      line = 0; +    object.try_emplace("line", line); +  } +  object.try_emplace("column", line_entry.GetColumn()); +  return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "Thread": { +//   "type": "object", +//   "description": "A Thread", +//   "properties": { +//     "id": { +//       "type": "integer", +//       "description": "Unique identifier for the thread." +//     }, +//     "name": { +//       "type": "string", +//       "description": "A name of the thread." +//     } +//   }, +//   "required": [ "id", "name" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateThread(lldb::SBThread &thread) { +  llvm::json::Object object; +  object.try_emplace("id", (int64_t)thread.GetThreadID()); +  char thread_str[64]; +  snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID()); +  const char *name = thread.GetName(); +  if (name) { +    std::string thread_with_name(thread_str); +    thread_with_name += ' '; +    thread_with_name += name; +    EmplaceSafeString(object, "name", thread_with_name); +  } else { +    EmplaceSafeString(object, "name", std::string(thread_str)); +  } +  return llvm::json::Value(std::move(object)); +} + +//---------------------------------------------------------------------- +// "StoppedEvent": { +//   "allOf": [ { "$ref": "#/definitions/Event" }, { +//     "type": "object", +//     "description": "Event message for 'stopped' event type. The event +//                     indicates that the execution of the debuggee has stopped +//                     due to some condition. This can be caused by a break +//                     point previously set, a stepping action has completed, +//                     by executing a debugger statement etc.", +//     "properties": { +//       "event": { +//         "type": "string", +//         "enum": [ "stopped" ] +//       }, +//       "body": { +//         "type": "object", +//         "properties": { +//           "reason": { +//             "type": "string", +//             "description": "The reason for the event. For backward +//                             compatibility this string is shown in the UI if +//                             the 'description' attribute is missing (but it +//                             must not be translated).", +//             "_enum": [ "step", "breakpoint", "exception", "pause", "entry" ] +//           }, +//           "description": { +//             "type": "string", +//             "description": "The full reason for the event, e.g. 'Paused +//                             on exception'. This string is shown in the UI +//                             as is." +//           }, +//           "threadId": { +//             "type": "integer", +//             "description": "The thread which was stopped." +//           }, +//           "text": { +//             "type": "string", +//             "description": "Additional information. E.g. if reason is +//                             'exception', text contains the exception name. +//                             This string is shown in the UI." +//           }, +//           "allThreadsStopped": { +//             "type": "boolean", +//             "description": "If allThreadsStopped is true, a debug adapter +//                             can announce that all threads have stopped. +//                             The client should use this information to +//                             enable that all threads can be expanded to +//                             access their stacktraces. If the attribute +//                             is missing or false, only the thread with the +//                             given threadId can be expanded." +//           } +//         }, +//         "required": [ "reason" ] +//       } +//     }, +//     "required": [ "event", "body" ] +//   }] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateThreadStopped(lldb::SBThread &thread, +                                      uint32_t stop_id) { +  llvm::json::Object event(CreateEventObject("stopped")); +  llvm::json::Object body; +  switch (thread.GetStopReason()) { +  case lldb::eStopReasonTrace: +  case lldb::eStopReasonPlanComplete: +    body.try_emplace("reason", "step"); +    break; +  case lldb::eStopReasonBreakpoint: { +    ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread); +    if (exc_bp) { +      body.try_emplace("reason", "exception"); +      EmplaceSafeString(body, "description", exc_bp->label); +    } else { +      body.try_emplace("reason", "breakpoint"); +    } +  } break; +  case lldb::eStopReasonWatchpoint: +  case lldb::eStopReasonInstrumentation: +    body.try_emplace("reason", "breakpoint"); +    break; +  case lldb::eStopReasonSignal: +    body.try_emplace("reason", "exception"); +    break; +  case lldb::eStopReasonException: +    body.try_emplace("reason", "exception"); +    break; +  case lldb::eStopReasonExec: +    body.try_emplace("reason", "entry"); +    break; +  case lldb::eStopReasonThreadExiting: +  case lldb::eStopReasonInvalid: +  case lldb::eStopReasonNone: +    break; +  } +  if (stop_id == 0) +    body.try_emplace("reason", "entry"); +  const lldb::tid_t tid = thread.GetThreadID(); +  body.try_emplace("threadId", (int64_t)tid); +  // If no description has been set, then set it to the default thread stopped +  // description. If we have breakpoints that get hit and shouldn't be reported +  // as breakpoints, then they will set the description above. +  if (ObjectContainsKey(body, "description")) { +    char description[1024]; +    if (thread.GetStopDescription(description, sizeof(description))) { +      EmplaceSafeString(body, "description", std::string(description)); +    } +  } +  if (tid == g_vsc.focus_tid) { +    body.try_emplace("threadCausedFocus", true); +  } +  body.try_emplace("preserveFocusHint", tid != g_vsc.focus_tid); +  body.try_emplace("allThreadsStopped", true); +  event.try_emplace("body", std::move(body)); +  return llvm::json::Value(std::move(event)); +} + +//---------------------------------------------------------------------- +// "Variable": { +//   "type": "object", +//   "description": "A Variable is a name/value pair. Optionally a variable +//                   can have a 'type' that is shown if space permits or when +//                   hovering over the variable's name. An optional 'kind' is +//                   used to render additional properties of the variable, +//                   e.g. different icons can be used to indicate that a +//                   variable is public or private. If the value is +//                   structured (has children), a handle is provided to +//                   retrieve the children with the VariablesRequest. If +//                   the number of named or indexed children is large, the +//                   numbers should be returned via the optional +//                   'namedVariables' and 'indexedVariables' attributes. The +//                   client can use this optional information to present the +//                   children in a paged UI and fetch them in chunks.", +//   "properties": { +//     "name": { +//       "type": "string", +//       "description": "The variable's name." +//     }, +//     "value": { +//       "type": "string", +//       "description": "The variable's value. This can be a multi-line text, +//                       e.g. for a function the body of a function." +//     }, +//     "type": { +//       "type": "string", +//       "description": "The type of the variable's value. Typically shown in +//                       the UI when hovering over the value." +//     }, +//     "presentationHint": { +//       "$ref": "#/definitions/VariablePresentationHint", +//       "description": "Properties of a variable that can be used to determine +//                       how to render the variable in the UI." +//     }, +//     "evaluateName": { +//       "type": "string", +//       "description": "Optional evaluatable name of this variable which can +//                       be passed to the 'EvaluateRequest' to fetch the +//                       variable's value." +//     }, +//     "variablesReference": { +//       "type": "integer", +//       "description": "If variablesReference is > 0, the variable is +//                       structured and its children can be retrieved by +//                       passing variablesReference to the VariablesRequest." +//     }, +//     "namedVariables": { +//       "type": "integer", +//       "description": "The number of named child variables. The client can +//                       use this optional information to present the children +//                       in a paged UI and fetch them in chunks." +//     }, +//     "indexedVariables": { +//       "type": "integer", +//       "description": "The number of indexed child variables. The client +//                       can use this optional information to present the +//                       children in a paged UI and fetch them in chunks." +//     } +//   }, +//   "required": [ "name", "value", "variablesReference" ] +// } +//---------------------------------------------------------------------- +llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference, +                                 int64_t varID, bool format_hex) { +  llvm::json::Object object; +  auto name = v.GetName(); +  EmplaceSafeString(object, "name", name ? name : "<null>"); +  if (format_hex) +    v.SetFormat(lldb::eFormatHex); +  SetValueForKey(v, object, "value"); +  auto type_cstr = v.GetType().GetDisplayTypeName(); +  EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME); +  if (varID != INT64_MAX) +    object.try_emplace("id", varID); +  if (v.MightHaveChildren()) +    object.try_emplace("variablesReference", variablesReference); +  else +    object.try_emplace("variablesReference", (int64_t)0); +  lldb::SBStream evaluateStream; +  v.GetExpressionPath(evaluateStream); +  const char *evaluateName = evaluateStream.GetData(); +  if (evaluateName && evaluateName[0]) +    EmplaceSafeString(object, "evaluateName", std::string(evaluateName)); +  return llvm::json::Value(std::move(object)); +} + +} // namespace lldb_vscode +  | 
