| //===-- 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 |