Convert Breakpoint & Watchpoints structs to classes to provide proper access control. This is in preparation for adopting SBMutex to protect the underlying SBBreakpoint and SBWatchpoint.
184 lines
6.7 KiB
C++
184 lines
6.7 KiB
C++
//===-- SetBreakpointsRequestHandler.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"
|
|
|
|
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;
|
|
|
|
// 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());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete any breakpoints in this source file that aren't in the
|
|
// request_bps set. There is no call to remove breakpoints other than
|
|
// calling this function with a smaller or empty "breakpoints" list.
|
|
auto old_src_bp_pos = dap.source_breakpoints.find(path);
|
|
if (old_src_bp_pos != dap.source_breakpoints.end()) {
|
|
for (auto &old_bp : old_src_bp_pos->second) {
|
|
auto request_pos = request_bps.find(old_bp.first);
|
|
if (request_pos == request_bps.end()) {
|
|
// This breakpoint no longer exists in this source file, delete it
|
|
dap.target.BreakpointDelete(old_bp.second.GetID());
|
|
old_src_bp_pos->second.erase(old_bp.first);
|
|
}
|
|
}
|
|
}
|
|
|
|
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)));
|
|
}
|
|
|
|
} // namespace lldb_dap
|