llvm-project/lldb/source/Symbol/CompileUnit.cpp
Zachary Turner 991e44534a Don't type-erase the SymbolContextItem enumeration.
When we get the `resolve_scope` parameter from the SB API, it's a
`uint32_t`.  We then pass it through all of LLDB this way, as a uint32.
This is unfortunate, because it means the user of an API never actually
knows what they're dealing with.  We can call it something like
`resolve_scope` and have comments saying "this is a value from the
`SymbolContextItem` enumeration, but it makes more sense to just have it
actually *be* the correct type in the actual C++ type system to begin
with.  This way the person reading the code just knows what it is.

The reason to use integers instead of enumerations for flags is because
when you do bitwise operations on enumerations they get promoted to
integers, so it makes it tedious to constantly be casting them back
to the enumeration types, so I've introduced a macro to make this
happen magically.  By writing LLDB_MARK_AS_BITMASK_ENUM after defining
an enumeration, it will define overloaded operators so that the
returned type will be the original enum.  This should address all
the mechanical issues surrounding using rich enum types directly.

This way, we get a better debugger experience, and new users to
the codebase can get more easily acquainted with the codebase because
their IDE features can help them understand what the types mean.

Differential Revision: https://reviews.llvm.org/D53597

llvm-svn: 345313
2018-10-25 20:45:19 +00:00

432 lines
16 KiB
C++

//===-- 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/Symbol/LineTable.h"
#include "lldb/Symbol/SymbolVendor.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, false), UserID(cu_sym_id),
m_user_data(user_data), m_language(language), m_flags(0),
m_support_files(), m_line_table_ap(), 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_ap(), 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<bool(const FunctionSP &)> lambda) const {
std::vector<lldb::FunctionSP> sorted_functions;
sorted_functions.reserve(m_functions_by_uid.size());
for (auto &p : m_functions_by_uid)
sorted_functions.push_back(p.second);
std::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<const void *>(this));
s->Indent();
*s << "CompileUnit" << static_cast<const UserID &>(*this) << ", language = \""
<< language << "\", file = '" << static_cast<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_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;
}
//----------------------------------------------------------------------
// Find functions using the Mangled::Tokens token list. This function currently
// implements an interactive approach designed to find all instances of certain
// functions. It isn't designed to 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<FunctionSP>::const_iterator pos;
// std::vector<FunctionSP>::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) {
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);
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() == nullptr) {
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 == nullptr)
m_flags.Clear(flagsParsedLineTable);
else
m_flags.Set(flagsParsedLineTable);
m_line_table_ap.reset(line_table);
}
DebugMacros *CompileUnit::GetDebugMacros() {
if (m_debug_macros_sp.get() == nullptr) {
if (m_flags.IsClear(flagsParsedDebugMacros)) {
m_flags.Set(flagsParsedDebugMacros);
SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor();
if (symbol_vendor) {
SymbolContext sc;
CalculateSymbolContext(&sc);
symbol_vendor->ParseCompileUnitDebugMacros(sc);
}
}
}
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->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 artificially 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,
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<uint32_t> 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 != 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 (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
SymbolContext sc;
CalculateSymbolContext(&sc);
if (symbol_vendor->ParseCompileUnitIsOptimized(sc))
m_is_optimized = eLazyBoolYes;
}
}
return m_is_optimized;
}
void CompileUnit::SetVariableList(VariableListSP &variables) {
m_variables = variables;
}
const std::vector<ConstString> &CompileUnit::GetImportedModules() {
if (m_imported_modules.empty() &&
m_flags.IsClear(flagsParsedImportedModules)) {
m_flags.Set(flagsParsedImportedModules);
if (SymbolVendor *symbol_vendor = GetModule()->GetSymbolVendor()) {
SymbolContext sc;
CalculateSymbolContext(&sc);
symbol_vendor->ParseImportedModules(sc, m_imported_modules);
}
}
return m_imported_modules;
}
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; }