diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/tools/lldb-vscode/JSONUtils.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-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.cpp | 873 |
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 + |