cmDebuggerBreakpointManager.cxx 6.6 KB

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