//===-- CompileUnit.cpp -----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "lldb/Symbol/CompileUnit.h" #include "lldb/Core/Module.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/Language.h" using namespace lldb; using namespace lldb_private; CompileUnit::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) : ModuleChild(module_sp), FileSpec(pathname), UserID(cu_sym_id), m_user_data(user_data), m_language(language), m_flags(0), m_support_files(), m_line_table_up(), m_variables(), m_is_optimized(is_optimized) { if (language != eLanguageTypeUnknown) m_flags.Set(flagsParsedLanguage); assert(module_sp); } CompileUnit::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) : ModuleChild(module_sp), FileSpec(fspec), UserID(cu_sym_id), m_user_data(user_data), m_language(language), m_flags(0), m_support_files(), m_line_table_up(), m_variables(), m_is_optimized(is_optimized) { if (language != eLanguageTypeUnknown) m_flags.Set(flagsParsedLanguage); assert(module_sp); } CompileUnit::~CompileUnit() {} void CompileUnit::CalculateSymbolContext(SymbolContext *sc) { sc->comp_unit = this; GetModule()->CalculateSymbolContext(sc); } ModuleSP CompileUnit::CalculateSymbolContextModule() { return GetModule(); } CompileUnit *CompileUnit::CalculateSymbolContextCompileUnit() { return this; } void CompileUnit::DumpSymbolContext(Stream *s) { GetModule()->DumpSymbolContext(s); s->Printf(", CompileUnit{0x%8.8" PRIx64 "}", GetID()); } void CompileUnit::GetDescription(Stream *s, lldb::DescriptionLevel level) const { const char *language = Language::GetNameForLanguageType(m_language); *s << "id = " << (const UserID &)*this << ", file = \"" << (const FileSpec &)*this << "\", language = \"" << language << '"'; } void CompileUnit::ForeachFunction( llvm::function_ref lambda) const { std::vector sorted_functions; sorted_functions.reserve(m_functions_by_uid.size()); for (auto &p : m_functions_by_uid) sorted_functions.push_back(p.second); llvm::sort(sorted_functions.begin(), sorted_functions.end(), [](const lldb::FunctionSP &a, const lldb::FunctionSP &b) { return a->GetID() < b->GetID(); }); for (auto &f : sorted_functions) if (lambda(f)) return; } // Dump the current contents of this object. No functions that cause on demand // parsing of functions, globals, statics are called, so this is a good // function to call to get an idea of the current contents of the CompileUnit // object. void CompileUnit::Dump(Stream *s, bool show_context) const { const char *language = Language::GetNameForLanguageType(m_language); s->Printf("%p: ", static_cast(this)); s->Indent(); *s << "CompileUnit" << static_cast(*this) << ", language = \"" << language << "\", file = '" << static_cast(*this) << "'\n"; // m_types.Dump(s); if (m_variables.get()) { s->IndentMore(); m_variables->Dump(s, show_context); s->IndentLess(); } if (!m_functions_by_uid.empty()) { s->IndentMore(); ForeachFunction([&s, show_context](const FunctionSP &f) { f->Dump(s, show_context); return false; }); s->IndentLess(); s->EOL(); } } // Add a function to this compile unit void CompileUnit::AddFunction(FunctionSP &funcSP) { m_functions_by_uid[funcSP->GetID()] = funcSP; } FunctionSP CompileUnit::FindFunctionByUID(lldb::user_id_t func_uid) { auto it = m_functions_by_uid.find(func_uid); if (it == m_functions_by_uid.end()) return FunctionSP(); return it->second; } lldb::LanguageType CompileUnit::GetLanguage() { if (m_language == eLanguageTypeUnknown) { if (m_flags.IsClear(flagsParsedLanguage)) { m_flags.Set(flagsParsedLanguage); if (SymbolFile *symfile = GetModule()->GetSymbolFile()) m_language = symfile->ParseLanguage(*this); } } return m_language; } LineTable *CompileUnit::GetLineTable() { if (m_line_table_up == nullptr) { if (m_flags.IsClear(flagsParsedLineTable)) { m_flags.Set(flagsParsedLineTable); if (SymbolFile *symfile = GetModule()->GetSymbolFile()) symfile->ParseLineTable(*this); } } return m_line_table_up.get(); } void CompileUnit::SetLineTable(LineTable *line_table) { if (line_table == nullptr) m_flags.Clear(flagsParsedLineTable); else m_flags.Set(flagsParsedLineTable); m_line_table_up.reset(line_table); } void CompileUnit::SetSupportFiles(const FileSpecList &support_files) { m_support_files = support_files; } DebugMacros *CompileUnit::GetDebugMacros() { if (m_debug_macros_sp.get() == nullptr) { if (m_flags.IsClear(flagsParsedDebugMacros)) { m_flags.Set(flagsParsedDebugMacros); if (SymbolFile *symfile = GetModule()->GetSymbolFile()) symfile->ParseDebugMacros(*this); } } return m_debug_macros_sp.get(); } void CompileUnit::SetDebugMacros(const DebugMacrosSP &debug_macros_sp) { if (debug_macros_sp.get() == nullptr) m_flags.Clear(flagsParsedDebugMacros); else m_flags.Set(flagsParsedDebugMacros); m_debug_macros_sp = debug_macros_sp; } VariableListSP CompileUnit::GetVariableList(bool can_create) { if (m_variables.get() == nullptr && can_create) { SymbolContext sc; CalculateSymbolContext(&sc); assert(sc.module_sp); sc.module_sp->GetSymbolFile()->ParseVariablesForContext(sc); } return m_variables; } uint32_t CompileUnit::FindLineEntry(uint32_t start_idx, uint32_t line, const FileSpec *file_spec_ptr, bool exact, LineEntry *line_entry_ptr) { uint32_t file_idx = 0; if (file_spec_ptr) { file_idx = GetSupportFiles().FindFileIndex(1, *file_spec_ptr, true); if (file_idx == UINT32_MAX) return UINT32_MAX; } else { // All the line table entries actually point to the version of the Compile // Unit that is in the support files (the one at 0 was artificially added.) // So prefer the one further on in the support files if it exists... const FileSpecList &support_files = GetSupportFiles(); const bool full = true; file_idx = support_files.FindFileIndex( 1, support_files.GetFileSpecAtIndex(0), full); if (file_idx == UINT32_MAX) file_idx = 0; } LineTable *line_table = GetLineTable(); if (line_table) return line_table->FindLineEntryIndexByFileIndex(start_idx, file_idx, line, exact, line_entry_ptr); return UINT32_MAX; } uint32_t CompileUnit::ResolveSymbolContext(const FileSpec &file_spec, uint32_t line, bool check_inlines, bool exact, SymbolContextItem resolve_scope, SymbolContextList &sc_list) { // First find all of the file indexes that match our "file_spec". If // "file_spec" has an empty directory, then only compare the basenames when // finding file indexes std::vector file_indexes; const bool full_match = (bool)file_spec.GetDirectory(); bool file_spec_matches_cu_file_spec = FileSpec::Equal(file_spec, *this, full_match); // If we are not looking for inlined functions and our file spec doesn't // match then we are done... if (!file_spec_matches_cu_file_spec && !check_inlines) return 0; uint32_t file_idx = GetSupportFiles().FindFileIndex(1, file_spec, true); while (file_idx != UINT32_MAX) { file_indexes.push_back(file_idx); file_idx = GetSupportFiles().FindFileIndex(file_idx + 1, file_spec, true); } const size_t num_file_indexes = file_indexes.size(); if (num_file_indexes == 0) return 0; const uint32_t prev_size = sc_list.GetSize(); SymbolContext sc(GetModule()); sc.comp_unit = this; if (line != 0) { LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table != nullptr) { uint32_t found_line; uint32_t line_idx; if (num_file_indexes == 1) { // We only have a single support file that matches, so use the line // table function that searches for a line entries that match a single // support file index LineEntry line_entry; line_idx = line_table->FindLineEntryIndexByFileIndex( 0, file_indexes.front(), line, exact, &line_entry); // If "exact == true", then "found_line" will be the same as "line". If // "exact == false", the "found_line" will be the closest line entry // with a line number greater than "line" and we will use this for our // subsequent line exact matches below. found_line = line_entry.line; while (line_idx != UINT32_MAX) { // If they only asked for the line entry, then we're done, we can // just copy that over. But if they wanted more than just the line // number, fill it in. if (resolve_scope == eSymbolContextLineEntry) { sc.line_entry = line_entry; } else { line_entry.range.GetBaseAddress().CalculateSymbolContext( &sc, resolve_scope); } sc_list.Append(sc); line_idx = line_table->FindLineEntryIndexByFileIndex( line_idx + 1, file_indexes.front(), found_line, true, &line_entry); } } else { // We found multiple support files that match "file_spec" so use the // line table function that searches for a line entries that match a // multiple support file indexes. LineEntry line_entry; line_idx = line_table->FindLineEntryIndexByFileIndex( 0, file_indexes, line, exact, &line_entry); // If "exact == true", then "found_line" will be the same as "line". If // "exact == false", the "found_line" will be the closest line entry // with a line number greater than "line" and we will use this for our // subsequent line exact matches below. found_line = line_entry.line; while (line_idx != UINT32_MAX) { if (resolve_scope == eSymbolContextLineEntry) { sc.line_entry = line_entry; } else { line_entry.range.GetBaseAddress().CalculateSymbolContext( &sc, resolve_scope); } sc_list.Append(sc); line_idx = line_table->FindLineEntryIndexByFileIndex( line_idx + 1, file_indexes, found_line, true, &line_entry); } } } } else if (file_spec_matches_cu_file_spec && !check_inlines) { // only append the context if we aren't looking for inline call sites by // file and line and if the file spec matches that of the compile unit sc_list.Append(sc); } return sc_list.GetSize() - prev_size; } bool CompileUnit::GetIsOptimized() { if (m_is_optimized == eLazyBoolCalculate) { m_is_optimized = eLazyBoolNo; if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { if (symfile->ParseIsOptimized(*this)) m_is_optimized = eLazyBoolYes; } } return m_is_optimized; } void CompileUnit::SetVariableList(VariableListSP &variables) { m_variables = variables; } const std::vector &CompileUnit::GetImportedModules() { if (m_imported_modules.empty() && m_flags.IsClear(flagsParsedImportedModules)) { m_flags.Set(flagsParsedImportedModules); if (SymbolFile *symfile = GetModule()->GetSymbolFile()) { SymbolContext sc; CalculateSymbolContext(&sc); symfile->ParseImportedModules(sc, m_imported_modules); } } return m_imported_modules; } const FileSpecList &CompileUnit::GetSupportFiles() { if (m_support_files.GetSize() == 0) { if (m_flags.IsClear(flagsParsedSupportFiles)) { m_flags.Set(flagsParsedSupportFiles); if (SymbolFile *symfile = GetModule()->GetSymbolFile()) symfile->ParseSupportFiles(*this, m_support_files); } } return m_support_files; } void *CompileUnit::GetUserData() const { return m_user_data; }