brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.2 KiB · cef1ef1 Raw
359 lines · cpp
1//===-- BreakpointResolverFileLine.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/BreakpointResolverFileLine.h"10 11#include "lldb/Breakpoint/BreakpointLocation.h"12#include "lldb/Core/Module.h"13#include "lldb/Symbol/CompileUnit.h"14#include "lldb/Symbol/Function.h"15#include "lldb/Target/Target.h"16#include "lldb/Utility/LLDBLog.h"17#include "lldb/Utility/Log.h"18#include "lldb/Utility/RealpathPrefixes.h"19#include "lldb/Utility/StreamString.h"20#include <optional>21 22using namespace lldb;23using namespace lldb_private;24 25// BreakpointResolverFileLine:26BreakpointResolverFileLine::BreakpointResolverFileLine(27    const BreakpointSP &bkpt, lldb::addr_t offset, bool skip_prologue,28    const SourceLocationSpec &location_spec,29    std::optional<llvm::StringRef> removed_prefix_opt)30    : BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset),31      m_location_spec(location_spec), m_skip_prologue(skip_prologue),32      m_removed_prefix_opt(removed_prefix_opt) {}33 34BreakpointResolverSP BreakpointResolverFileLine::CreateFromStructuredData(35    const StructuredData::Dictionary &options_dict, Status &error) {36  llvm::StringRef filename;37  uint32_t line;38  uint16_t column;39  bool check_inlines;40  bool skip_prologue;41  bool exact_match;42  bool success;43 44  lldb::addr_t offset = 0;45 46  success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName),47                                                filename);48  if (!success) {49    error =50        Status::FromErrorString("BRFL::CFSD: Couldn't find filename entry.");51    return nullptr;52  }53 54  success = options_dict.GetValueForKeyAsInteger(55      GetKey(OptionNames::LineNumber), line);56  if (!success) {57    error =58        Status::FromErrorString("BRFL::CFSD: Couldn't find line number entry.");59    return nullptr;60  }61 62  success =63      options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Column), column);64  if (!success) {65    // Backwards compatibility.66    column = 0;67  }68 69  success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines),70                                                 check_inlines);71  if (!success) {72    error = Status::FromErrorString(73        "BRFL::CFSD: Couldn't find check inlines entry.");74    return nullptr;75  }76 77  success = options_dict.GetValueForKeyAsBoolean(78      GetKey(OptionNames::SkipPrologue), skip_prologue);79  if (!success) {80    error = Status::FromErrorString(81        "BRFL::CFSD: Couldn't find skip prologue entry.");82    return nullptr;83  }84 85  success = options_dict.GetValueForKeyAsBoolean(86      GetKey(OptionNames::ExactMatch), exact_match);87  if (!success) {88    error =89        Status::FromErrorString("BRFL::CFSD: Couldn't find exact match entry.");90    return nullptr;91  }92 93  SourceLocationSpec location_spec(FileSpec(filename), line, column,94                                   check_inlines, exact_match);95  if (!location_spec)96    return nullptr;97 98  return std::make_shared<BreakpointResolverFileLine>(99      nullptr, offset, skip_prologue, location_spec);100}101 102StructuredData::ObjectSP103BreakpointResolverFileLine::SerializeToStructuredData() {104  StructuredData::DictionarySP options_dict_sp(105      new StructuredData::Dictionary());106 107  options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),108                                  m_skip_prologue);109  options_dict_sp->AddStringItem(GetKey(OptionNames::FileName),110                                 m_location_spec.GetFileSpec().GetPath());111  options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber),112                                  m_location_spec.GetLine().value_or(0));113  options_dict_sp->AddIntegerItem(114      GetKey(OptionNames::Column),115      m_location_spec.GetColumn().value_or(LLDB_INVALID_COLUMN_NUMBER));116  options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines),117                                  m_location_spec.GetCheckInlines());118  options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),119                                  m_location_spec.GetExactMatch());120 121  return WrapOptionsDict(options_dict_sp);122}123 124// Filter the symbol context list to remove contexts where the line number was125// moved into a new function. We do this conservatively, so if e.g. we cannot126// resolve the function in the context (which can happen in case of line-table-127// only debug info), we leave the context as is. The trickiest part here is128// handling inlined functions -- in this case we need to make sure we look at129// the declaration line of the inlined function, NOT the function it was130// inlined into.131void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list) {132  if (m_location_spec.GetExactMatch())133    return; // Nothing to do. Contexts are precise.134 135  Log *log = GetLog(LLDBLog::Breakpoints);136  for(uint32_t i = 0; i < sc_list.GetSize(); ++i) {137    SymbolContext sc;138    sc_list.GetContextAtIndex(i, sc);139    if (!sc.block)140      continue;141 142    SupportFileNSP file_sp = std::make_shared<SupportFile>();143    uint32_t line;144    const Block *inline_block = sc.block->GetContainingInlinedBlock();145    if (inline_block) {146      const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration();147      if (!inline_declaration.IsValid())148        continue;149      file_sp = std::make_shared<SupportFile>(inline_declaration.GetFile());150      line = inline_declaration.GetLine();151    } else if (sc.function)152      sc.function->GetStartLineSourceInfo(file_sp, line);153    else154      continue;155 156    if (!file_sp ||157        !file_sp->Equal(*sc.line_entry.file_sp,158                        SupportFile::eEqualFileSpecAndChecksumIfSet)) {159      LLDB_LOG(log, "unexpected symbol context file {0}",160               sc.line_entry.GetFile());161      continue;162    }163 164    // Compare the requested line number with the line of the function165    // declaration. In case of a function declared as:166    //167    // int168    // foo()169    // {170    //   ...171    //172    // the compiler will set the declaration line to the "foo" line, which is173    // the reason why we have -1 here. This can fail in case of two inline174    // functions defined back-to-back:175    //176    // inline int foo1() { ... }177    // inline int foo2() { ... }178    //179    // but that's the best we can do for now.180    // One complication, if the line number returned from GetStartLineSourceInfo181    // is 0, then we can't do this calculation.  That can happen if182    // GetStartLineSourceInfo gets an error, or if the first line number in183    // the function really is 0 - which happens for some languages.184 185    // But only do this calculation if the line number we found in the SC186    // was different from the one requested in the source file.  If we actually187    // found an exact match it must be valid.188 189    if (m_location_spec.GetLine() == sc.line_entry.line)190      continue;191 192    const int decl_line_is_too_late_fudge = 1;193    if (line &&194        m_location_spec.GetLine() < line - decl_line_is_too_late_fudge) {195      LLDB_LOG(log, "removing symbol context at {0}:{1}",196               file_sp->GetSpecOnly(), line);197      sc_list.RemoveContextAtIndex(i);198      --i;199    }200  }201}202 203void BreakpointResolverFileLine::DeduceSourceMapping(204    const SymbolContextList &sc_list) {205  Target &target = GetBreakpoint()->GetTarget();206  if (!target.GetAutoSourceMapRelative())207    return;208 209  Log *log = GetLog(LLDBLog::Breakpoints);210  // Check if "b" is a suffix of "a".211  // And return std::nullopt if not or the new path212  // of "a" after consuming "b" from the back.213  auto check_suffix =214      [](llvm::StringRef a, llvm::StringRef b,215         bool case_sensitive) -> std::optional<llvm::StringRef> {216    if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b)) {217      // Note sc_file_dir and request_file_dir below are normalized218      // and always contain the path separator '/'.219      if (a.empty() || a.ends_with("/")) {220        return a;221      }222    }223    return std::nullopt;224  };225 226  FileSpec request_file = m_location_spec.GetFileSpec();227 228  // Only auto deduce source map if breakpoint is full path.229  // Note: an existing source map reverse mapping (m_removed_prefix_opt has230  // value) may make request_file relative.231  if (!m_removed_prefix_opt.has_value() && request_file.IsRelative())232    return;233 234  const bool case_sensitive = request_file.IsCaseSensitive();235  for (const SymbolContext &sc : sc_list) {236    FileSpec sc_file = sc.line_entry.GetFile();237 238    if (FileSpec::Equal(sc_file, request_file, /*full*/ true))239      continue;240 241    llvm::StringRef sc_file_dir = sc_file.GetDirectory().GetStringRef();242    llvm::StringRef request_file_dir =243        request_file.GetDirectory().GetStringRef();244 245    llvm::StringRef new_mapping_from;246    llvm::SmallString<256> new_mapping_to;247 248    // Adding back any potentially reverse mapping stripped prefix.249    // for new_mapping_to.250    if (m_removed_prefix_opt.has_value())251      llvm::sys::path::append(new_mapping_to, *m_removed_prefix_opt);252 253    std::optional<llvm::StringRef> new_mapping_from_opt =254        check_suffix(sc_file_dir, request_file_dir, case_sensitive);255    if (new_mapping_from_opt) {256      new_mapping_from = *new_mapping_from_opt;257      if (new_mapping_to.empty())258        new_mapping_to = ".";259    } else {260      std::optional<llvm::StringRef> new_mapping_to_opt =261          check_suffix(request_file_dir, sc_file_dir, case_sensitive);262      if (new_mapping_to_opt) {263        new_mapping_from = ".";264        llvm::sys::path::append(new_mapping_to, *new_mapping_to_opt);265      }266    }267 268    if (!new_mapping_from.empty() && !new_mapping_to.empty()) {269      LLDB_LOG(log, "generating auto source map from {0} to {1}",270               new_mapping_from, new_mapping_to);271      if (target.GetSourcePathMap().AppendUnique(new_mapping_from,272                                                 new_mapping_to,273                                                 /*notify*/ true))274        target.GetStatistics().IncreaseSourceMapDeduceCount();275    }276  }277}278 279Searcher::CallbackReturn BreakpointResolverFileLine::SearchCallback(280    SearchFilter &filter, SymbolContext &context, Address *addr) {281  SymbolContextList sc_list;282 283  // There is a tricky bit here.  You can have two compilation units that284  // #include the same file, and in one of them the function at m_line_number285  // is used (and so code and a line entry for it is generated) but in the286  // other it isn't.  If we considered the CU's independently, then in the287  // second inclusion, we'd move the breakpoint to the next function that288  // actually generated code in the header file.  That would end up being289  // confusing.  So instead, we do the CU iterations by hand here, then scan290  // through the complete list of matches, and figure out the closest line291  // number match, and only set breakpoints on that match.292 293  // Note also that if file_spec only had a file name and not a directory,294  // there may be many different file spec's in the resultant list.  The295  // closest line match for one will not be right for some totally different296  // file.  So we go through the match list and pull out the sets that have the297  // same file spec in their line_entry and treat each set separately.298 299  const uint32_t line = m_location_spec.GetLine().value_or(0);300  const std::optional<uint16_t> column = m_location_spec.GetColumn();301 302  Target &target = GetBreakpoint()->GetTarget();303  RealpathPrefixes realpath_prefixes = target.GetSourceRealpathPrefixes();304 305  const size_t num_comp_units = context.module_sp->GetNumCompileUnits();306  for (size_t i = 0; i < num_comp_units; i++) {307    CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i));308    if (cu_sp) {309      if (filter.CompUnitPasses(*cu_sp))310        cu_sp->ResolveSymbolContext(m_location_spec, eSymbolContextEverything,311                                    sc_list, &realpath_prefixes);312    }313  }314 315  // Gather stats into the Target316  target.GetStatistics().IncreaseSourceRealpathAttemptCount(317      realpath_prefixes.GetSourceRealpathAttemptCount());318  target.GetStatistics().IncreaseSourceRealpathCompatibleCount(319      realpath_prefixes.GetSourceRealpathCompatibleCount());320 321  FilterContexts(sc_list);322 323  DeduceSourceMapping(sc_list);324 325  StreamString s;326  s.Printf("for %s:%d ",327           m_location_spec.GetFileSpec().GetFilename().AsCString("<Unknown>"),328           line);329 330  SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(), line,331                     column);332 333  return Searcher::eCallbackReturnContinue;334}335 336lldb::SearchDepth BreakpointResolverFileLine::GetDepth() {337  return lldb::eSearchDepthModule;338}339 340void BreakpointResolverFileLine::GetDescription(Stream *s) {341  s->Printf("file = '%s', line = %u, ",342            m_location_spec.GetFileSpec().GetPath().c_str(),343            m_location_spec.GetLine().value_or(0));344  auto column = m_location_spec.GetColumn();345  if (column)346    s->Printf("column = %u, ", *column);347  s->Printf("exact_match = %d", m_location_spec.GetExactMatch());348}349 350void BreakpointResolverFileLine::Dump(Stream *s) const {}351 352lldb::BreakpointResolverSP353BreakpointResolverFileLine::CopyForBreakpoint(BreakpointSP &breakpoint) {354  lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine(355      breakpoint, GetOffset(), m_skip_prologue, m_location_spec));356 357  return ret_sp;358}359