brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.0 KiB · 843a5eb Raw
421 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "SourceBreakpoint.h"10#include "BreakpointBase.h"11#include "DAP.h"12#include "JSONUtils.h"13#include "ProtocolUtils.h"14#include "lldb/API/SBBreakpoint.h"15#include "lldb/API/SBFileSpec.h"16#include "lldb/API/SBFileSpecList.h"17#include "lldb/API/SBFrame.h"18#include "lldb/API/SBInstruction.h"19#include "lldb/API/SBMutex.h"20#include "lldb/API/SBSymbol.h"21#include "lldb/API/SBTarget.h"22#include "lldb/API/SBThread.h"23#include "lldb/API/SBValue.h"24#include "lldb/lldb-enumerations.h"25#include "llvm/Support/Error.h"26#include <cassert>27#include <cctype>28#include <cstdlib>29#include <mutex>30#include <utility>31 32namespace lldb_dap {33 34SourceBreakpoint::SourceBreakpoint(DAP &dap,35                                   const protocol::SourceBreakpoint &breakpoint)36    : Breakpoint(dap, breakpoint.condition, breakpoint.hitCondition),37      m_log_message(breakpoint.logMessage.value_or("")),38      m_line(breakpoint.line),39      m_column(breakpoint.column.value_or(LLDB_INVALID_COLUMN_NUMBER)) {}40 41llvm::Error SourceBreakpoint::SetBreakpoint(const protocol::Source &source) {42  lldb::SBMutex lock = m_dap.GetAPIMutex();43  std::lock_guard<lldb::SBMutex> guard(lock);44 45  if (m_line == 0)46    return llvm::createStringError(llvm::inconvertibleErrorCode(),47                                   "Invalid line number.");48 49  if (source.sourceReference) {50    // Breakpoint set by assembly source.51    if (source.adapterData && source.adapterData->persistenceData) {52      // Prefer use the adapter persitence data, because this could be a53      // breakpoint from a previous session where the `sourceReference` is not54      // valid anymore.55      if (llvm::Error error = CreateAssemblyBreakpointWithPersistenceData(56              *source.adapterData->persistenceData))57        return error;58    } else {59      if (llvm::Error error = CreateAssemblyBreakpointWithSourceReference(60              *source.sourceReference))61        return error;62    }63  } else {64    CreatePathBreakpoint(source);65  }66 67  if (!m_log_message.empty())68    SetLogMessage();69  Breakpoint::SetBreakpoint();70  return llvm::Error::success();71}72 73void SourceBreakpoint::UpdateBreakpoint(const SourceBreakpoint &request_bp) {74  if (m_log_message != request_bp.m_log_message) {75    m_log_message = request_bp.m_log_message;76    SetLogMessage();77  }78  BreakpointBase::UpdateBreakpoint(request_bp);79}80 81void SourceBreakpoint::CreatePathBreakpoint(const protocol::Source &source) {82  const auto source_path = source.path.value_or("");83  lldb::SBFileSpecList module_list;84  m_bp = m_dap.target.BreakpointCreateByLocation(source_path.c_str(), m_line,85                                                 m_column, 0, module_list);86}87 88llvm::Error SourceBreakpoint::CreateAssemblyBreakpointWithSourceReference(89    int64_t source_reference) {90  std::optional<lldb::addr_t> raw_addr =91      m_dap.GetSourceReferenceAddress(source_reference);92  if (!raw_addr)93    return llvm::createStringError(llvm::inconvertibleErrorCode(),94                                   "Invalid sourceReference.");95 96  lldb::SBAddress source_address(*raw_addr, m_dap.target);97  if (!source_address.IsValid())98    return llvm::createStringError(llvm::inconvertibleErrorCode(),99                                   "Invalid sourceReference.");100 101  lldb::SBSymbol symbol = source_address.GetSymbol();102  if (!symbol.IsValid()) {103    // FIXME: Support assembly breakpoints without a valid symbol.104    return llvm::createStringError(llvm::inconvertibleErrorCode(),105                                   "Breakpoints in assembly without a valid "106                                   "symbol are not supported yet.");107  }108 109  lldb::SBInstructionList inst_list =110      m_dap.target.ReadInstructions(symbol.GetStartAddress(), m_line);111  if (inst_list.GetSize() < m_line)112    return llvm::createStringError(llvm::inconvertibleErrorCode(),113                                   "Invalid instruction list size.");114 115  lldb::SBAddress address =116      inst_list.GetInstructionAtIndex(m_line - 1).GetAddress();117 118  m_bp = m_dap.target.BreakpointCreateBySBAddress(address);119  return llvm::Error::success();120}121 122llvm::Error SourceBreakpoint::CreateAssemblyBreakpointWithPersistenceData(123    const protocol::PersistenceData &persistence_data) {124  lldb::SBFileSpec file_spec(persistence_data.module_path.c_str());125  lldb::SBFileSpecList comp_unit_list;126  lldb::SBFileSpecList file_spec_list;127  file_spec_list.Append(file_spec);128  m_bp = m_dap.target.BreakpointCreateByName(129      persistence_data.symbol_name.c_str(), lldb::eFunctionNameTypeFull,130      lldb::eLanguageTypeUnknown, m_line - 1, true, file_spec_list,131      comp_unit_list);132  return llvm::Error::success();133}134 135lldb::SBError SourceBreakpoint::AppendLogMessagePart(llvm::StringRef part,136                                                     bool is_expr) {137  if (is_expr) {138    m_log_message_parts.emplace_back(part, is_expr);139  } else {140    std::string formatted;141    lldb::SBError error = FormatLogText(part, formatted);142    if (error.Fail())143      return error;144    m_log_message_parts.emplace_back(formatted, is_expr);145  }146  return lldb::SBError();147}148 149// TODO: consolidate this code with the implementation in150// FormatEntity::ParseInternal().151lldb::SBError SourceBreakpoint::FormatLogText(llvm::StringRef text,152                                              std::string &formatted) {153  lldb::SBError error;154  while (!text.empty()) {155    size_t backslash_pos = text.find_first_of('\\');156    if (backslash_pos == std::string::npos) {157      formatted += text.str();158      return error;159    }160 161    formatted += text.substr(0, backslash_pos).str();162    // Skip the characters before and including '\'.163    text = text.drop_front(backslash_pos + 1);164 165    if (text.empty()) {166      error.SetErrorString(167          "'\\' character was not followed by another character");168      return error;169    }170 171    const char desens_char = text[0];172    text = text.drop_front(); // Skip the desensitized char character173    switch (desens_char) {174    case 'a':175      formatted.push_back('\a');176      break;177    case 'b':178      formatted.push_back('\b');179      break;180    case 'f':181      formatted.push_back('\f');182      break;183    case 'n':184      formatted.push_back('\n');185      break;186    case 'r':187      formatted.push_back('\r');188      break;189    case 't':190      formatted.push_back('\t');191      break;192    case 'v':193      formatted.push_back('\v');194      break;195    case '\'':196      formatted.push_back('\'');197      break;198    case '\\':199      formatted.push_back('\\');200      break;201    case '0':202      // 1 to 3 octal chars203      {204        if (text.empty()) {205          error.SetErrorString("missing octal number following '\\0'");206          return error;207        }208 209        // Make a string that can hold onto the initial zero char, up to 3210        // octal digits, and a terminating NULL.211        char oct_str[5] = {0, 0, 0, 0, 0};212 213        size_t i;214        for (i = 0;215             i < text.size() && i < 4 && (text[i] >= '0' && text[i] <= '7');216             ++i) {217          oct_str[i] = text[i];218        }219 220        text = text.drop_front(i);221        unsigned long octal_value = ::strtoul(oct_str, nullptr, 8);222        if (octal_value <= UINT8_MAX) {223          formatted.push_back((char)octal_value);224        } else {225          error.SetErrorString("octal number is larger than a single byte");226          return error;227        }228      }229      break;230 231    case 'x': {232      if (text.empty()) {233        error.SetErrorString("missing hex number following '\\x'");234        return error;235      }236      // hex number in the text237      if (std::isxdigit(text[0])) {238        // Make a string that can hold onto two hex chars plus a239        // NULL terminator240        char hex_str[3] = {0, 0, 0};241        hex_str[0] = text[0];242 243        text = text.drop_front();244 245        if (!text.empty() && std::isxdigit(text[0])) {246          hex_str[1] = text[0];247          text = text.drop_front();248        }249 250        unsigned long hex_value = strtoul(hex_str, nullptr, 16);251        if (hex_value <= UINT8_MAX) {252          formatted.push_back((char)hex_value);253        } else {254          error.SetErrorString("hex number is larger than a single byte");255          return error;256        }257      } else {258        formatted.push_back(desens_char);259      }260      break;261    }262 263    default:264      // Just desensitize any other character by just printing what came265      // after the '\'266      formatted.push_back(desens_char);267      break;268    }269  }270  return error;271}272 273// logMessage will be divided into array of LogMessagePart as two kinds:274// 1. raw print text message, and275// 2. interpolated expression for evaluation which is inside matching curly276//    braces.277//278// The function tries to parse logMessage into a list of LogMessageParts279// for easy later access in BreakpointHitCallback.280void SourceBreakpoint::SetLogMessage() {281  m_log_message_parts.clear();282 283  // Contains unmatched open curly braces indices.284  std::vector<int> unmatched_curly_braces;285 286  // Contains all matched curly braces in logMessage.287  // Loop invariant: matched_curly_braces_ranges are sorted by start index in288  // ascending order without any overlap between them.289  std::vector<std::pair<int, int>> matched_curly_braces_ranges;290 291  lldb::SBError error;292  // Part1 - parse matched_curly_braces_ranges.293  // locating all curly braced expression ranges in logMessage.294  // The algorithm takes care of nested and imbalanced curly braces.295  for (size_t i = 0; i < m_log_message.size(); ++i) {296    if (m_log_message[i] == '{') {297      unmatched_curly_braces.push_back(i);298    } else if (m_log_message[i] == '}') {299      if (unmatched_curly_braces.empty())300        // Nothing to match.301        continue;302 303      int last_unmatched_index = unmatched_curly_braces.back();304      unmatched_curly_braces.pop_back();305 306      // Erase any matched ranges included in the new match.307      while (!matched_curly_braces_ranges.empty()) {308        assert(matched_curly_braces_ranges.back().first !=309                   last_unmatched_index &&310               "How can a curley brace be matched twice?");311        if (matched_curly_braces_ranges.back().first < last_unmatched_index)312          break;313 314        // This is a nested range let's earse it.315        assert((size_t)matched_curly_braces_ranges.back().second < i);316        matched_curly_braces_ranges.pop_back();317      }318 319      // Assert invariant.320      assert(matched_curly_braces_ranges.empty() ||321             matched_curly_braces_ranges.back().first < last_unmatched_index);322      matched_curly_braces_ranges.emplace_back(last_unmatched_index, i);323    }324  }325 326  // Part2 - parse raw text and expresions parts.327  // All expression ranges have been parsed in matched_curly_braces_ranges.328  // The code below uses matched_curly_braces_ranges to divide logMessage329  // into raw text parts and expression parts.330  int last_raw_text_start = 0;331  for (const std::pair<int, int> &curly_braces_range :332       matched_curly_braces_ranges) {333    // Raw text before open curly brace.334    assert(curly_braces_range.first >= last_raw_text_start);335    size_t raw_text_len = curly_braces_range.first - last_raw_text_start;336    if (raw_text_len > 0) {337      error = AppendLogMessagePart(338          llvm::StringRef(m_log_message.c_str() + last_raw_text_start,339                          raw_text_len),340          /*is_expr=*/false);341      if (error.Fail()) {342        NotifyLogMessageError(error.GetCString());343        return;344      }345    }346 347    // Expression between curly braces.348    assert(curly_braces_range.second > curly_braces_range.first);349    size_t expr_len = curly_braces_range.second - curly_braces_range.first - 1;350    error = AppendLogMessagePart(351        llvm::StringRef(m_log_message.c_str() + curly_braces_range.first + 1,352                        expr_len),353        /*is_expr=*/true);354    if (error.Fail()) {355      NotifyLogMessageError(error.GetCString());356      return;357    }358 359    last_raw_text_start = curly_braces_range.second + 1;360  }361  // Trailing raw text after close curly brace.362  assert(last_raw_text_start >= 0);363  if (m_log_message.size() > (size_t)last_raw_text_start) {364    error = AppendLogMessagePart(365        llvm::StringRef(m_log_message.c_str() + last_raw_text_start,366                        m_log_message.size() - last_raw_text_start),367        /*is_expr=*/false);368    if (error.Fail()) {369      NotifyLogMessageError(error.GetCString());370      return;371    }372  }373 374  m_bp.SetCallback(BreakpointHitCallback, this);375}376 377void SourceBreakpoint::NotifyLogMessageError(llvm::StringRef error) {378  std::string message = "Log message has error: ";379  message += error;380  m_dap.SendOutput(OutputType::Console, message);381}382 383/*static*/384bool SourceBreakpoint::BreakpointHitCallback(385    void *baton, lldb::SBProcess &process, lldb::SBThread &thread,386    lldb::SBBreakpointLocation &location) {387  if (!baton)388    return true;389 390  SourceBreakpoint *bp = (SourceBreakpoint *)baton;391  lldb::SBFrame frame = thread.GetSelectedFrame();392 393  std::string output;394  for (const SourceBreakpoint::LogMessagePart &messagePart :395       bp->m_log_message_parts) {396    if (messagePart.is_expr) {397      // Try local frame variables first before fall back to expression398      // evaluation399      const std::string &expr_str = messagePart.text;400      const char *expr = expr_str.c_str();401      lldb::SBValue value =402          frame.GetValueForVariablePath(expr, lldb::eDynamicDontRunTarget);403      if (value.GetError().Fail())404        value = frame.EvaluateExpression(expr);405      output += VariableDescription(406                    value, bp->m_dap.configuration.enableAutoVariableSummaries)407                    .display_value;408    } else {409      output += messagePart.text;410    }411  }412  if (!output.empty() && output.back() != '\n')413    output.push_back('\n'); // Ensure log message has line break.414  bp->m_dap.SendOutput(OutputType::Console, output.c_str());415 416  // Do not stop.417  return false;418}419 420} // namespace lldb_dap421