summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2020-08-03 14:33:06 +0000
committerpatrick <patrick@openbsd.org>2020-08-03 14:33:06 +0000
commit061da546b983eb767bad15e67af1174fb0bcf31c (patch)
tree83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp
parentImport LLVM 10.0.0 release including clang, lld and lldb. (diff)
downloadwireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz
wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp')
-rw-r--r--gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp873
1 files changed, 873 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp
new file mode 100644
index 00000000000..b83f56445e2
--- /dev/null
+++ b/gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp
@@ -0,0 +1,873 @@
+//===-- JSONUtils.cpp -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#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
+