/src/CMake/Source/cmDebuggerBreakpointManager.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmDebuggerBreakpointManager.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <cstddef> |
7 | | #include <cstdint> |
8 | | #include <utility> |
9 | | |
10 | | #include <cm3p/cppdap/optional.h> |
11 | | #include <cm3p/cppdap/session.h> |
12 | | #include <cm3p/cppdap/types.h> |
13 | | |
14 | | #include "cmDebuggerSourceBreakpoint.h" |
15 | | #include "cmListFileCache.h" |
16 | | #include "cmSystemTools.h" |
17 | | |
18 | | namespace cmDebugger { |
19 | | |
20 | | cmDebuggerBreakpointManager::cmDebuggerBreakpointManager( |
21 | | dap::Session* dapSession) |
22 | 0 | : DapSession(dapSession) |
23 | 0 | { |
24 | | // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetBreakpoints |
25 | 0 | DapSession->registerHandler([&](dap::SetBreakpointsRequest const& request) { |
26 | 0 | return HandleSetBreakpointsRequest(request); |
27 | 0 | }); |
28 | 0 | } |
29 | | |
30 | | int64_t cmDebuggerBreakpointManager::FindFunctionStartLine( |
31 | | std::string const& sourcePath, int64_t line) |
32 | 0 | { |
33 | 0 | auto location = |
34 | 0 | find_if(ListFileFunctionLines[sourcePath].begin(), |
35 | 0 | ListFileFunctionLines[sourcePath].end(), |
36 | 0 | [=](cmDebuggerFunctionLocation loc) { |
37 | 0 | return loc.StartLine <= line && loc.EndLine >= line; |
38 | 0 | }); |
39 | |
|
40 | 0 | if (location != ListFileFunctionLines[sourcePath].end()) { |
41 | 0 | return location->StartLine; |
42 | 0 | } |
43 | | |
44 | 0 | return 0; |
45 | 0 | } |
46 | | |
47 | | int64_t cmDebuggerBreakpointManager::CalibrateBreakpointLine( |
48 | | std::string const& sourcePath, int64_t line) |
49 | 0 | { |
50 | 0 | auto location = find_if( |
51 | 0 | ListFileFunctionLines[sourcePath].begin(), |
52 | 0 | ListFileFunctionLines[sourcePath].end(), |
53 | 0 | [=](cmDebuggerFunctionLocation loc) { return loc.StartLine >= line; }); |
54 | |
|
55 | 0 | if (location != ListFileFunctionLines[sourcePath].end()) { |
56 | 0 | return location->StartLine; |
57 | 0 | } |
58 | | |
59 | 0 | if (!ListFileFunctionLines[sourcePath].empty() && |
60 | 0 | ListFileFunctionLines[sourcePath].back().EndLine <= line) { |
61 | | // return last function start line for any breakpoints after. |
62 | 0 | return ListFileFunctionLines[sourcePath].back().StartLine; |
63 | 0 | } |
64 | | |
65 | 0 | return 0; |
66 | 0 | } |
67 | | |
68 | | dap::SetBreakpointsResponse |
69 | | cmDebuggerBreakpointManager::HandleSetBreakpointsRequest( |
70 | | dap::SetBreakpointsRequest const& request) |
71 | 0 | { |
72 | 0 | std::unique_lock<std::mutex> lock(Mutex); |
73 | |
|
74 | 0 | dap::SetBreakpointsResponse response; |
75 | |
|
76 | 0 | auto sourcePath = |
77 | 0 | cmSystemTools::GetActualCaseForPath(request.source.path.value()); |
78 | 0 | dap::array<dap::SourceBreakpoint> const defaultValue{}; |
79 | 0 | auto const& breakpoints = request.breakpoints.value(defaultValue); |
80 | |
|
81 | 0 | if (Breakpoints.find(sourcePath) != Breakpoints.end()) { |
82 | 0 | Breakpoints[sourcePath].clear(); |
83 | 0 | } |
84 | 0 | response.breakpoints.resize(breakpoints.size()); |
85 | |
|
86 | 0 | if (ListFileFunctionLines.find(sourcePath) != ListFileFunctionLines.end()) { |
87 | | // The file has loaded, we can validate breakpoints. |
88 | 0 | for (size_t i = 0; i < breakpoints.size(); i++) { |
89 | 0 | int64_t correctedLine = |
90 | 0 | CalibrateBreakpointLine(sourcePath, breakpoints[i].line); |
91 | 0 | if (correctedLine > 0) { |
92 | 0 | Breakpoints[sourcePath].emplace_back(NextBreakpointId++, |
93 | 0 | correctedLine); |
94 | 0 | response.breakpoints[i].id = Breakpoints[sourcePath].back().GetId(); |
95 | 0 | response.breakpoints[i].line = |
96 | 0 | Breakpoints[sourcePath].back().GetLine(); |
97 | 0 | response.breakpoints[i].verified = true; |
98 | 0 | } else { |
99 | 0 | response.breakpoints[i].verified = false; |
100 | 0 | response.breakpoints[i].line = breakpoints[i].line; |
101 | 0 | } |
102 | 0 | dap::Source dapSrc; |
103 | 0 | dapSrc.path = sourcePath; |
104 | 0 | response.breakpoints[i].source = dapSrc; |
105 | 0 | } |
106 | 0 | } else { |
107 | | // The file has not loaded, validate breakpoints later. |
108 | 0 | ListFilePendingValidations.emplace(sourcePath); |
109 | |
|
110 | 0 | for (size_t i = 0; i < breakpoints.size(); i++) { |
111 | 0 | Breakpoints[sourcePath].emplace_back(NextBreakpointId++, |
112 | 0 | breakpoints[i].line); |
113 | 0 | response.breakpoints[i].id = Breakpoints[sourcePath].back().GetId(); |
114 | 0 | response.breakpoints[i].line = Breakpoints[sourcePath].back().GetLine(); |
115 | 0 | response.breakpoints[i].verified = false; |
116 | 0 | dap::Source dapSrc; |
117 | 0 | dapSrc.path = sourcePath; |
118 | 0 | response.breakpoints[i].source = dapSrc; |
119 | 0 | } |
120 | 0 | } |
121 | |
|
122 | 0 | return response; |
123 | 0 | } |
124 | | |
125 | | void cmDebuggerBreakpointManager::SourceFileLoaded( |
126 | | std::string const& sourcePath, |
127 | | std::vector<cmListFileFunction> const& functions) |
128 | 0 | { |
129 | 0 | std::unique_lock<std::mutex> lock(Mutex); |
130 | 0 | if (ListFileFunctionLines.find(sourcePath) != ListFileFunctionLines.end()) { |
131 | | // this is not expected. |
132 | 0 | return; |
133 | 0 | } |
134 | | |
135 | 0 | for (cmListFileFunction const& func : functions) { |
136 | 0 | ListFileFunctionLines[sourcePath].emplace_back( |
137 | 0 | cmDebuggerFunctionLocation{ func.Line(), func.LineEnd() }); |
138 | 0 | } |
139 | |
|
140 | 0 | if (ListFilePendingValidations.find(sourcePath) == |
141 | 0 | ListFilePendingValidations.end()) { |
142 | 0 | return; |
143 | 0 | } |
144 | | |
145 | 0 | ListFilePendingValidations.erase(sourcePath); |
146 | |
|
147 | 0 | for (size_t i = 0; i < Breakpoints[sourcePath].size(); i++) { |
148 | 0 | dap::BreakpointEvent breakpointEvent; |
149 | 0 | breakpointEvent.breakpoint.id = Breakpoints[sourcePath][i].GetId(); |
150 | 0 | breakpointEvent.breakpoint.line = Breakpoints[sourcePath][i].GetLine(); |
151 | 0 | auto source = dap::Source(); |
152 | 0 | source.path = sourcePath; |
153 | 0 | breakpointEvent.breakpoint.source = source; |
154 | 0 | int64_t correctedLine = CalibrateBreakpointLine( |
155 | 0 | sourcePath, Breakpoints[sourcePath][i].GetLine()); |
156 | 0 | if (correctedLine != Breakpoints[sourcePath][i].GetLine()) { |
157 | 0 | Breakpoints[sourcePath][i].ChangeLine(correctedLine); |
158 | 0 | } |
159 | 0 | breakpointEvent.reason = "changed"; |
160 | 0 | breakpointEvent.breakpoint.verified = (correctedLine > 0); |
161 | 0 | if (breakpointEvent.breakpoint.verified) { |
162 | 0 | breakpointEvent.breakpoint.line = correctedLine; |
163 | 0 | } else { |
164 | 0 | Breakpoints[sourcePath][i].Invalid(); |
165 | 0 | } |
166 | |
|
167 | 0 | DapSession->send(breakpointEvent); |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | | std::vector<int64_t> cmDebuggerBreakpointManager::GetBreakpoints( |
172 | | std::string const& sourcePath, int64_t line) |
173 | 0 | { |
174 | 0 | std::unique_lock<std::mutex> lock(Mutex); |
175 | 0 | auto const& all = Breakpoints[sourcePath]; |
176 | 0 | std::vector<int64_t> breakpoints; |
177 | 0 | if (all.empty()) { |
178 | 0 | return breakpoints; |
179 | 0 | } |
180 | | |
181 | 0 | auto it = all.begin(); |
182 | |
|
183 | 0 | while ((it = std::find_if( |
184 | 0 | it, all.end(), [&](cmDebuggerSourceBreakpoint const& breakpoint) { |
185 | 0 | return (breakpoint.GetIsValid() && breakpoint.GetLine() == line); |
186 | 0 | })) != all.end()) { |
187 | 0 | breakpoints.emplace_back(it->GetId()); |
188 | 0 | ++it; |
189 | 0 | } |
190 | |
|
191 | 0 | return breakpoints; |
192 | 0 | } |
193 | | |
194 | | size_t cmDebuggerBreakpointManager::GetBreakpointCount() const |
195 | 0 | { |
196 | 0 | size_t count = 0; |
197 | 0 | for (auto const& pair : Breakpoints) { |
198 | 0 | count += pair.second.size(); |
199 | 0 | } |
200 | 0 | return count; |
201 | 0 | } |
202 | | |
203 | | void cmDebuggerBreakpointManager::ClearAll() |
204 | 0 | { |
205 | 0 | std::unique_lock<std::mutex> lock(Mutex); |
206 | 0 | Breakpoints.clear(); |
207 | 0 | } |
208 | | |
209 | | } // namespace cmDebugger |