1 //===-- BreakpointResolverName.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 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 // Project includes
14 #include "lldb/Breakpoint/BreakpointResolverName.h"
15 
16 #include "lldb/Breakpoint/BreakpointLocation.h"
17 #include "lldb/Core/Log.h"
18 #include "lldb/Core/Module.h"
19 #include "lldb/Core/StreamString.h"
20 #include "lldb/Symbol/Block.h"
21 #include "lldb/Symbol/Function.h"
22 #include "lldb/Symbol/Symbol.h"
23 #include "lldb/Symbol/SymbolContext.h"
24 #include "lldb/Target/LanguageRuntime.h"
25 #include "Plugins/Language/ObjC/ObjCLanguage.h"
26 
27 using namespace lldb;
28 using namespace lldb_private;
29 
30 BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt,
31                                                 const char *name_cstr,
32                                                 uint32_t name_type_mask,
33                                                 LanguageType language,
34                                                 Breakpoint::MatchType type,
35                                                 bool skip_prologue) :
36     BreakpointResolver (bkpt, BreakpointResolver::NameResolver),
37     m_class_name (),
38     m_regex (),
39     m_match_type (type),
40     m_language (language),
41     m_skip_prologue (skip_prologue)
42 {
43     if (m_match_type == Breakpoint::Regexp)
44     {
45         if (!m_regex.Compile (name_cstr))
46         {
47             Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
48 
49             if (log)
50                 log->Warning ("function name regexp: \"%s\" did not compile.", name_cstr);
51         }
52     }
53     else
54     {
55         AddNameLookup (ConstString(name_cstr), name_type_mask);
56     }
57 }
58 
59 BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt,
60                                                 const char *names[],
61                                                 size_t num_names,
62                                                 uint32_t name_type_mask,
63                                                 LanguageType language,
64                                                 bool skip_prologue) :
65     BreakpointResolver (bkpt, BreakpointResolver::NameResolver),
66     m_match_type (Breakpoint::Exact),
67     m_language (language),
68     m_skip_prologue (skip_prologue)
69 {
70     for (size_t i = 0; i < num_names; i++)
71     {
72         AddNameLookup (ConstString (names[i]), name_type_mask);
73     }
74 }
75 
76 BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt,
77                                                 std::vector<std::string> names,
78                                                 uint32_t name_type_mask,
79                                                 LanguageType language,
80                                                 bool skip_prologue) :
81     BreakpointResolver (bkpt, BreakpointResolver::NameResolver),
82     m_match_type (Breakpoint::Exact),
83     m_language (language),
84     m_skip_prologue (skip_prologue)
85 {
86     for (const std::string& name : names)
87     {
88         AddNameLookup (ConstString (name.c_str(), name.size()), name_type_mask);
89     }
90 }
91 
92 BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt,
93                                                 RegularExpression &func_regex,
94                                                 lldb::LanguageType language,
95                                                 bool skip_prologue) :
96     BreakpointResolver (bkpt, BreakpointResolver::NameResolver),
97     m_class_name (nullptr),
98     m_regex (func_regex),
99     m_match_type (Breakpoint::Regexp),
100     m_language (language),
101     m_skip_prologue (skip_prologue)
102 {
103 }
104 
105 BreakpointResolverName::BreakpointResolverName(Breakpoint *bkpt,
106                                                const char *class_name,
107                                                const char *method,
108                                                Breakpoint::MatchType type,
109                                                bool skip_prologue ) :
110     BreakpointResolver (bkpt, BreakpointResolver::NameResolver),
111     m_class_name (class_name),
112     m_regex (),
113     m_match_type (type),
114     m_language (eLanguageTypeUnknown),
115     m_skip_prologue (skip_prologue)
116 {
117     LookupInfo lookup;
118     lookup.name.SetCString(method);
119     lookup.lookup_name = lookup.name;
120     lookup.name_type_mask = eFunctionNameTypeMethod;
121     lookup.match_name_after_lookup = false;
122     m_lookups.push_back (lookup);
123 }
124 
125 BreakpointResolverName::~BreakpointResolverName() = default;
126 
127 BreakpointResolverName::BreakpointResolverName(const BreakpointResolverName &rhs) :
128     BreakpointResolver(rhs.m_breakpoint, BreakpointResolver::NameResolver),
129     m_lookups(rhs.m_lookups),
130     m_class_name(rhs.m_class_name),
131     m_regex(rhs.m_regex),
132     m_match_type (rhs.m_match_type),
133     m_language (rhs.m_language),
134     m_skip_prologue (rhs.m_skip_prologue)
135 {
136 }
137 
138 void
139 BreakpointResolverName::AddNameLookup (const ConstString &name, uint32_t name_type_mask)
140 {
141     ObjCLanguage::MethodName objc_method(name.GetCString(), false);
142     if (objc_method.IsValid(false))
143     {
144         std::vector<ConstString> objc_names;
145         objc_method.GetFullNames(objc_names, true);
146         for (ConstString objc_name : objc_names)
147         {
148             LookupInfo lookup;
149             lookup.name = name;
150             lookup.lookup_name = objc_name;
151             lookup.name_type_mask = eFunctionNameTypeFull;
152             lookup.match_name_after_lookup = false;
153             m_lookups.push_back (lookup);
154         }
155     }
156     else
157     {
158         LookupInfo lookup;
159         lookup.name = name;
160         Module::PrepareForFunctionNameLookup(lookup.name, name_type_mask, m_language, lookup.lookup_name, lookup.name_type_mask, lookup.match_name_after_lookup);
161         m_lookups.push_back (lookup);
162     }
163 }
164 
165 void
166 BreakpointResolverName::LookupInfo::Prune (SymbolContextList &sc_list, size_t start_idx) const
167 {
168     if (match_name_after_lookup && name)
169     {
170         SymbolContext sc;
171         size_t i = start_idx;
172         while (i < sc_list.GetSize())
173         {
174             if (!sc_list.GetContextAtIndex(i, sc))
175                 break;
176             ConstString full_name (sc.GetFunctionName());
177             if (full_name && ::strstr(full_name.GetCString(), name.GetCString()) == nullptr)
178             {
179                 sc_list.RemoveContextAtIndex(i);
180             }
181             else
182             {
183                 ++i;
184             }
185         }
186     }
187 }
188 
189 // FIXME: Right now we look at the module level, and call the module's "FindFunctions".
190 // Greg says he will add function tables, maybe at the CompileUnit level to accelerate function
191 // lookup.  At that point, we should switch the depth to CompileUnit, and look in these tables.
192 
193 Searcher::CallbackReturn
194 BreakpointResolverName::SearchCallback(SearchFilter &filter,
195                                        SymbolContext &context,
196                                        Address *addr,
197                                        bool containing)
198 {
199     SymbolContextList func_list;
200     //SymbolContextList sym_list;
201 
202     uint32_t i;
203     bool new_location;
204     Address break_addr;
205     assert (m_breakpoint != nullptr);
206 
207     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
208 
209     if (m_class_name)
210     {
211         if (log)
212             log->Warning ("Class/method function specification not supported yet.\n");
213         return Searcher::eCallbackReturnStop;
214     }
215     bool filter_by_cu = (filter.GetFilterRequiredItems() & eSymbolContextCompUnit) != 0;
216     bool filter_by_language = (m_language != eLanguageTypeUnknown);
217     const bool include_symbols = !filter_by_cu;
218     const bool include_inlines = true;
219     const bool append = true;
220 
221     switch (m_match_type)
222     {
223         case Breakpoint::Exact:
224             if (context.module_sp)
225             {
226                 for (const LookupInfo &lookup : m_lookups)
227                 {
228                     const size_t start_func_idx = func_list.GetSize();
229                     context.module_sp->FindFunctions(lookup.lookup_name,
230                                                      nullptr,
231                                                      lookup.name_type_mask,
232                                                      include_symbols,
233                                                      include_inlines,
234                                                      append,
235                                                      func_list);
236                     const size_t end_func_idx = func_list.GetSize();
237 
238                     if (start_func_idx < end_func_idx)
239                         lookup.Prune (func_list, start_func_idx);
240                 }
241             }
242             break;
243         case Breakpoint::Regexp:
244             if (context.module_sp)
245             {
246                 context.module_sp->FindFunctions (m_regex,
247                                                   !filter_by_cu, // include symbols only if we aren't filtering by CU
248                                                   include_inlines,
249                                                   append,
250                                                   func_list);
251             }
252             break;
253         case Breakpoint::Glob:
254             if (log)
255                 log->Warning ("glob is not supported yet.");
256             break;
257     }
258 
259     // If the filter specifies a Compilation Unit, remove the ones that don't pass at this point.
260     if (filter_by_cu || filter_by_language)
261     {
262         Target &target = m_breakpoint->GetTarget();
263 
264         uint32_t num_functions = func_list.GetSize();
265 
266         for (size_t idx = 0; idx < num_functions; idx++)
267         {
268             bool remove_it = false;
269             SymbolContext sc;
270             func_list.GetContextAtIndex(idx, sc);
271             if (filter_by_cu)
272             {
273                 if (!sc.comp_unit || !filter.CompUnitPasses(*sc.comp_unit))
274                     remove_it = true;
275             }
276 
277             if (filter_by_language)
278             {
279                 const char *name = sc.GetFunctionName(Mangled::ePreferMangled).AsCString();
280                 if (name)
281                 {
282                     LanguageType sym_language = LanguageRuntime::GuessLanguageForSymbolByName(target, name);
283                     if (Language::LanguageIsC(m_language) ||
284                         Language::LanguageIsPascal(m_language))
285                     {
286                         // We don't currently have a way to say "This symbol name is C" so for now, C means
287                         // not ObjC and not C++, etc...
288                         if (sym_language == eLanguageTypeC_plus_plus
289                             || sym_language == eLanguageTypeObjC
290                             || sym_language == eLanguageTypeSwift)
291                         {
292                             remove_it = true;
293                         }
294                     }
295                     else if (sym_language != m_language)
296                     {
297                         // Note: This code prevents us from being able to find symbols
298                         // like 'printf' if the target language's option is set.  It
299                         // would be better to limit this filtering to only when the
300                         // breakpoint's language option is set (and not the target's),
301                         // but we can't know if m_language was set from the target or
302                         // breakpoint option.
303                         remove_it = true;
304                     }
305                 }
306             }
307 
308             if  (remove_it)
309             {
310                 func_list.RemoveContextAtIndex(idx);
311                 num_functions--;
312                 idx--;
313             }
314         }
315     }
316 
317     // Remove any duplicates between the function list and the symbol list
318     SymbolContext sc;
319     if (func_list.GetSize())
320     {
321         for (i = 0; i < func_list.GetSize(); i++)
322         {
323             if (func_list.GetContextAtIndex(i, sc))
324             {
325                 bool is_reexported = false;
326 
327                 if (sc.block && sc.block->GetInlinedFunctionInfo())
328                 {
329                     if (!sc.block->GetStartAddress(break_addr))
330                         break_addr.Clear();
331                 }
332                 else if (sc.function)
333                 {
334                     break_addr = sc.function->GetAddressRange().GetBaseAddress();
335                     if (m_skip_prologue && break_addr.IsValid())
336                     {
337                         const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize();
338                         if (prologue_byte_size)
339                             break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size);
340                     }
341                 }
342                 else if (sc.symbol)
343                 {
344                     if (sc.symbol->GetType() == eSymbolTypeReExported)
345                     {
346                         const Symbol *actual_symbol = sc.symbol->ResolveReExportedSymbol(m_breakpoint->GetTarget());
347                         if (actual_symbol)
348                         {
349                             is_reexported = true;
350                             break_addr = actual_symbol->GetAddress();
351                         }
352                     }
353                     else
354                     {
355                         break_addr = sc.symbol->GetAddress();
356                     }
357 
358                     if (m_skip_prologue && break_addr.IsValid())
359                     {
360                         const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize();
361                         if (prologue_byte_size)
362                             break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size);
363                     }
364                 }
365 
366                 if (break_addr.IsValid())
367                 {
368                     if (filter.AddressPasses(break_addr))
369                     {
370                         BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(break_addr, &new_location));
371                         bp_loc_sp->SetIsReExported(is_reexported);
372                         if (bp_loc_sp && new_location && !m_breakpoint->IsInternal())
373                         {
374                             if (log)
375                             {
376                                 StreamString s;
377                                 bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
378                                 log->Printf ("Added location: %s\n", s.GetData());
379                             }
380                         }
381                     }
382                 }
383             }
384         }
385     }
386 
387     return Searcher::eCallbackReturnContinue;
388 }
389 
390 Searcher::Depth
391 BreakpointResolverName::GetDepth()
392 {
393     return Searcher::eDepthModule;
394 }
395 
396 void
397 BreakpointResolverName::GetDescription (Stream *s)
398 {
399     if (m_match_type == Breakpoint::Regexp)
400         s->Printf("regex = '%s'", m_regex.GetText());
401     else
402     {
403         size_t num_names = m_lookups.size();
404         if (num_names == 1)
405             s->Printf("name = '%s'", m_lookups[0].name.GetCString());
406         else
407         {
408             s->Printf("names = {");
409             for (size_t i = 0; i < num_names - 1; i++)
410             {
411                 s->Printf ("'%s', ", m_lookups[i].name.GetCString());
412             }
413             s->Printf ("'%s'}", m_lookups[num_names - 1].name.GetCString());
414         }
415     }
416     if (m_language != eLanguageTypeUnknown)
417     {
418         s->Printf (", language = %s", Language::GetNameForLanguageType(m_language));
419     }
420 }
421 
422 void
423 BreakpointResolverName::Dump (Stream *s) const
424 {
425 }
426 
427 lldb::BreakpointResolverSP
428 BreakpointResolverName::CopyForBreakpoint (Breakpoint &breakpoint)
429 {
430     lldb::BreakpointResolverSP ret_sp(new BreakpointResolverName(*this));
431     ret_sp->SetBreakpoint(&breakpoint);
432     return ret_sp;
433 }
434