blob: 4d920f8556254633c48ac3f8b0226a8dbc33dcf4 [file] [log] [blame]
//===-- DataBreakpointInfoRequestHandler.cpp ------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "DAP.h"
#include "EventHelper.h"
#include "JSONUtils.h"
#include "RequestHandler.h"
#include "lldb/API/SBMemoryRegionInfo.h"
#include "llvm/ADT/StringExtras.h"
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);
std::string addr, size;
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()));
} else if (byte_size == 0) {
body.try_emplace("dataId", nullptr);
body.try_emplace("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());
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");
} else {
uint64_t load_addr = value.GetValueAsUnsigned();
lldb::SBData data = value.GetPointeeData();
if (data.IsValid()) {
size = llvm::utostr(data.GetByteSize());
addr = llvm::utohexstr(load_addr);
lldb::SBMemoryRegionInfo region;
lldb::SBError err =
dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
// Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
// 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");
}
}
} else {
body.try_emplace("dataId", nullptr);
body.try_emplace("description",
"unable to get byte size for expression: " +
name.str());
}
}
} else {
body.try_emplace("dataId", nullptr);
body.try_emplace("description", "variable not found: " + name.str());
}
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());
}
response.try_emplace("body", std::move(body));
dap.SendJSON(llvm::json::Value(std::move(response)));
}
} // namespace lldb_dap