xref: /llvm-project/lldb/tools/lldb-dap/SourceBreakpoint.cpp (revision b99d4112585302cbd01f9b851a04adc6e4fb5218)
101263c6cSJonas Devlieghere //===-- SourceBreakpoint.cpp ------------------------------------*- C++ -*-===//
201263c6cSJonas Devlieghere //
301263c6cSJonas Devlieghere // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
401263c6cSJonas Devlieghere // See https://llvm.org/LICENSE.txt for license information.
501263c6cSJonas Devlieghere // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
601263c6cSJonas Devlieghere //
701263c6cSJonas Devlieghere //===----------------------------------------------------------------------===//
801263c6cSJonas Devlieghere 
901263c6cSJonas Devlieghere #include "SourceBreakpoint.h"
10*b99d4112SJohn Harrison #include "BreakpointBase.h"
1101263c6cSJonas Devlieghere #include "DAP.h"
1209c258efSAdrian Vogelsgesang #include "JSONUtils.h"
13*b99d4112SJohn Harrison #include "lldb/API/SBBreakpoint.h"
14*b99d4112SJohn Harrison #include "lldb/API/SBFileSpecList.h"
15*b99d4112SJohn Harrison #include "lldb/API/SBFrame.h"
16*b99d4112SJohn Harrison #include "lldb/API/SBTarget.h"
17*b99d4112SJohn Harrison #include "lldb/API/SBThread.h"
18*b99d4112SJohn Harrison #include "lldb/API/SBValue.h"
19*b99d4112SJohn Harrison #include "lldb/lldb-enumerations.h"
20*b99d4112SJohn Harrison #include <cassert>
21*b99d4112SJohn Harrison #include <cctype>
22*b99d4112SJohn Harrison #include <cstdlib>
23*b99d4112SJohn Harrison #include <utility>
2401263c6cSJonas Devlieghere 
2501263c6cSJonas Devlieghere namespace lldb_dap {
2601263c6cSJonas Devlieghere 
27*b99d4112SJohn Harrison SourceBreakpoint::SourceBreakpoint(DAP &dap, const llvm::json::Object &obj)
28*b99d4112SJohn Harrison     : Breakpoint(dap, obj),
29*b99d4112SJohn Harrison       logMessage(std::string(GetString(obj, "logMessage"))),
30d58c128bSZequan Wu       line(GetUnsigned(obj, "line", 0)), column(GetUnsigned(obj, "column", 0)) {
31d58c128bSZequan Wu }
3201263c6cSJonas Devlieghere 
3301263c6cSJonas Devlieghere void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) {
3401263c6cSJonas Devlieghere   lldb::SBFileSpecList module_list;
35*b99d4112SJohn Harrison   bp = dap.target.BreakpointCreateByLocation(source_path.str().c_str(), line,
3601263c6cSJonas Devlieghere                                              column, 0, module_list);
3701263c6cSJonas Devlieghere   if (!logMessage.empty())
3801263c6cSJonas Devlieghere     SetLogMessage();
39d58c128bSZequan Wu   Breakpoint::SetBreakpoint();
40d58c128bSZequan Wu }
41d58c128bSZequan Wu 
42d58c128bSZequan Wu void SourceBreakpoint::UpdateBreakpoint(const SourceBreakpoint &request_bp) {
43d58c128bSZequan Wu   if (logMessage != request_bp.logMessage) {
44d58c128bSZequan Wu     logMessage = request_bp.logMessage;
45d58c128bSZequan Wu     SetLogMessage();
46d58c128bSZequan Wu   }
47d58c128bSZequan Wu   BreakpointBase::UpdateBreakpoint(request_bp);
48d58c128bSZequan Wu }
49d58c128bSZequan Wu 
50d58c128bSZequan Wu lldb::SBError SourceBreakpoint::AppendLogMessagePart(llvm::StringRef part,
51d58c128bSZequan Wu                                                      bool is_expr) {
52d58c128bSZequan Wu   if (is_expr) {
53d58c128bSZequan Wu     logMessageParts.emplace_back(part, is_expr);
54d58c128bSZequan Wu   } else {
55d58c128bSZequan Wu     std::string formatted;
56d58c128bSZequan Wu     lldb::SBError error = FormatLogText(part, formatted);
57d58c128bSZequan Wu     if (error.Fail())
58d58c128bSZequan Wu       return error;
59d58c128bSZequan Wu     logMessageParts.emplace_back(formatted, is_expr);
60d58c128bSZequan Wu   }
61d58c128bSZequan Wu   return lldb::SBError();
62d58c128bSZequan Wu }
63d58c128bSZequan Wu 
64d58c128bSZequan Wu // TODO: consolidate this code with the implementation in
65d58c128bSZequan Wu // FormatEntity::ParseInternal().
66d58c128bSZequan Wu lldb::SBError SourceBreakpoint::FormatLogText(llvm::StringRef text,
67d58c128bSZequan Wu                                               std::string &formatted) {
68d58c128bSZequan Wu   lldb::SBError error;
69d58c128bSZequan Wu   while (!text.empty()) {
70d58c128bSZequan Wu     size_t backslash_pos = text.find_first_of('\\');
71d58c128bSZequan Wu     if (backslash_pos == std::string::npos) {
72d58c128bSZequan Wu       formatted += text.str();
73d58c128bSZequan Wu       return error;
74d58c128bSZequan Wu     }
75d58c128bSZequan Wu 
76d58c128bSZequan Wu     formatted += text.substr(0, backslash_pos).str();
77d58c128bSZequan Wu     // Skip the characters before and including '\'.
78d58c128bSZequan Wu     text = text.drop_front(backslash_pos + 1);
79d58c128bSZequan Wu 
80d58c128bSZequan Wu     if (text.empty()) {
81d58c128bSZequan Wu       error.SetErrorString(
82d58c128bSZequan Wu           "'\\' character was not followed by another character");
83d58c128bSZequan Wu       return error;
84d58c128bSZequan Wu     }
85d58c128bSZequan Wu 
86d58c128bSZequan Wu     const char desens_char = text[0];
87d58c128bSZequan Wu     text = text.drop_front(); // Skip the desensitized char character
88d58c128bSZequan Wu     switch (desens_char) {
89d58c128bSZequan Wu     case 'a':
90d58c128bSZequan Wu       formatted.push_back('\a');
91d58c128bSZequan Wu       break;
92d58c128bSZequan Wu     case 'b':
93d58c128bSZequan Wu       formatted.push_back('\b');
94d58c128bSZequan Wu       break;
95d58c128bSZequan Wu     case 'f':
96d58c128bSZequan Wu       formatted.push_back('\f');
97d58c128bSZequan Wu       break;
98d58c128bSZequan Wu     case 'n':
99d58c128bSZequan Wu       formatted.push_back('\n');
100d58c128bSZequan Wu       break;
101d58c128bSZequan Wu     case 'r':
102d58c128bSZequan Wu       formatted.push_back('\r');
103d58c128bSZequan Wu       break;
104d58c128bSZequan Wu     case 't':
105d58c128bSZequan Wu       formatted.push_back('\t');
106d58c128bSZequan Wu       break;
107d58c128bSZequan Wu     case 'v':
108d58c128bSZequan Wu       formatted.push_back('\v');
109d58c128bSZequan Wu       break;
110d58c128bSZequan Wu     case '\'':
111d58c128bSZequan Wu       formatted.push_back('\'');
112d58c128bSZequan Wu       break;
113d58c128bSZequan Wu     case '\\':
114d58c128bSZequan Wu       formatted.push_back('\\');
115d58c128bSZequan Wu       break;
116d58c128bSZequan Wu     case '0':
117d58c128bSZequan Wu       // 1 to 3 octal chars
118d58c128bSZequan Wu       {
119d58c128bSZequan Wu         if (text.empty()) {
120d58c128bSZequan Wu           error.SetErrorString("missing octal number following '\\0'");
121d58c128bSZequan Wu           return error;
122d58c128bSZequan Wu         }
123d58c128bSZequan Wu 
124d58c128bSZequan Wu         // Make a string that can hold onto the initial zero char, up to 3
125d58c128bSZequan Wu         // octal digits, and a terminating NULL.
126d58c128bSZequan Wu         char oct_str[5] = {0, 0, 0, 0, 0};
127d58c128bSZequan Wu 
128d58c128bSZequan Wu         size_t i;
129d58c128bSZequan Wu         for (i = 0;
130d58c128bSZequan Wu              i < text.size() && i < 4 && (text[i] >= '0' && text[i] <= '7');
131d58c128bSZequan Wu              ++i) {
132d58c128bSZequan Wu           oct_str[i] = text[i];
133d58c128bSZequan Wu         }
134d58c128bSZequan Wu 
135d58c128bSZequan Wu         text = text.drop_front(i);
136d58c128bSZequan Wu         unsigned long octal_value = ::strtoul(oct_str, nullptr, 8);
137d58c128bSZequan Wu         if (octal_value <= UINT8_MAX) {
138d58c128bSZequan Wu           formatted.push_back((char)octal_value);
139d58c128bSZequan Wu         } else {
140d58c128bSZequan Wu           error.SetErrorString("octal number is larger than a single byte");
141d58c128bSZequan Wu           return error;
142d58c128bSZequan Wu         }
143d58c128bSZequan Wu       }
144d58c128bSZequan Wu       break;
145d58c128bSZequan Wu 
146d58c128bSZequan Wu     case 'x': {
147d58c128bSZequan Wu       if (text.empty()) {
148d58c128bSZequan Wu         error.SetErrorString("missing hex number following '\\x'");
149d58c128bSZequan Wu         return error;
150d58c128bSZequan Wu       }
151d58c128bSZequan Wu       // hex number in the text
152*b99d4112SJohn Harrison       if (std::isxdigit(text[0])) {
153d58c128bSZequan Wu         // Make a string that can hold onto two hex chars plus a
154d58c128bSZequan Wu         // NULL terminator
155d58c128bSZequan Wu         char hex_str[3] = {0, 0, 0};
156d58c128bSZequan Wu         hex_str[0] = text[0];
157d58c128bSZequan Wu 
158d58c128bSZequan Wu         text = text.drop_front();
159d58c128bSZequan Wu 
160*b99d4112SJohn Harrison         if (!text.empty() && std::isxdigit(text[0])) {
161d58c128bSZequan Wu           hex_str[1] = text[0];
162d58c128bSZequan Wu           text = text.drop_front();
163d58c128bSZequan Wu         }
164d58c128bSZequan Wu 
165d58c128bSZequan Wu         unsigned long hex_value = strtoul(hex_str, nullptr, 16);
166d58c128bSZequan Wu         if (hex_value <= UINT8_MAX) {
167d58c128bSZequan Wu           formatted.push_back((char)hex_value);
168d58c128bSZequan Wu         } else {
169d58c128bSZequan Wu           error.SetErrorString("hex number is larger than a single byte");
170d58c128bSZequan Wu           return error;
171d58c128bSZequan Wu         }
172d58c128bSZequan Wu       } else {
173d58c128bSZequan Wu         formatted.push_back(desens_char);
174d58c128bSZequan Wu       }
175d58c128bSZequan Wu       break;
176d58c128bSZequan Wu     }
177d58c128bSZequan Wu 
178d58c128bSZequan Wu     default:
179d58c128bSZequan Wu       // Just desensitize any other character by just printing what came
180d58c128bSZequan Wu       // after the '\'
181d58c128bSZequan Wu       formatted.push_back(desens_char);
182d58c128bSZequan Wu       break;
183d58c128bSZequan Wu     }
184d58c128bSZequan Wu   }
185d58c128bSZequan Wu   return error;
186d58c128bSZequan Wu }
187d58c128bSZequan Wu 
188d58c128bSZequan Wu // logMessage will be divided into array of LogMessagePart as two kinds:
189d58c128bSZequan Wu // 1. raw print text message, and
190d58c128bSZequan Wu // 2. interpolated expression for evaluation which is inside matching curly
191d58c128bSZequan Wu //    braces.
192d58c128bSZequan Wu //
193d58c128bSZequan Wu // The function tries to parse logMessage into a list of LogMessageParts
194d58c128bSZequan Wu // for easy later access in BreakpointHitCallback.
195d58c128bSZequan Wu void SourceBreakpoint::SetLogMessage() {
196d58c128bSZequan Wu   logMessageParts.clear();
197d58c128bSZequan Wu 
198d58c128bSZequan Wu   // Contains unmatched open curly braces indices.
199d58c128bSZequan Wu   std::vector<int> unmatched_curly_braces;
200d58c128bSZequan Wu 
201d58c128bSZequan Wu   // Contains all matched curly braces in logMessage.
202d58c128bSZequan Wu   // Loop invariant: matched_curly_braces_ranges are sorted by start index in
203d58c128bSZequan Wu   // ascending order without any overlap between them.
204d58c128bSZequan Wu   std::vector<std::pair<int, int>> matched_curly_braces_ranges;
205d58c128bSZequan Wu 
206d58c128bSZequan Wu   lldb::SBError error;
207d58c128bSZequan Wu   // Part1 - parse matched_curly_braces_ranges.
208d58c128bSZequan Wu   // locating all curly braced expression ranges in logMessage.
209d58c128bSZequan Wu   // The algorithm takes care of nested and imbalanced curly braces.
210d58c128bSZequan Wu   for (size_t i = 0; i < logMessage.size(); ++i) {
211d58c128bSZequan Wu     if (logMessage[i] == '{') {
212d58c128bSZequan Wu       unmatched_curly_braces.push_back(i);
213d58c128bSZequan Wu     } else if (logMessage[i] == '}') {
214d58c128bSZequan Wu       if (unmatched_curly_braces.empty())
215d58c128bSZequan Wu         // Nothing to match.
216d58c128bSZequan Wu         continue;
217d58c128bSZequan Wu 
218d58c128bSZequan Wu       int last_unmatched_index = unmatched_curly_braces.back();
219d58c128bSZequan Wu       unmatched_curly_braces.pop_back();
220d58c128bSZequan Wu 
221d58c128bSZequan Wu       // Erase any matched ranges included in the new match.
222d58c128bSZequan Wu       while (!matched_curly_braces_ranges.empty()) {
223d58c128bSZequan Wu         assert(matched_curly_braces_ranges.back().first !=
224d58c128bSZequan Wu                    last_unmatched_index &&
225d58c128bSZequan Wu                "How can a curley brace be matched twice?");
226d58c128bSZequan Wu         if (matched_curly_braces_ranges.back().first < last_unmatched_index)
227d58c128bSZequan Wu           break;
228d58c128bSZequan Wu 
229d58c128bSZequan Wu         // This is a nested range let's earse it.
230d58c128bSZequan Wu         assert((size_t)matched_curly_braces_ranges.back().second < i);
231d58c128bSZequan Wu         matched_curly_braces_ranges.pop_back();
232d58c128bSZequan Wu       }
233d58c128bSZequan Wu 
234d58c128bSZequan Wu       // Assert invariant.
235d58c128bSZequan Wu       assert(matched_curly_braces_ranges.empty() ||
236d58c128bSZequan Wu              matched_curly_braces_ranges.back().first < last_unmatched_index);
237d58c128bSZequan Wu       matched_curly_braces_ranges.emplace_back(last_unmatched_index, i);
238d58c128bSZequan Wu     }
239d58c128bSZequan Wu   }
240d58c128bSZequan Wu 
241d58c128bSZequan Wu   // Part2 - parse raw text and expresions parts.
242d58c128bSZequan Wu   // All expression ranges have been parsed in matched_curly_braces_ranges.
243d58c128bSZequan Wu   // The code below uses matched_curly_braces_ranges to divide logMessage
244d58c128bSZequan Wu   // into raw text parts and expression parts.
245d58c128bSZequan Wu   int last_raw_text_start = 0;
246d58c128bSZequan Wu   for (const std::pair<int, int> &curly_braces_range :
247d58c128bSZequan Wu        matched_curly_braces_ranges) {
248d58c128bSZequan Wu     // Raw text before open curly brace.
249d58c128bSZequan Wu     assert(curly_braces_range.first >= last_raw_text_start);
250d58c128bSZequan Wu     size_t raw_text_len = curly_braces_range.first - last_raw_text_start;
251d58c128bSZequan Wu     if (raw_text_len > 0) {
252d58c128bSZequan Wu       error = AppendLogMessagePart(
253d58c128bSZequan Wu           llvm::StringRef(logMessage.c_str() + last_raw_text_start,
254d58c128bSZequan Wu                           raw_text_len),
255d58c128bSZequan Wu           /*is_expr=*/false);
256d58c128bSZequan Wu       if (error.Fail()) {
257d58c128bSZequan Wu         NotifyLogMessageError(error.GetCString());
258d58c128bSZequan Wu         return;
259d58c128bSZequan Wu       }
260d58c128bSZequan Wu     }
261d58c128bSZequan Wu 
262d58c128bSZequan Wu     // Expression between curly braces.
263d58c128bSZequan Wu     assert(curly_braces_range.second > curly_braces_range.first);
264d58c128bSZequan Wu     size_t expr_len = curly_braces_range.second - curly_braces_range.first - 1;
265d58c128bSZequan Wu     error = AppendLogMessagePart(
266d58c128bSZequan Wu         llvm::StringRef(logMessage.c_str() + curly_braces_range.first + 1,
267d58c128bSZequan Wu                         expr_len),
268d58c128bSZequan Wu         /*is_expr=*/true);
269d58c128bSZequan Wu     if (error.Fail()) {
270d58c128bSZequan Wu       NotifyLogMessageError(error.GetCString());
271d58c128bSZequan Wu       return;
272d58c128bSZequan Wu     }
273d58c128bSZequan Wu 
274d58c128bSZequan Wu     last_raw_text_start = curly_braces_range.second + 1;
275d58c128bSZequan Wu   }
276d58c128bSZequan Wu   // Trailing raw text after close curly brace.
277d58c128bSZequan Wu   assert(last_raw_text_start >= 0);
278d58c128bSZequan Wu   if (logMessage.size() > (size_t)last_raw_text_start) {
279d58c128bSZequan Wu     error = AppendLogMessagePart(
280d58c128bSZequan Wu         llvm::StringRef(logMessage.c_str() + last_raw_text_start,
281d58c128bSZequan Wu                         logMessage.size() - last_raw_text_start),
282d58c128bSZequan Wu         /*is_expr=*/false);
283d58c128bSZequan Wu     if (error.Fail()) {
284d58c128bSZequan Wu       NotifyLogMessageError(error.GetCString());
285d58c128bSZequan Wu       return;
286d58c128bSZequan Wu     }
287d58c128bSZequan Wu   }
288d58c128bSZequan Wu 
289d58c128bSZequan Wu   bp.SetCallback(BreakpointHitCallback, this);
290d58c128bSZequan Wu }
291d58c128bSZequan Wu 
292d58c128bSZequan Wu void SourceBreakpoint::NotifyLogMessageError(llvm::StringRef error) {
293d58c128bSZequan Wu   std::string message = "Log message has error: ";
294d58c128bSZequan Wu   message += error;
295*b99d4112SJohn Harrison   dap.SendOutput(OutputType::Console, message);
296d58c128bSZequan Wu }
297d58c128bSZequan Wu 
298d58c128bSZequan Wu /*static*/
299d58c128bSZequan Wu bool SourceBreakpoint::BreakpointHitCallback(
300d58c128bSZequan Wu     void *baton, lldb::SBProcess &process, lldb::SBThread &thread,
301d58c128bSZequan Wu     lldb::SBBreakpointLocation &location) {
302d58c128bSZequan Wu   if (!baton)
303d58c128bSZequan Wu     return true;
304d58c128bSZequan Wu 
305d58c128bSZequan Wu   SourceBreakpoint *bp = (SourceBreakpoint *)baton;
306d58c128bSZequan Wu   lldb::SBFrame frame = thread.GetSelectedFrame();
307d58c128bSZequan Wu 
308d58c128bSZequan Wu   std::string output;
309d58c128bSZequan Wu   for (const SourceBreakpoint::LogMessagePart &messagePart :
310d58c128bSZequan Wu        bp->logMessageParts) {
311d58c128bSZequan Wu     if (messagePart.is_expr) {
312d58c128bSZequan Wu       // Try local frame variables first before fall back to expression
313d58c128bSZequan Wu       // evaluation
314d58c128bSZequan Wu       const std::string &expr_str = messagePart.text;
315d58c128bSZequan Wu       const char *expr = expr_str.c_str();
316d58c128bSZequan Wu       lldb::SBValue value =
317d58c128bSZequan Wu           frame.GetValueForVariablePath(expr, lldb::eDynamicDontRunTarget);
318d58c128bSZequan Wu       if (value.GetError().Fail())
319d58c128bSZequan Wu         value = frame.EvaluateExpression(expr);
320*b99d4112SJohn Harrison       output +=
321*b99d4112SJohn Harrison           VariableDescription(value, bp->dap.enable_auto_variable_summaries)
322*b99d4112SJohn Harrison               .display_value;
323d58c128bSZequan Wu     } else {
324d58c128bSZequan Wu       output += messagePart.text;
325d58c128bSZequan Wu     }
326d58c128bSZequan Wu   }
327d58c128bSZequan Wu   if (!output.empty() && output.back() != '\n')
328d58c128bSZequan Wu     output.push_back('\n'); // Ensure log message has line break.
329*b99d4112SJohn Harrison   bp->dap.SendOutput(OutputType::Console, output.c_str());
330d58c128bSZequan Wu 
331d58c128bSZequan Wu   // Do not stop.
332d58c128bSZequan Wu   return false;
33301263c6cSJonas Devlieghere }
33401263c6cSJonas Devlieghere 
33501263c6cSJonas Devlieghere } // namespace lldb_dap
336