| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmDebuggerBreakpointManager.h" |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <cstdint> |
| #include <utility> |
| |
| #include <cm3p/cppdap/optional.h> |
| #include <cm3p/cppdap/session.h> |
| #include <cm3p/cppdap/types.h> |
| |
| #include "cmDebuggerSourceBreakpoint.h" |
| #include "cmListFileCache.h" |
| #include "cmSystemTools.h" |
| |
| namespace cmDebugger { |
| |
| cmDebuggerBreakpointManager::cmDebuggerBreakpointManager( |
| dap::Session* dapSession) |
| : DapSession(dapSession) |
| { |
| // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_SetBreakpoints |
| DapSession->registerHandler([&](const dap::SetBreakpointsRequest& request) { |
| return HandleSetBreakpointsRequest(request); |
| }); |
| } |
| |
| int64_t cmDebuggerBreakpointManager::FindFunctionStartLine( |
| std::string const& sourcePath, int64_t line) |
| { |
| auto location = |
| find_if(ListFileFunctionLines[sourcePath].begin(), |
| ListFileFunctionLines[sourcePath].end(), |
| [=](cmDebuggerFunctionLocation const& loc) { |
| return loc.StartLine <= line && loc.EndLine >= line; |
| }); |
| |
| if (location != ListFileFunctionLines[sourcePath].end()) { |
| return location->StartLine; |
| } |
| |
| return 0; |
| } |
| |
| int64_t cmDebuggerBreakpointManager::CalibrateBreakpointLine( |
| std::string const& sourcePath, int64_t line) |
| { |
| auto location = find_if(ListFileFunctionLines[sourcePath].begin(), |
| ListFileFunctionLines[sourcePath].end(), |
| [=](cmDebuggerFunctionLocation const& loc) { |
| return loc.StartLine >= line; |
| }); |
| |
| if (location != ListFileFunctionLines[sourcePath].end()) { |
| return location->StartLine; |
| } |
| |
| if (!ListFileFunctionLines[sourcePath].empty() && |
| ListFileFunctionLines[sourcePath].back().EndLine <= line) { |
| // return last function start line for any breakpoints after. |
| return ListFileFunctionLines[sourcePath].back().StartLine; |
| } |
| |
| return 0; |
| } |
| |
| dap::SetBreakpointsResponse |
| cmDebuggerBreakpointManager::HandleSetBreakpointsRequest( |
| dap::SetBreakpointsRequest const& request) |
| { |
| std::unique_lock<std::mutex> lock(Mutex); |
| |
| dap::SetBreakpointsResponse response; |
| |
| auto sourcePath = |
| cmSystemTools::GetActualCaseForPath(request.source.path.value()); |
| const dap::array<dap::SourceBreakpoint> defaultValue{}; |
| const auto& breakpoints = request.breakpoints.value(defaultValue); |
| |
| if (Breakpoints.find(sourcePath) != Breakpoints.end()) { |
| Breakpoints[sourcePath].clear(); |
| } |
| response.breakpoints.resize(breakpoints.size()); |
| |
| if (ListFileFunctionLines.find(sourcePath) != ListFileFunctionLines.end()) { |
| // The file has loaded, we can validate breakpoints. |
| for (size_t i = 0; i < breakpoints.size(); i++) { |
| int64_t correctedLine = |
| CalibrateBreakpointLine(sourcePath, breakpoints[i].line); |
| if (correctedLine > 0) { |
| Breakpoints[sourcePath].emplace_back(NextBreakpointId++, |
| correctedLine); |
| response.breakpoints[i].id = Breakpoints[sourcePath].back().GetId(); |
| response.breakpoints[i].line = |
| Breakpoints[sourcePath].back().GetLine(); |
| response.breakpoints[i].verified = true; |
| } else { |
| response.breakpoints[i].verified = false; |
| response.breakpoints[i].line = breakpoints[i].line; |
| } |
| dap::Source dapSrc; |
| dapSrc.path = sourcePath; |
| response.breakpoints[i].source = dapSrc; |
| } |
| } else { |
| // The file has not loaded, validate breakpoints later. |
| ListFilePendingValidations.emplace(sourcePath); |
| |
| for (size_t i = 0; i < breakpoints.size(); i++) { |
| Breakpoints[sourcePath].emplace_back(NextBreakpointId++, |
| breakpoints[i].line); |
| response.breakpoints[i].id = Breakpoints[sourcePath].back().GetId(); |
| response.breakpoints[i].line = Breakpoints[sourcePath].back().GetLine(); |
| response.breakpoints[i].verified = false; |
| dap::Source dapSrc; |
| dapSrc.path = sourcePath; |
| response.breakpoints[i].source = dapSrc; |
| } |
| } |
| |
| return response; |
| } |
| |
| void cmDebuggerBreakpointManager::SourceFileLoaded( |
| std::string const& sourcePath, |
| std::vector<cmListFileFunction> const& functions) |
| { |
| std::unique_lock<std::mutex> lock(Mutex); |
| if (ListFileFunctionLines.find(sourcePath) != ListFileFunctionLines.end()) { |
| // this is not expected. |
| return; |
| } |
| |
| for (cmListFileFunction const& func : functions) { |
| ListFileFunctionLines[sourcePath].emplace_back( |
| cmDebuggerFunctionLocation{ func.Line(), func.LineEnd() }); |
| } |
| |
| if (ListFilePendingValidations.find(sourcePath) == |
| ListFilePendingValidations.end()) { |
| return; |
| } |
| |
| ListFilePendingValidations.erase(sourcePath); |
| |
| for (size_t i = 0; i < Breakpoints[sourcePath].size(); i++) { |
| dap::BreakpointEvent breakpointEvent; |
| breakpointEvent.breakpoint.id = Breakpoints[sourcePath][i].GetId(); |
| breakpointEvent.breakpoint.line = Breakpoints[sourcePath][i].GetLine(); |
| auto source = dap::Source(); |
| source.path = sourcePath; |
| breakpointEvent.breakpoint.source = source; |
| int64_t correctedLine = CalibrateBreakpointLine( |
| sourcePath, Breakpoints[sourcePath][i].GetLine()); |
| if (correctedLine != Breakpoints[sourcePath][i].GetLine()) { |
| Breakpoints[sourcePath][i].ChangeLine(correctedLine); |
| } |
| breakpointEvent.reason = "changed"; |
| breakpointEvent.breakpoint.verified = (correctedLine > 0); |
| if (breakpointEvent.breakpoint.verified) { |
| breakpointEvent.breakpoint.line = correctedLine; |
| } else { |
| Breakpoints[sourcePath][i].Invalid(); |
| } |
| |
| DapSession->send(breakpointEvent); |
| } |
| } |
| |
| std::vector<int64_t> cmDebuggerBreakpointManager::GetBreakpoints( |
| std::string const& sourcePath, int64_t line) |
| { |
| std::unique_lock<std::mutex> lock(Mutex); |
| const auto& all = Breakpoints[sourcePath]; |
| std::vector<int64_t> breakpoints; |
| if (all.empty()) { |
| return breakpoints; |
| } |
| |
| auto it = all.begin(); |
| |
| while ((it = std::find_if( |
| it, all.end(), [&](const cmDebuggerSourceBreakpoint& breakpoint) { |
| return (breakpoint.GetIsValid() && breakpoint.GetLine() == line); |
| })) != all.end()) { |
| breakpoints.emplace_back(it->GetId()); |
| ++it; |
| } |
| |
| return breakpoints; |
| } |
| |
| size_t cmDebuggerBreakpointManager::GetBreakpointCount() const |
| { |
| size_t count = 0; |
| for (auto const& pair : Breakpoints) { |
| count += pair.second.size(); |
| } |
| return count; |
| } |
| |
| void cmDebuggerBreakpointManager::ClearAll() |
| { |
| std::unique_lock<std::mutex> lock(Mutex); |
| Breakpoints.clear(); |
| } |
| |
| } // namespace cmDebugger |