blob: 7a477f3e97875ddcf11c6c38bb8420cdf7ede56d [file] [log] [blame]
//===-- BreakpointLocationsHandler..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 "JSONUtils.h"
#include "RequestHandler.h"
namespace lldb_dap {
// "BreakpointLocationsRequest": {
// "allOf": [ { "$ref": "#/definitions/Request" }, {
// "type": "object",
// "description": "The `breakpointLocations` request returns all possible
// locations for source breakpoints in a given range.\nClients should only
// call this request if the corresponding capability
// `supportsBreakpointLocationsRequest` is true.",
// "properties": {
// "command": {
// "type": "string",
// "enum": [ "breakpointLocations" ]
// },
// "arguments": {
// "$ref": "#/definitions/BreakpointLocationsArguments"
// }
// },
// "required": [ "command" ]
// }]
// },
// "BreakpointLocationsArguments": {
// "type": "object",
// "description": "Arguments for `breakpointLocations` request.",
// "properties": {
// "source": {
// "$ref": "#/definitions/Source",
// "description": "The source location of the breakpoints; either
// `source.path` or `source.sourceReference` must be specified."
// },
// "line": {
// "type": "integer",
// "description": "Start line of range to search possible breakpoint
// locations in. If only the line is specified, the request returns all
// possible locations in that line."
// },
// "column": {
// "type": "integer",
// "description": "Start position within `line` to search possible
// breakpoint locations in. It is measured in UTF-16 code units and the
// client capability `columnsStartAt1` determines whether it is 0- or
// 1-based. If no column is given, the first position in the start line is
// assumed."
// },
// "endLine": {
// "type": "integer",
// "description": "End line of range to search possible breakpoint
// locations in. If no end line is given, then the end line is assumed to
// be the start line."
// },
// "endColumn": {
// "type": "integer",
// "description": "End position within `endLine` to search possible
// breakpoint locations in. It is measured in UTF-16 code units and the
// client capability `columnsStartAt1` determines whether it is 0- or
// 1-based. If no end column is given, the last position in the end line
// is assumed."
// }
// },
// "required": [ "source", "line" ]
// },
// "BreakpointLocationsResponse": {
// "allOf": [ { "$ref": "#/definitions/Response" }, {
// "type": "object",
// "description": "Response to `breakpointLocations` request.\nContains
// possible locations for source breakpoints.",
// "properties": {
// "body": {
// "type": "object",
// "properties": {
// "breakpoints": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/BreakpointLocation"
// },
// "description": "Sorted set of possible breakpoint locations."
// }
// },
// "required": [ "breakpoints" ]
// }
// },
// "required": [ "body" ]
// }]
// },
// "BreakpointLocation": {
// "type": "object",
// "description": "Properties of a breakpoint location returned from the
// `breakpointLocations` request.",
// "properties": {
// "line": {
// "type": "integer",
// "description": "Start line of breakpoint location."
// },
// "column": {
// "type": "integer",
// "description": "The start position of a breakpoint location. Position
// 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 breakpoint location if the location
// covers a range."
// },
// "endColumn": {
// "type": "integer",
// "description": "The end position of a breakpoint location (if the
// location covers a range). Position is measured in UTF-16 code units and
// the client capability `columnsStartAt1` determines whether it is 0- or
// 1-based."
// }
// },
// "required": [ "line" ]
// },
void BreakpointLocationsRequestHandler::operator()(
const llvm::json::Object &request) const {
llvm::json::Object response;
FillResponse(request, response);
auto *arguments = request.getObject("arguments");
auto *source = arguments->getObject("source");
std::string path = GetString(source, "path").value_or("").str();
const auto start_line = GetInteger<uint64_t>(arguments, "line")
.value_or(LLDB_INVALID_LINE_NUMBER);
const auto start_column = GetInteger<uint64_t>(arguments, "column")
.value_or(LLDB_INVALID_COLUMN_NUMBER);
const auto end_line =
GetInteger<uint64_t>(arguments, "endLine").value_or(start_line);
const auto end_column = GetInteger<uint64_t>(arguments, "endColumn")
.value_or(std::numeric_limits<uint64_t>::max());
lldb::SBFileSpec file_spec(path.c_str(), true);
lldb::SBSymbolContextList compile_units =
dap.target.FindCompileUnits(file_spec);
// Find all relevant lines & columns
llvm::SmallVector<std::pair<uint32_t, uint32_t>, 8> locations;
for (uint32_t c_idx = 0, c_limit = compile_units.GetSize(); c_idx < c_limit;
++c_idx) {
const lldb::SBCompileUnit &compile_unit =
compile_units.GetContextAtIndex(c_idx).GetCompileUnit();
if (!compile_unit.IsValid())
continue;
lldb::SBFileSpec primary_file_spec = compile_unit.GetFileSpec();
// Go through the line table and find all matching lines / columns
for (uint32_t l_idx = 0, l_limit = compile_unit.GetNumLineEntries();
l_idx < l_limit; ++l_idx) {
lldb::SBLineEntry line_entry = compile_unit.GetLineEntryAtIndex(l_idx);
// Filter by line / column
uint32_t line = line_entry.GetLine();
if (line < start_line || line > end_line)
continue;
uint32_t column = line_entry.GetColumn();
if (column == LLDB_INVALID_COLUMN_NUMBER)
continue;
if (line == start_line && column < start_column)
continue;
if (line == end_line && column > end_column)
continue;
// Make sure we are in the right file.
// We might have a match on line & column range and still
// be in the wrong file, e.g. for included files.
// Given that the involved pointers point into LLDB's string pool,
// we can directly compare the `const char*` pointers.
if (line_entry.GetFileSpec().GetFilename() !=
primary_file_spec.GetFilename() ||
line_entry.GetFileSpec().GetDirectory() !=
primary_file_spec.GetDirectory())
continue;
locations.emplace_back(line, column);
}
}
// The line entries are sorted by addresses, but we must return the list
// ordered by line / column position.
std::sort(locations.begin(), locations.end());
locations.erase(llvm::unique(locations), locations.end());
llvm::json::Array locations_json;
for (auto &l : locations) {
llvm::json::Object location;
location.try_emplace("line", l.first);
location.try_emplace("column", l.second);
locations_json.emplace_back(std::move(location));
}
llvm::json::Object body;
body.try_emplace("breakpoints", std::move(locations_json));
response.try_emplace("body", std::move(body));
dap.SendJSON(llvm::json::Value(std::move(response)));
}
} // namespace lldb_dap