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