//===-- CompileUnit.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Symbol/CompileUnit.h" #include "lldb/Core/Module.h" #include "lldb/Core/Language.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/VariableList.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) : ModuleChild(module_sp), FileSpec (pathname, false), UserID(cu_sym_id), m_user_data (user_data), m_language (language), m_flags (0), m_functions (), m_support_files (), m_line_table_ap (), m_variables() { 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) : ModuleChild(module_sp), FileSpec (fspec), UserID(cu_sym_id), m_user_data (user_data), m_language (language), m_flags (0), m_functions (), m_support_files (), m_line_table_ap (), m_variables() { 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 { Language language(m_language); *s << "id = " << (const UserID&)*this << ", file = \"" << (const FileSpec&)*this << "\", language = \"" << language << '"'; } //---------------------------------------------------------------------- // 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 { s->Printf("%p: ", this); s->Indent(); *s << "CompileUnit" << (const UserID&)*this << ", language = \"" << (const Language&)*this << "\", file = '" << (const FileSpec&)*this << "'\n"; // m_types.Dump(s); if (m_variables.get()) { s->IndentMore(); m_variables->Dump(s, show_context); s->IndentLess(); } if (!m_functions.empty()) { s->IndentMore(); std::vector::const_iterator pos; std::vector::const_iterator end = m_functions.end(); for (pos = m_functions.begin(); pos != end; ++pos) { (*pos)->Dump(s, show_context); } s->IndentLess(); s->EOL(); } } //---------------------------------------------------------------------- // Add a function to this compile unit //---------------------------------------------------------------------- void CompileUnit::AddFunction(FunctionSP& funcSP) { // TODO: order these by address m_functions.push_back(funcSP); } FunctionSP CompileUnit::GetFunctionAtIndex (size_t idx) { FunctionSP funcSP; if (idx < m_functions.size()) funcSP = m_functions[idx]; return funcSP; } //---------------------------------------------------------------------- // Find functions using the a Mangled::Tokens token list. This // function currently implements an interative approach designed to find // all instances of certain functions. It isn't designed to the the // quickest way to lookup functions as it will need to iterate through // all functions and see if they match, though it does provide a powerful // and context sensitive way to search for all functions with a certain // name, all functions in a namespace, or all functions of a template // type. See Mangled::Tokens::Parse() comments for more information. // // The function prototype will need to change to return a list of // results. It was originally used to help debug the Mangled class // and the Mangled::Tokens::MatchesQuery() function and it currently // will print out a list of matching results for the functions that // are currently in this compile unit. // // A FindFunctions method should be called prior to this that takes // a regular function name (const char * or ConstString as a parameter) // before resorting to this slower but more complete function. The // other FindFunctions method should be able to take advantage of any // accelerator tables available in the debug information (which is // parsed by the SymbolFile parser plug-ins and registered with each // Module). //---------------------------------------------------------------------- //void //CompileUnit::FindFunctions(const Mangled::Tokens& tokens) //{ // if (!m_functions.empty()) // { // Stream s(stdout); // std::vector::const_iterator pos; // std::vector::const_iterator end = m_functions.end(); // for (pos = m_functions.begin(); pos != end; ++pos) // { // const ConstString& demangled = (*pos)->Mangled().Demangled(); // if (demangled) // { // const Mangled::Tokens& func_tokens = (*pos)->Mangled().GetTokens(); // if (func_tokens.MatchesQuery (tokens)) // s << "demangled MATCH found: " << demangled << "\n"; // } // } // } //} FunctionSP CompileUnit::FindFunctionByUID (lldb::user_id_t func_uid) { FunctionSP funcSP; if (!m_functions.empty()) { std::vector::const_iterator pos; std::vector::const_iterator end = m_functions.end(); for (pos = m_functions.begin(); pos != end; ++pos) { if ((*pos)->GetID() == func_uid) { funcSP = *pos; break; } } } return funcSP; } lldb::LanguageType CompileUnit::GetLanguage() { if (m_language == eLanguageTypeUnknown) { if (m_flags.IsClear(flagsParsedLanguage)) { m_flags.Set(flagsParsedLanguage); SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor(); if (symbol_vendor) { SymbolContext sc; CalculateSymbolContext(&sc); m_language = symbol_vendor->ParseCompileUnitLanguage(sc); } } } return m_language; } LineTable* CompileUnit::GetLineTable() { if (m_line_table_ap.get() == NULL) { if (m_flags.IsClear(flagsParsedLineTable)) { m_flags.Set(flagsParsedLineTable); SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor(); if (symbol_vendor) { SymbolContext sc; CalculateSymbolContext(&sc); symbol_vendor->ParseCompileUnitLineTable(sc); } } } return m_line_table_ap.get(); } void CompileUnit::SetLineTable(LineTable* line_table) { if (line_table == NULL) m_flags.Clear(flagsParsedLineTable); else m_flags.Set(flagsParsedLineTable); m_line_table_ap.reset(line_table); } VariableListSP CompileUnit::GetVariableList(bool can_create) { if (m_variables.get() == NULL && can_create) { SymbolContext sc; CalculateSymbolContext(&sc); assert(sc.module_sp); sc.module_sp->GetSymbolVendor()->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 artifically added.) // So prefer the one further on in the support files if it exists... 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, uint32_t 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 == false && check_inlines == false) 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 != NULL) { 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; } void CompileUnit::SetVariableList(VariableListSP &variables) { m_variables = variables; } FileSpecList& CompileUnit::GetSupportFiles () { if (m_support_files.GetSize() == 0) { if (m_flags.IsClear(flagsParsedSupportFiles)) { m_flags.Set(flagsParsedSupportFiles); SymbolVendor* symbol_vendor = GetModule()->GetSymbolVendor(); if (symbol_vendor) { SymbolContext sc; CalculateSymbolContext(&sc); symbol_vendor->ParseCompileUnitSupportFiles(sc, m_support_files); } } } return m_support_files; } void * CompileUnit::GetUserData () const { return m_user_data; }