1 //===-- CompileUnit.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/Symbol/CompileUnit.h"
11 #include "lldb/Core/Module.h"
12 #include "lldb/Symbol/LineTable.h"
13 #include "lldb/Symbol/SymbolVendor.h"
14 #include "lldb/Symbol/VariableList.h"
15 #include "lldb/Target/Language.h"
16 
17 using namespace lldb;
18 using namespace lldb_private;
19 
CompileUnit(const lldb::ModuleSP & module_sp,void * user_data,const char * pathname,const lldb::user_id_t cu_sym_id,lldb::LanguageType language,lldb_private::LazyBool is_optimized)20 CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
21                          const char *pathname, const lldb::user_id_t cu_sym_id,
22                          lldb::LanguageType language,
23                          lldb_private::LazyBool is_optimized)
24     : ModuleChild(module_sp), FileSpec(pathname), UserID(cu_sym_id),
25       m_user_data(user_data), m_language(language), m_flags(0),
26       m_support_files(), m_line_table_ap(), m_variables(),
27       m_is_optimized(is_optimized) {
28   if (language != eLanguageTypeUnknown)
29     m_flags.Set(flagsParsedLanguage);
30   assert(module_sp);
31 }
32 
CompileUnit(const lldb::ModuleSP & module_sp,void * user_data,const FileSpec & fspec,const lldb::user_id_t cu_sym_id,lldb::LanguageType language,lldb_private::LazyBool is_optimized)33 CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
34                          const FileSpec &fspec, const lldb::user_id_t cu_sym_id,
35                          lldb::LanguageType language,
36                          lldb_private::LazyBool is_optimized)
37     : ModuleChild(module_sp), FileSpec(fspec), UserID(cu_sym_id),
38       m_user_data(user_data), m_language(language), m_flags(0),
39       m_support_files(), m_line_table_ap(), m_variables(),
40       m_is_optimized(is_optimized) {
41   if (language != eLanguageTypeUnknown)
42     m_flags.Set(flagsParsedLanguage);
43   assert(module_sp);
44 }
45 
~CompileUnit()46 CompileUnit::~CompileUnit() {}
47 
CalculateSymbolContext(SymbolContext * sc)48 void CompileUnit::CalculateSymbolContext(SymbolContext *sc) {
49   sc->comp_unit = this;
50   GetModule()->CalculateSymbolContext(sc);
51 }
52 
CalculateSymbolContextModule()53 ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); }
54 
CalculateSymbolContextCompileUnit()55 CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; }
56 
DumpSymbolContext(Stream * s)57 void CompileUnit::DumpSymbolContext(Stream *s) {
58   GetModule()->DumpSymbolContext(s);
59   s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID());
60 }
61 
GetDescription(Stream * s,lldb::DescriptionLevel level) const62 void CompileUnit::GetDescription(Stream *s,
63                                  lldb::DescriptionLevel level) const {
64   const char *language = Language::GetNameForLanguageType(m_language);
65   *s << "id = " << (const UserID &)*this << ", file = \""
66      << (const FileSpec &)*this << "\", language = \"" << language << '"';
67 }
68 
ForeachFunction(llvm::function_ref<bool (const FunctionSP &)> lambda) const69 void CompileUnit::ForeachFunction(
70     llvm::function_ref<bool(const FunctionSP &)> lambda) const {
71   std::vector<lldb::FunctionSP> sorted_functions;
72   sorted_functions.reserve(m_functions_by_uid.size());
73   for (auto &p : m_functions_by_uid)
74     sorted_functions.push_back(p.second);
75   llvm::sort(sorted_functions.begin(), sorted_functions.end(),
76              [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) {
77                return a->GetID() < b->GetID();
78              });
79 
80   for (auto &f : sorted_functions)
81     if (lambda(f))
82       return;
83 }
84 
85 //----------------------------------------------------------------------
86 // Dump the current contents of this object. No functions that cause on demand
87 // parsing of functions, globals, statics are called, so this is a good
88 // function to call to get an idea of the current contents of the CompileUnit
89 // object.
90 //----------------------------------------------------------------------
Dump(Stream * s,bool show_context) const91 void CompileUnit::Dump(Stream *s, bool show_context) const {
92   const char *language = Language::GetNameForLanguageType(m_language);
93 
94   s->Printf("%p: ", static_cast<const void *>(this));
95   s->Indent();
96   *s << "CompileUnit" << static_cast<const UserID &>(*this) << ", language = \""
97      << language << "\", file = '" << static_cast<const FileSpec &>(*this)
98      << "'\n";
99 
100   //  m_types.Dump(s);
101 
102   if (m_variables.get()) {
103     s->IndentMore();
104     m_variables->Dump(s, show_context);
105     s->IndentLess();
106   }
107 
108   if (!m_functions_by_uid.empty()) {
109     s->IndentMore();
110     ForeachFunction([&s, show_context](const FunctionSP &f) {
111       f->Dump(s, show_context);
112       return false;
113     });
114 
115     s->IndentLess();
116     s->EOL();
117   }
118 }
119 
120 //----------------------------------------------------------------------
121 // Add a function to this compile unit
122 //----------------------------------------------------------------------
AddFunction(FunctionSP & funcSP)123 void CompileUnit::AddFunction(FunctionSP &funcSP) {
124   m_functions_by_uid[funcSP->GetID()] = funcSP;
125 }
126 
127 //----------------------------------------------------------------------
128 // Find functions using the Mangled::Tokens token list. This function currently
129 // implements an interactive approach designed to find all instances of certain
130 // functions. It isn't designed to the quickest way to lookup functions as it
131 // will need to iterate through all functions and see if they match, though it
132 // does provide a powerful and context sensitive way to search for all
133 // functions with a certain name, all functions in a namespace, or all
134 // functions of a template type. See Mangled::Tokens::Parse() comments for more
135 // information.
136 //
137 // The function prototype will need to change to return a list of results. It
138 // was originally used to help debug the Mangled class and the
139 // Mangled::Tokens::MatchesQuery() function and it currently will print out a
140 // list of matching results for the functions that are currently in this
141 // compile unit.
142 //
143 // A FindFunctions method should be called prior to this that takes
144 // a regular function name (const char * or ConstString as a parameter) before
145 // resorting to this slower but more complete function. The other FindFunctions
146 // method should be able to take advantage of any accelerator tables available
147 // in the debug information (which is parsed by the SymbolFile parser plug-ins
148 // and registered with each Module).
149 //----------------------------------------------------------------------
150 // void
151 // CompileUnit::FindFunctions(const Mangled::Tokens& tokens)
152 //{
153 //  if (!m_functions.empty())
154 //  {
155 //      Stream s(stdout);
156 //      std::vector<FunctionSP>::const_iterator pos;
157 //      std::vector<FunctionSP>::const_iterator end = m_functions.end();
158 //      for (pos = m_functions.begin(); pos != end; ++pos)
159 //      {
160 //          const ConstString& demangled = (*pos)->Mangled().Demangled();
161 //          if (demangled)
162 //          {
163 //              const Mangled::Tokens& func_tokens =
164 //              (*pos)->Mangled().GetTokens();
165 //              if (func_tokens.MatchesQuery (tokens))
166 //                  s << "demangled MATCH found: " << demangled << "\n";
167 //          }
168 //      }
169 //  }
170 //}
171 
FindFunctionByUID(lldb::user_id_t func_uid)172 FunctionSP CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid) {
173   auto it = m_functions_by_uid.find(func_uid);
174   if (it == m_functions_by_uid.end())
175     return FunctionSP();
176   return it->second;
177 }
178 
GetLanguage()179 lldb::LanguageType CompileUnit::GetLanguage() {
180   if (m_language == eLanguageTypeUnknown) {
181     if (m_flags.IsClear(flagsParsedLanguage)) {
182       m_flags.Set(flagsParsedLanguage);
183       SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
184       if (symbol_vendor) {
185         m_language = symbol_vendor->ParseLanguage(*this);
186       }
187     }
188   }
189   return m_language;
190 }
191 
GetLineTable()192 LineTable *CompileUnit::GetLineTable() {
193   if (m_line_table_ap.get() == nullptr) {
194     if (m_flags.IsClear(flagsParsedLineTable)) {
195       m_flags.Set(flagsParsedLineTable);
196       SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
197       if (symbol_vendor)
198         symbol_vendor->ParseLineTable(*this);
199     }
200   }
201   return m_line_table_ap.get();
202 }
203 
SetLineTable(LineTable * line_table)204 void CompileUnit::SetLineTable(LineTable *line_table) {
205   if (line_table == nullptr)
206     m_flags.Clear(flagsParsedLineTable);
207   else
208     m_flags.Set(flagsParsedLineTable);
209   m_line_table_ap.reset(line_table);
210 }
211 
GetDebugMacros()212 DebugMacros *CompileUnit::GetDebugMacros() {
213   if (m_debug_macros_sp.get() == nullptr) {
214     if (m_flags.IsClear(flagsParsedDebugMacros)) {
215       m_flags.Set(flagsParsedDebugMacros);
216       SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
217       if (symbol_vendor) {
218         symbol_vendor->ParseDebugMacros(*this);
219       }
220     }
221   }
222 
223   return m_debug_macros_sp.get();
224 }
225 
SetDebugMacros(const DebugMacrosSP & debug_macros_sp)226 void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) {
227   if (debug_macros_sp.get() == nullptr)
228     m_flags.Clear(flagsParsedDebugMacros);
229   else
230     m_flags.Set(flagsParsedDebugMacros);
231   m_debug_macros_sp = debug_macros_sp;
232 }
233 
GetVariableList(bool can_create)234 VariableListSP CompileUnit::GetVariableList(bool can_create) {
235   if (m_variables.get() == nullptr && can_create) {
236     SymbolContext sc;
237     CalculateSymbolContext(&sc);
238     assert(sc.module_sp);
239     sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc);
240   }
241 
242   return m_variables;
243 }
244 
FindLineEntry(uint32_t start_idx,uint32_t line,const FileSpec * file_spec_ptr,bool exact,LineEntry * line_entry_ptr)245 uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line,
246                                     const FileSpec *file_spec_ptr, bool exact,
247                                     LineEntry *line_entry_ptr) {
248   uint32_t file_idx = 0;
249 
250   if (file_spec_ptr) {
251     file_idx = GetSupportFiles().FindFileIndex(1, *file_spec_ptr, true);
252     if (file_idx == UINT32_MAX)
253       return UINT32_MAX;
254   } else {
255     // All the line table entries actually point to the version of the Compile
256     // Unit that is in the support files (the one at 0 was artificially added.)
257     // So prefer the one further on in the support files if it exists...
258     FileSpecList &support_files = GetSupportFiles();
259     const bool full = true;
260     file_idx = support_files.FindFileIndex(
261         1, support_files.GetFileSpecAtIndex(0), full);
262     if (file_idx == UINT32_MAX)
263       file_idx = 0;
264   }
265   LineTable *line_table = GetLineTable();
266   if (line_table)
267     return line_table->FindLineEntryIndexByFileIndex(start_idx, file_idx, line,
268                                                      exact, line_entry_ptr);
269   return UINT32_MAX;
270 }
271 
ResolveSymbolContext(const FileSpec & file_spec,uint32_t line,bool check_inlines,bool exact,SymbolContextItem resolve_scope,SymbolContextList & sc_list)272 uint32_t CompileUnit::ResolveSymbolContext(const FileSpec &file_spec,
273                                            uint32_t line, bool check_inlines,
274                                            bool exact,
275                                            SymbolContextItem resolve_scope,
276                                            SymbolContextList &sc_list) {
277   // First find all of the file indexes that match our "file_spec". If
278   // "file_spec" has an empty directory, then only compare the basenames when
279   // finding file indexes
280   std::vector<uint32_t> file_indexes;
281   const bool full_match = (bool)file_spec.GetDirectory();
282   bool file_spec_matches_cu_file_spec =
283       FileSpec::Equal(file_spec, *this, full_match);
284 
285   // If we are not looking for inlined functions and our file spec doesn't
286   // match then we are done...
287   if (!file_spec_matches_cu_file_spec && !check_inlines)
288     return 0;
289 
290   uint32_t file_idx =
291       GetSupportFiles().FindFileIndex(1, file_spec, true);
292   while (file_idx != UINT32_MAX) {
293     file_indexes.push_back(file_idx);
294     file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true);
295   }
296 
297   const size_t num_file_indexes = file_indexes.size();
298   if (num_file_indexes == 0)
299     return 0;
300 
301   const uint32_t prev_size = sc_list.GetSize();
302 
303   SymbolContext sc(GetModule());
304   sc.comp_unit = this;
305 
306   if (line != 0) {
307     LineTable *line_table = sc.comp_unit->GetLineTable();
308 
309     if (line_table != nullptr) {
310       uint32_t found_line;
311       uint32_t line_idx;
312 
313       if (num_file_indexes == 1) {
314         // We only have a single support file that matches, so use the line
315         // table function that searches for a line entries that match a single
316         // support file index
317         LineEntry line_entry;
318         line_idx = line_table->FindLineEntryIndexByFileIndex(
319             0, file_indexes.front(), line, exact, &line_entry);
320 
321         // If "exact == true", then "found_line" will be the same as "line". If
322         // "exact == false", the "found_line" will be the closest line entry
323         // with a line number greater than "line" and we will use this for our
324         // subsequent line exact matches below.
325         found_line = line_entry.line;
326 
327         while (line_idx != UINT32_MAX) {
328           // If they only asked for the line entry, then we're done, we can
329           // just copy that over. But if they wanted more than just the line
330           // number, fill it in.
331           if (resolve_scope == eSymbolContextLineEntry) {
332             sc.line_entry = line_entry;
333           } else {
334             line_entry.range.GetBaseAddress().CalculateSymbolContext(
335                 &sc, resolve_scope);
336           }
337 
338           sc_list.Append(sc);
339           line_idx = line_table->FindLineEntryIndexByFileIndex(
340               line_idx + 1, file_indexes.front(), found_line, true,
341               &line_entry);
342         }
343       } else {
344         // We found multiple support files that match "file_spec" so use the
345         // line table function that searches for a line entries that match a
346         // multiple support file indexes.
347         LineEntry line_entry;
348         line_idx = line_table->FindLineEntryIndexByFileIndex(
349             0, file_indexes, line, exact, &line_entry);
350 
351         // If "exact == true", then "found_line" will be the same as "line". If
352         // "exact == false", the "found_line" will be the closest line entry
353         // with a line number greater than "line" and we will use this for our
354         // subsequent line exact matches below.
355         found_line = line_entry.line;
356 
357         while (line_idx != UINT32_MAX) {
358           if (resolve_scope == eSymbolContextLineEntry) {
359             sc.line_entry = line_entry;
360           } else {
361             line_entry.range.GetBaseAddress().CalculateSymbolContext(
362                 &sc, resolve_scope);
363           }
364 
365           sc_list.Append(sc);
366           line_idx = line_table->FindLineEntryIndexByFileIndex(
367               line_idx + 1, file_indexes, found_line, true, &line_entry);
368         }
369       }
370     }
371   } else if (file_spec_matches_cu_file_spec && !check_inlines) {
372     // only append the context if we aren't looking for inline call sites by
373     // file and line and if the file spec matches that of the compile unit
374     sc_list.Append(sc);
375   }
376   return sc_list.GetSize() - prev_size;
377 }
378 
GetIsOptimized()379 bool CompileUnit::GetIsOptimized() {
380   if (m_is_optimized == eLazyBoolCalculate) {
381     m_is_optimized = eLazyBoolNo;
382     if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
383       if (symbol_vendor->ParseIsOptimized(*this))
384         m_is_optimized = eLazyBoolYes;
385     }
386   }
387   return m_is_optimized;
388 }
389 
SetVariableList(VariableListSP & variables)390 void CompileUnit::SetVariableList(VariableListSP &variables) {
391   m_variables = variables;
392 }
393 
GetImportedModules()394 const std::vector<ConstString> &CompileUnit::GetImportedModules() {
395   if (m_imported_modules.empty() &&
396       m_flags.IsClear(flagsParsedImportedModules)) {
397     m_flags.Set(flagsParsedImportedModules);
398     if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
399       SymbolContext sc;
400       CalculateSymbolContext(&sc);
401       symbol_vendor->ParseImportedModules(sc, m_imported_modules);
402     }
403   }
404   return m_imported_modules;
405 }
406 
GetSupportFiles()407 FileSpecList &CompileUnit::GetSupportFiles() {
408   if (m_support_files.GetSize() == 0) {
409     if (m_flags.IsClear(flagsParsedSupportFiles)) {
410       m_flags.Set(flagsParsedSupportFiles);
411       SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
412       if (symbol_vendor) {
413         symbol_vendor->ParseSupportFiles(*this, m_support_files);
414       }
415     }
416   }
417   return m_support_files;
418 }
419 
GetUserData() const420 void *CompileUnit::GetUserData() const { return m_user_data; }
421