1 //===-- CompileUnit.cpp -----------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Symbol/CompileUnit.h"
10 #include "lldb/Core/Module.h"
11 #include "lldb/Symbol/LineTable.h"
12 #include "lldb/Symbol/SymbolVendor.h"
13 #include "lldb/Symbol/VariableList.h"
14 #include "lldb/Target/Language.h"
17 using namespace lldb_private;
19 CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
20 const char *pathname, const lldb::user_id_t cu_sym_id,
21 lldb::LanguageType language,
22 lldb_private::LazyBool is_optimized)
23 : ModuleChild(module_sp), FileSpec(pathname), UserID(cu_sym_id),
24 m_user_data(user_data), m_language(language), m_flags(0),
25 m_support_files(), m_line_table_up(), m_variables(),
26 m_is_optimized(is_optimized) {
27 if (language != eLanguageTypeUnknown)
28 m_flags.Set(flagsParsedLanguage);
32 CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data,
33 const FileSpec &fspec, const lldb::user_id_t cu_sym_id,
34 lldb::LanguageType language,
35 lldb_private::LazyBool is_optimized)
36 : ModuleChild(module_sp), FileSpec(fspec), UserID(cu_sym_id),
37 m_user_data(user_data), m_language(language), m_flags(0),
38 m_support_files(), m_line_table_up(), m_variables(),
39 m_is_optimized(is_optimized) {
40 if (language != eLanguageTypeUnknown)
41 m_flags.Set(flagsParsedLanguage);
45 CompileUnit::~CompileUnit() {}
47 void CompileUnit::CalculateSymbolContext(SymbolContext *sc) {
49 GetModule()->CalculateSymbolContext(sc);
52 ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); }
54 CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; }
56 void CompileUnit::DumpSymbolContext(Stream *s) {
57 GetModule()->DumpSymbolContext(s);
58 s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID());
61 void CompileUnit::GetDescription(Stream *s,
62 lldb::DescriptionLevel level) const {
63 const char *language = Language::GetNameForLanguageType(m_language);
64 *s << "id = " << (const UserID &)*this << ", file = \""
65 << (const FileSpec &)*this << "\", language = \"" << language << '"';
68 void CompileUnit::ForeachFunction(
69 llvm::function_ref<bool(const FunctionSP &)> lambda) const {
70 std::vector<lldb::FunctionSP> sorted_functions;
71 sorted_functions.reserve(m_functions_by_uid.size());
72 for (auto &p : m_functions_by_uid)
73 sorted_functions.push_back(p.second);
74 llvm::sort(sorted_functions.begin(), sorted_functions.end(),
75 [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) {
76 return a->GetID() < b->GetID();
79 for (auto &f : sorted_functions)
84 // Dump the current contents of this object. No functions that cause on demand
85 // parsing of functions, globals, statics are called, so this is a good
86 // function to call to get an idea of the current contents of the CompileUnit
88 void CompileUnit::Dump(Stream *s, bool show_context) const {
89 const char *language = Language::GetNameForLanguageType(m_language);
91 s->Printf("%p: ", static_cast<const void *>(this));
93 *s << "CompileUnit" << static_cast<const UserID &>(*this) << ", language = \""
94 << language << "\", file = '" << static_cast<const FileSpec &>(*this)
99 if (m_variables.get()) {
101 m_variables->Dump(s, show_context);
105 if (!m_functions_by_uid.empty()) {
107 ForeachFunction([&s, show_context](const FunctionSP &f) {
108 f->Dump(s, show_context);
117 // Add a function to this compile unit
118 void CompileUnit::AddFunction(FunctionSP &funcSP) {
119 m_functions_by_uid[funcSP->GetID()] = funcSP;
122 // Find functions using the Mangled::Tokens token list. This function currently
123 // implements an interactive approach designed to find all instances of certain
124 // functions. It isn't designed to the quickest way to lookup functions as it
125 // will need to iterate through all functions and see if they match, though it
126 // does provide a powerful and context sensitive way to search for all
127 // functions with a certain name, all functions in a namespace, or all
128 // functions of a template type. See Mangled::Tokens::Parse() comments for more
131 // The function prototype will need to change to return a list of results. It
132 // was originally used to help debug the Mangled class and the
133 // Mangled::Tokens::MatchesQuery() function and it currently will print out a
134 // list of matching results for the functions that are currently in this
137 // A FindFunctions method should be called prior to this that takes
138 // a regular function name (const char * or ConstString as a parameter) before
139 // resorting to this slower but more complete function. The other FindFunctions
140 // method should be able to take advantage of any accelerator tables available
141 // in the debug information (which is parsed by the SymbolFile parser plug-ins
142 // and registered with each Module).
144 // CompileUnit::FindFunctions(const Mangled::Tokens& tokens)
146 // if (!m_functions.empty())
149 // std::vector<FunctionSP>::const_iterator pos;
150 // std::vector<FunctionSP>::const_iterator end = m_functions.end();
151 // for (pos = m_functions.begin(); pos != end; ++pos)
153 // const ConstString& demangled = (*pos)->Mangled().Demangled();
156 // const Mangled::Tokens& func_tokens =
157 // (*pos)->Mangled().GetTokens();
158 // if (func_tokens.MatchesQuery (tokens))
159 // s << "demangled MATCH found: " << demangled << "\n";
165 FunctionSP CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid) {
166 auto it = m_functions_by_uid.find(func_uid);
167 if (it == m_functions_by_uid.end())
172 lldb::LanguageType CompileUnit::GetLanguage() {
173 if (m_language == eLanguageTypeUnknown) {
174 if (m_flags.IsClear(flagsParsedLanguage)) {
175 m_flags.Set(flagsParsedLanguage);
176 SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
178 m_language = symbol_vendor->ParseLanguage(*this);
185 LineTable *CompileUnit::GetLineTable() {
186 if (m_line_table_up == nullptr) {
187 if (m_flags.IsClear(flagsParsedLineTable)) {
188 m_flags.Set(flagsParsedLineTable);
189 SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
191 symbol_vendor->ParseLineTable(*this);
194 return m_line_table_up.get();
197 void CompileUnit::SetLineTable(LineTable *line_table) {
198 if (line_table == nullptr)
199 m_flags.Clear(flagsParsedLineTable);
201 m_flags.Set(flagsParsedLineTable);
202 m_line_table_up.reset(line_table);
205 DebugMacros *CompileUnit::GetDebugMacros() {
206 if (m_debug_macros_sp.get() == nullptr) {
207 if (m_flags.IsClear(flagsParsedDebugMacros)) {
208 m_flags.Set(flagsParsedDebugMacros);
209 SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
211 symbol_vendor->ParseDebugMacros(*this);
216 return m_debug_macros_sp.get();
219 void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) {
220 if (debug_macros_sp.get() == nullptr)
221 m_flags.Clear(flagsParsedDebugMacros);
223 m_flags.Set(flagsParsedDebugMacros);
224 m_debug_macros_sp = debug_macros_sp;
227 VariableListSP CompileUnit::GetVariableList(bool can_create) {
228 if (m_variables.get() == nullptr && can_create) {
230 CalculateSymbolContext(&sc);
231 assert(sc.module_sp);
232 sc.module_sp->GetSymbolVendor()->ParseVariablesForContext(sc);
238 uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line,
239 const FileSpec *file_spec_ptr, bool exact,
240 LineEntry *line_entry_ptr) {
241 uint32_t file_idx = 0;
244 file_idx = GetSupportFiles().FindFileIndex(1, *file_spec_ptr, true);
245 if (file_idx == UINT32_MAX)
248 // All the line table entries actually point to the version of the Compile
249 // Unit that is in the support files (the one at 0 was artificially added.)
250 // So prefer the one further on in the support files if it exists...
251 const FileSpecList &support_files = GetSupportFiles();
252 const bool full = true;
253 file_idx = support_files.FindFileIndex(
254 1, support_files.GetFileSpecAtIndex(0), full);
255 if (file_idx == UINT32_MAX)
258 LineTable *line_table = GetLineTable();
260 return line_table->FindLineEntryIndexByFileIndex(start_idx, file_idx, line,
261 exact, line_entry_ptr);
265 uint32_t CompileUnit::ResolveSymbolContext(const FileSpec &file_spec,
266 uint32_t line, bool check_inlines,
268 SymbolContextItem resolve_scope,
269 SymbolContextList &sc_list) {
270 // First find all of the file indexes that match our "file_spec". If
271 // "file_spec" has an empty directory, then only compare the basenames when
272 // finding file indexes
273 std::vector<uint32_t> file_indexes;
274 const bool full_match = (bool)file_spec.GetDirectory();
275 bool file_spec_matches_cu_file_spec =
276 FileSpec::Equal(file_spec, *this, full_match);
278 // If we are not looking for inlined functions and our file spec doesn't
279 // match then we are done...
280 if (!file_spec_matches_cu_file_spec && !check_inlines)
284 GetSupportFiles().FindFileIndex(1, file_spec, true);
285 while (file_idx != UINT32_MAX) {
286 file_indexes.push_back(file_idx);
287 file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true);
290 const size_t num_file_indexes = file_indexes.size();
291 if (num_file_indexes == 0)
294 const uint32_t prev_size = sc_list.GetSize();
296 SymbolContext sc(GetModule());
300 LineTable *line_table = sc.comp_unit->GetLineTable();
302 if (line_table != nullptr) {
306 if (num_file_indexes == 1) {
307 // We only have a single support file that matches, so use the line
308 // table function that searches for a line entries that match a single
309 // support file index
310 LineEntry line_entry;
311 line_idx = line_table->FindLineEntryIndexByFileIndex(
312 0, file_indexes.front(), line, exact, &line_entry);
314 // If "exact == true", then "found_line" will be the same as "line". If
315 // "exact == false", the "found_line" will be the closest line entry
316 // with a line number greater than "line" and we will use this for our
317 // subsequent line exact matches below.
318 found_line = line_entry.line;
320 while (line_idx != UINT32_MAX) {
321 // If they only asked for the line entry, then we're done, we can
322 // just copy that over. But if they wanted more than just the line
323 // number, fill it in.
324 if (resolve_scope == eSymbolContextLineEntry) {
325 sc.line_entry = line_entry;
327 line_entry.range.GetBaseAddress().CalculateSymbolContext(
332 line_idx = line_table->FindLineEntryIndexByFileIndex(
333 line_idx + 1, file_indexes.front(), found_line, true,
337 // We found multiple support files that match "file_spec" so use the
338 // line table function that searches for a line entries that match a
339 // multiple support file indexes.
340 LineEntry line_entry;
341 line_idx = line_table->FindLineEntryIndexByFileIndex(
342 0, file_indexes, line, exact, &line_entry);
344 // If "exact == true", then "found_line" will be the same as "line". If
345 // "exact == false", the "found_line" will be the closest line entry
346 // with a line number greater than "line" and we will use this for our
347 // subsequent line exact matches below.
348 found_line = line_entry.line;
350 while (line_idx != UINT32_MAX) {
351 if (resolve_scope == eSymbolContextLineEntry) {
352 sc.line_entry = line_entry;
354 line_entry.range.GetBaseAddress().CalculateSymbolContext(
359 line_idx = line_table->FindLineEntryIndexByFileIndex(
360 line_idx + 1, file_indexes, found_line, true, &line_entry);
364 } else if (file_spec_matches_cu_file_spec && !check_inlines) {
365 // only append the context if we aren't looking for inline call sites by
366 // file and line and if the file spec matches that of the compile unit
369 return sc_list.GetSize() - prev_size;
372 bool CompileUnit::GetIsOptimized() {
373 if (m_is_optimized == eLazyBoolCalculate) {
374 m_is_optimized = eLazyBoolNo;
375 if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
376 if (symbol_vendor->ParseIsOptimized(*this))
377 m_is_optimized = eLazyBoolYes;
380 return m_is_optimized;
383 void CompileUnit::SetVariableList(VariableListSP &variables) {
384 m_variables = variables;
387 const std::vector<SourceModule> &CompileUnit::GetImportedModules() {
388 if (m_imported_modules.empty() &&
389 m_flags.IsClear(flagsParsedImportedModules)) {
390 m_flags.Set(flagsParsedImportedModules);
391 if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
393 CalculateSymbolContext(&sc);
394 symbol_vendor->ParseImportedModules(sc, m_imported_modules);
397 return m_imported_modules;
400 const FileSpecList &CompileUnit::GetSupportFiles() {
401 if (m_support_files.GetSize() == 0) {
402 if (m_flags.IsClear(flagsParsedSupportFiles)) {
403 m_flags.Set(flagsParsedSupportFiles);
404 SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
406 symbol_vendor->ParseSupportFiles(*this, m_support_files);
410 return m_support_files;
413 void *CompileUnit::GetUserData() const { return m_user_data; }