brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.5 KiB · 4ac4050 Raw
407 lines · cpp
1//===-- BreakpointResolver.cpp --------------------------------------------===//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 "lldb/Breakpoint/BreakpointResolver.h"10 11#include "lldb/Breakpoint/Breakpoint.h"12#include "lldb/Breakpoint/BreakpointLocation.h"13// Have to include the other breakpoint resolver types here so the static14// create from StructuredData can call them.15#include "lldb/Breakpoint/BreakpointResolverAddress.h"16#include "lldb/Breakpoint/BreakpointResolverFileLine.h"17#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"18#include "lldb/Breakpoint/BreakpointResolverName.h"19#include "lldb/Breakpoint/BreakpointResolverScripted.h"20#include "lldb/Core/Address.h"21#include "lldb/Core/ModuleList.h"22#include "lldb/Core/SearchFilter.h"23#include "lldb/Symbol/CompileUnit.h"24#include "lldb/Symbol/Function.h"25#include "lldb/Symbol/SymbolContext.h"26#include "lldb/Target/Language.h"27#include "lldb/Target/Target.h"28#include "lldb/Utility/LLDBLog.h"29#include "lldb/Utility/Log.h"30#include "lldb/Utility/Stream.h"31#include "lldb/Utility/StreamString.h"32#include <optional>33 34using namespace lldb_private;35using namespace lldb;36 37// BreakpointResolver:38const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address",39                                                  "SymbolName",  "SourceRegex",40                                                  "Python",   "Exception",41                                                  "Unknown"};42 43const char *BreakpointResolver::g_option_names[static_cast<uint32_t>(44    BreakpointResolver::OptionNames::LastOptionName)] = {45    "AddressOffset", "Exact",      "FileName",   "Inlines",     "Language",46    "LineNumber",    "Column",     "ModuleName", "NameMask",    "Offset",47    "PythonClass",   "Regex",      "ScriptArgs", "SectionName", "SearchDepth",48    "SkipPrologue",  "SymbolNames"};49 50const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type) {51  if (type > LastKnownResolverType)52    return g_ty_to_name[UnknownResolver];53 54  return g_ty_to_name[type];55}56 57BreakpointResolver::ResolverTy58BreakpointResolver::NameToResolverTy(llvm::StringRef name) {59  for (size_t i = 0; i < LastKnownResolverType; i++) {60    if (name == g_ty_to_name[i])61      return (ResolverTy)i;62  }63  return UnknownResolver;64}65 66BreakpointResolver::BreakpointResolver(const BreakpointSP &bkpt,67                                       const unsigned char resolverTy,68                                       lldb::addr_t offset,69                                       bool offset_is_insn_count)70    : m_breakpoint(bkpt), m_offset(offset),71      m_offset_is_insn_count(offset_is_insn_count), SubclassID(resolverTy) {}72 73BreakpointResolver::~BreakpointResolver() = default;74 75BreakpointResolverSP BreakpointResolver::CreateFromStructuredData(76    const StructuredData::Dictionary &resolver_dict, Status &error) {77  BreakpointResolverSP result_sp;78  if (!resolver_dict.IsValid()) {79    error = Status::FromErrorString(80        "Can't deserialize from an invalid data object.");81    return result_sp;82  }83 84  llvm::StringRef subclass_name;85 86  bool success = resolver_dict.GetValueForKeyAsString(87      GetSerializationSubclassKey(), subclass_name);88 89  if (!success) {90    error =91        Status::FromErrorString("Resolver data missing subclass resolver key");92    return result_sp;93  }94 95  ResolverTy resolver_type = NameToResolverTy(subclass_name);96  if (resolver_type == UnknownResolver) {97    error = Status::FromErrorStringWithFormatv("Unknown resolver type: {0}.",98                                               subclass_name);99    return result_sp;100  }101 102  StructuredData::Dictionary *subclass_options = nullptr;103  success = resolver_dict.GetValueForKeyAsDictionary(104      GetSerializationSubclassOptionsKey(), subclass_options);105  if (!success || !subclass_options || !subclass_options->IsValid()) {106    error =107        Status::FromErrorString("Resolver data missing subclass options key.");108    return result_sp;109  }110 111  lldb::offset_t offset;112  success = subclass_options->GetValueForKeyAsInteger(113      GetKey(OptionNames::Offset), offset);114  if (!success) {115    error =116        Status::FromErrorString("Resolver data missing offset options key.");117    return result_sp;118  }119 120  switch (resolver_type) {121  case FileLineResolver:122    result_sp = BreakpointResolverFileLine::CreateFromStructuredData(123        *subclass_options, error);124    break;125  case AddressResolver:126    result_sp = BreakpointResolverAddress::CreateFromStructuredData(127        *subclass_options, error);128    break;129  case NameResolver:130    result_sp = BreakpointResolverName::CreateFromStructuredData(131        *subclass_options, error);132    break;133  case FileRegexResolver:134    result_sp = BreakpointResolverFileRegex::CreateFromStructuredData(135        *subclass_options, error);136    break;137  case PythonResolver:138    result_sp = BreakpointResolverScripted::CreateFromStructuredData(139        *subclass_options, error);140    break;141  case ExceptionResolver:142    error = Status::FromErrorString("Exception resolvers are hard.");143    break;144  default:145    llvm_unreachable("Should never get an unresolvable resolver type.");146  }147 148  if (error.Fail() || !result_sp)149    return {};150 151  // Add on the global offset option:152  result_sp->SetOffset(offset);153  return result_sp;154}155 156StructuredData::DictionarySP BreakpointResolver::WrapOptionsDict(157    StructuredData::DictionarySP options_dict_sp) {158  if (!options_dict_sp || !options_dict_sp->IsValid())159    return StructuredData::DictionarySP();160 161  StructuredData::DictionarySP type_dict_sp(new StructuredData::Dictionary());162  type_dict_sp->AddStringItem(GetSerializationSubclassKey(), GetResolverName());163  type_dict_sp->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp);164 165  // Add the m_offset to the dictionary:166  options_dict_sp->AddIntegerItem(GetKey(OptionNames::Offset), m_offset);167 168  return type_dict_sp;169}170 171void BreakpointResolver::SetBreakpoint(const BreakpointSP &bkpt) {172  assert(bkpt);173  m_breakpoint = bkpt;174  NotifyBreakpointSet();175}176 177void BreakpointResolver::ResolveBreakpointInModules(SearchFilter &filter,178                                                    ModuleList &modules) {179  filter.SearchInModuleList(*this, modules);180}181 182void BreakpointResolver::ResolveBreakpoint(SearchFilter &filter) {183  filter.Search(*this);184}185 186namespace {187struct SourceLoc {188  uint32_t line = UINT32_MAX;189  uint16_t column;190  SourceLoc(uint32_t l, std::optional<uint16_t> c)191      : line(l), column(c ? *c : LLDB_INVALID_COLUMN_NUMBER) {}192  SourceLoc(const SymbolContext &sc)193      : line(sc.line_entry.line),194        column(sc.line_entry.column ? sc.line_entry.column195                                    : LLDB_INVALID_COLUMN_NUMBER) {}196};197 198bool operator<(const SourceLoc lhs, const SourceLoc rhs) {199  if (lhs.line < rhs.line)200    return true;201  if (lhs.line > rhs.line)202    return false;203  //  uint32_t a_col = lhs.column ? lhs.column : LLDB_INVALID_COLUMN_NUMBER;204  //  uint32_t b_col = rhs.column ? rhs.column : LLDB_INVALID_COLUMN_NUMBER;205  return lhs.column < rhs.column;206}207} // namespace208 209void BreakpointResolver::SetSCMatchesByLine(210    SearchFilter &filter, SymbolContextList &sc_list, bool skip_prologue,211    llvm::StringRef log_ident, uint32_t line, std::optional<uint16_t> column) {212  llvm::SmallVector<SymbolContext, 16> all_scs(sc_list.begin(), sc_list.end());213 214  // Let the language plugin filter `sc_list`. Because all symbol contexts in215  // sc_list are assumed to belong to the same File, Line and CU, the code below216  // assumes they have the same language.217  if (!sc_list.IsEmpty() && Language::GetGlobalLanguageProperties()218                                .GetEnableFilterForLineBreakpoints())219    if (Language *lang = Language::FindPlugin(sc_list[0].GetLanguage()))220      lang->FilterForLineBreakpoints(all_scs);221 222  while (all_scs.size()) {223    uint32_t closest_line = UINT32_MAX;224 225    // Move all the elements with a matching file spec to the end.226    auto &match = all_scs[0];227    auto worklist_begin = std::partition(228        all_scs.begin(), all_scs.end(), [&](const SymbolContext &sc) {229          if (sc.line_entry.GetFile() == match.line_entry.GetFile() ||230              sc.line_entry.original_file_sp->Equal(231                  *match.line_entry.original_file_sp,232                  SupportFile::eEqualFileSpecAndChecksumIfSet)) {233            // When a match is found, keep track of the smallest line number.234            closest_line = std::min(closest_line, sc.line_entry.line);235            return false;236          }237          return true;238        });239 240    // (worklist_begin, worklist_end) now contains all entries for one filespec.241    auto worklist_end = all_scs.end();242 243    if (column) {244      // If a column was requested, do a more precise match and only245      // return the first location that comes before or at the246      // requested location.247      SourceLoc requested(line, *column);248      // First, filter out all entries left of the requested column.249      worklist_end = std::remove_if(250          worklist_begin, worklist_end,251          [&](const SymbolContext &sc) { return requested < SourceLoc(sc); });252      // Sort the remaining entries by (line, column).253      llvm::sort(worklist_begin, worklist_end,254                 [](const SymbolContext &a, const SymbolContext &b) {255                   return SourceLoc(a) < SourceLoc(b);256                 });257 258      // Filter out all locations with a source location after the closest match.259      if (worklist_begin != worklist_end)260        worklist_end = std::remove_if(261            worklist_begin, worklist_end, [&](const SymbolContext &sc) {262              return SourceLoc(*worklist_begin) < SourceLoc(sc);263            });264    } else {265      // Remove all entries with a larger line number.266      // ResolveSymbolContext will always return a number that is >=267      // the line number you pass in. So the smaller line number is268      // always better.269      worklist_end = std::remove_if(worklist_begin, worklist_end,270                                    [&](const SymbolContext &sc) {271                                      return closest_line != sc.line_entry.line;272                                    });273    }274 275    // Sort by file address.276    llvm::sort(worklist_begin, worklist_end,277               [](const SymbolContext &a, const SymbolContext &b) {278                 return a.line_entry.range.GetBaseAddress().GetFileAddress() <279                        b.line_entry.range.GetBaseAddress().GetFileAddress();280               });281 282    // Go through and see if there are line table entries that are283    // contiguous, and if so keep only the first of the contiguous range.284    // We do this by picking the first location in each lexical block.285    llvm::SmallDenseSet<Block *, 8> blocks_with_breakpoints;286    for (auto first = worklist_begin; first != worklist_end; ++first) {287      assert(!blocks_with_breakpoints.count(first->block));288      blocks_with_breakpoints.insert(first->block);289      worklist_end =290          std::remove_if(std::next(first), worklist_end,291                         [&](const SymbolContext &sc) {292                           return blocks_with_breakpoints.count(sc.block);293                         });294    }295 296    // Make breakpoints out of the closest line number match.297    for (auto &sc : llvm::make_range(worklist_begin, worklist_end))298      AddLocation(filter, sc, skip_prologue, log_ident);299 300    // Remove all contexts processed by this iteration.301    all_scs.erase(worklist_begin, all_scs.end());302  }303}304 305void BreakpointResolver::AddLocation(SearchFilter &filter,306                                     const SymbolContext &sc,307                                     bool skip_prologue,308                                     llvm::StringRef log_ident) {309  Log *log = GetLog(LLDBLog::Breakpoints);310  Address line_start = sc.line_entry.range.GetBaseAddress();311  if (!line_start.IsValid()) {312    LLDB_LOGF(log,313              "error: Unable to set breakpoint %s at file address "314              "0x%" PRIx64 "\n",315              log_ident.str().c_str(), line_start.GetFileAddress());316    return;317  }318 319  if (!filter.AddressPasses(line_start)) {320    LLDB_LOGF(log,321              "Breakpoint %s at file address 0x%" PRIx64322              " didn't pass the filter.\n",323              log_ident.str().c_str(), line_start.GetFileAddress());324  }325 326  // If the line number is before the prologue end, move it there...327  bool skipped_prologue = false;328  if (skip_prologue && sc.function) {329    Address prologue_addr = sc.function->GetAddress();330    if (prologue_addr.IsValid() && (line_start == prologue_addr)) {331      const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize();332      if (prologue_byte_size) {333        prologue_addr.Slide(prologue_byte_size);334 335        if (filter.AddressPasses(prologue_addr)) {336          skipped_prologue = true;337          line_start = prologue_addr;338        }339      }340    }341  }342 343  BreakpointLocationSP bp_loc_sp(AddLocation(line_start));344  // If the address that we resolved the location to returns a different345  // LineEntry from the one in the incoming SC, we're probably dealing with an346  // inlined call site, so set that as the preferred LineEntry:347  LineEntry resolved_entry;348  if (!skipped_prologue && bp_loc_sp &&349      line_start.CalculateSymbolContextLineEntry(resolved_entry) &&350      LineEntry::Compare(resolved_entry, sc.line_entry)) {351    // FIXME: The function name will also be wrong here.  Do we need to record352    // that as well, or can we figure that out again when we report this353    // breakpoint location.354    if (!bp_loc_sp->SetPreferredLineEntry(sc.line_entry)) {355      LLDB_LOG(log, "Tried to add a preferred line entry that didn't have the "356                    "same address as this location's address.");357    }358  }359  if (log && bp_loc_sp && !GetBreakpoint()->IsInternal()) {360    StreamString s;361    bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);362    LLDB_LOGF(log, "Added location (skipped prologue: %s): %s \n",363              skipped_prologue ? "yes" : "no", s.GetData());364  }365}366 367BreakpointLocationSP BreakpointResolver::AddLocation(Address loc_addr,368                                                     bool *new_location) {369  if (m_offset_is_insn_count) {370    Target &target = GetBreakpoint()->GetTarget();371    llvm::Expected<DisassemblerSP> expected_instructions =372        target.ReadInstructions(loc_addr, m_offset);373    if (!expected_instructions) {374      LLDB_LOG_ERROR(GetLog(LLDBLog::Breakpoints),375                     expected_instructions.takeError(),376                     "error: Unable to read instructions at address 0x{0:x}",377                     loc_addr.GetLoadAddress(&target));378      return BreakpointLocationSP();379    }380 381    const DisassemblerSP instructions = *expected_instructions;382    if (!instructions ||383        instructions->GetInstructionList().GetSize() != m_offset) {384      LLDB_LOG(GetLog(LLDBLog::Breakpoints),385               "error: Unable to read {0} instructions at address 0x{1:x}",386               m_offset, loc_addr.GetLoadAddress(&target));387      return BreakpointLocationSP();388    }389 390    loc_addr.Slide(instructions->GetInstructionList().GetTotalByteSize());391  } else {392    loc_addr.Slide(m_offset);393  }394 395  return GetBreakpoint()->AddLocation(loc_addr, new_location);396}397 398void BreakpointResolver::SetOffset(lldb::addr_t offset) {399  // There may already be an offset, so we are actually adjusting location400  // addresses by the difference.401  // lldb::addr_t slide = offset - m_offset;402  // FIXME: We should go fix up all the already set locations for the new403  // slide.404 405  m_offset = offset;406}407