1ac7ddfbfSEd Maste //===-- BreakpointResolverFileLine.cpp --------------------------*- C++ -*-===//
2ac7ddfbfSEd Maste //
3ac7ddfbfSEd Maste // The LLVM Compiler Infrastructure
4ac7ddfbfSEd Maste //
5ac7ddfbfSEd Maste // This file is distributed under the University of Illinois Open Source
6ac7ddfbfSEd Maste // License. See LICENSE.TXT for details.
7ac7ddfbfSEd Maste //
8ac7ddfbfSEd Maste //===----------------------------------------------------------------------===//
9ac7ddfbfSEd Maste
10ac7ddfbfSEd Maste #include "lldb/Breakpoint/BreakpointResolverFileLine.h"
11ac7ddfbfSEd Maste
12ac7ddfbfSEd Maste #include "lldb/Breakpoint/BreakpointLocation.h"
13ac7ddfbfSEd Maste #include "lldb/Core/Module.h"
14ac7ddfbfSEd Maste #include "lldb/Symbol/CompileUnit.h"
15ac7ddfbfSEd Maste #include "lldb/Symbol/Function.h"
16f678e45dSDimitry Andric #include "lldb/Utility/Log.h"
17f678e45dSDimitry Andric #include "lldb/Utility/StreamString.h"
18ac7ddfbfSEd Maste
19ac7ddfbfSEd Maste using namespace lldb;
20ac7ddfbfSEd Maste using namespace lldb_private;
21ac7ddfbfSEd Maste
22ac7ddfbfSEd Maste //----------------------------------------------------------------------
23ac7ddfbfSEd Maste // BreakpointResolverFileLine:
24ac7ddfbfSEd Maste //----------------------------------------------------------------------
BreakpointResolverFileLine(Breakpoint * bkpt,const FileSpec & file_spec,uint32_t line_no,uint32_t column,lldb::addr_t offset,bool check_inlines,bool skip_prologue,bool exact_match)25435933ddSDimitry Andric BreakpointResolverFileLine::BreakpointResolverFileLine(
26435933ddSDimitry Andric Breakpoint *bkpt, const FileSpec &file_spec, uint32_t line_no,
27*b5893f02SDimitry Andric uint32_t column, lldb::addr_t offset, bool check_inlines,
28*b5893f02SDimitry Andric bool skip_prologue, bool exact_match)
29435933ddSDimitry Andric : BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset),
30*b5893f02SDimitry Andric m_file_spec(file_spec), m_line_number(line_no), m_column(column),
31*b5893f02SDimitry Andric m_inlines(check_inlines), m_skip_prologue(skip_prologue),
32*b5893f02SDimitry Andric m_exact_match(exact_match) {}
33435933ddSDimitry Andric
~BreakpointResolverFileLine()34435933ddSDimitry Andric BreakpointResolverFileLine::~BreakpointResolverFileLine() {}
35435933ddSDimitry Andric
CreateFromStructuredData(Breakpoint * bkpt,const StructuredData::Dictionary & options_dict,Status & error)36435933ddSDimitry Andric BreakpointResolver *BreakpointResolverFileLine::CreateFromStructuredData(
37435933ddSDimitry Andric Breakpoint *bkpt, const StructuredData::Dictionary &options_dict,
385517e702SDimitry Andric Status &error) {
395517e702SDimitry Andric llvm::StringRef filename;
40435933ddSDimitry Andric uint32_t line_no;
41*b5893f02SDimitry Andric uint32_t column;
42435933ddSDimitry Andric bool check_inlines;
43435933ddSDimitry Andric bool skip_prologue;
44435933ddSDimitry Andric bool exact_match;
45435933ddSDimitry Andric bool success;
46435933ddSDimitry Andric
47435933ddSDimitry Andric lldb::addr_t offset = 0;
48435933ddSDimitry Andric
49435933ddSDimitry Andric success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName),
50435933ddSDimitry Andric filename);
51435933ddSDimitry Andric if (!success) {
52435933ddSDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find filename entry.");
53435933ddSDimitry Andric return nullptr;
54ac7ddfbfSEd Maste }
55ac7ddfbfSEd Maste
56435933ddSDimitry Andric success = options_dict.GetValueForKeyAsInteger(
57435933ddSDimitry Andric GetKey(OptionNames::LineNumber), line_no);
58435933ddSDimitry Andric if (!success) {
59435933ddSDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find line number entry.");
60435933ddSDimitry Andric return nullptr;
61435933ddSDimitry Andric }
62435933ddSDimitry Andric
63*b5893f02SDimitry Andric success =
64*b5893f02SDimitry Andric options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Column), column);
65*b5893f02SDimitry Andric if (!success) {
66*b5893f02SDimitry Andric // Backwards compatibility.
67*b5893f02SDimitry Andric column = 0;
68*b5893f02SDimitry Andric }
69*b5893f02SDimitry Andric
70435933ddSDimitry Andric success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines),
71435933ddSDimitry Andric check_inlines);
72435933ddSDimitry Andric if (!success) {
73435933ddSDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find check inlines entry.");
74435933ddSDimitry Andric return nullptr;
75435933ddSDimitry Andric }
76435933ddSDimitry Andric
77435933ddSDimitry Andric success = options_dict.GetValueForKeyAsBoolean(
78435933ddSDimitry Andric GetKey(OptionNames::SkipPrologue), skip_prologue);
79435933ddSDimitry Andric if (!success) {
80435933ddSDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find skip prologue entry.");
81435933ddSDimitry Andric return nullptr;
82435933ddSDimitry Andric }
83435933ddSDimitry Andric
84435933ddSDimitry Andric success = options_dict.GetValueForKeyAsBoolean(
85435933ddSDimitry Andric GetKey(OptionNames::ExactMatch), exact_match);
86435933ddSDimitry Andric if (!success) {
87435933ddSDimitry Andric error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry.");
88435933ddSDimitry Andric return nullptr;
89435933ddSDimitry Andric }
90435933ddSDimitry Andric
91*b5893f02SDimitry Andric FileSpec file_spec(filename);
92435933ddSDimitry Andric
93*b5893f02SDimitry Andric return new BreakpointResolverFileLine(bkpt, file_spec, line_no, column,
94*b5893f02SDimitry Andric offset, check_inlines, skip_prologue,
95435933ddSDimitry Andric exact_match);
96435933ddSDimitry Andric }
97435933ddSDimitry Andric
98435933ddSDimitry Andric StructuredData::ObjectSP
SerializeToStructuredData()99435933ddSDimitry Andric BreakpointResolverFileLine::SerializeToStructuredData() {
100435933ddSDimitry Andric StructuredData::DictionarySP options_dict_sp(
101435933ddSDimitry Andric new StructuredData::Dictionary());
102435933ddSDimitry Andric
103435933ddSDimitry Andric options_dict_sp->AddStringItem(GetKey(OptionNames::FileName),
104435933ddSDimitry Andric m_file_spec.GetPath());
105435933ddSDimitry Andric options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber),
106435933ddSDimitry Andric m_line_number);
107*b5893f02SDimitry Andric options_dict_sp->AddIntegerItem(GetKey(OptionNames::Column),
108*b5893f02SDimitry Andric m_column);
109435933ddSDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines), m_inlines);
110435933ddSDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),
111435933ddSDimitry Andric m_skip_prologue);
112435933ddSDimitry Andric options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),
113435933ddSDimitry Andric m_exact_match);
114435933ddSDimitry Andric
115435933ddSDimitry Andric return WrapOptionsDict(options_dict_sp);
116ac7ddfbfSEd Maste }
117ac7ddfbfSEd Maste
118f678e45dSDimitry Andric // Filter the symbol context list to remove contexts where the line number was
119f678e45dSDimitry Andric // moved into a new function. We do this conservatively, so if e.g. we cannot
1204ba319b5SDimitry Andric // resolve the function in the context (which can happen in case of line-table-
1214ba319b5SDimitry Andric // only debug info), we leave the context as is. The trickiest part here is
1224ba319b5SDimitry Andric // handling inlined functions -- in this case we need to make sure we look at
1234ba319b5SDimitry Andric // the declaration line of the inlined function, NOT the function it was
124f678e45dSDimitry Andric // inlined into.
FilterContexts(SymbolContextList & sc_list,bool is_relative)1254ba319b5SDimitry Andric void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list,
1264ba319b5SDimitry Andric bool is_relative) {
127f678e45dSDimitry Andric if (m_exact_match)
128f678e45dSDimitry Andric return; // Nothing to do. Contexts are precise.
129f678e45dSDimitry Andric
1304ba319b5SDimitry Andric llvm::StringRef relative_path;
1314ba319b5SDimitry Andric if (is_relative)
1324ba319b5SDimitry Andric relative_path = m_file_spec.GetDirectory().GetStringRef();
1334ba319b5SDimitry Andric
134f678e45dSDimitry Andric Log * log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS);
135f678e45dSDimitry Andric for(uint32_t i = 0; i < sc_list.GetSize(); ++i) {
136f678e45dSDimitry Andric SymbolContext sc;
137f678e45dSDimitry Andric sc_list.GetContextAtIndex(i, sc);
1384ba319b5SDimitry Andric if (is_relative) {
1394ba319b5SDimitry Andric // If the path was relative, make sure any matches match as long as the
1404ba319b5SDimitry Andric // relative parts of the path match the path from support files
1414ba319b5SDimitry Andric auto sc_dir = sc.line_entry.file.GetDirectory().GetStringRef();
1424ba319b5SDimitry Andric if (!sc_dir.endswith(relative_path)) {
1434ba319b5SDimitry Andric // We had a relative path specified and the relative directory doesn't
1444ba319b5SDimitry Andric // match so remove this one
1454ba319b5SDimitry Andric LLDB_LOG(log, "removing not matching relative path {0} since it "
1464ba319b5SDimitry Andric "doesn't end with {1}", sc_dir, relative_path);
1474ba319b5SDimitry Andric sc_list.RemoveContextAtIndex(i);
1484ba319b5SDimitry Andric --i;
1494ba319b5SDimitry Andric continue;
1504ba319b5SDimitry Andric }
1514ba319b5SDimitry Andric }
1524ba319b5SDimitry Andric
153f678e45dSDimitry Andric if (!sc.block)
154f678e45dSDimitry Andric continue;
155f678e45dSDimitry Andric
156f678e45dSDimitry Andric FileSpec file;
157f678e45dSDimitry Andric uint32_t line;
158f678e45dSDimitry Andric const Block *inline_block = sc.block->GetContainingInlinedBlock();
159f678e45dSDimitry Andric if (inline_block) {
160f678e45dSDimitry Andric const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration();
161f678e45dSDimitry Andric if (!inline_declaration.IsValid())
162f678e45dSDimitry Andric continue;
163f678e45dSDimitry Andric file = inline_declaration.GetFile();
164f678e45dSDimitry Andric line = inline_declaration.GetLine();
165f678e45dSDimitry Andric } else if (sc.function)
166f678e45dSDimitry Andric sc.function->GetStartLineSourceInfo(file, line);
167f678e45dSDimitry Andric else
168f678e45dSDimitry Andric continue;
169f678e45dSDimitry Andric
170f678e45dSDimitry Andric if (file != sc.line_entry.file) {
171f678e45dSDimitry Andric LLDB_LOG(log, "unexpected symbol context file {0}", sc.line_entry.file);
172f678e45dSDimitry Andric continue;
173f678e45dSDimitry Andric }
174f678e45dSDimitry Andric
175f678e45dSDimitry Andric // Compare the requested line number with the line of the function
176f678e45dSDimitry Andric // declaration. In case of a function declared as:
177f678e45dSDimitry Andric //
178f678e45dSDimitry Andric // int
179f678e45dSDimitry Andric // foo()
180f678e45dSDimitry Andric // {
181f678e45dSDimitry Andric // ...
182f678e45dSDimitry Andric //
183f678e45dSDimitry Andric // the compiler will set the declaration line to the "foo" line, which is
184f678e45dSDimitry Andric // the reason why we have -1 here. This can fail in case of two inline
185f678e45dSDimitry Andric // functions defined back-to-back:
186f678e45dSDimitry Andric //
187f678e45dSDimitry Andric // inline int foo1() { ... }
188f678e45dSDimitry Andric // inline int foo2() { ... }
189f678e45dSDimitry Andric //
190f678e45dSDimitry Andric // but that's the best we can do for now.
191*b5893f02SDimitry Andric // One complication, if the line number returned from GetStartLineSourceInfo
192*b5893f02SDimitry Andric // is 0, then we can't do this calculation. That can happen if
193*b5893f02SDimitry Andric // GetStartLineSourceInfo gets an error, or if the first line number in
194*b5893f02SDimitry Andric // the function really is 0 - which happens for some languages.
195f678e45dSDimitry Andric const int decl_line_is_too_late_fudge = 1;
196*b5893f02SDimitry Andric if (line && m_line_number < line - decl_line_is_too_late_fudge) {
197f678e45dSDimitry Andric LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line);
198f678e45dSDimitry Andric sc_list.RemoveContextAtIndex(i);
199f678e45dSDimitry Andric --i;
200f678e45dSDimitry Andric }
201f678e45dSDimitry Andric }
202f678e45dSDimitry Andric }
203f678e45dSDimitry Andric
204ac7ddfbfSEd Maste Searcher::CallbackReturn
SearchCallback(SearchFilter & filter,SymbolContext & context,Address * addr,bool containing)205435933ddSDimitry Andric BreakpointResolverFileLine::SearchCallback(SearchFilter &filter,
206ac7ddfbfSEd Maste SymbolContext &context,
207435933ddSDimitry Andric Address *addr, bool containing) {
208ac7ddfbfSEd Maste SymbolContextList sc_list;
209ac7ddfbfSEd Maste
210ac7ddfbfSEd Maste assert(m_breakpoint != NULL);
211ac7ddfbfSEd Maste
212435933ddSDimitry Andric // There is a tricky bit here. You can have two compilation units that
2134ba319b5SDimitry Andric // #include the same file, and in one of them the function at m_line_number
2144ba319b5SDimitry Andric // is used (and so code and a line entry for it is generated) but in the
2154ba319b5SDimitry Andric // other it isn't. If we considered the CU's independently, then in the
2164ba319b5SDimitry Andric // second inclusion, we'd move the breakpoint to the next function that
2174ba319b5SDimitry Andric // actually generated code in the header file. That would end up being
2184ba319b5SDimitry Andric // confusing. So instead, we do the CU iterations by hand here, then scan
2194ba319b5SDimitry Andric // through the complete list of matches, and figure out the closest line
2204ba319b5SDimitry Andric // number match, and only set breakpoints on that match.
221ac7ddfbfSEd Maste
2224ba319b5SDimitry Andric // Note also that if file_spec only had a file name and not a directory,
2234ba319b5SDimitry Andric // there may be many different file spec's in the resultant list. The
2244ba319b5SDimitry Andric // closest line match for one will not be right for some totally different
2254ba319b5SDimitry Andric // file. So we go through the match list and pull out the sets that have the
2264ba319b5SDimitry Andric // same file spec in their line_entry and treat each set separately.
2274ba319b5SDimitry Andric
2284ba319b5SDimitry Andric FileSpec search_file_spec = m_file_spec;
2294ba319b5SDimitry Andric const bool is_relative = m_file_spec.IsRelative();
2304ba319b5SDimitry Andric if (is_relative)
2314ba319b5SDimitry Andric search_file_spec.GetDirectory().Clear();
232ac7ddfbfSEd Maste
233ac7ddfbfSEd Maste const size_t num_comp_units = context.module_sp->GetNumCompileUnits();
234435933ddSDimitry Andric for (size_t i = 0; i < num_comp_units; i++) {
235ac7ddfbfSEd Maste CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i));
236435933ddSDimitry Andric if (cu_sp) {
237ac7ddfbfSEd Maste if (filter.CompUnitPasses(*cu_sp))
2384ba319b5SDimitry Andric cu_sp->ResolveSymbolContext(search_file_spec, m_line_number, m_inlines,
239435933ddSDimitry Andric m_exact_match, eSymbolContextEverything,
240435933ddSDimitry Andric sc_list);
241ac7ddfbfSEd Maste }
242ac7ddfbfSEd Maste }
243f678e45dSDimitry Andric
2444ba319b5SDimitry Andric FilterContexts(sc_list, is_relative);
245f678e45dSDimitry Andric
246ac7ddfbfSEd Maste StreamString s;
247435933ddSDimitry Andric s.Printf("for %s:%d ", m_file_spec.GetFilename().AsCString("<Unknown>"),
248ac7ddfbfSEd Maste m_line_number);
24935617911SEd Maste
250*b5893f02SDimitry Andric SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(),
251*b5893f02SDimitry Andric m_line_number, m_column);
252ac7ddfbfSEd Maste
253ac7ddfbfSEd Maste return Searcher::eCallbackReturnContinue;
254ac7ddfbfSEd Maste }
255ac7ddfbfSEd Maste
GetDepth()256*b5893f02SDimitry Andric lldb::SearchDepth BreakpointResolverFileLine::GetDepth() {
257*b5893f02SDimitry Andric return lldb::eSearchDepthModule;
258ac7ddfbfSEd Maste }
259ac7ddfbfSEd Maste
GetDescription(Stream * s)260435933ddSDimitry Andric void BreakpointResolverFileLine::GetDescription(Stream *s) {
261*b5893f02SDimitry Andric s->Printf("file = '%s', line = %u, ", m_file_spec.GetPath().c_str(),
262*b5893f02SDimitry Andric m_line_number);
263*b5893f02SDimitry Andric if (m_column)
264*b5893f02SDimitry Andric s->Printf("column = %u, ", m_column);
265*b5893f02SDimitry Andric s->Printf("exact_match = %d", m_exact_match);
266ac7ddfbfSEd Maste }
267ac7ddfbfSEd Maste
Dump(Stream * s) const268435933ddSDimitry Andric void BreakpointResolverFileLine::Dump(Stream *s) const {}
269ac7ddfbfSEd Maste
2707aa51b79SEd Maste lldb::BreakpointResolverSP
CopyForBreakpoint(Breakpoint & breakpoint)271435933ddSDimitry Andric BreakpointResolverFileLine::CopyForBreakpoint(Breakpoint &breakpoint) {
272435933ddSDimitry Andric lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine(
273*b5893f02SDimitry Andric &breakpoint, m_file_spec, m_line_number, m_column, m_offset, m_inlines,
274435933ddSDimitry Andric m_skip_prologue, m_exact_match));
2757aa51b79SEd Maste
2767aa51b79SEd Maste return ret_sp;
2777aa51b79SEd Maste }
278