cmDebuggerBreakpointManager.cxx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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. #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([&](dap::SetBreakpointsRequest const& 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 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(
  42. ListFileFunctionLines[sourcePath].begin(),
  43. ListFileFunctionLines[sourcePath].end(),
  44. [=](cmDebuggerFunctionLocation loc) { return loc.StartLine >= line; });
  45. if (location != ListFileFunctionLines[sourcePath].end()) {
  46. return location->StartLine;
  47. }
  48. if (!ListFileFunctionLines[sourcePath].empty() &&
  49. ListFileFunctionLines[sourcePath].back().EndLine <= line) {
  50. // return last function start line for any breakpoints after.
  51. return ListFileFunctionLines[sourcePath].back().StartLine;
  52. }
  53. return 0;
  54. }
  55. dap::SetBreakpointsResponse
  56. cmDebuggerBreakpointManager::HandleSetBreakpointsRequest(
  57. dap::SetBreakpointsRequest const& request)
  58. {
  59. std::unique_lock<std::mutex> lock(Mutex);
  60. dap::SetBreakpointsResponse response;
  61. auto sourcePath =
  62. cmSystemTools::GetActualCaseForPath(request.source.path.value());
  63. dap::array<dap::SourceBreakpoint> const defaultValue{};
  64. auto const& breakpoints = request.breakpoints.value(defaultValue);
  65. if (Breakpoints.find(sourcePath) != Breakpoints.end()) {
  66. Breakpoints[sourcePath].clear();
  67. }
  68. response.breakpoints.resize(breakpoints.size());
  69. if (ListFileFunctionLines.find(sourcePath) != ListFileFunctionLines.end()) {
  70. // The file has loaded, we can validate breakpoints.
  71. for (size_t i = 0; i < breakpoints.size(); i++) {
  72. int64_t correctedLine =
  73. CalibrateBreakpointLine(sourcePath, breakpoints[i].line);
  74. if (correctedLine > 0) {
  75. Breakpoints[sourcePath].emplace_back(NextBreakpointId++,
  76. correctedLine);
  77. response.breakpoints[i].id = Breakpoints[sourcePath].back().GetId();
  78. response.breakpoints[i].line =
  79. Breakpoints[sourcePath].back().GetLine();
  80. response.breakpoints[i].verified = true;
  81. } else {
  82. response.breakpoints[i].verified = false;
  83. response.breakpoints[i].line = breakpoints[i].line;
  84. }
  85. dap::Source dapSrc;
  86. dapSrc.path = sourcePath;
  87. response.breakpoints[i].source = dapSrc;
  88. }
  89. } else {
  90. // The file has not loaded, validate breakpoints later.
  91. ListFilePendingValidations.emplace(sourcePath);
  92. for (size_t i = 0; i < breakpoints.size(); i++) {
  93. Breakpoints[sourcePath].emplace_back(NextBreakpointId++,
  94. breakpoints[i].line);
  95. response.breakpoints[i].id = Breakpoints[sourcePath].back().GetId();
  96. response.breakpoints[i].line = Breakpoints[sourcePath].back().GetLine();
  97. response.breakpoints[i].verified = false;
  98. dap::Source dapSrc;
  99. dapSrc.path = sourcePath;
  100. response.breakpoints[i].source = dapSrc;
  101. }
  102. }
  103. return response;
  104. }
  105. void cmDebuggerBreakpointManager::SourceFileLoaded(
  106. std::string const& sourcePath,
  107. std::vector<cmListFileFunction> const& functions)
  108. {
  109. std::unique_lock<std::mutex> lock(Mutex);
  110. if (ListFileFunctionLines.find(sourcePath) != ListFileFunctionLines.end()) {
  111. // this is not expected.
  112. return;
  113. }
  114. for (cmListFileFunction const& func : functions) {
  115. ListFileFunctionLines[sourcePath].emplace_back(
  116. cmDebuggerFunctionLocation{ func.Line(), func.LineEnd() });
  117. }
  118. if (ListFilePendingValidations.find(sourcePath) ==
  119. ListFilePendingValidations.end()) {
  120. return;
  121. }
  122. ListFilePendingValidations.erase(sourcePath);
  123. for (size_t i = 0; i < Breakpoints[sourcePath].size(); i++) {
  124. dap::BreakpointEvent breakpointEvent;
  125. breakpointEvent.breakpoint.id = Breakpoints[sourcePath][i].GetId();
  126. breakpointEvent.breakpoint.line = Breakpoints[sourcePath][i].GetLine();
  127. auto source = dap::Source();
  128. source.path = sourcePath;
  129. breakpointEvent.breakpoint.source = source;
  130. int64_t correctedLine = CalibrateBreakpointLine(
  131. sourcePath, Breakpoints[sourcePath][i].GetLine());
  132. if (correctedLine != Breakpoints[sourcePath][i].GetLine()) {
  133. Breakpoints[sourcePath][i].ChangeLine(correctedLine);
  134. }
  135. breakpointEvent.reason = "changed";
  136. breakpointEvent.breakpoint.verified = (correctedLine > 0);
  137. if (breakpointEvent.breakpoint.verified) {
  138. breakpointEvent.breakpoint.line = correctedLine;
  139. } else {
  140. Breakpoints[sourcePath][i].Invalid();
  141. }
  142. DapSession->send(breakpointEvent);
  143. }
  144. }
  145. std::vector<int64_t> cmDebuggerBreakpointManager::GetBreakpoints(
  146. std::string const& sourcePath, int64_t line)
  147. {
  148. std::unique_lock<std::mutex> lock(Mutex);
  149. auto const& all = Breakpoints[sourcePath];
  150. std::vector<int64_t> breakpoints;
  151. if (all.empty()) {
  152. return breakpoints;
  153. }
  154. auto it = all.begin();
  155. while ((it = std::find_if(
  156. it, all.end(), [&](cmDebuggerSourceBreakpoint const& breakpoint) {
  157. return (breakpoint.GetIsValid() && breakpoint.GetLine() == line);
  158. })) != all.end()) {
  159. breakpoints.emplace_back(it->GetId());
  160. ++it;
  161. }
  162. return breakpoints;
  163. }
  164. size_t cmDebuggerBreakpointManager::GetBreakpointCount() const
  165. {
  166. size_t count = 0;
  167. for (auto const& pair : Breakpoints) {
  168. count += pair.second.size();
  169. }
  170. return count;
  171. }
  172. void cmDebuggerBreakpointManager::ClearAll()
  173. {
  174. std::unique_lock<std::mutex> lock(Mutex);
  175. Breakpoints.clear();
  176. }
  177. } // namespace cmDebugger