Path: blob/main/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
39587 views
//===-- BreakpointResolverFileLine.cpp ------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Breakpoint/BreakpointResolverFileLine.h"910#include "lldb/Breakpoint/BreakpointLocation.h"11#include "lldb/Core/Module.h"12#include "lldb/Symbol/CompileUnit.h"13#include "lldb/Symbol/Function.h"14#include "lldb/Target/Target.h"15#include "lldb/Utility/LLDBLog.h"16#include "lldb/Utility/Log.h"17#include "lldb/Utility/StreamString.h"18#include <optional>1920using namespace lldb;21using namespace lldb_private;2223// BreakpointResolverFileLine:24BreakpointResolverFileLine::BreakpointResolverFileLine(25const BreakpointSP &bkpt, lldb::addr_t offset, bool skip_prologue,26const SourceLocationSpec &location_spec,27std::optional<llvm::StringRef> removed_prefix_opt)28: BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset),29m_location_spec(location_spec), m_skip_prologue(skip_prologue),30m_removed_prefix_opt(removed_prefix_opt) {}3132BreakpointResolverSP BreakpointResolverFileLine::CreateFromStructuredData(33const StructuredData::Dictionary &options_dict, Status &error) {34llvm::StringRef filename;35uint32_t line;36uint16_t column;37bool check_inlines;38bool skip_prologue;39bool exact_match;40bool success;4142lldb::addr_t offset = 0;4344success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName),45filename);46if (!success) {47error.SetErrorString("BRFL::CFSD: Couldn't find filename entry.");48return nullptr;49}5051success = options_dict.GetValueForKeyAsInteger(52GetKey(OptionNames::LineNumber), line);53if (!success) {54error.SetErrorString("BRFL::CFSD: Couldn't find line number entry.");55return nullptr;56}5758success =59options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Column), column);60if (!success) {61// Backwards compatibility.62column = 0;63}6465success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines),66check_inlines);67if (!success) {68error.SetErrorString("BRFL::CFSD: Couldn't find check inlines entry.");69return nullptr;70}7172success = options_dict.GetValueForKeyAsBoolean(73GetKey(OptionNames::SkipPrologue), skip_prologue);74if (!success) {75error.SetErrorString("BRFL::CFSD: Couldn't find skip prologue entry.");76return nullptr;77}7879success = options_dict.GetValueForKeyAsBoolean(80GetKey(OptionNames::ExactMatch), exact_match);81if (!success) {82error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry.");83return nullptr;84}8586SourceLocationSpec location_spec(FileSpec(filename), line, column,87check_inlines, exact_match);88if (!location_spec)89return nullptr;9091return std::make_shared<BreakpointResolverFileLine>(92nullptr, offset, skip_prologue, location_spec);93}9495StructuredData::ObjectSP96BreakpointResolverFileLine::SerializeToStructuredData() {97StructuredData::DictionarySP options_dict_sp(98new StructuredData::Dictionary());99100options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),101m_skip_prologue);102options_dict_sp->AddStringItem(GetKey(OptionNames::FileName),103m_location_spec.GetFileSpec().GetPath());104options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber),105m_location_spec.GetLine().value_or(0));106options_dict_sp->AddIntegerItem(107GetKey(OptionNames::Column),108m_location_spec.GetColumn().value_or(LLDB_INVALID_COLUMN_NUMBER));109options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines),110m_location_spec.GetCheckInlines());111options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),112m_location_spec.GetExactMatch());113114return WrapOptionsDict(options_dict_sp);115}116117// Filter the symbol context list to remove contexts where the line number was118// moved into a new function. We do this conservatively, so if e.g. we cannot119// resolve the function in the context (which can happen in case of line-table-120// only debug info), we leave the context as is. The trickiest part here is121// handling inlined functions -- in this case we need to make sure we look at122// the declaration line of the inlined function, NOT the function it was123// inlined into.124void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list) {125if (m_location_spec.GetExactMatch())126return; // Nothing to do. Contexts are precise.127128Log *log = GetLog(LLDBLog::Breakpoints);129for(uint32_t i = 0; i < sc_list.GetSize(); ++i) {130SymbolContext sc;131sc_list.GetContextAtIndex(i, sc);132if (!sc.block)133continue;134135FileSpec file;136uint32_t line;137const Block *inline_block = sc.block->GetContainingInlinedBlock();138if (inline_block) {139const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration();140if (!inline_declaration.IsValid())141continue;142file = inline_declaration.GetFile();143line = inline_declaration.GetLine();144} else if (sc.function)145sc.function->GetStartLineSourceInfo(file, line);146else147continue;148149if (file != sc.line_entry.GetFile()) {150LLDB_LOG(log, "unexpected symbol context file {0}",151sc.line_entry.GetFile());152continue;153}154155// Compare the requested line number with the line of the function156// declaration. In case of a function declared as:157//158// int159// foo()160// {161// ...162//163// the compiler will set the declaration line to the "foo" line, which is164// the reason why we have -1 here. This can fail in case of two inline165// functions defined back-to-back:166//167// inline int foo1() { ... }168// inline int foo2() { ... }169//170// but that's the best we can do for now.171// One complication, if the line number returned from GetStartLineSourceInfo172// is 0, then we can't do this calculation. That can happen if173// GetStartLineSourceInfo gets an error, or if the first line number in174// the function really is 0 - which happens for some languages.175176// But only do this calculation if the line number we found in the SC177// was different from the one requested in the source file. If we actually178// found an exact match it must be valid.179180if (m_location_spec.GetLine() == sc.line_entry.line)181continue;182183const int decl_line_is_too_late_fudge = 1;184if (line &&185m_location_spec.GetLine() < line - decl_line_is_too_late_fudge) {186LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line);187sc_list.RemoveContextAtIndex(i);188--i;189}190}191}192193void BreakpointResolverFileLine::DeduceSourceMapping(194const SymbolContextList &sc_list) {195Target &target = GetBreakpoint()->GetTarget();196if (!target.GetAutoSourceMapRelative())197return;198199Log *log = GetLog(LLDBLog::Breakpoints);200// Check if "b" is a suffix of "a".201// And return std::nullopt if not or the new path202// of "a" after consuming "b" from the back.203auto check_suffix =204[](llvm::StringRef a, llvm::StringRef b,205bool case_sensitive) -> std::optional<llvm::StringRef> {206if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b)) {207// Note sc_file_dir and request_file_dir below are normalized208// and always contain the path separator '/'.209if (a.empty() || a.ends_with("/")) {210return a;211}212}213return std::nullopt;214};215216FileSpec request_file = m_location_spec.GetFileSpec();217218// Only auto deduce source map if breakpoint is full path.219// Note: an existing source map reverse mapping (m_removed_prefix_opt has220// value) may make request_file relative.221if (!m_removed_prefix_opt.has_value() && request_file.IsRelative())222return;223224const bool case_sensitive = request_file.IsCaseSensitive();225for (const SymbolContext &sc : sc_list) {226FileSpec sc_file = sc.line_entry.GetFile();227228if (FileSpec::Equal(sc_file, request_file, /*full*/ true))229continue;230231llvm::StringRef sc_file_dir = sc_file.GetDirectory().GetStringRef();232llvm::StringRef request_file_dir =233request_file.GetDirectory().GetStringRef();234235llvm::StringRef new_mapping_from;236llvm::SmallString<256> new_mapping_to;237238// Adding back any potentially reverse mapping stripped prefix.239// for new_mapping_to.240if (m_removed_prefix_opt.has_value())241llvm::sys::path::append(new_mapping_to, *m_removed_prefix_opt);242243std::optional<llvm::StringRef> new_mapping_from_opt =244check_suffix(sc_file_dir, request_file_dir, case_sensitive);245if (new_mapping_from_opt) {246new_mapping_from = *new_mapping_from_opt;247if (new_mapping_to.empty())248new_mapping_to = ".";249} else {250std::optional<llvm::StringRef> new_mapping_to_opt =251check_suffix(request_file_dir, sc_file_dir, case_sensitive);252if (new_mapping_to_opt) {253new_mapping_from = ".";254llvm::sys::path::append(new_mapping_to, *new_mapping_to_opt);255}256}257258if (!new_mapping_from.empty() && !new_mapping_to.empty()) {259LLDB_LOG(log, "generating auto source map from {0} to {1}",260new_mapping_from, new_mapping_to);261if (target.GetSourcePathMap().AppendUnique(new_mapping_from,262new_mapping_to,263/*notify*/ true))264target.GetStatistics().IncreaseSourceMapDeduceCount();265}266}267}268269Searcher::CallbackReturn BreakpointResolverFileLine::SearchCallback(270SearchFilter &filter, SymbolContext &context, Address *addr) {271SymbolContextList sc_list;272273// There is a tricky bit here. You can have two compilation units that274// #include the same file, and in one of them the function at m_line_number275// is used (and so code and a line entry for it is generated) but in the276// other it isn't. If we considered the CU's independently, then in the277// second inclusion, we'd move the breakpoint to the next function that278// actually generated code in the header file. That would end up being279// confusing. So instead, we do the CU iterations by hand here, then scan280// through the complete list of matches, and figure out the closest line281// number match, and only set breakpoints on that match.282283// Note also that if file_spec only had a file name and not a directory,284// there may be many different file spec's in the resultant list. The285// closest line match for one will not be right for some totally different286// file. So we go through the match list and pull out the sets that have the287// same file spec in their line_entry and treat each set separately.288289const uint32_t line = m_location_spec.GetLine().value_or(0);290const std::optional<uint16_t> column = m_location_spec.GetColumn();291292const size_t num_comp_units = context.module_sp->GetNumCompileUnits();293for (size_t i = 0; i < num_comp_units; i++) {294CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i));295if (cu_sp) {296if (filter.CompUnitPasses(*cu_sp))297cu_sp->ResolveSymbolContext(m_location_spec, eSymbolContextEverything,298sc_list);299}300}301302FilterContexts(sc_list);303304DeduceSourceMapping(sc_list);305306StreamString s;307s.Printf("for %s:%d ",308m_location_spec.GetFileSpec().GetFilename().AsCString("<Unknown>"),309line);310311SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(), line,312column);313314return Searcher::eCallbackReturnContinue;315}316317lldb::SearchDepth BreakpointResolverFileLine::GetDepth() {318return lldb::eSearchDepthModule;319}320321void BreakpointResolverFileLine::GetDescription(Stream *s) {322s->Printf("file = '%s', line = %u, ",323m_location_spec.GetFileSpec().GetPath().c_str(),324m_location_spec.GetLine().value_or(0));325auto column = m_location_spec.GetColumn();326if (column)327s->Printf("column = %u, ", *column);328s->Printf("exact_match = %d", m_location_spec.GetExactMatch());329}330331void BreakpointResolverFileLine::Dump(Stream *s) const {}332333lldb::BreakpointResolverSP334BreakpointResolverFileLine::CopyForBreakpoint(BreakpointSP &breakpoint) {335lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine(336breakpoint, GetOffset(), m_skip_prologue, m_location_spec));337338return ret_sp;339}340341342