[lldb-dap] migrate set breakpoint requests (#137448)
- Migrate set breakpoint requests to use typed RequestHandler - `SetBreakpointsRequestHandler` - `SetDataBreakpointsRequestHandler` - `SetFunctionBreakpointsRequestHandler` - `SetInstructionBreakpointsRequestHandler` - `DataBreakpointInfoRequestHandler` - Decouple JSON from lldb-dap `Breakpoint` classes
This commit is contained in:
parent
716062d943
commit
8630c22083
@ -14,7 +14,6 @@
|
||||
#include "lldb/API/SBLineEntry.h"
|
||||
#include "lldb/API/SBMutex.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
@ -30,13 +29,16 @@ void Breakpoint::SetHitCondition() {
|
||||
m_bp.SetIgnoreCount(hitCount - 1);
|
||||
}
|
||||
|
||||
void Breakpoint::CreateJsonObject(llvm::json::Object &object) {
|
||||
protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() {
|
||||
protocol::Breakpoint breakpoint;
|
||||
|
||||
// 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.
|
||||
if (!m_bp.IsValid())
|
||||
return;
|
||||
object.try_emplace("verified", m_bp.GetNumResolvedLocations() > 0);
|
||||
object.try_emplace("id", m_bp.GetID());
|
||||
return breakpoint;
|
||||
|
||||
breakpoint.verified = m_bp.GetNumResolvedLocations() > 0;
|
||||
breakpoint.id = m_bp.GetID();
|
||||
// VS Code DAP doesn't currently allow one breakpoint to have multiple
|
||||
// locations so we just report the first one. If we report all locations
|
||||
// then the IDE starts showing the wrong line numbers and locations for
|
||||
@ -60,16 +62,18 @@ void Breakpoint::CreateJsonObject(llvm::json::Object &object) {
|
||||
if (bp_addr.IsValid()) {
|
||||
std::string formatted_addr =
|
||||
"0x" + llvm::utohexstr(bp_addr.GetLoadAddress(m_bp.GetTarget()));
|
||||
object.try_emplace("instructionReference", formatted_addr);
|
||||
breakpoint.instructionReference = formatted_addr;
|
||||
auto line_entry = bp_addr.GetLineEntry();
|
||||
const auto line = line_entry.GetLine();
|
||||
if (line != UINT32_MAX)
|
||||
object.try_emplace("line", line);
|
||||
breakpoint.line = line;
|
||||
const auto column = line_entry.GetColumn();
|
||||
if (column != 0)
|
||||
object.try_emplace("column", column);
|
||||
object.try_emplace("source", CreateSource(line_entry));
|
||||
breakpoint.column = column;
|
||||
breakpoint.source = CreateSource(line_entry);
|
||||
}
|
||||
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
bool Breakpoint::MatchesName(const char *name) {
|
||||
|
@ -17,14 +17,16 @@ namespace lldb_dap {
|
||||
|
||||
class Breakpoint : public BreakpointBase {
|
||||
public:
|
||||
Breakpoint(DAP &d, const llvm::json::Object &obj) : BreakpointBase(d, obj) {}
|
||||
Breakpoint(DAP &d, const std::optional<std::string> &condition,
|
||||
const std::optional<std::string> &hit_condition)
|
||||
: BreakpointBase(d, condition, hit_condition) {}
|
||||
Breakpoint(DAP &d, lldb::SBBreakpoint bp) : BreakpointBase(d), m_bp(bp) {}
|
||||
|
||||
lldb::break_id_t GetID() const { return m_bp.GetID(); }
|
||||
|
||||
void SetCondition() override;
|
||||
void SetHitCondition() override;
|
||||
void CreateJsonObject(llvm::json::Object &object) override;
|
||||
protocol::Breakpoint ToProtocolBreakpoint() override;
|
||||
|
||||
bool MatchesName(const char *name);
|
||||
void SetBreakpoint();
|
||||
|
@ -7,16 +7,14 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "BreakpointBase.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
using namespace lldb_dap;
|
||||
|
||||
BreakpointBase::BreakpointBase(DAP &d, const llvm::json::Object &obj)
|
||||
: m_dap(d),
|
||||
m_condition(std::string(GetString(obj, "condition").value_or(""))),
|
||||
m_hit_condition(
|
||||
std::string(GetString(obj, "hitCondition").value_or(""))) {}
|
||||
BreakpointBase::BreakpointBase(DAP &d,
|
||||
const std::optional<std::string> &condition,
|
||||
const std::optional<std::string> &hit_condition)
|
||||
: m_dap(d), m_condition(condition.value_or("")),
|
||||
m_hit_condition(hit_condition.value_or("")) {}
|
||||
|
||||
void BreakpointBase::UpdateBreakpoint(const BreakpointBase &request_bp) {
|
||||
if (m_condition != request_bp.m_condition) {
|
||||
|
@ -10,7 +10,8 @@
|
||||
#define LLDB_TOOLS_LLDB_DAP_BREAKPOINTBASE_H
|
||||
|
||||
#include "DAPForward.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "Protocol/ProtocolTypes.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace lldb_dap {
|
||||
@ -18,12 +19,13 @@ namespace lldb_dap {
|
||||
class BreakpointBase {
|
||||
public:
|
||||
explicit BreakpointBase(DAP &d) : m_dap(d) {}
|
||||
BreakpointBase(DAP &d, const llvm::json::Object &obj);
|
||||
BreakpointBase(DAP &d, const std::optional<std::string> &condition,
|
||||
const std::optional<std::string> &hit_condition);
|
||||
virtual ~BreakpointBase() = default;
|
||||
|
||||
virtual void SetCondition() = 0;
|
||||
virtual void SetHitCondition() = 0;
|
||||
virtual void CreateJsonObject(llvm::json::Object &object) = 0;
|
||||
virtual protocol::Breakpoint ToProtocolBreakpoint() = 0;
|
||||
|
||||
void UpdateBreakpoint(const BreakpointBase &request_bp);
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
@ -552,9 +553,7 @@ lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
|
||||
return target.GetProcess().GetThreadByID(tid);
|
||||
}
|
||||
|
||||
lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
|
||||
const uint64_t frame_id =
|
||||
GetInteger<uint64_t>(arguments, "frameId").value_or(UINT64_MAX);
|
||||
lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) {
|
||||
lldb::SBProcess process = target.GetProcess();
|
||||
// Upper 32 bits is the thread index ID
|
||||
lldb::SBThread thread =
|
||||
@ -563,6 +562,12 @@ lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
|
||||
return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
|
||||
}
|
||||
|
||||
lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
|
||||
const auto frame_id =
|
||||
GetInteger<uint64_t>(arguments, "frameId").value_or(UINT64_MAX);
|
||||
return GetLLDBFrame(frame_id);
|
||||
}
|
||||
|
||||
llvm::json::Value DAP::CreateTopLevelScopes() {
|
||||
llvm::json::Array scopes;
|
||||
scopes.emplace_back(
|
||||
@ -1602,7 +1607,7 @@ void DAP::EventThread() {
|
||||
// avoids sending paths that should be source mapped. Note that
|
||||
// CreateBreakpoint doesn't apply source mapping and certain
|
||||
// implementation ignore the source part of this event anyway.
|
||||
llvm::json::Value source_bp = CreateBreakpoint(&bp);
|
||||
llvm::json::Value source_bp = bp.ToProtocolBreakpoint();
|
||||
source_bp.getAsObject()->erase("source");
|
||||
|
||||
llvm::json::Object body;
|
||||
|
@ -275,6 +275,7 @@ struct DAP {
|
||||
lldb::SBThread GetLLDBThread(lldb::tid_t id);
|
||||
lldb::SBThread GetLLDBThread(const llvm::json::Object &arguments);
|
||||
|
||||
lldb::SBFrame GetLLDBFrame(uint64_t frame_id);
|
||||
lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments);
|
||||
|
||||
llvm::json::Value CreateTopLevelScopes();
|
||||
|
@ -8,15 +8,15 @@
|
||||
|
||||
#include "FunctionBreakpoint.h"
|
||||
#include "DAP.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "lldb/API/SBMutex.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
FunctionBreakpoint::FunctionBreakpoint(DAP &d, const llvm::json::Object &obj)
|
||||
: Breakpoint(d, obj),
|
||||
m_function_name(std::string(GetString(obj, "name").value_or(""))) {}
|
||||
FunctionBreakpoint::FunctionBreakpoint(
|
||||
DAP &d, const protocol::FunctionBreakpoint &breakpoint)
|
||||
: Breakpoint(d, breakpoint.condition, breakpoint.hitCondition),
|
||||
m_function_name(breakpoint.name) {}
|
||||
|
||||
void FunctionBreakpoint::SetBreakpoint() {
|
||||
lldb::SBMutex lock = m_dap.GetAPIMutex();
|
||||
|
@ -11,12 +11,13 @@
|
||||
|
||||
#include "Breakpoint.h"
|
||||
#include "DAPForward.h"
|
||||
#include "Protocol/ProtocolTypes.h"
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
class FunctionBreakpoint : public Breakpoint {
|
||||
public:
|
||||
FunctionBreakpoint(DAP &dap, const llvm::json::Object &obj);
|
||||
FunctionBreakpoint(DAP &dap, const protocol::FunctionBreakpoint &breakpoint);
|
||||
|
||||
/// Set this breakpoint in LLDB as a new breakpoint.
|
||||
void SetBreakpoint();
|
||||
|
@ -8,144 +8,50 @@
|
||||
|
||||
#include "DAP.h"
|
||||
#include "EventHelper.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "Protocol/ProtocolTypes.h"
|
||||
#include "RequestHandler.h"
|
||||
#include "lldb/API/SBMemoryRegionInfo.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include <optional>
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
// "DataBreakpointInfoRequest": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Request" }, {
|
||||
// "type": "object",
|
||||
// "description": "Obtains information on a possible data breakpoint that
|
||||
// could be set on an expression or variable.\nClients should only call this
|
||||
// request if the corresponding capability `supportsDataBreakpoints` is
|
||||
// true.", "properties": {
|
||||
// "command": {
|
||||
// "type": "string",
|
||||
// "enum": [ "dataBreakpointInfo" ]
|
||||
// },
|
||||
// "arguments": {
|
||||
// "$ref": "#/definitions/DataBreakpointInfoArguments"
|
||||
// }
|
||||
// },
|
||||
// "required": [ "command", "arguments" ]
|
||||
// }]
|
||||
// },
|
||||
// "DataBreakpointInfoArguments": {
|
||||
// "type": "object",
|
||||
// "description": "Arguments for `dataBreakpointInfo` request.",
|
||||
// "properties": {
|
||||
// "variablesReference": {
|
||||
// "type": "integer",
|
||||
// "description": "Reference to the variable container if the data
|
||||
// breakpoint is requested for a child of the container. The
|
||||
// `variablesReference` must have been obtained in the current suspended
|
||||
// state. See 'Lifetime of Object References' in the Overview section for
|
||||
// details."
|
||||
// },
|
||||
// "name": {
|
||||
// "type": "string",
|
||||
// "description": "The name of the variable's child to obtain data
|
||||
// breakpoint information for.\nIf `variablesReference` isn't specified,
|
||||
// this can be an expression."
|
||||
// },
|
||||
// "frameId": {
|
||||
// "type": "integer",
|
||||
// "description": "When `name` is an expression, evaluate it in the scope
|
||||
// of this stack frame. If not specified, the expression is evaluated in
|
||||
// the global scope. When `variablesReference` is specified, this property
|
||||
// has no effect."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "name" ]
|
||||
// },
|
||||
// "DataBreakpointInfoResponse": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Response" }, {
|
||||
// "type": "object",
|
||||
// "description": "Response to `dataBreakpointInfo` request.",
|
||||
// "properties": {
|
||||
// "body": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "dataId": {
|
||||
// "type": [ "string", "null" ],
|
||||
// "description": "An identifier for the data on which a data
|
||||
// breakpoint can be registered with the `setDataBreakpoints`
|
||||
// request or null if no data breakpoint is available. If a
|
||||
// `variablesReference` or `frameId` is passed, the `dataId` is
|
||||
// valid in the current suspended state, otherwise it's valid
|
||||
// indefinitely. See 'Lifetime of Object References' in the Overview
|
||||
// section for details. Breakpoints set using the `dataId` in the
|
||||
// `setDataBreakpoints` request may outlive the lifetime of the
|
||||
// associated `dataId`."
|
||||
// },
|
||||
// "description": {
|
||||
// "type": "string",
|
||||
// "description": "UI string that describes on what data the
|
||||
// breakpoint is set on or why a data breakpoint is not available."
|
||||
// },
|
||||
// "accessTypes": {
|
||||
// "type": "array",
|
||||
// "items": {
|
||||
// "$ref": "#/definitions/DataBreakpointAccessType"
|
||||
// },
|
||||
// "description": "Attribute lists the available access types for a
|
||||
// potential data breakpoint. A UI client could surface this
|
||||
// information."
|
||||
// },
|
||||
// "canPersist": {
|
||||
// "type": "boolean",
|
||||
// "description": "Attribute indicates that a potential data
|
||||
// breakpoint could be persisted across sessions."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "dataId", "description" ]
|
||||
// }
|
||||
// },
|
||||
// "required": [ "body" ]
|
||||
// }]
|
||||
// }
|
||||
void DataBreakpointInfoRequestHandler::operator()(
|
||||
const llvm::json::Object &request) const {
|
||||
llvm::json::Object response;
|
||||
FillResponse(request, response);
|
||||
llvm::json::Object body;
|
||||
lldb::SBError error;
|
||||
llvm::json::Array accessTypes{"read", "write", "readWrite"};
|
||||
const auto *arguments = request.getObject("arguments");
|
||||
const auto variablesReference =
|
||||
GetInteger<uint64_t>(arguments, "variablesReference").value_or(0);
|
||||
llvm::StringRef name = GetString(arguments, "name").value_or("");
|
||||
lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
|
||||
lldb::SBValue variable = dap.variables.FindVariable(variablesReference, name);
|
||||
/// Obtains information on a possible data breakpoint that could be set on an
|
||||
/// expression or variable. Clients should only call this request if the
|
||||
/// corresponding capability supportsDataBreakpoints is true.
|
||||
llvm::Expected<protocol::DataBreakpointInfoResponseBody>
|
||||
DataBreakpointInfoRequestHandler::Run(
|
||||
const protocol::DataBreakpointInfoArguments &args) const {
|
||||
protocol::DataBreakpointInfoResponseBody response;
|
||||
lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId.value_or(UINT64_MAX));
|
||||
lldb::SBValue variable = dap.variables.FindVariable(
|
||||
args.variablesReference.value_or(0), args.name);
|
||||
std::string addr, size;
|
||||
|
||||
bool is_data_ok = true;
|
||||
if (variable.IsValid()) {
|
||||
lldb::addr_t load_addr = variable.GetLoadAddress();
|
||||
size_t byte_size = variable.GetByteSize();
|
||||
if (load_addr == LLDB_INVALID_ADDRESS) {
|
||||
body.try_emplace("dataId", nullptr);
|
||||
body.try_emplace("description",
|
||||
"does not exist in memory, its location is " +
|
||||
std::string(variable.GetLocation()));
|
||||
is_data_ok = false;
|
||||
response.description = "does not exist in memory, its location is " +
|
||||
std::string(variable.GetLocation());
|
||||
} else if (byte_size == 0) {
|
||||
body.try_emplace("dataId", nullptr);
|
||||
body.try_emplace("description", "variable size is 0");
|
||||
is_data_ok = false;
|
||||
response.description = "variable size is 0";
|
||||
} else {
|
||||
addr = llvm::utohexstr(load_addr);
|
||||
size = llvm::utostr(byte_size);
|
||||
}
|
||||
} else if (variablesReference == 0 && frame.IsValid()) {
|
||||
lldb::SBValue value = frame.EvaluateExpression(name.data());
|
||||
} else if (args.variablesReference.value_or(0) == 0 && frame.IsValid()) {
|
||||
lldb::SBValue value = frame.EvaluateExpression(args.name.c_str());
|
||||
if (value.GetError().Fail()) {
|
||||
lldb::SBError error = value.GetError();
|
||||
const char *error_cstr = error.GetCString();
|
||||
body.try_emplace("dataId", nullptr);
|
||||
body.try_emplace("description", error_cstr && error_cstr[0]
|
||||
? std::string(error_cstr)
|
||||
: "evaluation failed");
|
||||
is_data_ok = false;
|
||||
response.description = error_cstr && error_cstr[0]
|
||||
? std::string(error_cstr)
|
||||
: "evaluation failed";
|
||||
} else {
|
||||
uint64_t load_addr = value.GetValueAsUnsigned();
|
||||
lldb::SBData data = value.GetPointeeData();
|
||||
@ -159,32 +65,31 @@ void DataBreakpointInfoRequestHandler::operator()(
|
||||
// request if SBProcess::GetMemoryRegionInfo returns error.
|
||||
if (err.Success()) {
|
||||
if (!(region.IsReadable() || region.IsWritable())) {
|
||||
body.try_emplace("dataId", nullptr);
|
||||
body.try_emplace("description",
|
||||
"memory region for address " + addr +
|
||||
" has no read or write permissions");
|
||||
is_data_ok = false;
|
||||
response.description = "memory region for address " + addr +
|
||||
" has no read or write permissions";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
body.try_emplace("dataId", nullptr);
|
||||
body.try_emplace("description",
|
||||
"unable to get byte size for expression: " +
|
||||
name.str());
|
||||
is_data_ok = false;
|
||||
response.description =
|
||||
"unable to get byte size for expression: " + args.name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
body.try_emplace("dataId", nullptr);
|
||||
body.try_emplace("description", "variable not found: " + name.str());
|
||||
is_data_ok = false;
|
||||
response.description = "variable not found: " + args.name;
|
||||
}
|
||||
|
||||
if (!body.getObject("dataId")) {
|
||||
body.try_emplace("dataId", addr + "/" + size);
|
||||
body.try_emplace("accessTypes", std::move(accessTypes));
|
||||
body.try_emplace("description",
|
||||
size + " bytes at " + addr + " " + name.str());
|
||||
if (is_data_ok) {
|
||||
response.dataId = addr + "/" + size;
|
||||
response.accessTypes = {protocol::eDataBreakpointAccessTypeRead,
|
||||
protocol::eDataBreakpointAccessTypeWrite,
|
||||
protocol::eDataBreakpointAccessTypeReadWrite};
|
||||
response.description = size + " bytes at " + addr + " " + args.name;
|
||||
}
|
||||
response.try_emplace("body", std::move(body));
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "Protocol/ProtocolBase.h"
|
||||
#include "Protocol/ProtocolRequests.h"
|
||||
#include "Protocol/ProtocolTypes.h"
|
||||
#include "lldb/API/SBError.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
@ -349,15 +348,19 @@ public:
|
||||
llvm::Error Run(const protocol::StepOutArguments &args) const override;
|
||||
};
|
||||
|
||||
class SetBreakpointsRequestHandler : public LegacyRequestHandler {
|
||||
class SetBreakpointsRequestHandler
|
||||
: public RequestHandler<
|
||||
protocol::SetBreakpointsArguments,
|
||||
llvm::Expected<protocol::SetBreakpointsResponseBody>> {
|
||||
public:
|
||||
using LegacyRequestHandler::LegacyRequestHandler;
|
||||
using RequestHandler::RequestHandler;
|
||||
static llvm::StringLiteral GetCommand() { return "setBreakpoints"; }
|
||||
FeatureSet GetSupportedFeatures() const override {
|
||||
return {protocol::eAdapterFeatureConditionalBreakpoints,
|
||||
protocol::eAdapterFeatureHitConditionalBreakpoints};
|
||||
}
|
||||
void operator()(const llvm::json::Object &request) const override;
|
||||
llvm::Expected<protocol::SetBreakpointsResponseBody>
|
||||
Run(const protocol::SetBreakpointsArguments &args) const override;
|
||||
};
|
||||
|
||||
class SetExceptionBreakpointsRequestHandler : public LegacyRequestHandler {
|
||||
@ -370,43 +373,59 @@ public:
|
||||
void operator()(const llvm::json::Object &request) const override;
|
||||
};
|
||||
|
||||
class SetFunctionBreakpointsRequestHandler : public LegacyRequestHandler {
|
||||
class SetFunctionBreakpointsRequestHandler
|
||||
: public RequestHandler<
|
||||
protocol::SetFunctionBreakpointsArguments,
|
||||
llvm::Expected<protocol::SetFunctionBreakpointsResponseBody>> {
|
||||
public:
|
||||
using LegacyRequestHandler::LegacyRequestHandler;
|
||||
using RequestHandler::RequestHandler;
|
||||
static llvm::StringLiteral GetCommand() { return "setFunctionBreakpoints"; }
|
||||
FeatureSet GetSupportedFeatures() const override {
|
||||
return {protocol::eAdapterFeatureFunctionBreakpoints};
|
||||
}
|
||||
void operator()(const llvm::json::Object &request) const override;
|
||||
llvm::Expected<protocol::SetFunctionBreakpointsResponseBody>
|
||||
Run(const protocol::SetFunctionBreakpointsArguments &args) const override;
|
||||
};
|
||||
|
||||
class DataBreakpointInfoRequestHandler : public LegacyRequestHandler {
|
||||
class DataBreakpointInfoRequestHandler
|
||||
: public RequestHandler<
|
||||
protocol::DataBreakpointInfoArguments,
|
||||
llvm::Expected<protocol::DataBreakpointInfoResponseBody>> {
|
||||
public:
|
||||
using LegacyRequestHandler::LegacyRequestHandler;
|
||||
using RequestHandler::RequestHandler;
|
||||
static llvm::StringLiteral GetCommand() { return "dataBreakpointInfo"; }
|
||||
void operator()(const llvm::json::Object &request) const override;
|
||||
llvm::Expected<protocol::DataBreakpointInfoResponseBody>
|
||||
Run(const protocol::DataBreakpointInfoArguments &args) const override;
|
||||
};
|
||||
|
||||
class SetDataBreakpointsRequestHandler : public LegacyRequestHandler {
|
||||
class SetDataBreakpointsRequestHandler
|
||||
: public RequestHandler<
|
||||
protocol::SetDataBreakpointsArguments,
|
||||
llvm::Expected<protocol::SetDataBreakpointsResponseBody>> {
|
||||
public:
|
||||
using LegacyRequestHandler::LegacyRequestHandler;
|
||||
using RequestHandler::RequestHandler;
|
||||
static llvm::StringLiteral GetCommand() { return "setDataBreakpoints"; }
|
||||
void operator()(const llvm::json::Object &request) const override;
|
||||
FeatureSet GetSupportedFeatures() const override {
|
||||
return {protocol::eAdapterFeatureDataBreakpoints};
|
||||
}
|
||||
llvm::Expected<protocol::SetDataBreakpointsResponseBody>
|
||||
Run(const protocol::SetDataBreakpointsArguments &args) const override;
|
||||
};
|
||||
|
||||
class SetInstructionBreakpointsRequestHandler : public LegacyRequestHandler {
|
||||
class SetInstructionBreakpointsRequestHandler
|
||||
: public RequestHandler<
|
||||
protocol::SetInstructionBreakpointsArguments,
|
||||
llvm::Expected<protocol::SetInstructionBreakpointsResponseBody>> {
|
||||
public:
|
||||
using LegacyRequestHandler::LegacyRequestHandler;
|
||||
using RequestHandler::RequestHandler;
|
||||
static llvm::StringLiteral GetCommand() {
|
||||
return "setInstructionBreakpoints";
|
||||
}
|
||||
void operator()(const llvm::json::Object &request) const override;
|
||||
FeatureSet GetSupportedFeatures() const override {
|
||||
return {protocol::eAdapterFeatureInstructionBreakpoints};
|
||||
}
|
||||
llvm::Expected<protocol::SetInstructionBreakpointsResponseBody>
|
||||
Run(const protocol::SetInstructionBreakpointsArguments &args) const override;
|
||||
};
|
||||
|
||||
class CompileUnitsRequestHandler : public LegacyRequestHandler {
|
||||
|
@ -9,153 +9,52 @@
|
||||
#include "DAP.h"
|
||||
#include "EventHelper.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "Protocol/ProtocolRequests.h"
|
||||
#include "RequestHandler.h"
|
||||
#include <vector>
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
// "SetBreakpointsRequest": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Request" }, {
|
||||
// "type": "object",
|
||||
// "description": "SetBreakpoints request; value of command field is
|
||||
// 'setBreakpoints'. Sets multiple breakpoints for a single source and
|
||||
// clears all previous breakpoints in that source. To clear all breakpoint
|
||||
// for a source, specify an empty array. When a breakpoint is hit, a
|
||||
// StoppedEvent (event type 'breakpoint') is generated.", "properties": {
|
||||
// "command": {
|
||||
// "type": "string",
|
||||
// "enum": [ "setBreakpoints" ]
|
||||
// },
|
||||
// "arguments": {
|
||||
// "$ref": "#/definitions/SetBreakpointsArguments"
|
||||
// }
|
||||
// },
|
||||
// "required": [ "command", "arguments" ]
|
||||
// }]
|
||||
// },
|
||||
// "SetBreakpointsArguments": {
|
||||
// "type": "object",
|
||||
// "description": "Arguments for 'setBreakpoints' request.",
|
||||
// "properties": {
|
||||
// "source": {
|
||||
// "$ref": "#/definitions/Source",
|
||||
// "description": "The source location of the breakpoints; either
|
||||
// source.path or source.reference must be specified."
|
||||
// },
|
||||
// "breakpoints": {
|
||||
// "type": "array",
|
||||
// "items": {
|
||||
// "$ref": "#/definitions/SourceBreakpoint"
|
||||
// },
|
||||
// "description": "The code locations of the breakpoints."
|
||||
// },
|
||||
// "lines": {
|
||||
// "type": "array",
|
||||
// "items": {
|
||||
// "type": "integer"
|
||||
// },
|
||||
// "description": "Deprecated: The code locations of the breakpoints."
|
||||
// },
|
||||
// "sourceModified": {
|
||||
// "type": "boolean",
|
||||
// "description": "A value of true indicates that the underlying source
|
||||
// has been modified which results in new breakpoint locations."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "source" ]
|
||||
// },
|
||||
// "SetBreakpointsResponse": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Response" }, {
|
||||
// "type": "object",
|
||||
// "description": "Response to 'setBreakpoints' request. Returned is
|
||||
// information about each breakpoint created by this request. This includes
|
||||
// the actual code location and whether the breakpoint could be verified.
|
||||
// The breakpoints returned are in the same order as the elements of the
|
||||
// 'breakpoints' (or the deprecated 'lines') in the
|
||||
// SetBreakpointsArguments.", "properties": {
|
||||
// "body": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "breakpoints": {
|
||||
// "type": "array",
|
||||
// "items": {
|
||||
// "$ref": "#/definitions/Breakpoint"
|
||||
// },
|
||||
// "description": "Information about the breakpoints. The array
|
||||
// elements are in the same order as the elements of the
|
||||
// 'breakpoints' (or the deprecated 'lines') in the
|
||||
// SetBreakpointsArguments."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "breakpoints" ]
|
||||
// }
|
||||
// },
|
||||
// "required": [ "body" ]
|
||||
// }]
|
||||
// },
|
||||
// "SourceBreakpoint": {
|
||||
// "type": "object",
|
||||
// "description": "Properties of a breakpoint or logpoint passed to the
|
||||
// setBreakpoints request.", "properties": {
|
||||
// "line": {
|
||||
// "type": "integer",
|
||||
// "description": "The source line of the breakpoint or logpoint."
|
||||
// },
|
||||
// "column": {
|
||||
// "type": "integer",
|
||||
// "description": "An optional source column of the breakpoint."
|
||||
// },
|
||||
// "condition": {
|
||||
// "type": "string",
|
||||
// "description": "An optional expression for conditional breakpoints."
|
||||
// },
|
||||
// "hitCondition": {
|
||||
// "type": "string",
|
||||
// "description": "An optional expression that controls how many hits of
|
||||
// the breakpoint are ignored. The backend is expected to interpret the
|
||||
// expression as needed."
|
||||
// },
|
||||
// "logMessage": {
|
||||
// "type": "string",
|
||||
// "description": "If this attribute exists and is non-empty, the backend
|
||||
// must not 'break' (stop) but log the message instead. Expressions within
|
||||
// {} are interpolated."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "line" ]
|
||||
// }
|
||||
void SetBreakpointsRequestHandler::operator()(
|
||||
const llvm::json::Object &request) const {
|
||||
llvm::json::Object response;
|
||||
lldb::SBError error;
|
||||
FillResponse(request, response);
|
||||
const auto *arguments = request.getObject("arguments");
|
||||
const auto *source = arguments->getObject("source");
|
||||
const auto path = GetString(source, "path").value_or("");
|
||||
const auto *breakpoints = arguments->getArray("breakpoints");
|
||||
llvm::json::Array response_breakpoints;
|
||||
/// Sets multiple breakpoints for a single source and clears all previous
|
||||
/// breakpoints in that source. To clear all breakpoint for a source, specify an
|
||||
/// empty array. When a breakpoint is hit, a `stopped` event (with reason
|
||||
/// `breakpoint`) is generated.
|
||||
llvm::Expected<protocol::SetBreakpointsResponseBody>
|
||||
SetBreakpointsRequestHandler::Run(
|
||||
const protocol::SetBreakpointsArguments &args) const {
|
||||
const auto &source = args.source;
|
||||
const auto path = source.path.value_or("");
|
||||
std::vector<protocol::Breakpoint> response_breakpoints;
|
||||
|
||||
// Decode the source breakpoint infos for this "setBreakpoints" request
|
||||
SourceBreakpointMap request_bps;
|
||||
// "breakpoints" may be unset, in which case we treat it the same as being set
|
||||
// to an empty array.
|
||||
if (breakpoints) {
|
||||
for (const auto &bp : *breakpoints) {
|
||||
const auto *bp_obj = bp.getAsObject();
|
||||
if (bp_obj) {
|
||||
SourceBreakpoint src_bp(dap, *bp_obj);
|
||||
std::pair<uint32_t, uint32_t> bp_pos(src_bp.GetLine(),
|
||||
src_bp.GetColumn());
|
||||
request_bps.try_emplace(bp_pos, src_bp);
|
||||
const auto [iv, inserted] =
|
||||
dap.source_breakpoints[path].try_emplace(bp_pos, src_bp);
|
||||
// We check if this breakpoint already exists to update it
|
||||
if (inserted)
|
||||
iv->getSecond().SetBreakpoint(path.data());
|
||||
else
|
||||
iv->getSecond().UpdateBreakpoint(src_bp);
|
||||
AppendBreakpoint(&iv->getSecond(), response_breakpoints, path,
|
||||
src_bp.GetLine());
|
||||
}
|
||||
if (args.breakpoints) {
|
||||
for (const auto &bp : *args.breakpoints) {
|
||||
SourceBreakpoint src_bp(dap, bp);
|
||||
std::pair<uint32_t, uint32_t> bp_pos(src_bp.GetLine(),
|
||||
src_bp.GetColumn());
|
||||
request_bps.try_emplace(bp_pos, src_bp);
|
||||
const auto [iv, inserted] =
|
||||
dap.source_breakpoints[path].try_emplace(bp_pos, src_bp);
|
||||
// We check if this breakpoint already exists to update it
|
||||
if (inserted)
|
||||
iv->getSecond().SetBreakpoint(path.data());
|
||||
else
|
||||
iv->getSecond().UpdateBreakpoint(src_bp);
|
||||
|
||||
protocol::Breakpoint response_bp = iv->getSecond().ToProtocolBreakpoint();
|
||||
|
||||
// Use the path from the request if it is set
|
||||
if (!path.empty())
|
||||
response_bp.source = CreateSource(path);
|
||||
|
||||
if (!response_bp.line)
|
||||
response_bp.line = src_bp.GetLine();
|
||||
if (!response_bp.column)
|
||||
response_bp.column = src_bp.GetColumn();
|
||||
response_breakpoints.push_back(response_bp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,10 +73,7 @@ void SetBreakpointsRequestHandler::operator()(
|
||||
}
|
||||
}
|
||||
|
||||
llvm::json::Object body;
|
||||
body.try_emplace("breakpoints", std::move(response_breakpoints));
|
||||
response.try_emplace("body", std::move(body));
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
return protocol::SetBreakpointsResponseBody{std::move(response_breakpoints)};
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
@ -8,90 +8,28 @@
|
||||
|
||||
#include "DAP.h"
|
||||
#include "EventHelper.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "Protocol/ProtocolRequests.h"
|
||||
#include "RequestHandler.h"
|
||||
#include "Watchpoint.h"
|
||||
#include <set>
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
// "SetDataBreakpointsRequest": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Request" }, {
|
||||
// "type": "object",
|
||||
// "description": "Replaces all existing data breakpoints with new data
|
||||
// breakpoints.\nTo clear all data breakpoints, specify an empty
|
||||
// array.\nWhen a data breakpoint is hit, a `stopped` event (with reason
|
||||
// `data breakpoint`) is generated.\nClients should only call this request
|
||||
// if the corresponding capability `supportsDataBreakpoints` is true.",
|
||||
// "properties": {
|
||||
// "command": {
|
||||
// "type": "string",
|
||||
// "enum": [ "setDataBreakpoints" ]
|
||||
// },
|
||||
// "arguments": {
|
||||
// "$ref": "#/definitions/SetDataBreakpointsArguments"
|
||||
// }
|
||||
// },
|
||||
// "required": [ "command", "arguments" ]
|
||||
// }]
|
||||
// },
|
||||
// "SetDataBreakpointsArguments": {
|
||||
// "type": "object",
|
||||
// "description": "Arguments for `setDataBreakpoints` request.",
|
||||
// "properties": {
|
||||
// "breakpoints": {
|
||||
// "type": "array",
|
||||
// "items": {
|
||||
// "$ref": "#/definitions/DataBreakpoint"
|
||||
// },
|
||||
// "description": "The contents of this array replaces all existing data
|
||||
// breakpoints. An empty array clears all data breakpoints."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "breakpoints" ]
|
||||
// },
|
||||
// "SetDataBreakpointsResponse": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Response" }, {
|
||||
// "type": "object",
|
||||
// "description": "Response to `setDataBreakpoints` request.\nReturned is
|
||||
// information about each breakpoint created by this request.",
|
||||
// "properties": {
|
||||
// "body": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "breakpoints": {
|
||||
// "type": "array",
|
||||
// "items": {
|
||||
// "$ref": "#/definitions/Breakpoint"
|
||||
// },
|
||||
// "description": "Information about the data breakpoints. The array
|
||||
// elements correspond to the elements of the input argument
|
||||
// `breakpoints` array."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "breakpoints" ]
|
||||
// }
|
||||
// },
|
||||
// "required": [ "body" ]
|
||||
// }]
|
||||
// }
|
||||
void SetDataBreakpointsRequestHandler::operator()(
|
||||
const llvm::json::Object &request) const {
|
||||
llvm::json::Object response;
|
||||
lldb::SBError error;
|
||||
FillResponse(request, response);
|
||||
const auto *arguments = request.getObject("arguments");
|
||||
const auto *breakpoints = arguments->getArray("breakpoints");
|
||||
llvm::json::Array response_breakpoints;
|
||||
/// Replaces all existing data breakpoints with new data breakpoints.
|
||||
/// To clear all data breakpoints, specify an empty array.
|
||||
/// When a data breakpoint is hit, a stopped event (with reason data breakpoint)
|
||||
/// is generated. Clients should only call this request if the corresponding
|
||||
/// capability supportsDataBreakpoints is true.
|
||||
llvm::Expected<protocol::SetDataBreakpointsResponseBody>
|
||||
SetDataBreakpointsRequestHandler::Run(
|
||||
const protocol::SetDataBreakpointsArguments &args) const {
|
||||
std::vector<protocol::Breakpoint> response_breakpoints;
|
||||
|
||||
dap.target.DeleteAllWatchpoints();
|
||||
std::vector<Watchpoint> watchpoints;
|
||||
if (breakpoints) {
|
||||
for (const auto &bp : *breakpoints) {
|
||||
const auto *bp_obj = bp.getAsObject();
|
||||
if (bp_obj)
|
||||
watchpoints.emplace_back(dap, *bp_obj);
|
||||
}
|
||||
}
|
||||
for (const auto &bp : args.breakpoints)
|
||||
watchpoints.emplace_back(dap, bp);
|
||||
|
||||
// If two watchpoints start at the same address, the latter overwrite the
|
||||
// former. So, we only enable those at first-seen addresses when iterating
|
||||
// backward.
|
||||
@ -103,12 +41,10 @@ void SetDataBreakpointsRequestHandler::operator()(
|
||||
}
|
||||
}
|
||||
for (auto wp : watchpoints)
|
||||
AppendBreakpoint(&wp, response_breakpoints);
|
||||
response_breakpoints.push_back(wp.ToProtocolBreakpoint());
|
||||
|
||||
llvm::json::Object body;
|
||||
body.try_emplace("breakpoints", std::move(response_breakpoints));
|
||||
response.try_emplace("body", std::move(body));
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
return protocol::SetDataBreakpointsResponseBody{
|
||||
std::move(response_breakpoints)};
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
@ -8,116 +8,35 @@
|
||||
|
||||
#include "DAP.h"
|
||||
#include "EventHelper.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "RequestHandler.h"
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
// "SetFunctionBreakpointsRequest": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Request" }, {
|
||||
// "type": "object",
|
||||
// "description": "SetFunctionBreakpoints request; value of command field is
|
||||
// 'setFunctionBreakpoints'. Sets multiple function breakpoints and clears
|
||||
// all previous function breakpoints. To clear all function breakpoint,
|
||||
// specify an empty array. When a function breakpoint is hit, a StoppedEvent
|
||||
// (event type 'function breakpoint') is generated.", "properties": {
|
||||
// "command": {
|
||||
// "type": "string",
|
||||
// "enum": [ "setFunctionBreakpoints" ]
|
||||
// },
|
||||
// "arguments": {
|
||||
// "$ref": "#/definitions/SetFunctionBreakpointsArguments"
|
||||
// }
|
||||
// },
|
||||
// "required": [ "command", "arguments" ]
|
||||
// }]
|
||||
// },
|
||||
// "SetFunctionBreakpointsArguments": {
|
||||
// "type": "object",
|
||||
// "description": "Arguments for 'setFunctionBreakpoints' request.",
|
||||
// "properties": {
|
||||
// "breakpoints": {
|
||||
// "type": "array",
|
||||
// "items": {
|
||||
// "$ref": "#/definitions/FunctionBreakpoint"
|
||||
// },
|
||||
// "description": "The function names of the breakpoints."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "breakpoints" ]
|
||||
// },
|
||||
// "FunctionBreakpoint": {
|
||||
// "type": "object",
|
||||
// "description": "Properties of a breakpoint passed to the
|
||||
// setFunctionBreakpoints request.", "properties": {
|
||||
// "name": {
|
||||
// "type": "string",
|
||||
// "description": "The name of the function."
|
||||
// },
|
||||
// "condition": {
|
||||
// "type": "string",
|
||||
// "description": "An optional expression for conditional breakpoints."
|
||||
// },
|
||||
// "hitCondition": {
|
||||
// "type": "string",
|
||||
// "description": "An optional expression that controls how many hits of
|
||||
// the breakpoint are ignored. The backend is expected to interpret the
|
||||
// expression as needed."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "name" ]
|
||||
// },
|
||||
// "SetFunctionBreakpointsResponse": {
|
||||
// "allOf": [ { "$ref": "#/definitions/Response" }, {
|
||||
// "type": "object",
|
||||
// "description": "Response to 'setFunctionBreakpoints' request. Returned is
|
||||
// information about each breakpoint created by this request.",
|
||||
// "properties": {
|
||||
// "body": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "breakpoints": {
|
||||
// "type": "array",
|
||||
// "items": {
|
||||
// "$ref": "#/definitions/Breakpoint"
|
||||
// },
|
||||
// "description": "Information about the breakpoints. The array
|
||||
// elements correspond to the elements of the 'breakpoints' array."
|
||||
// }
|
||||
// },
|
||||
// "required": [ "breakpoints" ]
|
||||
// }
|
||||
// },
|
||||
// "required": [ "body" ]
|
||||
// }]
|
||||
// }
|
||||
void SetFunctionBreakpointsRequestHandler::operator()(
|
||||
const llvm::json::Object &request) const {
|
||||
llvm::json::Object response;
|
||||
lldb::SBError error;
|
||||
FillResponse(request, response);
|
||||
const auto *arguments = request.getObject("arguments");
|
||||
const auto *breakpoints = arguments->getArray("breakpoints");
|
||||
llvm::json::Array response_breakpoints;
|
||||
/// Replaces all existing function breakpoints with new function breakpoints.
|
||||
/// To clear all function breakpoints, specify an empty array.
|
||||
/// When a function breakpoint is hit, a stopped event (with reason function
|
||||
/// breakpoint) is generated. Clients should only call this request if the
|
||||
/// corresponding capability supportsFunctionBreakpoints is true.
|
||||
llvm::Expected<protocol::SetFunctionBreakpointsResponseBody>
|
||||
SetFunctionBreakpointsRequestHandler::Run(
|
||||
const protocol::SetFunctionBreakpointsArguments &args) const {
|
||||
std::vector<protocol::Breakpoint> response_breakpoints;
|
||||
|
||||
// Disable any function breakpoints that aren't in this request.
|
||||
// There is no call to remove function breakpoints other than calling this
|
||||
// function with a smaller or empty "breakpoints" list.
|
||||
const auto name_iter = dap.function_breakpoints.keys();
|
||||
llvm::DenseSet<llvm::StringRef> seen(name_iter.begin(), name_iter.end());
|
||||
for (const auto &value : *breakpoints) {
|
||||
const auto *bp_obj = value.getAsObject();
|
||||
if (!bp_obj)
|
||||
continue;
|
||||
FunctionBreakpoint fn_bp(dap, *bp_obj);
|
||||
const auto [it, inserted] = dap.function_breakpoints.try_emplace(
|
||||
fn_bp.GetFunctionName(), dap, *bp_obj);
|
||||
for (const auto &fb : args.breakpoints) {
|
||||
FunctionBreakpoint fn_bp(dap, fb);
|
||||
const auto [it, inserted] =
|
||||
dap.function_breakpoints.try_emplace(fn_bp.GetFunctionName(), dap, fb);
|
||||
if (inserted)
|
||||
it->second.SetBreakpoint();
|
||||
else
|
||||
it->second.UpdateBreakpoint(fn_bp);
|
||||
|
||||
AppendBreakpoint(&it->second, response_breakpoints);
|
||||
response_breakpoints.push_back(it->second.ToProtocolBreakpoint());
|
||||
seen.erase(fn_bp.GetFunctionName());
|
||||
}
|
||||
|
||||
@ -130,10 +49,8 @@ void SetFunctionBreakpointsRequestHandler::operator()(
|
||||
dap.function_breakpoints.erase(name);
|
||||
}
|
||||
|
||||
llvm::json::Object body;
|
||||
body.try_emplace("breakpoints", std::move(response_breakpoints));
|
||||
response.try_emplace("body", std::move(body));
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
return protocol::SetFunctionBreakpointsResponseBody{
|
||||
std::move(response_breakpoints)};
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
@ -8,207 +8,20 @@
|
||||
|
||||
#include "DAP.h"
|
||||
#include "EventHelper.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "RequestHandler.h"
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
// "SetInstructionBreakpointsRequest": {
|
||||
// "allOf": [
|
||||
// {"$ref": "#/definitions/Request"},
|
||||
// {
|
||||
// "type": "object",
|
||||
// "description" :
|
||||
// "Replaces all existing instruction breakpoints. Typically, "
|
||||
// "instruction breakpoints would be set from a disassembly window. "
|
||||
// "\nTo clear all instruction breakpoints, specify an empty "
|
||||
// "array.\nWhen an instruction breakpoint is hit, a `stopped` event "
|
||||
// "(with reason `instruction breakpoint`) is generated.\nClients "
|
||||
// "should only call this request if the corresponding capability "
|
||||
// "`supportsInstructionBreakpoints` is true.",
|
||||
// "properties": {
|
||||
// "command": { "type": "string", "enum": ["setInstructionBreakpoints"]
|
||||
// }, "arguments": {"$ref":
|
||||
// "#/definitions/SetInstructionBreakpointsArguments"}
|
||||
// },
|
||||
// "required": [ "command", "arguments" ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// "SetInstructionBreakpointsArguments": {
|
||||
// "type": "object",
|
||||
// "description": "Arguments for `setInstructionBreakpoints` request",
|
||||
// "properties": {
|
||||
// "breakpoints": {
|
||||
// "type": "array",
|
||||
// "items": {"$ref": "#/definitions/InstructionBreakpoint"},
|
||||
// "description": "The instruction references of the breakpoints"
|
||||
// }
|
||||
// },
|
||||
// "required": ["breakpoints"]
|
||||
// },
|
||||
// "SetInstructionBreakpointsResponse": {
|
||||
// "allOf": [
|
||||
// {"$ref": "#/definitions/Response"},
|
||||
// {
|
||||
// "type": "object",
|
||||
// "description": "Response to `setInstructionBreakpoints` request",
|
||||
// "properties": {
|
||||
// "body": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "breakpoints": {
|
||||
// "type": "array",
|
||||
// "items": {"$ref": "#/definitions/Breakpoint"},
|
||||
// "description":
|
||||
// "Information about the breakpoints. The array elements
|
||||
// " "correspond to the elements of the `breakpoints`
|
||||
// array."
|
||||
// }
|
||||
// },
|
||||
// "required": ["breakpoints"]
|
||||
// }
|
||||
// },
|
||||
// "required": ["body"]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// "InstructionBreakpoint": {
|
||||
// "type": "object",
|
||||
// "description": "Properties of a breakpoint passed to the "
|
||||
// "`setInstructionBreakpoints` request",
|
||||
// "properties": {
|
||||
// "instructionReference": {
|
||||
// "type": "string",
|
||||
// "description" :
|
||||
// "The instruction reference of the breakpoint.\nThis should be a "
|
||||
// "memory or instruction pointer reference from an
|
||||
// `EvaluateResponse`, "
|
||||
// "`Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`."
|
||||
// },
|
||||
// "offset": {
|
||||
// "type": "integer",
|
||||
// "description": "The offset from the instruction reference in "
|
||||
// "bytes.\nThis can be negative."
|
||||
// },
|
||||
// "condition": {
|
||||
// "type": "string",
|
||||
// "description": "An expression for conditional breakpoints.\nIt is only
|
||||
// "
|
||||
// "honored by a debug adapter if the corresponding "
|
||||
// "capability `supportsConditionalBreakpoints` is true."
|
||||
// },
|
||||
// "hitCondition": {
|
||||
// "type": "string",
|
||||
// "description": "An expression that controls how many hits of the "
|
||||
// "breakpoint are ignored.\nThe debug adapter is expected
|
||||
// " "to interpret the expression as needed.\nThe
|
||||
// attribute " "is only honored by a debug adapter if the
|
||||
// corresponding " "capability
|
||||
// `supportsHitConditionalBreakpoints` is true."
|
||||
// },
|
||||
// "mode": {
|
||||
// "type": "string",
|
||||
// "description": "The mode of this breakpoint. If defined, this must be
|
||||
// "
|
||||
// "one of the `breakpointModes` the debug adapter "
|
||||
// "advertised in its `Capabilities`."
|
||||
// }
|
||||
// },
|
||||
// "required": ["instructionReference"]
|
||||
// },
|
||||
// "Breakpoint": {
|
||||
// "type": "object",
|
||||
// "description" :
|
||||
// "Information about a breakpoint created in `setBreakpoints`, "
|
||||
// "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or "
|
||||
// "`setDataBreakpoints` requests.",
|
||||
// "properties": {
|
||||
// "id": {
|
||||
// "type": "integer",
|
||||
// "description" :
|
||||
// "The identifier for the breakpoint. It is needed if breakpoint
|
||||
// " "events are used to update or remove breakpoints."
|
||||
// },
|
||||
// "verified": {
|
||||
// "type": "boolean",
|
||||
// "description": "If true, the breakpoint could be set (but not "
|
||||
// "necessarily at the desired location)."
|
||||
// },
|
||||
// "message": {
|
||||
// "type": "string",
|
||||
// "description": "A message about the state of the breakpoint.\nThis
|
||||
// "
|
||||
// "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" :
|
||||
// "Start position of the source range covered by the breakpoint.
|
||||
// " "It is measured in UTF-16 code units and the client
|
||||
// capability "
|
||||
// "`columnsStartAt1` determines whether it is 0- or 1-based."
|
||||
// },
|
||||
// "endLine": {
|
||||
// "type": "integer",
|
||||
// "description" :
|
||||
// "The end line of the actual range covered by the breakpoint."
|
||||
// },
|
||||
// "endColumn": {
|
||||
// "type": "integer",
|
||||
// "description" :
|
||||
// "End position of the source range covered by the breakpoint. It
|
||||
// " "is measured in UTF-16 code units and the client capability "
|
||||
// "`columnsStartAt1` determines whether it is 0- or 1-based.\nIf
|
||||
// " "no end line is given, then the end column is assumed to be
|
||||
// in " "the start line."
|
||||
// },
|
||||
// "instructionReference": {
|
||||
// "type": "string",
|
||||
// "description": "A memory reference to where the breakpoint is
|
||||
// set."
|
||||
// },
|
||||
// "offset": {
|
||||
// "type": "integer",
|
||||
// "description": "The offset from the instruction reference.\nThis "
|
||||
// "can be negative."
|
||||
// },
|
||||
// "reason": {
|
||||
// "type": "string",
|
||||
// "description" :
|
||||
// "A machine-readable explanation of why a breakpoint may not be
|
||||
// " "verified. If a breakpoint is verified or a specific reason
|
||||
// is " "not known, the adapter should omit this property.
|
||||
// Possible " "values include:\n\n- `pending`: Indicates a
|
||||
// breakpoint might be " "verified in the future, but the adapter
|
||||
// cannot verify it in the " "current state.\n - `failed`:
|
||||
// Indicates a breakpoint was not " "able to be verified, and the
|
||||
// adapter does not believe it can be " "verified without
|
||||
// intervention.",
|
||||
// "enum": [ "pending", "failed" ]
|
||||
// }
|
||||
// },
|
||||
// "required": ["verified"]
|
||||
// },
|
||||
void SetInstructionBreakpointsRequestHandler::operator()(
|
||||
const llvm::json::Object &request) const {
|
||||
llvm::json::Object response;
|
||||
llvm::json::Array response_breakpoints;
|
||||
llvm::json::Object body;
|
||||
FillResponse(request, response);
|
||||
|
||||
const auto *arguments = request.getObject("arguments");
|
||||
const auto *breakpoints = arguments->getArray("breakpoints");
|
||||
/// Replaces all existing instruction breakpoints. Typically, instruction
|
||||
/// breakpoints would be set from a disassembly window. To clear all instruction
|
||||
/// breakpoints, specify an empty array. When an instruction breakpoint is hit,
|
||||
/// a stopped event (with reason instruction breakpoint) is generated. Clients
|
||||
/// should only call this request if the corresponding capability
|
||||
/// supportsInstructionBreakpoints is true.
|
||||
llvm::Expected<protocol::SetInstructionBreakpointsResponseBody>
|
||||
SetInstructionBreakpointsRequestHandler::Run(
|
||||
const protocol::SetInstructionBreakpointsArguments &args) const {
|
||||
std::vector<protocol::Breakpoint> response_breakpoints;
|
||||
|
||||
// Disable any instruction breakpoints that aren't in this request.
|
||||
// There is no call to remove instruction breakpoints other than calling this
|
||||
@ -216,19 +29,16 @@ void SetInstructionBreakpointsRequestHandler::operator()(
|
||||
llvm::DenseSet<lldb::addr_t> seen(
|
||||
llvm::from_range, llvm::make_first_range(dap.instruction_breakpoints));
|
||||
|
||||
for (const auto &bp : *breakpoints) {
|
||||
const auto *bp_obj = bp.getAsObject();
|
||||
if (!bp_obj)
|
||||
continue;
|
||||
for (const auto &bp : args.breakpoints) {
|
||||
// Read instruction breakpoint request.
|
||||
InstructionBreakpoint inst_bp(dap, *bp_obj);
|
||||
InstructionBreakpoint inst_bp(dap, bp);
|
||||
const auto [iv, inserted] = dap.instruction_breakpoints.try_emplace(
|
||||
inst_bp.GetInstructionAddressReference(), dap, *bp_obj);
|
||||
inst_bp.GetInstructionAddressReference(), dap, bp);
|
||||
if (inserted)
|
||||
iv->second.SetBreakpoint();
|
||||
else
|
||||
iv->second.UpdateBreakpoint(inst_bp);
|
||||
AppendBreakpoint(&iv->second, response_breakpoints);
|
||||
response_breakpoints.push_back(iv->second.ToProtocolBreakpoint());
|
||||
seen.erase(inst_bp.GetInstructionAddressReference());
|
||||
}
|
||||
|
||||
@ -240,9 +50,8 @@ void SetInstructionBreakpointsRequestHandler::operator()(
|
||||
dap.instruction_breakpoints.erase(addr);
|
||||
}
|
||||
|
||||
body.try_emplace("breakpoints", std::move(response_breakpoints));
|
||||
response.try_emplace("body", std::move(body));
|
||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
return protocol::SetInstructionBreakpointsResponseBody{
|
||||
std::move(response_breakpoints)};
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
@ -20,7 +20,7 @@ void TestGetTargetBreakpointsRequestHandler::operator()(
|
||||
llvm::json::Array response_breakpoints;
|
||||
for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
|
||||
auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i));
|
||||
AppendBreakpoint(&bp, response_breakpoints);
|
||||
response_breakpoints.push_back(bp.ToProtocolBreakpoint());
|
||||
}
|
||||
llvm::json::Object body;
|
||||
body.try_emplace("breakpoints", std::move(response_breakpoints));
|
||||
|
@ -9,20 +9,19 @@
|
||||
|
||||
#include "InstructionBreakpoint.h"
|
||||
#include "DAP.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "lldb/API/SBBreakpoint.h"
|
||||
#include "lldb/API/SBTarget.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
InstructionBreakpoint::InstructionBreakpoint(DAP &d,
|
||||
const llvm::json::Object &obj)
|
||||
: Breakpoint(d, obj), m_instruction_address_reference(LLDB_INVALID_ADDRESS),
|
||||
m_offset(GetInteger<int64_t>(obj, "offset").value_or(0)) {
|
||||
GetString(obj, "instructionReference")
|
||||
.value_or("")
|
||||
.getAsInteger(0, m_instruction_address_reference);
|
||||
InstructionBreakpoint::InstructionBreakpoint(
|
||||
DAP &d, const protocol::InstructionBreakpoint &breakpoint)
|
||||
: Breakpoint(d, breakpoint.condition, breakpoint.hitCondition),
|
||||
m_instruction_address_reference(LLDB_INVALID_ADDRESS),
|
||||
m_offset(breakpoint.offset.value_or(0)) {
|
||||
llvm::StringRef instruction_reference(breakpoint.instructionReference);
|
||||
instruction_reference.getAsInteger(0, m_instruction_address_reference);
|
||||
m_instruction_address_reference += m_offset;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "Breakpoint.h"
|
||||
#include "DAPForward.h"
|
||||
#include "Protocol/ProtocolTypes.h"
|
||||
#include "lldb/lldb-types.h"
|
||||
#include <cstdint>
|
||||
|
||||
@ -20,7 +21,8 @@ namespace lldb_dap {
|
||||
/// Instruction Breakpoint
|
||||
class InstructionBreakpoint : public Breakpoint {
|
||||
public:
|
||||
InstructionBreakpoint(DAP &d, const llvm::json::Object &obj);
|
||||
InstructionBreakpoint(DAP &d,
|
||||
const protocol::InstructionBreakpoint &breakpoint);
|
||||
|
||||
/// Set instruction breakpoint in LLDB as a new breakpoint.
|
||||
void SetBreakpoint();
|
||||
|
@ -7,7 +7,6 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "JSONUtils.h"
|
||||
#include "BreakpointBase.h"
|
||||
#include "DAP.h"
|
||||
#include "ExceptionBreakpoint.h"
|
||||
#include "LLDBUtils.h"
|
||||
@ -354,70 +353,6 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
|
||||
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(BreakpointBase *bp,
|
||||
std::optional<llvm::StringRef> request_path,
|
||||
std::optional<uint32_t> request_line,
|
||||
std::optional<uint32_t> request_column) {
|
||||
llvm::json::Object object;
|
||||
if (request_path)
|
||||
object.try_emplace("source", CreateSource(*request_path));
|
||||
bp->CreateJsonObject(object);
|
||||
// We try to add request_line as a fallback
|
||||
if (request_line)
|
||||
object.try_emplace("line", *request_line);
|
||||
if (request_column)
|
||||
object.try_emplace("column", *request_column);
|
||||
return llvm::json::Value(std::move(object));
|
||||
}
|
||||
|
||||
static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
|
||||
uint64_t debug_info_size = 0;
|
||||
llvm::StringRef section_name(section.GetName());
|
||||
@ -506,12 +441,6 @@ llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) {
|
||||
return llvm::json::Value(std::move(object));
|
||||
}
|
||||
|
||||
void AppendBreakpoint(BreakpointBase *bp, llvm::json::Array &breakpoints,
|
||||
std::optional<llvm::StringRef> request_path,
|
||||
std::optional<uint32_t> request_line) {
|
||||
breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
|
||||
}
|
||||
|
||||
// "Event": {
|
||||
// "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
|
||||
// "type": "object",
|
||||
@ -567,96 +496,30 @@ CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
// "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(const lldb::SBFileSpec &file) {
|
||||
llvm::json::Object object;
|
||||
protocol::Source CreateSource(const lldb::SBFileSpec &file) {
|
||||
protocol::Source source;
|
||||
if (file.IsValid()) {
|
||||
const char *name = file.GetFilename();
|
||||
if (name)
|
||||
EmplaceSafeString(object, "name", name);
|
||||
source.name = name;
|
||||
char path[PATH_MAX] = "";
|
||||
if (file.GetPath(path, sizeof(path)) &&
|
||||
lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
|
||||
EmplaceSafeString(object, "path", std::string(path));
|
||||
}
|
||||
lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX))
|
||||
source.path = path;
|
||||
}
|
||||
return llvm::json::Value(std::move(object));
|
||||
return source;
|
||||
}
|
||||
|
||||
llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry) {
|
||||
protocol::Source CreateSource(const lldb::SBLineEntry &line_entry) {
|
||||
return CreateSource(line_entry.GetFileSpec());
|
||||
}
|
||||
|
||||
llvm::json::Value CreateSource(llvm::StringRef source_path) {
|
||||
llvm::json::Object source;
|
||||
protocol::Source CreateSource(llvm::StringRef source_path) {
|
||||
protocol::Source source;
|
||||
llvm::StringRef name = llvm::sys::path::filename(source_path);
|
||||
EmplaceSafeString(source, "name", name);
|
||||
EmplaceSafeString(source, "path", source_path);
|
||||
return llvm::json::Value(std::move(source));
|
||||
source.name = name;
|
||||
source.path = source_path;
|
||||
return source;
|
||||
}
|
||||
|
||||
bool ShouldDisplayAssemblySource(
|
||||
|
@ -198,67 +198,6 @@ GetStringMap(const llvm::json::Object &obj, llvm::StringRef key);
|
||||
void FillResponse(const llvm::json::Object &request,
|
||||
llvm::json::Object &response);
|
||||
|
||||
/// Converts \a bp to a JSON value and appends the first valid location to the
|
||||
/// \a breakpoints array.
|
||||
///
|
||||
/// \param[in] bp
|
||||
/// A LLDB breakpoint object which will get the first valid location
|
||||
/// extracted and converted into a JSON object in the \a breakpoints array
|
||||
///
|
||||
/// \param[in] breakpoints
|
||||
/// A JSON array that will get a llvm::json::Value for \a bp
|
||||
/// appended to it.
|
||||
///
|
||||
/// \param[in] request_path
|
||||
/// An optional source path to use when creating the "Source" object of this
|
||||
/// breakpoint. If not specified, the "Source" object is created from the
|
||||
/// breakpoint's address' LineEntry. It is useful to ensure the same source
|
||||
/// paths provided by the setBreakpoints request are returned to the IDE.
|
||||
///
|
||||
/// \param[in] request_line
|
||||
/// An optional line to use when creating the "Breakpoint" object to append.
|
||||
/// It is used if the breakpoint has no valid locations.
|
||||
/// It is useful to ensure the same line
|
||||
/// provided by the setBreakpoints request are returned to the IDE as a
|
||||
/// fallback.
|
||||
void AppendBreakpoint(
|
||||
BreakpointBase *bp, llvm::json::Array &breakpoints,
|
||||
std::optional<llvm::StringRef> request_path = std::nullopt,
|
||||
std::optional<uint32_t> request_line = std::nullopt);
|
||||
|
||||
/// Converts breakpoint location to a debug adapter protocol "Breakpoint".
|
||||
///
|
||||
/// \param[in] bp
|
||||
/// A LLDB breakpoint object to convert into a JSON value
|
||||
///
|
||||
/// \param[in] request_path
|
||||
/// An optional source path to use when creating the "Source" object of this
|
||||
/// breakpoint. If not specified, the "Source" object is created from the
|
||||
/// breakpoint's address' LineEntry. It is useful to ensure the same source
|
||||
/// paths provided by the setBreakpoints request are returned to the IDE.
|
||||
///
|
||||
/// \param[in] request_line
|
||||
/// An optional line to use when creating the resulting "Breakpoint" object.
|
||||
/// It is used if the breakpoint has no valid locations.
|
||||
/// It is useful to ensure the same line
|
||||
/// provided by the setBreakpoints request are returned to the IDE as a
|
||||
/// fallback.
|
||||
///
|
||||
/// \param[in] request_column
|
||||
/// An optional column to use when creating the resulting "Breakpoint"
|
||||
/// object. It is used if the breakpoint has no valid locations. It is
|
||||
/// useful to ensure the same column provided by the setBreakpoints request
|
||||
/// are returned to the IDE as a fallback.
|
||||
///
|
||||
/// \return
|
||||
/// A "Breakpoint" JSON object with that follows the formal JSON
|
||||
/// definition outlined by Microsoft.
|
||||
llvm::json::Value
|
||||
CreateBreakpoint(BreakpointBase *bp,
|
||||
std::optional<llvm::StringRef> request_path = std::nullopt,
|
||||
std::optional<uint32_t> request_line = std::nullopt,
|
||||
std::optional<uint32_t> request_column = std::nullopt);
|
||||
|
||||
/// Converts a LLDB module to a VS Code DAP module for use in "modules" events.
|
||||
///
|
||||
/// \param[in] target
|
||||
@ -323,7 +262,7 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
|
||||
/// \return
|
||||
/// A "Source" JSON object that follows the formal JSON
|
||||
/// definition outlined by Microsoft.
|
||||
llvm::json::Value CreateSource(const lldb::SBFileSpec &file);
|
||||
protocol::Source CreateSource(const lldb::SBFileSpec &file);
|
||||
|
||||
/// Create a "Source" JSON object as described in the debug adapter definition.
|
||||
///
|
||||
@ -334,7 +273,7 @@ llvm::json::Value CreateSource(const lldb::SBFileSpec &file);
|
||||
/// \return
|
||||
/// A "Source" JSON object that follows the formal JSON
|
||||
/// definition outlined by Microsoft.
|
||||
llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry);
|
||||
protocol::Source CreateSource(const lldb::SBLineEntry &line_entry);
|
||||
|
||||
/// Create a "Source" object for a given source path.
|
||||
///
|
||||
@ -344,7 +283,7 @@ llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry);
|
||||
/// \return
|
||||
/// A "Source" JSON object that follows the formal JSON
|
||||
/// definition outlined by Microsoft.
|
||||
llvm::json::Value CreateSource(llvm::StringRef source_path);
|
||||
protocol::Source CreateSource(llvm::StringRef source_path);
|
||||
|
||||
/// Return true if the given line entry should be displayed as assembly.
|
||||
///
|
||||
|
@ -376,4 +376,74 @@ bool fromJSON(const llvm::json::Value &Params, StepOutArguments &SOA,
|
||||
OM.mapOptional("granularity", SOA.granularity);
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, SetBreakpointsArguments &SBA,
|
||||
llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("source", SBA.source) &&
|
||||
O.map("breakpoints", SBA.breakpoints) && O.map("lines", SBA.lines) &&
|
||||
O.map("sourceModified", SBA.sourceModified);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const SetBreakpointsResponseBody &SBR) {
|
||||
json::Object result;
|
||||
result["breakpoints"] = SBR.breakpoints;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params,
|
||||
SetFunctionBreakpointsArguments &SFBA, llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("breakpoints", SFBA.breakpoints);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const SetFunctionBreakpointsResponseBody &SFBR) {
|
||||
json::Object result;
|
||||
result["breakpoints"] = SFBR.breakpoints;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params,
|
||||
SetInstructionBreakpointsArguments &SIBA, llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("breakpoints", SIBA.breakpoints);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &SIBR) {
|
||||
json::Object result;
|
||||
result["breakpoints"] = SIBR.breakpoints;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params,
|
||||
DataBreakpointInfoArguments &DBIA, llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("variablesReference", DBIA.variablesReference) &&
|
||||
O.map("name", DBIA.name) && O.map("frameId", DBIA.frameId) &&
|
||||
O.map("bytes", DBIA.bytes) && O.map("asAddress", DBIA.asAddress) &&
|
||||
O.map("mode", DBIA.mode);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const DataBreakpointInfoResponseBody &DBIRB) {
|
||||
json::Object result;
|
||||
result["dataId"] = DBIRB.dataId ? *DBIRB.dataId : llvm::json::Value(nullptr);
|
||||
result["description"] = DBIRB.description;
|
||||
if (DBIRB.accessTypes)
|
||||
result["accessTypes"] = *DBIRB.accessTypes;
|
||||
if (DBIRB.canPersist)
|
||||
result["canPersist"] = *DBIRB.canPersist;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params,
|
||||
SetDataBreakpointsArguments &SDBA, llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("breakpoints", SDBA.breakpoints);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &SDBR) {
|
||||
json::Object result;
|
||||
result["breakpoints"] = SDBR.breakpoints;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace lldb_dap::protocol
|
||||
|
@ -559,6 +559,153 @@ struct BreakpointLocationsResponseBody {
|
||||
};
|
||||
llvm::json::Value toJSON(const BreakpointLocationsResponseBody &);
|
||||
|
||||
/// Arguments for `setBreakpoints` request.
|
||||
struct SetBreakpointsArguments {
|
||||
/// The source location of the breakpoints; either `source.path` or
|
||||
/// `source.sourceReference` must be specified.
|
||||
Source source;
|
||||
|
||||
/// The code locations of the breakpoints.
|
||||
std::optional<std::vector<SourceBreakpoint>> breakpoints;
|
||||
|
||||
/// Deprecated: The code locations of the breakpoints.
|
||||
std::optional<std::vector<uint32_t>> lines;
|
||||
|
||||
/// A value of true indicates that the underlying source has been modified
|
||||
/// which results in new breakpoint locations.
|
||||
std::optional<bool> sourceModified;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, SetBreakpointsArguments &,
|
||||
llvm::json::Path);
|
||||
|
||||
/// Response to `setBreakpoints` request.
|
||||
/// Returned is information about each breakpoint created by this request.
|
||||
/// This includes the actual code location and whether the breakpoint could be
|
||||
/// verified. The breakpoints returned are in the same order as the elements of
|
||||
/// the breakpoints (or the deprecated lines) array in the arguments.
|
||||
struct SetBreakpointsResponseBody {
|
||||
/// Information about the breakpoints.
|
||||
/// The array elements are in the same order as the elements of the
|
||||
/// `breakpoints` (or the deprecated `lines`) array in the arguments.
|
||||
std::vector<Breakpoint> breakpoints;
|
||||
};
|
||||
llvm::json::Value toJSON(const SetBreakpointsResponseBody &);
|
||||
|
||||
/// Arguments for `setFunctionBreakpoints` request.
|
||||
struct SetFunctionBreakpointsArguments {
|
||||
/// The function names of the breakpoints.
|
||||
std::vector<FunctionBreakpoint> breakpoints;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, SetFunctionBreakpointsArguments &,
|
||||
llvm::json::Path);
|
||||
|
||||
/// Response to `setFunctionBreakpoints` request.
|
||||
/// Returned is information about each breakpoint created by this request.
|
||||
struct SetFunctionBreakpointsResponseBody {
|
||||
/// Information about the breakpoints. The array elements correspond to the
|
||||
/// elements of the `breakpoints` array.
|
||||
std::vector<Breakpoint> breakpoints;
|
||||
};
|
||||
llvm::json::Value toJSON(const SetFunctionBreakpointsResponseBody &);
|
||||
|
||||
/// Arguments for `setInstructionBreakpoints` request.
|
||||
struct SetInstructionBreakpointsArguments {
|
||||
/// The instruction references of the breakpoints.
|
||||
std::vector<InstructionBreakpoint> breakpoints;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, SetInstructionBreakpointsArguments &,
|
||||
llvm::json::Path);
|
||||
|
||||
/// Response to `setInstructionBreakpoints` request.
|
||||
struct SetInstructionBreakpointsResponseBody {
|
||||
/// Information about the breakpoints. The array elements correspond to the
|
||||
/// elements of the `breakpoints` array.
|
||||
std::vector<Breakpoint> breakpoints;
|
||||
};
|
||||
llvm::json::Value toJSON(const SetInstructionBreakpointsResponseBody &);
|
||||
|
||||
/// Arguments for `dataBreakpointInfo` request.
|
||||
struct DataBreakpointInfoArguments {
|
||||
/// Reference to the variable container if the data breakpoint is requested
|
||||
/// for a child of the container. The `variablesReference` must have been
|
||||
/// obtained in the current suspended state.See 'Lifetime of Object
|
||||
/// References' in the Overview section for details.
|
||||
std::optional<int64_t> variablesReference;
|
||||
|
||||
/// The name of the variable's child to obtain data breakpoint information
|
||||
/// for. If `variablesReference` isn't specified, this can be an expression,
|
||||
/// or an address if `asAddress` is also true.
|
||||
std::string name;
|
||||
|
||||
/// When `name` is an expression, evaluate it in the scope of this stack
|
||||
/// frame. If not specified, the expression is evaluated in the global scope.
|
||||
/// When `asAddress` is true, the `frameId` is ignored.
|
||||
std::optional<uint64_t> frameId;
|
||||
|
||||
/// If specified, a debug adapter should return information for the range of
|
||||
/// memory extending `bytes` number of bytes from the address or variable
|
||||
/// specified by `name`. Breakpoints set using the resulting data ID should
|
||||
/// pause on data access anywhere within that range.
|
||||
/// Clients may set this property only if the `supportsDataBreakpointBytes`
|
||||
/// capability is true.
|
||||
std::optional<int64_t> bytes;
|
||||
|
||||
/// If `true`, the `name` is a memory address and the debugger should
|
||||
/// interpret it as a decimal value, or hex value if it is prefixed with `0x`.
|
||||
/// Clients may set this property only if the `supportsDataBreakpointBytes`
|
||||
/// capability is true.
|
||||
std::optional<bool> asAddress;
|
||||
|
||||
/// The mode of the desired breakpoint. If defined, this must be one of the
|
||||
/// `breakpointModes` the debug adapter advertised in its `Capabilities`.
|
||||
std::optional<std::string> mode;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, DataBreakpointInfoArguments &,
|
||||
llvm::json::Path);
|
||||
|
||||
/// Response to `dataBreakpointInfo` request.
|
||||
struct DataBreakpointInfoResponseBody {
|
||||
/// An identifier for the data on which a data breakpoint can be registered
|
||||
/// with the `setDataBreakpoints` request or null if no data breakpoint is
|
||||
/// available. If a `variablesReference` or `frameId` is passed, the `dataId`
|
||||
/// is valid in the current suspended state, otherwise it's valid
|
||||
/// indefinitely. See 'Lifetime of Object References' in the Overview section
|
||||
/// for details. Breakpoints set using the `dataId` in the
|
||||
/// `setDataBreakpoints` request may outlive the lifetime of the associated
|
||||
/// `dataId`.
|
||||
std::optional<std::string> dataId;
|
||||
|
||||
/// UI string that describes on what data the breakpoint is set on or why a
|
||||
/// data breakpoint is not available.
|
||||
std::string description;
|
||||
|
||||
/// Attribute lists the available access types for a potential data
|
||||
/// breakpoint. A UI client could surface this information.
|
||||
std::optional<std::vector<DataBreakpointAccessType>> accessTypes;
|
||||
|
||||
/// Attribute indicates that a potential data breakpoint could be persisted
|
||||
/// across sessions.
|
||||
std::optional<bool> canPersist;
|
||||
};
|
||||
llvm::json::Value toJSON(const DataBreakpointInfoResponseBody &);
|
||||
|
||||
/// Arguments for `setDataBreakpoints` request.
|
||||
struct SetDataBreakpointsArguments {
|
||||
/// The contents of this array replaces all existing data breakpoints. An
|
||||
/// empty array clears all data breakpoints.
|
||||
std::vector<DataBreakpointInfo> breakpoints;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, SetDataBreakpointsArguments &,
|
||||
llvm::json::Path);
|
||||
|
||||
/// Response to `setDataBreakpoints` request.
|
||||
struct SetDataBreakpointsResponseBody {
|
||||
/// Information about the data breakpoints. The array elements correspond to
|
||||
/// the elements of the input argument `breakpoints` array.
|
||||
std::vector<Breakpoint> breakpoints;
|
||||
};
|
||||
llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &);
|
||||
|
||||
} // namespace lldb_dap::protocol
|
||||
|
||||
#endif
|
||||
|
@ -43,6 +43,32 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
|
||||
O.map("sourceReference", S.sourceReference);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(PresentationHint hint) {
|
||||
switch (hint) {
|
||||
case ePresentationHintNormal:
|
||||
return "normal";
|
||||
case ePresentationHintEmphasize:
|
||||
return "emphasize";
|
||||
case ePresentationHintDeemphasize:
|
||||
return "deemphasize";
|
||||
}
|
||||
llvm_unreachable("unhandled presentation hint.");
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const Source &S) {
|
||||
json::Object result;
|
||||
if (S.name)
|
||||
result.insert({"name", *S.name});
|
||||
if (S.path)
|
||||
result.insert({"path", *S.path});
|
||||
if (S.sourceReference)
|
||||
result.insert({"sourceReference", *S.sourceReference});
|
||||
if (S.presentationHint)
|
||||
result.insert({"presentationHint", *S.presentationHint});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
json::Value toJSON(const ExceptionBreakpointsFilter &EBF) {
|
||||
json::Object result{{"filter", EBF.filter}, {"label", EBF.label}};
|
||||
|
||||
@ -274,4 +300,108 @@ json::Value toJSON(const BreakpointLocation &B) {
|
||||
return result;
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const BreakpointReason &BR) {
|
||||
switch (BR) {
|
||||
case BreakpointReason::eBreakpointReasonPending:
|
||||
return "pending";
|
||||
case BreakpointReason::eBreakpointReasonFailed:
|
||||
return "failed";
|
||||
}
|
||||
llvm_unreachable("unhandled breakpoint reason.");
|
||||
}
|
||||
|
||||
json::Value toJSON(const Breakpoint &BP) {
|
||||
json::Object result{{"verified", BP.verified}};
|
||||
|
||||
if (BP.id)
|
||||
result.insert({"id", *BP.id});
|
||||
if (BP.message)
|
||||
result.insert({"message", *BP.message});
|
||||
if (BP.source)
|
||||
result.insert({"source", *BP.source});
|
||||
if (BP.line)
|
||||
result.insert({"line", *BP.line});
|
||||
if (BP.column)
|
||||
result.insert({"column", *BP.column});
|
||||
if (BP.endLine)
|
||||
result.insert({"endLine", *BP.endLine});
|
||||
if (BP.endColumn)
|
||||
result.insert({"endColumn", *BP.endColumn});
|
||||
if (BP.instructionReference)
|
||||
result.insert({"instructionReference", *BP.instructionReference});
|
||||
if (BP.offset)
|
||||
result.insert({"offset", *BP.offset});
|
||||
if (BP.reason) {
|
||||
result.insert({"reason", *BP.reason});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, SourceBreakpoint &SB,
|
||||
llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("line", SB.line) && O.map("column", SB.column) &&
|
||||
O.map("condition", SB.condition) &&
|
||||
O.map("hitCondition", SB.hitCondition) &&
|
||||
O.map("logMessage", SB.logMessage) && O.map("mode", SB.mode);
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, FunctionBreakpoint &FB,
|
||||
llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("name", FB.name) && O.map("condition", FB.condition) &&
|
||||
O.map("hitCondition", FB.hitCondition);
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, DataBreakpointAccessType &DBAT,
|
||||
llvm::json::Path P) {
|
||||
auto rawAccessType = Params.getAsString();
|
||||
if (!rawAccessType) {
|
||||
P.report("expected a string");
|
||||
return false;
|
||||
}
|
||||
std::optional<DataBreakpointAccessType> accessType =
|
||||
StringSwitch<std::optional<DataBreakpointAccessType>>(*rawAccessType)
|
||||
.Case("read", eDataBreakpointAccessTypeRead)
|
||||
.Case("write", eDataBreakpointAccessTypeWrite)
|
||||
.Case("readWrite", eDataBreakpointAccessTypeReadWrite)
|
||||
.Default(std::nullopt);
|
||||
if (!accessType) {
|
||||
P.report("unexpected value, expected 'read', 'write', or 'readWrite'");
|
||||
return false;
|
||||
}
|
||||
DBAT = *accessType;
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const DataBreakpointAccessType &DBAT) {
|
||||
switch (DBAT) {
|
||||
case eDataBreakpointAccessTypeRead:
|
||||
return "read";
|
||||
case eDataBreakpointAccessTypeWrite:
|
||||
return "write";
|
||||
case eDataBreakpointAccessTypeReadWrite:
|
||||
return "readWrite";
|
||||
}
|
||||
llvm_unreachable("unhandled data breakpoint access type.");
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, DataBreakpointInfo &DBI,
|
||||
llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("dataId", DBI.dataId) &&
|
||||
O.map("accessType", DBI.accessType) &&
|
||||
O.map("condition", DBI.condition) &&
|
||||
O.map("hitCondition", DBI.hitCondition);
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, InstructionBreakpoint &IB,
|
||||
llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("instructionReference", IB.instructionReference) &&
|
||||
O.map("offset", IB.offset) && O.map("condition", IB.condition) &&
|
||||
O.map("hitCondition", IB.hitCondition) && O.map("mode", IB.mode);
|
||||
}
|
||||
|
||||
} // namespace lldb_dap::protocol
|
||||
|
@ -20,6 +20,7 @@
|
||||
#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
|
||||
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
|
||||
|
||||
#include "lldb/lldb-defines.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
#include <cstdint>
|
||||
@ -273,6 +274,7 @@ enum PresentationHint : unsigned {
|
||||
ePresentationHintEmphasize,
|
||||
ePresentationHintDeemphasize,
|
||||
};
|
||||
llvm::json::Value toJSON(PresentationHint hint);
|
||||
|
||||
/// 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
|
||||
@ -302,6 +304,7 @@ struct Source {
|
||||
// unsupported keys: origin, sources, adapterData, checksums
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, Source &, llvm::json::Path);
|
||||
llvm::json::Value toJSON(const Source &);
|
||||
|
||||
/// The granularity of one `step` in the stepping requests `next`, `stepIn`,
|
||||
/// `stepOut` and `stepBack`.
|
||||
@ -350,6 +353,187 @@ struct BreakpointLocation {
|
||||
};
|
||||
llvm::json::Value toJSON(const BreakpointLocation &);
|
||||
|
||||
/// A machine-readable explanation of why a breakpoint may not be verified.
|
||||
enum class BreakpointReason : unsigned {
|
||||
/// Indicates a breakpoint might be verified in the future, but
|
||||
/// the adapter cannot verify it in the current state.
|
||||
eBreakpointReasonPending,
|
||||
/// Indicates a breakpoint was not able to be verified, and the
|
||||
/// adapter does not believe it can be verified without intervention.
|
||||
eBreakpointReasonFailed,
|
||||
};
|
||||
llvm::json::Value toJSON(const BreakpointReason &);
|
||||
|
||||
/// Information about a breakpoint created in `setBreakpoints`,
|
||||
/// `setFunctionBreakpoints`, `setInstructionBreakpoints`, or
|
||||
/// `setDataBreakpoints` requests.
|
||||
struct Breakpoint {
|
||||
/// The identifier for the breakpoint. It is needed if breakpoint events are
|
||||
/// used to update or remove breakpoints.
|
||||
std::optional<int> id;
|
||||
|
||||
/// If true, the breakpoint could be set (but not necessarily at the desired
|
||||
/// location).
|
||||
bool verified = false;
|
||||
|
||||
/// A 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.
|
||||
std::optional<std::string> message;
|
||||
|
||||
/// The source where the breakpoint is located.
|
||||
std::optional<Source> source;
|
||||
|
||||
/// The start line of the actual range covered by the breakpoint.
|
||||
std::optional<uint32_t> line;
|
||||
|
||||
/// Start position of the source range covered by the breakpoint. It is
|
||||
/// measured in UTF-16 code units and the client capability `columnsStartAt1`
|
||||
/// determines whether it is 0- or 1-based.
|
||||
std::optional<uint32_t> column;
|
||||
|
||||
/// The end line of the actual range covered by the breakpoint.
|
||||
std::optional<uint32_t> endLine;
|
||||
|
||||
/// End position of the source range covered by the breakpoint. It is measured
|
||||
/// in UTF-16 code units and the client capability `columnsStartAt1`
|
||||
/// determines whether it is 0- or 1-based. If no end line is given, then the
|
||||
/// end column is assumed to be in the start line.
|
||||
std::optional<uint32_t> endColumn;
|
||||
|
||||
/// A memory reference to where the breakpoint is set.
|
||||
std::optional<std::string> instructionReference;
|
||||
|
||||
/// The offset from the instruction reference.
|
||||
/// This can be negative.
|
||||
std::optional<int32_t> offset;
|
||||
|
||||
/// A machine-readable explanation of why a breakpoint may not be verified. If
|
||||
/// a breakpoint is verified or a specific reason is not known, the adapter
|
||||
/// should omit this property.
|
||||
std::optional<BreakpointReason> reason;
|
||||
};
|
||||
llvm::json::Value toJSON(const Breakpoint &);
|
||||
|
||||
/// Properties of a breakpoint or logpoint passed to the `setBreakpoints`
|
||||
/// request
|
||||
struct SourceBreakpoint {
|
||||
/// The source line of the breakpoint or logpoint.
|
||||
uint32_t line = LLDB_INVALID_LINE_NUMBER;
|
||||
|
||||
/// Start position within source line of the breakpoint or logpoint. It is
|
||||
/// measured in UTF-16 code units and the client capability `columnsStartAt1`
|
||||
/// determines whether it is 0- or 1-based.
|
||||
std::optional<uint32_t> column;
|
||||
|
||||
/// The expression for conditional breakpoints.
|
||||
/// It is only honored by a debug adapter if the corresponding capability
|
||||
/// `supportsConditionalBreakpoints` is true.
|
||||
std::optional<std::string> condition;
|
||||
|
||||
/// The expression that controls how many hits of the breakpoint are ignored.
|
||||
/// The debug adapter is expected to interpret the expression as needed.
|
||||
/// The attribute is only honored by a debug adapter if the corresponding
|
||||
/// capability `supportsHitConditionalBreakpoints` is true.
|
||||
/// If both this property and `condition` are specified, `hitCondition` should
|
||||
/// be evaluated only if the `condition` is met, and the debug adapter should
|
||||
/// stop only if both conditions are met.
|
||||
std::optional<std::string> hitCondition;
|
||||
|
||||
/// If this attribute exists and is non-empty, the debug adapter must not
|
||||
/// 'break' (stop)
|
||||
/// but log the message instead. Expressions within `{}` are interpolated.
|
||||
/// The attribute is only honored by a debug adapter if the corresponding
|
||||
/// capability `supportsLogPoints` is true.
|
||||
/// If either `hitCondition` or `condition` is specified, then the message
|
||||
/// should only be logged if those conditions are met.
|
||||
std::optional<std::string> logMessage;
|
||||
|
||||
/// The mode of this breakpoint. If defined, this must be one of the
|
||||
/// `breakpointModes` the debug adapter advertised in its `Capabilities`.
|
||||
std::optional<std::string> mode;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, SourceBreakpoint &, llvm::json::Path);
|
||||
|
||||
/// Properties of a breakpoint passed to the `setFunctionBreakpoints` request.
|
||||
struct FunctionBreakpoint {
|
||||
/// The name of the function.
|
||||
std::string name;
|
||||
|
||||
/// An expression for conditional breakpoints.
|
||||
/// It is only honored by a debug adapter if the corresponding capability
|
||||
/// `supportsConditionalBreakpoints` is true.
|
||||
std::optional<std::string> condition;
|
||||
|
||||
/// An expression that controls how many hits of the breakpoint are ignored.
|
||||
/// The debug adapter is expected to interpret the expression as needed.
|
||||
/// The attribute is only honored by a debug adapter if the corresponding
|
||||
/// capability `supportsHitConditionalBreakpoints` is true.
|
||||
std::optional<std::string> hitCondition;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, FunctionBreakpoint &,
|
||||
llvm::json::Path);
|
||||
|
||||
/// This enumeration defines all possible access types for data breakpoints.
|
||||
/// Values: ‘read’, ‘write’, ‘readWrite’
|
||||
enum DataBreakpointAccessType : unsigned {
|
||||
eDataBreakpointAccessTypeRead,
|
||||
eDataBreakpointAccessTypeWrite,
|
||||
eDataBreakpointAccessTypeReadWrite
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, DataBreakpointAccessType &,
|
||||
llvm::json::Path);
|
||||
llvm::json::Value toJSON(const DataBreakpointAccessType &);
|
||||
|
||||
/// Properties of a data breakpoint passed to the `setDataBreakpoints` request.
|
||||
struct DataBreakpointInfo {
|
||||
/// An id representing the data. This id is returned from the
|
||||
/// `dataBreakpointInfo` request.
|
||||
std::string dataId;
|
||||
|
||||
/// The access type of the data.
|
||||
std::optional<DataBreakpointAccessType> accessType;
|
||||
|
||||
/// An expression for conditional breakpoints.
|
||||
std::optional<std::string> condition;
|
||||
|
||||
/// An expression that controls how many hits of the breakpoint are ignored.
|
||||
/// The debug adapter is expected to interpret the expression as needed.
|
||||
std::optional<std::string> hitCondition;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, DataBreakpointInfo &,
|
||||
llvm::json::Path);
|
||||
|
||||
/// Properties of a breakpoint passed to the `setInstructionBreakpoints` request
|
||||
struct InstructionBreakpoint {
|
||||
/// The instruction reference of the breakpoint.
|
||||
/// This should be a memory or instruction pointer reference from an
|
||||
/// `EvaluateResponse`, `Variable`, `StackFrame`, `GotoTarget`, or
|
||||
/// `Breakpoint`.
|
||||
std::string instructionReference;
|
||||
|
||||
/// The offset from the instruction reference in bytes.
|
||||
/// This can be negative.
|
||||
std::optional<int32_t> offset;
|
||||
|
||||
/// An expression for conditional breakpoints.
|
||||
/// It is only honored by a debug adapter if the corresponding capability
|
||||
/// `supportsConditionalBreakpoints` is true.
|
||||
std::optional<std::string> condition;
|
||||
|
||||
/// An expression that controls how many hits of the breakpoint are ignored.
|
||||
/// The debug adapter is expected to interpret the expression as needed.
|
||||
/// The attribute is only honored by a debug adapter if the corresponding
|
||||
/// capability `supportsHitConditionalBreakpoints` is true.
|
||||
std::optional<std::string> hitCondition;
|
||||
|
||||
/// The mode of this breakpoint. If defined, this must be one of the
|
||||
/// `breakpointModes` the debug adapter advertised in its `Capabilities`.
|
||||
std::optional<std::string> mode;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &,
|
||||
llvm::json::Path);
|
||||
|
||||
} // namespace lldb_dap::protocol
|
||||
|
||||
#endif
|
||||
|
@ -26,13 +26,12 @@
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
SourceBreakpoint::SourceBreakpoint(DAP &dap, const llvm::json::Object &obj)
|
||||
: Breakpoint(dap, obj),
|
||||
m_log_message(GetString(obj, "logMessage").value_or("").str()),
|
||||
m_line(
|
||||
GetInteger<uint64_t>(obj, "line").value_or(LLDB_INVALID_LINE_NUMBER)),
|
||||
m_column(GetInteger<uint64_t>(obj, "column")
|
||||
.value_or(LLDB_INVALID_COLUMN_NUMBER)) {}
|
||||
SourceBreakpoint::SourceBreakpoint(DAP &dap,
|
||||
const protocol::SourceBreakpoint &breakpoint)
|
||||
: Breakpoint(dap, breakpoint.condition, breakpoint.hitCondition),
|
||||
m_log_message(breakpoint.logMessage.value_or("")),
|
||||
m_line(breakpoint.line),
|
||||
m_column(breakpoint.column.value_or(LLDB_INVALID_COLUMN_NUMBER)) {}
|
||||
|
||||
void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) {
|
||||
lldb::SBMutex lock = m_dap.GetAPIMutex();
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "Breakpoint.h"
|
||||
#include "DAPForward.h"
|
||||
#include "Protocol/ProtocolTypes.h"
|
||||
#include "lldb/API/SBError.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cstdint>
|
||||
@ -21,7 +22,7 @@ namespace lldb_dap {
|
||||
|
||||
class SourceBreakpoint : public Breakpoint {
|
||||
public:
|
||||
SourceBreakpoint(DAP &d, const llvm::json::Object &obj);
|
||||
SourceBreakpoint(DAP &d, const protocol::SourceBreakpoint &breakpoint);
|
||||
|
||||
// Set this breakpoint in LLDB as a new breakpoint
|
||||
void SetBreakpoint(const llvm::StringRef source_path);
|
||||
|
@ -8,25 +8,24 @@
|
||||
|
||||
#include "Watchpoint.h"
|
||||
#include "DAP.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "Protocol/ProtocolTypes.h"
|
||||
#include "lldb/API/SBTarget.h"
|
||||
#include "lldb/lldb-enumerations.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace lldb_dap {
|
||||
Watchpoint::Watchpoint(DAP &d, const llvm::json::Object &obj)
|
||||
: BreakpointBase(d, obj) {
|
||||
llvm::StringRef dataId = GetString(obj, "dataId").value_or("");
|
||||
std::string accessType = GetString(obj, "accessType").value_or("").str();
|
||||
Watchpoint::Watchpoint(DAP &d, const protocol::DataBreakpointInfo &breakpoint)
|
||||
: BreakpointBase(d, breakpoint.condition, breakpoint.hitCondition) {
|
||||
llvm::StringRef dataId = breakpoint.dataId;
|
||||
auto [addr_str, size_str] = dataId.split('/');
|
||||
llvm::to_integer(addr_str, m_addr, 16);
|
||||
llvm::to_integer(size_str, m_size);
|
||||
m_options.SetWatchpointTypeRead(accessType != "write");
|
||||
if (accessType != "read")
|
||||
m_options.SetWatchpointTypeRead(breakpoint.accessType !=
|
||||
protocol::eDataBreakpointAccessTypeWrite);
|
||||
if (breakpoint.accessType != protocol::eDataBreakpointAccessTypeRead)
|
||||
m_options.SetWatchpointTypeWrite(lldb::eWatchpointWriteTypeOnModify);
|
||||
}
|
||||
|
||||
@ -38,14 +37,17 @@ void Watchpoint::SetHitCondition() {
|
||||
m_wp.SetIgnoreCount(hitCount - 1);
|
||||
}
|
||||
|
||||
void Watchpoint::CreateJsonObject(llvm::json::Object &object) {
|
||||
protocol::Breakpoint Watchpoint::ToProtocolBreakpoint() {
|
||||
protocol::Breakpoint breakpoint;
|
||||
if (!m_error.IsValid() || m_error.Fail()) {
|
||||
object.try_emplace("verified", false);
|
||||
breakpoint.verified = false;
|
||||
if (m_error.Fail())
|
||||
EmplaceSafeString(object, "message", m_error.GetCString());
|
||||
breakpoint.message = m_error.GetCString();
|
||||
} else {
|
||||
object.try_emplace("verified", true);
|
||||
breakpoint.verified = true;
|
||||
}
|
||||
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
void Watchpoint::SetWatchpoint() {
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "BreakpointBase.h"
|
||||
#include "DAPForward.h"
|
||||
#include "Protocol/ProtocolTypes.h"
|
||||
#include "lldb/API/SBError.h"
|
||||
#include "lldb/API/SBWatchpoint.h"
|
||||
#include "lldb/API/SBWatchpointOptions.h"
|
||||
@ -21,12 +22,13 @@ namespace lldb_dap {
|
||||
|
||||
class Watchpoint : public BreakpointBase {
|
||||
public:
|
||||
Watchpoint(DAP &d, const llvm::json::Object &obj);
|
||||
Watchpoint(DAP &d, const protocol::DataBreakpointInfo &breakpoint);
|
||||
Watchpoint(DAP &d, lldb::SBWatchpoint wp) : BreakpointBase(d), m_wp(wp) {}
|
||||
|
||||
void SetCondition() override;
|
||||
void SetHitCondition() override;
|
||||
void CreateJsonObject(llvm::json::Object &object) override;
|
||||
|
||||
protocol::Breakpoint ToProtocolBreakpoint() override;
|
||||
|
||||
void SetWatchpoint();
|
||||
|
||||
|
@ -781,7 +781,7 @@ inline bool fromJSON(const Value &E, unsigned int &Out, Path P) {
|
||||
Out = *S;
|
||||
return true;
|
||||
}
|
||||
P.report("expected integer");
|
||||
P.report("expected unsigned integer");
|
||||
return false;
|
||||
}
|
||||
inline bool fromJSON(const Value &E, uint64_t &Out, Path P) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user