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 
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, false), 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 
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 
46 CompileUnit::~CompileUnit() {}
47 
48 void CompileUnit::CalculateSymbolContext(SymbolContext *sc) {
49   sc->comp_unit = this;
50   GetModule()->CalculateSymbolContext(sc);
51 }
52 
53 ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); }
54 
55 CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; }
56 
57 void CompileUnit::DumpSymbolContext(Stream *s) {
58   GetModule()->DumpSymbolContext(s);
59   s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID());
60 }
61 
62 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 
69 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   std::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 //----------------------------------------------------------------------
91 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 //----------------------------------------------------------------------
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 
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 
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         SymbolContext sc;
186         CalculateSymbolContext(&sc);
187         m_language = symbol_vendor->ParseCompileUnitLanguage(sc);
188       }
189     }
190   }
191   return m_language;
192 }
193 
194 LineTable *CompileUnit::GetLineTable() {
195   if (m_line_table_ap.get() == nullptr) {
196     if (m_flags.IsClear(flagsParsedLineTable)) {
197       m_flags.Set(flagsParsedLineTable);
198       SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
199       if (symbol_vendor) {
200         SymbolContext sc;
201         CalculateSymbolContext(&sc);
202         symbol_vendor->ParseCompileUnitLineTable(sc);
203       }
204     }
205   }
206   return m_line_table_ap.get();
207 }
208 
209 void CompileUnit::SetLineTable(LineTable *line_table) {
210   if (line_table == nullptr)
211     m_flags.Clear(flagsParsedLineTable);
212   else
213     m_flags.Set(flagsParsedLineTable);
214   m_line_table_ap.reset(line_table);
215 }
216 
217 DebugMacros *CompileUnit::GetDebugMacros() {
218   if (m_debug_macros_sp.get() == nullptr) {
219     if (m_flags.IsClear(flagsParsedDebugMacros)) {
220       m_flags.Set(flagsParsedDebugMacros);
221       SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
222       if (symbol_vendor) {
223         SymbolContext sc;
224         CalculateSymbolContext(&sc);
225         symbol_vendor->ParseCompileUnitDebugMacros(sc);
226       }
227     }
228   }
229 
230   return m_debug_macros_sp.get();
231 }
232 
233 void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) {
234   if (debug_macros_sp.get() == nullptr)
235     m_flags.Clear(flagsParsedDebugMacros);
236   else
237     m_flags.Set(flagsParsedDebugMacros);
238   m_debug_macros_sp = debug_macros_sp;
239 }
240 
241 VariableListSP CompileUnit::GetVariableList(bool can_create) {
242   if (m_variables.get() == nullptr && can_create) {
243     SymbolContext sc;
244     CalculateSymbolContext(&sc);
245     assert(sc.module_sp);
246     sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc);
247   }
248 
249   return m_variables;
250 }
251 
252 uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line,
253                                     const FileSpec *file_spec_ptr, bool exact,
254                                     LineEntry *line_entry_ptr) {
255   uint32_t file_idx = 0;
256 
257   if (file_spec_ptr) {
258     file_idx = GetSupportFiles().FindFileIndex(1, *file_spec_ptr, true);
259     if (file_idx == UINT32_MAX)
260       return UINT32_MAX;
261   } else {
262     // All the line table entries actually point to the version of the Compile
263     // Unit that is in the support files (the one at 0 was artificially added.)
264     // So prefer the one further on in the support files if it exists...
265     FileSpecList &support_files = GetSupportFiles();
266     const bool full = true;
267     file_idx = support_files.FindFileIndex(
268         1, support_files.GetFileSpecAtIndex(0), full);
269     if (file_idx == UINT32_MAX)
270       file_idx = 0;
271   }
272   LineTable *line_table = GetLineTable();
273   if (line_table)
274     return line_table->FindLineEntryIndexByFileIndex(start_idx, file_idx, line,
275                                                      exact, line_entry_ptr);
276   return UINT32_MAX;
277 }
278 
279 uint32_t CompileUnit::ResolveSymbolContext(const FileSpec &file_spec,
280                                            uint32_t line, bool check_inlines,
281                                            bool exact, uint32_t resolve_scope,
282                                            SymbolContextList &sc_list) {
283   // First find all of the file indexes that match our "file_spec". If
284   // "file_spec" has an empty directory, then only compare the basenames when
285   // finding file indexes
286   std::vector<uint32_t> file_indexes;
287   const bool full_match = (bool)file_spec.GetDirectory();
288   bool file_spec_matches_cu_file_spec =
289       FileSpec::Equal(file_spec, *this, full_match);
290 
291   // If we are not looking for inlined functions and our file spec doesn't
292   // match then we are done...
293   if (file_spec_matches_cu_file_spec == false && check_inlines == false)
294     return 0;
295 
296   uint32_t file_idx =
297       GetSupportFiles().FindFileIndex(1, file_spec, true);
298   while (file_idx != UINT32_MAX) {
299     file_indexes.push_back(file_idx);
300     file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true);
301   }
302 
303   const size_t num_file_indexes = file_indexes.size();
304   if (num_file_indexes == 0)
305     return 0;
306 
307   const uint32_t prev_size = sc_list.GetSize();
308 
309   SymbolContext sc(GetModule());
310   sc.comp_unit = this;
311 
312   if (line != 0) {
313     LineTable *line_table = sc.comp_unit->GetLineTable();
314 
315     if (line_table != nullptr) {
316       uint32_t found_line;
317       uint32_t line_idx;
318 
319       if (num_file_indexes == 1) {
320         // We only have a single support file that matches, so use the line
321         // table function that searches for a line entries that match a single
322         // support file index
323         LineEntry line_entry;
324         line_idx = line_table->FindLineEntryIndexByFileIndex(
325             0, file_indexes.front(), line, exact, &line_entry);
326 
327         // If "exact == true", then "found_line" will be the same as "line". If
328         // "exact == false", the "found_line" will be the closest line entry
329         // with a line number greater than "line" and we will use this for our
330         // subsequent line exact matches below.
331         found_line = line_entry.line;
332 
333         while (line_idx != UINT32_MAX) {
334           // If they only asked for the line entry, then we're done, we can
335           // just copy that over. But if they wanted more than just the line
336           // number, fill it in.
337           if (resolve_scope == eSymbolContextLineEntry) {
338             sc.line_entry = line_entry;
339           } else {
340             line_entry.range.GetBaseAddress().CalculateSymbolContext(
341                 &sc, resolve_scope);
342           }
343 
344           sc_list.Append(sc);
345           line_idx = line_table->FindLineEntryIndexByFileIndex(
346               line_idx + 1, file_indexes.front(), found_line, true,
347               &line_entry);
348         }
349       } else {
350         // We found multiple support files that match "file_spec" so use the
351         // line table function that searches for a line entries that match a
352         // multiple support file indexes.
353         LineEntry line_entry;
354         line_idx = line_table->FindLineEntryIndexByFileIndex(
355             0, file_indexes, line, exact, &line_entry);
356 
357         // If "exact == true", then "found_line" will be the same as "line". If
358         // "exact == false", the "found_line" will be the closest line entry
359         // with a line number greater than "line" and we will use this for our
360         // subsequent line exact matches below.
361         found_line = line_entry.line;
362 
363         while (line_idx != UINT32_MAX) {
364           if (resolve_scope == eSymbolContextLineEntry) {
365             sc.line_entry = line_entry;
366           } else {
367             line_entry.range.GetBaseAddress().CalculateSymbolContext(
368                 &sc, resolve_scope);
369           }
370 
371           sc_list.Append(sc);
372           line_idx = line_table->FindLineEntryIndexByFileIndex(
373               line_idx + 1, file_indexes, found_line, true, &line_entry);
374         }
375       }
376     }
377   } else if (file_spec_matches_cu_file_spec && !check_inlines) {
378     // only append the context if we aren't looking for inline call sites by
379     // file and line and if the file spec matches that of the compile unit
380     sc_list.Append(sc);
381   }
382   return sc_list.GetSize() - prev_size;
383 }
384 
385 bool CompileUnit::GetIsOptimized() {
386   if (m_is_optimized == eLazyBoolCalculate) {
387     m_is_optimized = eLazyBoolNo;
388     if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
389       SymbolContext sc;
390       CalculateSymbolContext(&sc);
391       if (symbol_vendor->ParseCompileUnitIsOptimized(sc))
392         m_is_optimized = eLazyBoolYes;
393     }
394   }
395   return m_is_optimized;
396 }
397 
398 void CompileUnit::SetVariableList(VariableListSP &variables) {
399   m_variables = variables;
400 }
401 
402 const std::vector<ConstString> &CompileUnit::GetImportedModules() {
403   if (m_imported_modules.empty() &&
404       m_flags.IsClear(flagsParsedImportedModules)) {
405     m_flags.Set(flagsParsedImportedModules);
406     if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
407       SymbolContext sc;
408       CalculateSymbolContext(&sc);
409       symbol_vendor->ParseImportedModules(sc, m_imported_modules);
410     }
411   }
412   return m_imported_modules;
413 }
414 
415 FileSpecList &CompileUnit::GetSupportFiles() {
416   if (m_support_files.GetSize() == 0) {
417     if (m_flags.IsClear(flagsParsedSupportFiles)) {
418       m_flags.Set(flagsParsedSupportFiles);
419       SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
420       if (symbol_vendor) {
421         SymbolContext sc;
422         CalculateSymbolContext(&sc);
423         symbol_vendor->ParseCompileUnitSupportFiles(sc, m_support_files);
424       }
425     }
426   }
427   return m_support_files;
428 }
429 
430 void *CompileUnit::GetUserData() const { return m_user_data; }
431