15ffd83dbSDimitry Andric //===-- BreakpointResolverFileLine.cpp ------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "lldb/Breakpoint/BreakpointResolverFileLine.h"
100b57cec5SDimitry Andric
110b57cec5SDimitry Andric #include "lldb/Breakpoint/BreakpointLocation.h"
120b57cec5SDimitry Andric #include "lldb/Core/Module.h"
130b57cec5SDimitry Andric #include "lldb/Symbol/CompileUnit.h"
140b57cec5SDimitry Andric #include "lldb/Symbol/Function.h"
150b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
160b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h"
170b57cec5SDimitry Andric
180b57cec5SDimitry Andric using namespace lldb;
190b57cec5SDimitry Andric using namespace lldb_private;
200b57cec5SDimitry Andric
210b57cec5SDimitry Andric // BreakpointResolverFileLine:
BreakpointResolverFileLine(const BreakpointSP & bkpt,lldb::addr_t offset,bool skip_prologue,const SourceLocationSpec & location_spec)220b57cec5SDimitry Andric BreakpointResolverFileLine::BreakpointResolverFileLine(
23*5f7ddb14SDimitry Andric const BreakpointSP &bkpt, lldb::addr_t offset, bool skip_prologue,
24*5f7ddb14SDimitry Andric const SourceLocationSpec &location_spec)
250b57cec5SDimitry Andric : BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset),
26*5f7ddb14SDimitry Andric m_location_spec(location_spec), m_skip_prologue(skip_prologue) {}
270b57cec5SDimitry Andric
CreateFromStructuredData(const BreakpointSP & bkpt,const StructuredData::Dictionary & options_dict,Status & error)280b57cec5SDimitry Andric BreakpointResolver *BreakpointResolverFileLine::CreateFromStructuredData(
295ffd83dbSDimitry Andric const BreakpointSP &bkpt, const StructuredData::Dictionary &options_dict,
300b57cec5SDimitry Andric Status &error) {
310b57cec5SDimitry Andric llvm::StringRef filename;
32*5f7ddb14SDimitry Andric uint32_t line;
33*5f7ddb14SDimitry Andric uint16_t column;
340b57cec5SDimitry Andric bool check_inlines;
350b57cec5SDimitry Andric bool skip_prologue;
360b57cec5SDimitry Andric bool exact_match;
370b57cec5SDimitry Andric bool success;
380b57cec5SDimitry Andric
390b57cec5SDimitry Andric lldb::addr_t offset = 0;
400b57cec5SDimitry Andric
410b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName),
420b57cec5SDimitry Andric filename);
430b57cec5SDimitry Andric if (!success) {
440b57cec5SDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find filename entry.");
450b57cec5SDimitry Andric return nullptr;
460b57cec5SDimitry Andric }
470b57cec5SDimitry Andric
480b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsInteger(
49*5f7ddb14SDimitry Andric GetKey(OptionNames::LineNumber), line);
500b57cec5SDimitry Andric if (!success) {
510b57cec5SDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find line number entry.");
520b57cec5SDimitry Andric return nullptr;
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric
550b57cec5SDimitry Andric success =
560b57cec5SDimitry Andric options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Column), column);
570b57cec5SDimitry Andric if (!success) {
580b57cec5SDimitry Andric // Backwards compatibility.
590b57cec5SDimitry Andric column = 0;
600b57cec5SDimitry Andric }
610b57cec5SDimitry Andric
620b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines),
630b57cec5SDimitry Andric check_inlines);
640b57cec5SDimitry Andric if (!success) {
650b57cec5SDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find check inlines entry.");
660b57cec5SDimitry Andric return nullptr;
670b57cec5SDimitry Andric }
680b57cec5SDimitry Andric
690b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsBoolean(
700b57cec5SDimitry Andric GetKey(OptionNames::SkipPrologue), skip_prologue);
710b57cec5SDimitry Andric if (!success) {
720b57cec5SDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find skip prologue entry.");
730b57cec5SDimitry Andric return nullptr;
740b57cec5SDimitry Andric }
750b57cec5SDimitry Andric
760b57cec5SDimitry Andric success = options_dict.GetValueForKeyAsBoolean(
770b57cec5SDimitry Andric GetKey(OptionNames::ExactMatch), exact_match);
780b57cec5SDimitry Andric if (!success) {
790b57cec5SDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry.");
800b57cec5SDimitry Andric return nullptr;
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric
83*5f7ddb14SDimitry Andric SourceLocationSpec location_spec(FileSpec(filename), line, column,
84*5f7ddb14SDimitry Andric check_inlines, exact_match);
85*5f7ddb14SDimitry Andric if (!location_spec)
86*5f7ddb14SDimitry Andric return nullptr;
870b57cec5SDimitry Andric
88*5f7ddb14SDimitry Andric return new BreakpointResolverFileLine(bkpt, offset, skip_prologue,
89*5f7ddb14SDimitry Andric location_spec);
900b57cec5SDimitry Andric }
910b57cec5SDimitry Andric
920b57cec5SDimitry Andric StructuredData::ObjectSP
SerializeToStructuredData()930b57cec5SDimitry Andric BreakpointResolverFileLine::SerializeToStructuredData() {
940b57cec5SDimitry Andric StructuredData::DictionarySP options_dict_sp(
950b57cec5SDimitry Andric new StructuredData::Dictionary());
960b57cec5SDimitry Andric
970b57cec5SDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),
980b57cec5SDimitry Andric m_skip_prologue);
99*5f7ddb14SDimitry Andric options_dict_sp->AddStringItem(GetKey(OptionNames::FileName),
100*5f7ddb14SDimitry Andric m_location_spec.GetFileSpec().GetPath());
101*5f7ddb14SDimitry Andric options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber),
102*5f7ddb14SDimitry Andric m_location_spec.GetLine().getValueOr(0));
103*5f7ddb14SDimitry Andric options_dict_sp->AddIntegerItem(
104*5f7ddb14SDimitry Andric GetKey(OptionNames::Column),
105*5f7ddb14SDimitry Andric m_location_spec.GetColumn().getValueOr(LLDB_INVALID_COLUMN_NUMBER));
106*5f7ddb14SDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines),
107*5f7ddb14SDimitry Andric m_location_spec.GetCheckInlines());
1080b57cec5SDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),
109*5f7ddb14SDimitry Andric m_location_spec.GetExactMatch());
1100b57cec5SDimitry Andric
1110b57cec5SDimitry Andric return WrapOptionsDict(options_dict_sp);
1120b57cec5SDimitry Andric }
1130b57cec5SDimitry Andric
1140b57cec5SDimitry Andric // Filter the symbol context list to remove contexts where the line number was
1150b57cec5SDimitry Andric // moved into a new function. We do this conservatively, so if e.g. we cannot
1160b57cec5SDimitry Andric // resolve the function in the context (which can happen in case of line-table-
1170b57cec5SDimitry Andric // only debug info), we leave the context as is. The trickiest part here is
1180b57cec5SDimitry Andric // handling inlined functions -- in this case we need to make sure we look at
1190b57cec5SDimitry Andric // the declaration line of the inlined function, NOT the function it was
1200b57cec5SDimitry Andric // inlined into.
FilterContexts(SymbolContextList & sc_list,bool is_relative)1210b57cec5SDimitry Andric void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list,
1220b57cec5SDimitry Andric bool is_relative) {
123*5f7ddb14SDimitry Andric if (m_location_spec.GetExactMatch())
1240b57cec5SDimitry Andric return; // Nothing to do. Contexts are precise.
1250b57cec5SDimitry Andric
1260b57cec5SDimitry Andric llvm::StringRef relative_path;
1270b57cec5SDimitry Andric if (is_relative)
128*5f7ddb14SDimitry Andric relative_path = m_location_spec.GetFileSpec().GetDirectory().GetStringRef();
1290b57cec5SDimitry Andric
1300b57cec5SDimitry Andric Log * log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS);
1310b57cec5SDimitry Andric for(uint32_t i = 0; i < sc_list.GetSize(); ++i) {
1320b57cec5SDimitry Andric SymbolContext sc;
1330b57cec5SDimitry Andric sc_list.GetContextAtIndex(i, sc);
1340b57cec5SDimitry Andric if (is_relative) {
1350b57cec5SDimitry Andric // If the path was relative, make sure any matches match as long as the
1360b57cec5SDimitry Andric // relative parts of the path match the path from support files
1370b57cec5SDimitry Andric auto sc_dir = sc.line_entry.file.GetDirectory().GetStringRef();
1380b57cec5SDimitry Andric if (!sc_dir.endswith(relative_path)) {
1390b57cec5SDimitry Andric // We had a relative path specified and the relative directory doesn't
1400b57cec5SDimitry Andric // match so remove this one
1410b57cec5SDimitry Andric LLDB_LOG(log, "removing not matching relative path {0} since it "
1420b57cec5SDimitry Andric "doesn't end with {1}", sc_dir, relative_path);
1430b57cec5SDimitry Andric sc_list.RemoveContextAtIndex(i);
1440b57cec5SDimitry Andric --i;
1450b57cec5SDimitry Andric continue;
1460b57cec5SDimitry Andric }
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric
1490b57cec5SDimitry Andric if (!sc.block)
1500b57cec5SDimitry Andric continue;
1510b57cec5SDimitry Andric
1520b57cec5SDimitry Andric FileSpec file;
1530b57cec5SDimitry Andric uint32_t line;
1540b57cec5SDimitry Andric const Block *inline_block = sc.block->GetContainingInlinedBlock();
1550b57cec5SDimitry Andric if (inline_block) {
1560b57cec5SDimitry Andric const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration();
1570b57cec5SDimitry Andric if (!inline_declaration.IsValid())
1580b57cec5SDimitry Andric continue;
1590b57cec5SDimitry Andric file = inline_declaration.GetFile();
1600b57cec5SDimitry Andric line = inline_declaration.GetLine();
1610b57cec5SDimitry Andric } else if (sc.function)
1620b57cec5SDimitry Andric sc.function->GetStartLineSourceInfo(file, line);
1630b57cec5SDimitry Andric else
1640b57cec5SDimitry Andric continue;
1650b57cec5SDimitry Andric
1660b57cec5SDimitry Andric if (file != sc.line_entry.file) {
1670b57cec5SDimitry Andric LLDB_LOG(log, "unexpected symbol context file {0}", sc.line_entry.file);
1680b57cec5SDimitry Andric continue;
1690b57cec5SDimitry Andric }
1700b57cec5SDimitry Andric
1710b57cec5SDimitry Andric // Compare the requested line number with the line of the function
1720b57cec5SDimitry Andric // declaration. In case of a function declared as:
1730b57cec5SDimitry Andric //
1740b57cec5SDimitry Andric // int
1750b57cec5SDimitry Andric // foo()
1760b57cec5SDimitry Andric // {
1770b57cec5SDimitry Andric // ...
1780b57cec5SDimitry Andric //
1790b57cec5SDimitry Andric // the compiler will set the declaration line to the "foo" line, which is
1800b57cec5SDimitry Andric // the reason why we have -1 here. This can fail in case of two inline
1810b57cec5SDimitry Andric // functions defined back-to-back:
1820b57cec5SDimitry Andric //
1830b57cec5SDimitry Andric // inline int foo1() { ... }
1840b57cec5SDimitry Andric // inline int foo2() { ... }
1850b57cec5SDimitry Andric //
1860b57cec5SDimitry Andric // but that's the best we can do for now.
1870b57cec5SDimitry Andric // One complication, if the line number returned from GetStartLineSourceInfo
1880b57cec5SDimitry Andric // is 0, then we can't do this calculation. That can happen if
1890b57cec5SDimitry Andric // GetStartLineSourceInfo gets an error, or if the first line number in
1900b57cec5SDimitry Andric // the function really is 0 - which happens for some languages.
191af732203SDimitry Andric
192af732203SDimitry Andric // But only do this calculation if the line number we found in the SC
193af732203SDimitry Andric // was different from the one requested in the source file. If we actually
194af732203SDimitry Andric // found an exact match it must be valid.
195af732203SDimitry Andric
196*5f7ddb14SDimitry Andric if (m_location_spec.GetLine() == sc.line_entry.line)
197af732203SDimitry Andric continue;
198af732203SDimitry Andric
1990b57cec5SDimitry Andric const int decl_line_is_too_late_fudge = 1;
200*5f7ddb14SDimitry Andric if (line &&
201*5f7ddb14SDimitry Andric m_location_spec.GetLine() < line - decl_line_is_too_late_fudge) {
2020b57cec5SDimitry Andric LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line);
2030b57cec5SDimitry Andric sc_list.RemoveContextAtIndex(i);
2040b57cec5SDimitry Andric --i;
2050b57cec5SDimitry Andric }
2060b57cec5SDimitry Andric }
2070b57cec5SDimitry Andric }
2080b57cec5SDimitry Andric
SearchCallback(SearchFilter & filter,SymbolContext & context,Address * addr)2099dba64beSDimitry Andric Searcher::CallbackReturn BreakpointResolverFileLine::SearchCallback(
2109dba64beSDimitry Andric SearchFilter &filter, SymbolContext &context, Address *addr) {
2110b57cec5SDimitry Andric SymbolContextList sc_list;
2120b57cec5SDimitry Andric
2130b57cec5SDimitry Andric // There is a tricky bit here. You can have two compilation units that
2140b57cec5SDimitry Andric // #include the same file, and in one of them the function at m_line_number
2150b57cec5SDimitry Andric // is used (and so code and a line entry for it is generated) but in the
2160b57cec5SDimitry Andric // other it isn't. If we considered the CU's independently, then in the
2170b57cec5SDimitry Andric // second inclusion, we'd move the breakpoint to the next function that
2180b57cec5SDimitry Andric // actually generated code in the header file. That would end up being
2190b57cec5SDimitry Andric // confusing. So instead, we do the CU iterations by hand here, then scan
2200b57cec5SDimitry Andric // through the complete list of matches, and figure out the closest line
2210b57cec5SDimitry Andric // number match, and only set breakpoints on that match.
2220b57cec5SDimitry Andric
2230b57cec5SDimitry Andric // Note also that if file_spec only had a file name and not a directory,
2240b57cec5SDimitry Andric // there may be many different file spec's in the resultant list. The
2250b57cec5SDimitry Andric // closest line match for one will not be right for some totally different
2260b57cec5SDimitry Andric // file. So we go through the match list and pull out the sets that have the
2270b57cec5SDimitry Andric // same file spec in their line_entry and treat each set separately.
2280b57cec5SDimitry Andric
229*5f7ddb14SDimitry Andric const uint32_t line = m_location_spec.GetLine().getValueOr(0);
230*5f7ddb14SDimitry Andric const llvm::Optional<uint16_t> column = m_location_spec.GetColumn();
231*5f7ddb14SDimitry Andric
232*5f7ddb14SDimitry Andric FileSpec search_file_spec = m_location_spec.GetFileSpec();
233*5f7ddb14SDimitry Andric const bool is_relative = search_file_spec.IsRelative();
2340b57cec5SDimitry Andric if (is_relative)
2350b57cec5SDimitry Andric search_file_spec.GetDirectory().Clear();
2360b57cec5SDimitry Andric
2370b57cec5SDimitry Andric const size_t num_comp_units = context.module_sp->GetNumCompileUnits();
2380b57cec5SDimitry Andric for (size_t i = 0; i < num_comp_units; i++) {
2390b57cec5SDimitry Andric CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i));
2400b57cec5SDimitry Andric if (cu_sp) {
2410b57cec5SDimitry Andric if (filter.CompUnitPasses(*cu_sp))
242*5f7ddb14SDimitry Andric cu_sp->ResolveSymbolContext(m_location_spec, eSymbolContextEverything,
2430b57cec5SDimitry Andric sc_list);
2440b57cec5SDimitry Andric }
2450b57cec5SDimitry Andric }
2460b57cec5SDimitry Andric
2470b57cec5SDimitry Andric FilterContexts(sc_list, is_relative);
2480b57cec5SDimitry Andric
2490b57cec5SDimitry Andric StreamString s;
250*5f7ddb14SDimitry Andric s.Printf("for %s:%d ",
251*5f7ddb14SDimitry Andric m_location_spec.GetFileSpec().GetFilename().AsCString("<Unknown>"),
252*5f7ddb14SDimitry Andric line);
2530b57cec5SDimitry Andric
254*5f7ddb14SDimitry Andric SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(), line,
255*5f7ddb14SDimitry Andric column);
2560b57cec5SDimitry Andric
2570b57cec5SDimitry Andric return Searcher::eCallbackReturnContinue;
2580b57cec5SDimitry Andric }
2590b57cec5SDimitry Andric
GetDepth()2600b57cec5SDimitry Andric lldb::SearchDepth BreakpointResolverFileLine::GetDepth() {
2610b57cec5SDimitry Andric return lldb::eSearchDepthModule;
2620b57cec5SDimitry Andric }
2630b57cec5SDimitry Andric
GetDescription(Stream * s)2640b57cec5SDimitry Andric void BreakpointResolverFileLine::GetDescription(Stream *s) {
265*5f7ddb14SDimitry Andric s->Printf("file = '%s', line = %u, ",
266*5f7ddb14SDimitry Andric m_location_spec.GetFileSpec().GetPath().c_str(),
267*5f7ddb14SDimitry Andric m_location_spec.GetLine().getValueOr(0));
268*5f7ddb14SDimitry Andric auto column = m_location_spec.GetColumn();
269*5f7ddb14SDimitry Andric if (column)
270*5f7ddb14SDimitry Andric s->Printf("column = %u, ", *column);
271*5f7ddb14SDimitry Andric s->Printf("exact_match = %d", m_location_spec.GetExactMatch());
2720b57cec5SDimitry Andric }
2730b57cec5SDimitry Andric
Dump(Stream * s) const2740b57cec5SDimitry Andric void BreakpointResolverFileLine::Dump(Stream *s) const {}
2750b57cec5SDimitry Andric
2760b57cec5SDimitry Andric lldb::BreakpointResolverSP
CopyForBreakpoint(BreakpointSP & breakpoint)2775ffd83dbSDimitry Andric BreakpointResolverFileLine::CopyForBreakpoint(BreakpointSP &breakpoint) {
2780b57cec5SDimitry Andric lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine(
279*5f7ddb14SDimitry Andric breakpoint, GetOffset(), m_skip_prologue, m_location_spec));
2800b57cec5SDimitry Andric
2810b57cec5SDimitry Andric return ret_sp;
2820b57cec5SDimitry Andric }
283