1//===-- BreakpointLocationsHandler..cpp -----------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "DAP.h"
10#include "RequestHandler.h"
11#include <optional>
12#include <vector>
13
14namespace lldb_dap {
15
16/// The `breakpointLocations` request returns all possible locations for source
17/// breakpoints in a given range. Clients should only call this request if the
18/// corresponding capability `supportsBreakpointLocationsRequest` is true.
19llvm::Expected<protocol::BreakpointLocationsResponseBody>
20BreakpointLocationsRequestHandler::Run(
21 const protocol::BreakpointLocationsArguments &args) const {
22 uint32_t start_line = args.line;
23 uint32_t start_column = args.column.value_or(LLDB_INVALID_COLUMN_NUMBER);
24 uint32_t end_line = args.endLine.value_or(u&: start_line);
25 uint32_t end_column =
26 args.endColumn.value_or(u: std::numeric_limits<uint32_t>::max());
27
28 // Find all relevant lines & columns.
29 std::vector<std::pair<uint32_t, uint32_t>> locations;
30 if (args.source.sourceReference) {
31 locations = GetAssemblyBreakpointLocations(source_reference: *args.source.sourceReference,
32 start_line, end_line);
33 } else {
34 std::string path = args.source.path.value_or(u: "");
35 locations = GetSourceBreakpointLocations(
36 path: std::move(path), start_line, start_column, end_line, end_column);
37 }
38
39 // The line entries are sorted by addresses, but we must return the list
40 // ordered by line / column position.
41 std::sort(first: locations.begin(), last: locations.end());
42 locations.erase(first: llvm::unique(R&: locations), last: locations.end());
43
44 std::vector<protocol::BreakpointLocation> breakpoint_locations;
45 for (auto &l : locations)
46 breakpoint_locations.push_back(
47 x: {.line: l.first, .column: l.second, .endLine: std::nullopt, .endColumn: std::nullopt});
48
49 return protocol::BreakpointLocationsResponseBody{
50 /*breakpoints=*/std::move(breakpoint_locations)};
51}
52
53std::vector<std::pair<uint32_t, uint32_t>>
54BreakpointLocationsRequestHandler::GetSourceBreakpointLocations(
55 std::string path, uint32_t start_line, uint32_t start_column,
56 uint32_t end_line, uint32_t end_column) const {
57 std::vector<std::pair<uint32_t, uint32_t>> locations;
58 lldb::SBFileSpec file_spec(path.c_str(), true);
59 lldb::SBSymbolContextList compile_units =
60 dap.target.FindCompileUnits(sb_file_spec: file_spec);
61
62 for (uint32_t c_idx = 0, c_limit = compile_units.GetSize(); c_idx < c_limit;
63 ++c_idx) {
64 const lldb::SBCompileUnit &compile_unit =
65 compile_units.GetContextAtIndex(idx: c_idx).GetCompileUnit();
66 if (!compile_unit.IsValid())
67 continue;
68 lldb::SBFileSpec primary_file_spec = compile_unit.GetFileSpec();
69
70 // Go through the line table and find all matching lines / columns
71 for (uint32_t l_idx = 0, l_limit = compile_unit.GetNumLineEntries();
72 l_idx < l_limit; ++l_idx) {
73 lldb::SBLineEntry line_entry = compile_unit.GetLineEntryAtIndex(idx: l_idx);
74
75 // Filter by line / column
76 uint32_t line = line_entry.GetLine();
77 if (line < start_line || line > end_line)
78 continue;
79 uint32_t column = line_entry.GetColumn();
80 if (column == LLDB_INVALID_COLUMN_NUMBER)
81 continue;
82 if (line == start_line && column < start_column)
83 continue;
84 if (line == end_line && column > end_column)
85 continue;
86
87 // Make sure we are in the right file.
88 // We might have a match on line & column range and still
89 // be in the wrong file, e.g. for included files.
90 // Given that the involved pointers point into LLDB's string pool,
91 // we can directly compare the `const char*` pointers.
92 if (line_entry.GetFileSpec().GetFilename() !=
93 primary_file_spec.GetFilename() ||
94 line_entry.GetFileSpec().GetDirectory() !=
95 primary_file_spec.GetDirectory())
96 continue;
97
98 locations.emplace_back(args&: line, args&: column);
99 }
100 }
101
102 return locations;
103}
104
105std::vector<std::pair<uint32_t, uint32_t>>
106BreakpointLocationsRequestHandler::GetAssemblyBreakpointLocations(
107 int64_t source_reference, uint32_t start_line, uint32_t end_line) const {
108 std::vector<std::pair<uint32_t, uint32_t>> locations;
109 lldb::SBAddress address(source_reference, dap.target);
110 if (!address.IsValid())
111 return locations;
112
113 lldb::SBSymbol symbol = address.GetSymbol();
114 if (!symbol.IsValid())
115 return locations;
116
117 // start_line is relative to the symbol's start address.
118 lldb::SBInstructionList insts = symbol.GetInstructions(target: dap.target);
119 if (insts.GetSize() > (start_line - 1))
120 locations.reserve(n: insts.GetSize() - (start_line - 1));
121 for (uint32_t i = start_line - 1; i < insts.GetSize() && i <= (end_line - 1);
122 ++i) {
123 locations.emplace_back(args&: i, args: 1);
124 }
125
126 return locations;
127}
128
129} // namespace lldb_dap
130

source code of lldb/tools/lldb-dap/Handler/BreakpointLocationsHandler.cpp