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