Pavel Labath wrote in D73206: The internal representation of DebugNames and Apple indexes is fixed by the relevant (pseudo-)standards, so we can't really change it. The question is how to efficiently (and cleanly) convert from the internal representation to some common thing. The conversion from AppleIndex to DIERef is trivial (which is not surprising as it was the first and the overall design was optimized for that). With debug_names, the situation gets more tricky. The internal representation of debug_names uses CU-relative DIE offsets, but DIERef wants an absolute offset. That means the index has to do more work to produce the common representation. And it needs to do that for all results, even though a lot of the index users are really interested only in a single entry. With the switch to user_id_t, _all_ indexes would have to do some extra work to encode it, only for their users to have to immediately decode it back. Having a iterator/callback based api would allow us to minimize the impact of that, as it would only need to happen for the entries that are really used. And /I think/ we could make it interface returns DWARFDies directly, and each index converts to that using the most direct approach available. Jan Kratochvil: It also makes all the callers shorter as they no longer need to fetch DWARFDIE from DIERef (and handling if not found by ReportInvalidDIERef) but the callers are already served DWARFDIE which they need. In some cases the DWARFDIE had to be fetched both by callee (DWARFIndex implementation) and caller. Differential Revision: https://reviews.llvm.org/D77970
295 lines
9.7 KiB
C++
295 lines
9.7 KiB
C++
//===-- DebugNamesDWARFIndex.cpp ------------------------------------------===//
|
|
//
|
|
// 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 "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h"
|
|
#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
|
|
#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
|
|
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Utility/RegularExpression.h"
|
|
#include "lldb/Utility/Stream.h"
|
|
|
|
using namespace lldb_private;
|
|
using namespace lldb;
|
|
|
|
llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>>
|
|
DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names,
|
|
DWARFDataExtractor debug_str,
|
|
SymbolFileDWARF &dwarf) {
|
|
auto index_up = std::make_unique<DebugNames>(debug_names.GetAsLLVM(),
|
|
debug_str.GetAsLLVM());
|
|
if (llvm::Error E = index_up->extract())
|
|
return std::move(E);
|
|
|
|
return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex(
|
|
module, std::move(index_up), debug_names, debug_str, dwarf));
|
|
}
|
|
|
|
llvm::DenseSet<dw_offset_t>
|
|
DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) {
|
|
llvm::DenseSet<dw_offset_t> result;
|
|
for (const DebugNames::NameIndex &ni : debug_names) {
|
|
for (uint32_t cu = 0; cu < ni.getCUCount(); ++cu)
|
|
result.insert(ni.getCUOffset(cu));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
llvm::Optional<DIERef>
|
|
DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) {
|
|
llvm::Optional<uint64_t> cu_offset = entry.getCUOffset();
|
|
if (!cu_offset)
|
|
return llvm::None;
|
|
|
|
DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset);
|
|
if (!cu)
|
|
return llvm::None;
|
|
|
|
cu = &cu->GetNonSkeletonUnit();
|
|
if (llvm::Optional<uint64_t> die_offset = entry.getDIEUnitOffset())
|
|
return DIERef(cu->GetSymbolFileDWARF().GetDwoNum(),
|
|
DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset);
|
|
|
|
return llvm::None;
|
|
}
|
|
|
|
bool DebugNamesDWARFIndex::ProcessEntry(
|
|
const DebugNames::Entry &entry,
|
|
llvm::function_ref<bool(DWARFDIE die)> callback, llvm::StringRef name) {
|
|
llvm::Optional<DIERef> ref = ToDIERef(entry);
|
|
if (!ref)
|
|
return true;
|
|
SymbolFileDWARF &dwarf =
|
|
*llvm::cast<SymbolFileDWARF>(m_module.GetSymbolFile());
|
|
DWARFDIE die = dwarf.GetDIE(*ref);
|
|
if (!die)
|
|
return true;
|
|
return callback(die);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error,
|
|
const DebugNames::NameIndex &ni,
|
|
llvm::StringRef name) {
|
|
// Ignore SentinelErrors, log everything else.
|
|
LLDB_LOG_ERROR(
|
|
LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS),
|
|
handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}),
|
|
"Failed to parse index entries for index at {1:x}, name {2}: {0}",
|
|
ni.getUnitOffset(), name);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::GetGlobalVariables(
|
|
ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) {
|
|
for (const DebugNames::Entry &entry :
|
|
m_debug_names_up->equal_range(basename.GetStringRef())) {
|
|
if (entry.tag() != DW_TAG_variable)
|
|
continue;
|
|
|
|
if (!ProcessEntry(entry, callback, basename.GetStringRef()))
|
|
return;
|
|
}
|
|
|
|
m_fallback.GetGlobalVariables(basename, callback);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::GetGlobalVariables(
|
|
const RegularExpression ®ex,
|
|
llvm::function_ref<bool(DWARFDIE die)> callback) {
|
|
for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
|
|
for (DebugNames::NameTableEntry nte: ni) {
|
|
if (!regex.Execute(nte.getString()))
|
|
continue;
|
|
|
|
uint64_t entry_offset = nte.getEntryOffset();
|
|
llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
|
|
for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
|
|
if (entry_or->tag() != DW_TAG_variable)
|
|
continue;
|
|
|
|
if (!ProcessEntry(*entry_or, callback,
|
|
llvm::StringRef(nte.getString())))
|
|
return;
|
|
}
|
|
MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
|
|
}
|
|
}
|
|
|
|
m_fallback.GetGlobalVariables(regex, callback);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::GetGlobalVariables(
|
|
const DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) {
|
|
uint64_t cu_offset = cu.GetOffset();
|
|
for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
|
|
for (DebugNames::NameTableEntry nte: ni) {
|
|
uint64_t entry_offset = nte.getEntryOffset();
|
|
llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
|
|
for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
|
|
if (entry_or->tag() != DW_TAG_variable)
|
|
continue;
|
|
if (entry_or->getCUOffset() != cu_offset)
|
|
continue;
|
|
|
|
if (!ProcessEntry(*entry_or, callback,
|
|
llvm::StringRef(nte.getString())))
|
|
return;
|
|
}
|
|
MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
|
|
}
|
|
}
|
|
|
|
m_fallback.GetGlobalVariables(cu, callback);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::GetCompleteObjCClass(
|
|
ConstString class_name, bool must_be_implementation,
|
|
llvm::function_ref<bool(DWARFDIE die)> callback) {
|
|
// Keep a list of incomplete types as fallback for when we don't find the
|
|
// complete type.
|
|
DIEArray incomplete_types;
|
|
|
|
for (const DebugNames::Entry &entry :
|
|
m_debug_names_up->equal_range(class_name.GetStringRef())) {
|
|
if (entry.tag() != DW_TAG_structure_type &&
|
|
entry.tag() != DW_TAG_class_type)
|
|
continue;
|
|
|
|
llvm::Optional<DIERef> ref = ToDIERef(entry);
|
|
if (!ref)
|
|
continue;
|
|
|
|
DWARFUnit *cu = m_debug_info.GetUnit(*ref);
|
|
if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) {
|
|
incomplete_types.push_back(*ref);
|
|
continue;
|
|
}
|
|
|
|
DWARFDIE die = m_debug_info.GetDIE(*ref);
|
|
if (!die) {
|
|
ReportInvalidDIERef(*ref, class_name.GetStringRef());
|
|
continue;
|
|
}
|
|
|
|
if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) {
|
|
// If we find the complete version we're done.
|
|
callback(die);
|
|
return;
|
|
}
|
|
incomplete_types.push_back(*ref);
|
|
}
|
|
|
|
auto dierefcallback = DIERefCallback(callback, class_name.GetStringRef());
|
|
for (DIERef ref : incomplete_types)
|
|
if (!dierefcallback(ref))
|
|
return;
|
|
|
|
m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::GetTypes(
|
|
ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
|
|
for (const DebugNames::Entry &entry :
|
|
m_debug_names_up->equal_range(name.GetStringRef())) {
|
|
if (isType(entry.tag())) {
|
|
if (!ProcessEntry(entry, callback, name.GetStringRef()))
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_fallback.GetTypes(name, callback);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::GetTypes(
|
|
const DWARFDeclContext &context,
|
|
llvm::function_ref<bool(DWARFDIE die)> callback) {
|
|
auto name = context[0].name;
|
|
for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) {
|
|
if (entry.tag() == context[0].tag) {
|
|
if (!ProcessEntry(entry, callback, name))
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_fallback.GetTypes(context, callback);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::GetNamespaces(
|
|
ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
|
|
for (const DebugNames::Entry &entry :
|
|
m_debug_names_up->equal_range(name.GetStringRef())) {
|
|
if (entry.tag() == DW_TAG_namespace) {
|
|
if (!ProcessEntry(entry, callback, name.GetStringRef()))
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_fallback.GetNamespaces(name, callback);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::GetFunctions(
|
|
ConstString name, SymbolFileDWARF &dwarf,
|
|
const CompilerDeclContext &parent_decl_ctx, uint32_t name_type_mask,
|
|
llvm::function_ref<bool(DWARFDIE die)> callback) {
|
|
|
|
std::set<DWARFDebugInfoEntry *> seen;
|
|
for (const DebugNames::Entry &entry :
|
|
m_debug_names_up->equal_range(name.GetStringRef())) {
|
|
Tag tag = entry.tag();
|
|
if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
|
|
continue;
|
|
|
|
if (llvm::Optional<DIERef> ref = ToDIERef(entry)) {
|
|
if (!ProcessFunctionDIE(name.GetStringRef(), *ref, dwarf, parent_decl_ctx,
|
|
name_type_mask, [&](DWARFDIE die) {
|
|
if (!seen.insert(die.GetDIE()).second)
|
|
return true;
|
|
return callback(die);
|
|
}))
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_fallback.GetFunctions(name, dwarf, parent_decl_ctx, name_type_mask,
|
|
callback);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::GetFunctions(
|
|
const RegularExpression ®ex,
|
|
llvm::function_ref<bool(DWARFDIE die)> callback) {
|
|
for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
|
|
for (DebugNames::NameTableEntry nte: ni) {
|
|
if (!regex.Execute(nte.getString()))
|
|
continue;
|
|
|
|
uint64_t entry_offset = nte.getEntryOffset();
|
|
llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
|
|
for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
|
|
Tag tag = entry_or->tag();
|
|
if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
|
|
continue;
|
|
|
|
if (!ProcessEntry(*entry_or, callback,
|
|
llvm::StringRef(nte.getString())))
|
|
return;
|
|
}
|
|
MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
|
|
}
|
|
}
|
|
|
|
m_fallback.GetFunctions(regex, callback);
|
|
}
|
|
|
|
void DebugNamesDWARFIndex::Dump(Stream &s) {
|
|
m_fallback.Dump(s);
|
|
|
|
std::string data;
|
|
llvm::raw_string_ostream os(data);
|
|
m_debug_names_up->dump(os);
|
|
s.PutCString(os.str());
|
|
}
|