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