Summary: When searching for methods only, we need to do extra work to make sure the functions we get from the apple tables are indeed methods. Previously we were resolving the DIE into a SymbolContext and then checked whether the enclosing CompilerDeclContext is a class (or struct, or union). This patch changes that to operate on the debug info directly. This should be: - simpler - faster - more consistent with the ManualDWARFIndex (which does the same check, only at indexing time). What we lose this ways is for the language plugin to have a say in what it considers to be a "class", but that's probably more flexibility than we need (and if we really wanted to do that in the future, we could implement a more direct way to consult the plugin about this). This also fixes the find-method-local-struct test, which was failing because we were not able to construct a CompilerDeclContext for a local struct correctly. As a drive-by, I rename the DWARFDIE's IsStructClassOrUnion method to match the name on the CompilerDeclContext class. Reviewers: clayborg, JDevlieghere Subscribers: aprantl, lldb-commits Differential Revision: https://reviews.llvm.org/D47470 llvm-svn: 333878
283 lines
11 KiB
C++
283 lines
11 KiB
C++
//===-- AppleDWARFIndex.cpp ------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Plugins/SymbolFile/DWARF/AppleDWARFIndex.h"
|
|
#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
|
|
#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
|
|
#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
|
|
#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"
|
|
|
|
#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
|
|
#include "Plugins/Language/ObjC/ObjCLanguage.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Symbol/Function.h"
|
|
|
|
using namespace lldb_private;
|
|
using namespace lldb;
|
|
|
|
std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create(
|
|
Module &module, DWARFDataExtractor apple_names,
|
|
DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types,
|
|
DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str) {
|
|
auto apple_names_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>(
|
|
apple_names, debug_str, ".apple_names");
|
|
if (!apple_names_table_up->IsValid())
|
|
apple_names_table_up.reset();
|
|
|
|
auto apple_namespaces_table_up =
|
|
llvm::make_unique<DWARFMappedHash::MemoryTable>(
|
|
apple_namespaces, debug_str, ".apple_namespaces");
|
|
if (!apple_namespaces_table_up->IsValid())
|
|
apple_namespaces_table_up.reset();
|
|
|
|
auto apple_types_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>(
|
|
apple_types, debug_str, ".apple_types");
|
|
if (!apple_types_table_up->IsValid())
|
|
apple_types_table_up.reset();
|
|
|
|
auto apple_objc_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>(
|
|
apple_objc, debug_str, ".apple_objc");
|
|
if (!apple_objc_table_up->IsValid())
|
|
apple_objc_table_up.reset();
|
|
|
|
if (apple_names_table_up || apple_names_table_up || apple_types_table_up ||
|
|
apple_objc_table_up)
|
|
return llvm::make_unique<AppleDWARFIndex>(
|
|
module, std::move(apple_names_table_up),
|
|
std::move(apple_namespaces_table_up), std::move(apple_types_table_up),
|
|
std::move(apple_objc_table_up));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void AppleDWARFIndex::GetGlobalVariables(ConstString name, DIEArray &offsets) {
|
|
if (!m_apple_names_up)
|
|
return;
|
|
|
|
const char *name_cstr = name.GetCString();
|
|
llvm::StringRef basename;
|
|
llvm::StringRef context;
|
|
|
|
if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context,
|
|
basename))
|
|
basename = name_cstr;
|
|
|
|
m_apple_names_up->FindByName(basename, offsets);
|
|
}
|
|
|
|
void AppleDWARFIndex::GetGlobalVariables(const RegularExpression ®ex,
|
|
DIEArray &offsets) {
|
|
if (!m_apple_names_up)
|
|
return;
|
|
|
|
DWARFMappedHash::DIEInfoArray hash_data;
|
|
if (m_apple_names_up->AppendAllDIEsThatMatchingRegex(regex, hash_data))
|
|
DWARFMappedHash::ExtractDIEArray(hash_data, offsets);
|
|
}
|
|
|
|
void AppleDWARFIndex::GetGlobalVariables(const DWARFUnit &cu,
|
|
DIEArray &offsets) {
|
|
if (!m_apple_names_up)
|
|
return;
|
|
|
|
DWARFMappedHash::DIEInfoArray hash_data;
|
|
if (m_apple_names_up->AppendAllDIEsInRange(
|
|
cu.GetOffset(), cu.GetNextCompileUnitOffset(), hash_data))
|
|
DWARFMappedHash::ExtractDIEArray(hash_data, offsets);
|
|
}
|
|
|
|
void AppleDWARFIndex::GetObjCMethods(ConstString class_name,
|
|
DIEArray &offsets) {
|
|
if (m_apple_objc_up)
|
|
m_apple_objc_up->FindByName(class_name.GetStringRef(), offsets);
|
|
}
|
|
|
|
void AppleDWARFIndex::GetCompleteObjCClass(ConstString class_name,
|
|
bool must_be_implementation,
|
|
DIEArray &offsets) {
|
|
if (m_apple_types_up) {
|
|
m_apple_types_up->FindCompleteObjCClassByName(
|
|
class_name.GetStringRef(), offsets, must_be_implementation);
|
|
}
|
|
}
|
|
|
|
void AppleDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) {
|
|
if (m_apple_types_up)
|
|
m_apple_types_up->FindByName(name.GetStringRef(), offsets);
|
|
}
|
|
|
|
void AppleDWARFIndex::GetTypes(const DWARFDeclContext &context,
|
|
DIEArray &offsets) {
|
|
if (!m_apple_types_up)
|
|
return;
|
|
|
|
Log *log = LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION |
|
|
DWARF_LOG_LOOKUPS);
|
|
const bool has_tag = m_apple_types_up->GetHeader().header_data.ContainsAtom(
|
|
DWARFMappedHash::eAtomTypeTag);
|
|
const bool has_qualified_name_hash =
|
|
m_apple_types_up->GetHeader().header_data.ContainsAtom(
|
|
DWARFMappedHash::eAtomTypeQualNameHash);
|
|
const ConstString type_name(context[0].name);
|
|
const dw_tag_t tag = context[0].tag;
|
|
if (has_tag && has_qualified_name_hash) {
|
|
const char *qualified_name = context.GetQualifiedName();
|
|
const uint32_t qualified_name_hash = llvm::djbHash(qualified_name);
|
|
if (log)
|
|
m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()");
|
|
m_apple_types_up->FindByNameAndTagAndQualifiedNameHash(
|
|
type_name.GetStringRef(), tag, qualified_name_hash, offsets);
|
|
} else if (has_tag) {
|
|
if (log)
|
|
m_module.LogMessage(log, "FindByNameAndTag()");
|
|
m_apple_types_up->FindByNameAndTag(type_name.GetStringRef(), tag, offsets);
|
|
} else
|
|
m_apple_types_up->FindByName(type_name.GetStringRef(), offsets);
|
|
}
|
|
|
|
void AppleDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {
|
|
if (m_apple_namespaces_up)
|
|
m_apple_namespaces_up->FindByName(name.GetStringRef(), offsets);
|
|
}
|
|
|
|
static bool KeepFunctionDIE(DWARFDIE die, uint32_t name_type_mask) {
|
|
bool looking_for_methods = name_type_mask & eFunctionNameTypeMethod;
|
|
bool looking_for_functions = name_type_mask & eFunctionNameTypeBase;
|
|
if (looking_for_methods && looking_for_functions)
|
|
return true;
|
|
return looking_for_methods == die.IsMethod();
|
|
}
|
|
|
|
void AppleDWARFIndex::GetFunctions(
|
|
ConstString name, DWARFDebugInfo &info,
|
|
llvm::function_ref<bool(const DWARFDIE &die, bool include_inlines,
|
|
lldb_private::SymbolContextList &sc_list)>
|
|
resolve_function,
|
|
llvm::function_ref<CompilerDeclContext(lldb::user_id_t type_uid)>
|
|
get_decl_context_containing_uid,
|
|
const CompilerDeclContext *parent_decl_ctx, uint32_t name_type_mask,
|
|
bool include_inlines, SymbolContextList &sc_list) {
|
|
if (!m_apple_names_up)
|
|
return;
|
|
|
|
std::set<const DWARFDebugInfoEntry *> resolved_dies;
|
|
DIEArray offsets;
|
|
|
|
uint32_t num_matches = 0;
|
|
|
|
if (name_type_mask & eFunctionNameTypeFull) {
|
|
// If they asked for the full name, match what they typed. At some
|
|
// point we may want to canonicalize this (strip double spaces, etc.
|
|
// For now, we just add all the dies that we find by exact match.
|
|
num_matches = m_apple_names_up->FindByName(name.GetStringRef(), offsets);
|
|
for (uint32_t i = 0; i < num_matches; i++) {
|
|
const DIERef &die_ref = offsets[i];
|
|
DWARFDIE die = info.GetDIE(die_ref);
|
|
if (die) {
|
|
if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, die))
|
|
continue; // The containing decl contexts don't match
|
|
|
|
if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) {
|
|
if (resolve_function(die, include_inlines, sc_list))
|
|
resolved_dies.insert(die.GetDIE());
|
|
}
|
|
} else
|
|
ReportInvalidDIEOffset(die_ref.die_offset, name.GetStringRef());
|
|
}
|
|
}
|
|
|
|
if (name_type_mask & eFunctionNameTypeSelector) {
|
|
if (parent_decl_ctx && parent_decl_ctx->IsValid())
|
|
return; // no selectors in namespaces
|
|
|
|
num_matches = m_apple_names_up->FindByName(name.GetStringRef(), offsets);
|
|
// Now make sure these are actually ObjC methods. In this case we can
|
|
// simply look up the name, and if it is an ObjC method name, we're
|
|
// good.
|
|
|
|
for (uint32_t i = 0; i < num_matches; i++) {
|
|
const DIERef &die_ref = offsets[i];
|
|
DWARFDIE die = info.GetDIE(die_ref);
|
|
if (die) {
|
|
const char *die_name = die.GetName();
|
|
if (ObjCLanguage::IsPossibleObjCMethodName(die_name)) {
|
|
if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) {
|
|
if (resolve_function(die, include_inlines, sc_list))
|
|
resolved_dies.insert(die.GetDIE());
|
|
}
|
|
}
|
|
} else
|
|
ReportInvalidDIEOffset(die_ref.die_offset, name.GetStringRef());
|
|
}
|
|
offsets.clear();
|
|
}
|
|
|
|
if (((name_type_mask & eFunctionNameTypeMethod) && !parent_decl_ctx) ||
|
|
name_type_mask & eFunctionNameTypeBase) {
|
|
// The apple_names table stores just the "base name" of C++ methods in
|
|
// the table. So we have to extract the base name, look that up, and
|
|
// if there is any other information in the name we were passed in we
|
|
// have to post-filter based on that.
|
|
|
|
// FIXME: Arrange the logic above so that we don't calculate the base
|
|
// name twice:
|
|
num_matches = m_apple_names_up->FindByName(name.GetStringRef(), offsets);
|
|
|
|
for (uint32_t i = 0; i < num_matches; i++) {
|
|
const DIERef &die_ref = offsets[i];
|
|
DWARFDIE die = info.GetDIE(die_ref);
|
|
if (die) {
|
|
if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, die))
|
|
continue; // The containing decl contexts don't match
|
|
|
|
if (!KeepFunctionDIE(die, name_type_mask))
|
|
continue;
|
|
|
|
if (resolved_dies.find(die.GetDIE()) != resolved_dies.end())
|
|
continue;
|
|
|
|
// If we get to here, the die is good, and we should add it:
|
|
if (resolve_function(die, include_inlines, sc_list))
|
|
resolved_dies.insert(die.GetDIE());
|
|
} else
|
|
ReportInvalidDIEOffset(die_ref.die_offset, name.GetStringRef());
|
|
}
|
|
offsets.clear();
|
|
}
|
|
}
|
|
|
|
void AppleDWARFIndex::GetFunctions(
|
|
const RegularExpression ®ex, DWARFDebugInfo &info,
|
|
llvm::function_ref<bool(const DWARFDIE &die, bool include_inlines,
|
|
lldb_private::SymbolContextList &sc_list)>
|
|
resolve_function,
|
|
bool include_inlines, SymbolContextList &sc_list) {
|
|
if (!m_apple_names_up)
|
|
return;
|
|
|
|
DIEArray offsets;
|
|
DWARFMappedHash::DIEInfoArray hash_data;
|
|
if (m_apple_names_up->AppendAllDIEsThatMatchingRegex(regex, hash_data)) {
|
|
DWARFMappedHash::ExtractDIEArray(hash_data, offsets);
|
|
ParseFunctions(offsets, info, resolve_function, include_inlines, sc_list);
|
|
}
|
|
}
|
|
|
|
void AppleDWARFIndex::ReportInvalidDIEOffset(dw_offset_t offset,
|
|
llvm::StringRef name) {
|
|
m_module.ReportErrorIfModifyDetected(
|
|
"the DWARF debug information has been modified (accelerator table had "
|
|
"bad die 0x%8.8x for '%s')\n",
|
|
offset, name.str().c_str());
|
|
}
|
|
|
|
void AppleDWARFIndex::Dump(Stream &s) {
|
|
// TODO: Implement dumping.
|
|
}
|