1 //===-- BreakpointResolverFileLine.cpp --------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Breakpoint/BreakpointResolverFileLine.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Breakpoint/BreakpointLocation.h"
17 #include "lldb/Core/Log.h"
18 #include "lldb/Core/StreamString.h"
19 #include "lldb/Target/Target.h"
20 #include "lldb/lldb-private-log.h"
21 
22 using namespace lldb;
23 using namespace lldb_private;
24 
25 //----------------------------------------------------------------------
26 // BreakpointResolverFileLine:
27 //----------------------------------------------------------------------
28 BreakpointResolverFileLine::BreakpointResolverFileLine
29 (
30     Breakpoint *bkpt,
31     const FileSpec &file_spec,
32     uint32_t line_no,
33     bool check_inlines,
34     bool skip_prologue
35 ) :
36     BreakpointResolver (bkpt, BreakpointResolver::FileLineResolver),
37     m_file_spec (file_spec),
38     m_line_number (line_no),
39     m_inlines (check_inlines),
40     m_skip_prologue(skip_prologue)
41 {
42 }
43 
44 BreakpointResolverFileLine::~BreakpointResolverFileLine ()
45 {
46 }
47 
48 Searcher::CallbackReturn
49 BreakpointResolverFileLine::SearchCallback
50 (
51     SearchFilter &filter,
52     SymbolContext &context,
53     Address *addr,
54     bool containing
55 )
56 {
57     SymbolContextList sc_list;
58 
59     assert (m_breakpoint != NULL);
60     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
61 
62     // There is a tricky bit here.  You can have two compilation units that #include the same file, and
63     // in one of them the function at m_line_number is used (and so code and a line entry for it is generated) but in the
64     // other it isn't.  If we considered the CU's independently, then in the second inclusion, we'd move the breakpoint
65     // to the next function that actually generated code in the header file.  That would end up being confusing.
66     // So instead, we do the CU iterations by hand here, then scan through the complete list of matches, and figure out
67     // the closest line number match, and only set breakpoints on that match.
68 
69     // Note also that if file_spec only had a file name and not a directory, there may be many different file spec's in
70     // the resultant list.  The closest line match for one will not be right for some totally different file.
71     // So we go through the match list and pull out the sets that have the same file spec in their line_entry
72     // and treat each set separately.
73 
74     uint32_t num_comp_units = context.module_sp->GetNumCompileUnits();
75     for (uint32_t i = 0; i < num_comp_units; i++)
76     {
77         CompUnitSP cu_sp (context.module_sp->GetCompileUnitAtIndex (i));
78         if (cu_sp)
79         {
80             if (filter.CompUnitPasses(*cu_sp))
81                 cu_sp->ResolveSymbolContext (m_file_spec, m_line_number, m_inlines, false, eSymbolContextEverything, sc_list);
82         }
83     }
84 
85     while (sc_list.GetSize() > 0)
86     {
87         SymbolContextList tmp_sc_list;
88         int current_idx = 0;
89         SymbolContext sc;
90         bool first_entry = true;
91 
92         FileSpec match_file_spec;
93         uint32_t closest_line_number = UINT32_MAX;
94 
95         // Pull out the first entry, and all the others that match its file spec, and stuff them in the tmp list.
96         while (current_idx < sc_list.GetSize())
97         {
98             bool matches;
99 
100             sc_list.GetContextAtIndex (current_idx, sc);
101             if (first_entry)
102             {
103                 match_file_spec = sc.line_entry.file;
104                 matches = true;
105                 first_entry = false;
106             }
107             else
108                 matches = (sc.line_entry.file == match_file_spec);
109 
110             if (matches)
111             {
112                 tmp_sc_list.Append (sc);
113                 sc_list.RemoveContextAtIndex(current_idx);
114 
115                 // ResolveSymbolContext will always return a number that is >= the line number you pass in.
116                 // So the smaller line number is always better.
117                 if (sc.line_entry.line < closest_line_number)
118                     closest_line_number = sc.line_entry.line;
119             }
120             else
121                 current_idx++;
122         }
123 
124         // Okay, we've found the closest line number match, now throw away all the others,
125         // and make breakpoints out of the closest line number match.
126 
127         uint32_t tmp_sc_list_size = tmp_sc_list.GetSize();
128 
129         for (uint32_t i = 0; i < tmp_sc_list_size; i++)
130         {
131             SymbolContext sc;
132             if (tmp_sc_list.GetContextAtIndex(i, sc))
133             {
134                 if (sc.line_entry.line == closest_line_number)
135                 {
136                     Address line_start = sc.line_entry.range.GetBaseAddress();
137                     if (line_start.IsValid())
138                     {
139                         if (filter.AddressPasses(line_start))
140                         {
141                             // If the line number is before the prologue end, move it there...
142                             bool skipped_prologue = false;
143                             if (m_skip_prologue)
144                             {
145                                 if (sc.function)
146                                 {
147                                     Address prologue_addr(sc.function->GetAddressRange().GetBaseAddress());
148                                     if (prologue_addr.IsValid() && (line_start == prologue_addr))
149                                     {
150                                         const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize();
151                                         if (prologue_byte_size)
152                                         {
153                                             prologue_addr.Slide(prologue_byte_size);
154 
155                                             if (filter.AddressPasses(prologue_addr))
156                                             {
157                                                 skipped_prologue = true;
158                                                 line_start = prologue_addr;
159                                             }
160                                         }
161                                     }
162                                 }
163                             }
164 
165                             BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(line_start));
166                             if (log && bp_loc_sp && !m_breakpoint->IsInternal())
167                             {
168                                 StreamString s;
169                                 bp_loc_sp->GetDescription (&s, lldb::eDescriptionLevelVerbose);
170                                 log->Printf ("Added location (skipped prologue: %s): %s \n", skipped_prologue ? "yes" : "no", s.GetData());
171                             }
172                         }
173                         else if (log)
174                         {
175                             log->Printf ("Breakpoint at file address 0x%llx for %s:%d didn't pass the filter.\n",
176                                          line_start.GetFileAddress(),
177                                          m_file_spec.GetFilename().AsCString("<Unknown>"),
178                                          m_line_number);
179                         }
180                     }
181                     else
182                     {
183                         if (log)
184                             log->Printf ("error: Unable to set breakpoint at file address 0x%llx for %s:%d\n",
185                                          line_start.GetFileAddress(),
186                                          m_file_spec.GetFilename().AsCString("<Unknown>"),
187                                          m_line_number);
188                     }
189                 }
190                 else
191                 {
192         #if 0
193                     s << "error: Breakpoint at '" << pos->c_str() << "' isn't resolved yet: \n";
194                     if (sc.line_entry.address.Dump(&s, Address::DumpStyleSectionNameOffset))
195                         s.EOL();
196                     if (sc.line_entry.address.Dump(&s, Address::DumpStyleSectionPointerOffset))
197                         s.EOL();
198                     if (sc.line_entry.address.Dump(&s, Address::DumpStyleFileAddress))
199                         s.EOL();
200                     if (sc.line_entry.address.Dump(&s, Address::DumpStyleLoadAddress))
201                         s.EOL();
202         #endif
203                 }
204             }
205         }
206     }
207 
208     return Searcher::eCallbackReturnContinue;
209 }
210 
211 Searcher::Depth
212 BreakpointResolverFileLine::GetDepth()
213 {
214     return Searcher::eDepthModule;
215 }
216 
217 void
218 BreakpointResolverFileLine::GetDescription (Stream *s)
219 {
220     s->Printf ("file ='%s', line = %u", m_file_spec.GetFilename().AsCString(), m_line_number);
221 }
222 
223 void
224 BreakpointResolverFileLine::Dump (Stream *s) const
225 {
226 
227 }
228 
229