1061da546Spatrick //===-- BreakpointBase.cpp --------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "BreakpointBase.h"
10*f6aab3d8Srobert #include "VSCode.h"
11061da546Spatrick #include "llvm/ADT/StringExtras.h"
12061da546Spatrick
13061da546Spatrick using namespace lldb_vscode;
14061da546Spatrick
BreakpointBase(const llvm::json::Object & obj)15061da546Spatrick BreakpointBase::BreakpointBase(const llvm::json::Object &obj)
16dda28197Spatrick : condition(std::string(GetString(obj, "condition"))),
17dda28197Spatrick hitCondition(std::string(GetString(obj, "hitCondition"))),
18dda28197Spatrick logMessage(std::string(GetString(obj, "logMessage"))) {}
19061da546Spatrick
SetCondition()20061da546Spatrick void BreakpointBase::SetCondition() { bp.SetCondition(condition.c_str()); }
21061da546Spatrick
SetHitCondition()22061da546Spatrick void BreakpointBase::SetHitCondition() {
23061da546Spatrick uint64_t hitCount = 0;
24061da546Spatrick if (llvm::to_integer(hitCondition, hitCount))
25061da546Spatrick bp.SetIgnoreCount(hitCount - 1);
26061da546Spatrick }
27061da546Spatrick
AppendLogMessagePart(llvm::StringRef part,bool is_expr)28*f6aab3d8Srobert lldb::SBError BreakpointBase::AppendLogMessagePart(llvm::StringRef part,
29*f6aab3d8Srobert bool is_expr) {
30*f6aab3d8Srobert if (is_expr) {
31*f6aab3d8Srobert logMessageParts.emplace_back(part, is_expr);
32*f6aab3d8Srobert } else {
33*f6aab3d8Srobert std::string formatted;
34*f6aab3d8Srobert lldb::SBError error = FormatLogText(part, formatted);
35*f6aab3d8Srobert if (error.Fail())
36*f6aab3d8Srobert return error;
37*f6aab3d8Srobert logMessageParts.emplace_back(formatted, is_expr);
38*f6aab3d8Srobert }
39*f6aab3d8Srobert return lldb::SBError();
40*f6aab3d8Srobert }
41*f6aab3d8Srobert
42*f6aab3d8Srobert // TODO: consolidate this code with the implementation in
43*f6aab3d8Srobert // FormatEntity::ParseInternal().
FormatLogText(llvm::StringRef text,std::string & formatted)44*f6aab3d8Srobert lldb::SBError BreakpointBase::FormatLogText(llvm::StringRef text,
45*f6aab3d8Srobert std::string &formatted) {
46*f6aab3d8Srobert lldb::SBError error;
47*f6aab3d8Srobert while (!text.empty()) {
48*f6aab3d8Srobert size_t backslash_pos = text.find_first_of('\\');
49*f6aab3d8Srobert if (backslash_pos == std::string::npos) {
50*f6aab3d8Srobert formatted += text.str();
51*f6aab3d8Srobert return error;
52*f6aab3d8Srobert }
53*f6aab3d8Srobert
54*f6aab3d8Srobert formatted += text.substr(0, backslash_pos).str();
55*f6aab3d8Srobert // Skip the characters before and including '\'.
56*f6aab3d8Srobert text = text.drop_front(backslash_pos + 1);
57*f6aab3d8Srobert
58*f6aab3d8Srobert if (text.empty()) {
59*f6aab3d8Srobert error.SetErrorString(
60*f6aab3d8Srobert "'\\' character was not followed by another character");
61*f6aab3d8Srobert return error;
62*f6aab3d8Srobert }
63*f6aab3d8Srobert
64*f6aab3d8Srobert const char desens_char = text[0];
65*f6aab3d8Srobert text = text.drop_front(); // Skip the desensitized char character
66*f6aab3d8Srobert switch (desens_char) {
67*f6aab3d8Srobert case 'a':
68*f6aab3d8Srobert formatted.push_back('\a');
69*f6aab3d8Srobert break;
70*f6aab3d8Srobert case 'b':
71*f6aab3d8Srobert formatted.push_back('\b');
72*f6aab3d8Srobert break;
73*f6aab3d8Srobert case 'f':
74*f6aab3d8Srobert formatted.push_back('\f');
75*f6aab3d8Srobert break;
76*f6aab3d8Srobert case 'n':
77*f6aab3d8Srobert formatted.push_back('\n');
78*f6aab3d8Srobert break;
79*f6aab3d8Srobert case 'r':
80*f6aab3d8Srobert formatted.push_back('\r');
81*f6aab3d8Srobert break;
82*f6aab3d8Srobert case 't':
83*f6aab3d8Srobert formatted.push_back('\t');
84*f6aab3d8Srobert break;
85*f6aab3d8Srobert case 'v':
86*f6aab3d8Srobert formatted.push_back('\v');
87*f6aab3d8Srobert break;
88*f6aab3d8Srobert case '\'':
89*f6aab3d8Srobert formatted.push_back('\'');
90*f6aab3d8Srobert break;
91*f6aab3d8Srobert case '\\':
92*f6aab3d8Srobert formatted.push_back('\\');
93*f6aab3d8Srobert break;
94*f6aab3d8Srobert case '0':
95*f6aab3d8Srobert // 1 to 3 octal chars
96*f6aab3d8Srobert {
97*f6aab3d8Srobert if (text.empty()) {
98*f6aab3d8Srobert error.SetErrorString("missing octal number following '\\0'");
99*f6aab3d8Srobert return error;
100*f6aab3d8Srobert }
101*f6aab3d8Srobert
102*f6aab3d8Srobert // Make a string that can hold onto the initial zero char, up to 3
103*f6aab3d8Srobert // octal digits, and a terminating NULL.
104*f6aab3d8Srobert char oct_str[5] = {0, 0, 0, 0, 0};
105*f6aab3d8Srobert
106*f6aab3d8Srobert size_t i;
107*f6aab3d8Srobert for (i = 0;
108*f6aab3d8Srobert i < text.size() && i < 4 && (text[i] >= '0' && text[i] <= '7');
109*f6aab3d8Srobert ++i) {
110*f6aab3d8Srobert oct_str[i] = text[i];
111*f6aab3d8Srobert }
112*f6aab3d8Srobert
113*f6aab3d8Srobert text = text.drop_front(i);
114*f6aab3d8Srobert unsigned long octal_value = ::strtoul(oct_str, nullptr, 8);
115*f6aab3d8Srobert if (octal_value <= UINT8_MAX) {
116*f6aab3d8Srobert formatted.push_back((char)octal_value);
117*f6aab3d8Srobert } else {
118*f6aab3d8Srobert error.SetErrorString("octal number is larger than a single byte");
119*f6aab3d8Srobert return error;
120*f6aab3d8Srobert }
121*f6aab3d8Srobert }
122*f6aab3d8Srobert break;
123*f6aab3d8Srobert
124*f6aab3d8Srobert case 'x': {
125*f6aab3d8Srobert if (text.empty()) {
126*f6aab3d8Srobert error.SetErrorString("missing hex number following '\\x'");
127*f6aab3d8Srobert return error;
128*f6aab3d8Srobert }
129*f6aab3d8Srobert // hex number in the text
130*f6aab3d8Srobert if (isxdigit(text[0])) {
131*f6aab3d8Srobert // Make a string that can hold onto two hex chars plus a
132*f6aab3d8Srobert // NULL terminator
133*f6aab3d8Srobert char hex_str[3] = {0, 0, 0};
134*f6aab3d8Srobert hex_str[0] = text[0];
135*f6aab3d8Srobert
136*f6aab3d8Srobert text = text.drop_front();
137*f6aab3d8Srobert
138*f6aab3d8Srobert if (!text.empty() && isxdigit(text[0])) {
139*f6aab3d8Srobert hex_str[1] = text[0];
140*f6aab3d8Srobert text = text.drop_front();
141*f6aab3d8Srobert }
142*f6aab3d8Srobert
143*f6aab3d8Srobert unsigned long hex_value = strtoul(hex_str, nullptr, 16);
144*f6aab3d8Srobert if (hex_value <= UINT8_MAX) {
145*f6aab3d8Srobert formatted.push_back((char)hex_value);
146*f6aab3d8Srobert } else {
147*f6aab3d8Srobert error.SetErrorString("hex number is larger than a single byte");
148*f6aab3d8Srobert return error;
149*f6aab3d8Srobert }
150*f6aab3d8Srobert } else {
151*f6aab3d8Srobert formatted.push_back(desens_char);
152*f6aab3d8Srobert }
153*f6aab3d8Srobert break;
154*f6aab3d8Srobert }
155*f6aab3d8Srobert
156*f6aab3d8Srobert default:
157*f6aab3d8Srobert // Just desensitize any other character by just printing what came
158*f6aab3d8Srobert // after the '\'
159*f6aab3d8Srobert formatted.push_back(desens_char);
160*f6aab3d8Srobert break;
161*f6aab3d8Srobert }
162*f6aab3d8Srobert }
163*f6aab3d8Srobert return error;
164*f6aab3d8Srobert }
165*f6aab3d8Srobert
166*f6aab3d8Srobert // logMessage will be divided into array of LogMessagePart as two kinds:
167*f6aab3d8Srobert // 1. raw print text message, and
168*f6aab3d8Srobert // 2. interpolated expression for evaluation which is inside matching curly
169*f6aab3d8Srobert // braces.
170*f6aab3d8Srobert //
171*f6aab3d8Srobert // The function tries to parse logMessage into a list of LogMessageParts
172*f6aab3d8Srobert // for easy later access in BreakpointHitCallback.
SetLogMessage()173*f6aab3d8Srobert void BreakpointBase::SetLogMessage() {
174*f6aab3d8Srobert logMessageParts.clear();
175*f6aab3d8Srobert
176*f6aab3d8Srobert // Contains unmatched open curly braces indices.
177*f6aab3d8Srobert std::vector<int> unmatched_curly_braces;
178*f6aab3d8Srobert
179*f6aab3d8Srobert // Contains all matched curly braces in logMessage.
180*f6aab3d8Srobert // Loop invariant: matched_curly_braces_ranges are sorted by start index in
181*f6aab3d8Srobert // ascending order without any overlap between them.
182*f6aab3d8Srobert std::vector<std::pair<int, int>> matched_curly_braces_ranges;
183*f6aab3d8Srobert
184*f6aab3d8Srobert lldb::SBError error;
185*f6aab3d8Srobert // Part1 - parse matched_curly_braces_ranges.
186*f6aab3d8Srobert // locating all curly braced expression ranges in logMessage.
187*f6aab3d8Srobert // The algorithm takes care of nested and imbalanced curly braces.
188*f6aab3d8Srobert for (size_t i = 0; i < logMessage.size(); ++i) {
189*f6aab3d8Srobert if (logMessage[i] == '{') {
190*f6aab3d8Srobert unmatched_curly_braces.push_back(i);
191*f6aab3d8Srobert } else if (logMessage[i] == '}') {
192*f6aab3d8Srobert if (unmatched_curly_braces.empty())
193*f6aab3d8Srobert // Nothing to match.
194*f6aab3d8Srobert continue;
195*f6aab3d8Srobert
196*f6aab3d8Srobert int last_unmatched_index = unmatched_curly_braces.back();
197*f6aab3d8Srobert unmatched_curly_braces.pop_back();
198*f6aab3d8Srobert
199*f6aab3d8Srobert // Erase any matched ranges included in the new match.
200*f6aab3d8Srobert while (!matched_curly_braces_ranges.empty()) {
201*f6aab3d8Srobert assert(matched_curly_braces_ranges.back().first !=
202*f6aab3d8Srobert last_unmatched_index &&
203*f6aab3d8Srobert "How can a curley brace be matched twice?");
204*f6aab3d8Srobert if (matched_curly_braces_ranges.back().first < last_unmatched_index)
205*f6aab3d8Srobert break;
206*f6aab3d8Srobert
207*f6aab3d8Srobert // This is a nested range let's earse it.
208*f6aab3d8Srobert assert((size_t)matched_curly_braces_ranges.back().second < i);
209*f6aab3d8Srobert matched_curly_braces_ranges.pop_back();
210*f6aab3d8Srobert }
211*f6aab3d8Srobert
212*f6aab3d8Srobert // Assert invariant.
213*f6aab3d8Srobert assert(matched_curly_braces_ranges.empty() ||
214*f6aab3d8Srobert matched_curly_braces_ranges.back().first < last_unmatched_index);
215*f6aab3d8Srobert matched_curly_braces_ranges.emplace_back(last_unmatched_index, i);
216*f6aab3d8Srobert }
217*f6aab3d8Srobert }
218*f6aab3d8Srobert
219*f6aab3d8Srobert // Part2 - parse raw text and expresions parts.
220*f6aab3d8Srobert // All expression ranges have been parsed in matched_curly_braces_ranges.
221*f6aab3d8Srobert // The code below uses matched_curly_braces_ranges to divide logMessage
222*f6aab3d8Srobert // into raw text parts and expression parts.
223*f6aab3d8Srobert int last_raw_text_start = 0;
224*f6aab3d8Srobert for (const std::pair<int, int> &curly_braces_range :
225*f6aab3d8Srobert matched_curly_braces_ranges) {
226*f6aab3d8Srobert // Raw text before open curly brace.
227*f6aab3d8Srobert assert(curly_braces_range.first >= last_raw_text_start);
228*f6aab3d8Srobert size_t raw_text_len = curly_braces_range.first - last_raw_text_start;
229*f6aab3d8Srobert if (raw_text_len > 0) {
230*f6aab3d8Srobert error = AppendLogMessagePart(
231*f6aab3d8Srobert llvm::StringRef(logMessage.c_str() + last_raw_text_start,
232*f6aab3d8Srobert raw_text_len),
233*f6aab3d8Srobert /*is_expr=*/false);
234*f6aab3d8Srobert if (error.Fail()) {
235*f6aab3d8Srobert NotifyLogMessageError(error.GetCString());
236*f6aab3d8Srobert return;
237*f6aab3d8Srobert }
238*f6aab3d8Srobert }
239*f6aab3d8Srobert
240*f6aab3d8Srobert // Expression between curly braces.
241*f6aab3d8Srobert assert(curly_braces_range.second > curly_braces_range.first);
242*f6aab3d8Srobert size_t expr_len = curly_braces_range.second - curly_braces_range.first - 1;
243*f6aab3d8Srobert error = AppendLogMessagePart(
244*f6aab3d8Srobert llvm::StringRef(logMessage.c_str() + curly_braces_range.first + 1,
245*f6aab3d8Srobert expr_len),
246*f6aab3d8Srobert /*is_expr=*/true);
247*f6aab3d8Srobert if (error.Fail()) {
248*f6aab3d8Srobert NotifyLogMessageError(error.GetCString());
249*f6aab3d8Srobert return;
250*f6aab3d8Srobert }
251*f6aab3d8Srobert
252*f6aab3d8Srobert last_raw_text_start = curly_braces_range.second + 1;
253*f6aab3d8Srobert }
254*f6aab3d8Srobert // Trailing raw text after close curly brace.
255*f6aab3d8Srobert assert(last_raw_text_start >= 0);
256*f6aab3d8Srobert if (logMessage.size() > (size_t)last_raw_text_start) {
257*f6aab3d8Srobert error = AppendLogMessagePart(
258*f6aab3d8Srobert llvm::StringRef(logMessage.c_str() + last_raw_text_start,
259*f6aab3d8Srobert logMessage.size() - last_raw_text_start),
260*f6aab3d8Srobert /*is_expr=*/false);
261*f6aab3d8Srobert if (error.Fail()) {
262*f6aab3d8Srobert NotifyLogMessageError(error.GetCString());
263*f6aab3d8Srobert return;
264*f6aab3d8Srobert }
265*f6aab3d8Srobert }
266*f6aab3d8Srobert
267*f6aab3d8Srobert bp.SetCallback(BreakpointBase::BreakpointHitCallback, this);
268*f6aab3d8Srobert }
269*f6aab3d8Srobert
NotifyLogMessageError(llvm::StringRef error)270*f6aab3d8Srobert void BreakpointBase::NotifyLogMessageError(llvm::StringRef error) {
271*f6aab3d8Srobert std::string message = "Log message has error: ";
272*f6aab3d8Srobert message += error;
273*f6aab3d8Srobert g_vsc.SendOutput(OutputType::Console, message);
274*f6aab3d8Srobert }
275*f6aab3d8Srobert
276*f6aab3d8Srobert /*static*/
BreakpointHitCallback(void * baton,lldb::SBProcess & process,lldb::SBThread & thread,lldb::SBBreakpointLocation & location)277*f6aab3d8Srobert bool BreakpointBase::BreakpointHitCallback(
278*f6aab3d8Srobert void *baton, lldb::SBProcess &process, lldb::SBThread &thread,
279*f6aab3d8Srobert lldb::SBBreakpointLocation &location) {
280*f6aab3d8Srobert if (!baton)
281*f6aab3d8Srobert return true;
282*f6aab3d8Srobert
283*f6aab3d8Srobert BreakpointBase *bp = (BreakpointBase *)baton;
284*f6aab3d8Srobert lldb::SBFrame frame = thread.GetSelectedFrame();
285*f6aab3d8Srobert
286*f6aab3d8Srobert std::string output;
287*f6aab3d8Srobert for (const BreakpointBase::LogMessagePart &messagePart :
288*f6aab3d8Srobert bp->logMessageParts) {
289*f6aab3d8Srobert if (messagePart.is_expr) {
290*f6aab3d8Srobert // Try local frame variables first before fall back to expression
291*f6aab3d8Srobert // evaluation
292*f6aab3d8Srobert const std::string &expr_str = messagePart.text;
293*f6aab3d8Srobert const char *expr = expr_str.c_str();
294*f6aab3d8Srobert lldb::SBValue value =
295*f6aab3d8Srobert frame.GetValueForVariablePath(expr, lldb::eDynamicDontRunTarget);
296*f6aab3d8Srobert if (value.GetError().Fail())
297*f6aab3d8Srobert value = frame.EvaluateExpression(expr);
298*f6aab3d8Srobert const char *expr_val = value.GetValue();
299*f6aab3d8Srobert if (expr_val)
300*f6aab3d8Srobert output += expr_val;
301*f6aab3d8Srobert } else {
302*f6aab3d8Srobert output += messagePart.text;
303*f6aab3d8Srobert }
304*f6aab3d8Srobert }
305*f6aab3d8Srobert if (!output.empty() && output.back() != '\n')
306*f6aab3d8Srobert output.push_back('\n'); // Ensure log message has line break.
307*f6aab3d8Srobert g_vsc.SendOutput(OutputType::Console, output.c_str());
308*f6aab3d8Srobert
309*f6aab3d8Srobert // Do not stop.
310*f6aab3d8Srobert return false;
311*f6aab3d8Srobert }
312*f6aab3d8Srobert
UpdateBreakpoint(const BreakpointBase & request_bp)313061da546Spatrick void BreakpointBase::UpdateBreakpoint(const BreakpointBase &request_bp) {
314061da546Spatrick if (condition != request_bp.condition) {
315061da546Spatrick condition = request_bp.condition;
316061da546Spatrick SetCondition();
317061da546Spatrick }
318061da546Spatrick if (hitCondition != request_bp.hitCondition) {
319061da546Spatrick hitCondition = request_bp.hitCondition;
320061da546Spatrick SetHitCondition();
321061da546Spatrick }
322*f6aab3d8Srobert if (logMessage != request_bp.logMessage) {
323*f6aab3d8Srobert logMessage = request_bp.logMessage;
324*f6aab3d8Srobert SetLogMessage();
325*f6aab3d8Srobert }
326061da546Spatrick }
327dda28197Spatrick
GetBreakpointLabel()328dda28197Spatrick const char *BreakpointBase::GetBreakpointLabel() {
329dda28197Spatrick // Breakpoints in LLDB can have names added to them which are kind of like
330dda28197Spatrick // labels or categories. All breakpoints that are set through the IDE UI get
331dda28197Spatrick // sent through the various VS code DAP set*Breakpoint packets, and these
332dda28197Spatrick // breakpoints will be labeled with this name so if breakpoint update events
333dda28197Spatrick // come in for breakpoints that the IDE doesn't know about, like if a
334dda28197Spatrick // breakpoint is set manually using the debugger console, we won't report any
335dda28197Spatrick // updates on them and confused the IDE. This function gets called by all of
336dda28197Spatrick // the breakpoint classes after they set breakpoints to mark a breakpoint as
337dda28197Spatrick // a UI breakpoint. We can later check a lldb::SBBreakpoint object that comes
338dda28197Spatrick // in via LLDB breakpoint changed events and check the breakpoint by calling
339dda28197Spatrick // "bool lldb::SBBreakpoint::MatchesName(const char *)" to check if a
340dda28197Spatrick // breakpoint in one of the UI breakpoints that we should report changes for.
341dda28197Spatrick return "vscode";
342dda28197Spatrick }
343