-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb][DWARFIndex] Use IDX_parent to implement GetFullyQualifiedType query #79932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ | |
#include "lldb/Core/Module.h" | ||
#include "lldb/Utility/RegularExpression.h" | ||
#include "lldb/Utility/Stream.h" | ||
#include "llvm/ADT/Sequence.h" | ||
#include <optional> | ||
|
||
using namespace lldb_private; | ||
|
@@ -218,6 +219,108 @@ void DebugNamesDWARFIndex::GetCompleteObjCClass( | |
m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback); | ||
} | ||
|
||
namespace { | ||
using Entry = llvm::DWARFDebugNames::Entry; | ||
|
||
/// If `entry` and all of its parents have an `IDX_parent`, use that information | ||
/// to build and return a list of at most `max_parents` parent Entries. | ||
/// `entry` itself is not included in the list. | ||
/// If any parent does not have an `IDX_parent`, or the Entry data is corrupted, | ||
/// nullopt is returned. | ||
std::optional<llvm::SmallVector<Entry, 4>> | ||
getParentChain(Entry entry, uint32_t max_parents) { | ||
llvm::SmallVector<Entry, 4> parent_entries; | ||
|
||
do { | ||
if (!entry.hasParentInformation()) | ||
return std::nullopt; | ||
|
||
llvm::Expected<std::optional<Entry>> parent = entry.getParentDIEEntry(); | ||
if (!parent) { | ||
// Bad data. | ||
LLDB_LOG_ERROR( | ||
GetLog(DWARFLog::Lookups), parent.takeError(), | ||
"Failed to extract parent entry from a non-empty IDX_parent"); | ||
return std::nullopt; | ||
} | ||
|
||
// Last parent in the chain. | ||
if (!parent->has_value()) | ||
break; | ||
|
||
parent_entries.push_back(**parent); | ||
entry = **parent; | ||
} while (parent_entries.size() < max_parents); | ||
|
||
return parent_entries; | ||
} | ||
} // namespace | ||
|
||
void DebugNamesDWARFIndex::GetFullyQualifiedType( | ||
const DWARFDeclContext &context, | ||
llvm::function_ref<bool(DWARFDIE die)> callback) { | ||
if (context.GetSize() == 0) | ||
return; | ||
|
||
llvm::StringRef leaf_name = context[0].name; | ||
llvm::SmallVector<llvm::StringRef> parent_names; | ||
for (auto idx : llvm::seq<int>(1, context.GetSize())) | ||
parent_names.emplace_back(context[idx].name); | ||
|
||
// For each entry, grab its parent chain and check if we have a match. | ||
for (const DebugNames::Entry &entry : | ||
m_debug_names_up->equal_range(leaf_name)) { | ||
if (!isType(entry.tag())) | ||
continue; | ||
|
||
// Grab at most one extra parent, subsequent parents are not necessary to | ||
// test equality. | ||
std::optional<llvm::SmallVector<Entry, 4>> parent_chain = | ||
getParentChain(entry, parent_names.size() + 1); | ||
|
||
if (!parent_chain) { | ||
// Fallback: use the base class implementation. | ||
if (!ProcessEntry(entry, [&](DWARFDIE die) { | ||
return GetFullyQualifiedTypeImpl(context, die, callback); | ||
})) | ||
return; | ||
continue; | ||
} | ||
|
||
if (SameParentChain(parent_names, *parent_chain) && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might benefit fro a few high-level comments about what's happening in this loop? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add something at the top of loop |
||
!ProcessEntry(entry, callback)) | ||
return; | ||
} | ||
} | ||
|
||
bool DebugNamesDWARFIndex::SameParentChain( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any comments that would be useful in this function? |
||
llvm::ArrayRef<llvm::StringRef> parent_names, | ||
llvm::ArrayRef<DebugNames::Entry> parent_entries) const { | ||
|
||
if (parent_entries.size() != parent_names.size()) | ||
return false; | ||
|
||
auto SameAsEntryATName = [this](llvm::StringRef name, | ||
const DebugNames::Entry &entry) { | ||
// Peek at the AT_name of `entry` and test equality to `name`. | ||
auto maybe_dieoffset = entry.getDIEUnitOffset(); | ||
if (!maybe_dieoffset) | ||
return false; | ||
auto die_ref = ToDIERef(entry); | ||
if (!die_ref) | ||
return false; | ||
return name == m_debug_info.PeekDIEName(*die_ref); | ||
}; | ||
|
||
// If the AT_name of any parent fails to match the expected name, we don't | ||
// have a match. | ||
for (auto [parent_name, parent_entry] : | ||
llvm::zip_equal(parent_names, parent_entries)) | ||
if (!SameAsEntryATName(parent_name, parent_entry)) | ||
return false; | ||
return true; | ||
} | ||
|
||
void DebugNamesDWARFIndex::GetTypes( | ||
ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { | ||
for (const DebugNames::Entry &entry : | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
//===-- DWARFDIETest.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/DWARFDIE.h" | ||
#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" | ||
#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h" | ||
#include "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h" | ||
#include "TestingSupport/Symbol/YAMLModuleTester.h" | ||
#include "llvm/ADT/STLExtras.h" | ||
#include "gmock/gmock.h" | ||
#include "gtest/gtest.h" | ||
|
||
using namespace lldb; | ||
using namespace lldb_private; | ||
using namespace lldb_private::plugin::dwarf; | ||
using StringRef = llvm::StringRef; | ||
|
||
static void | ||
check_num_matches(DebugNamesDWARFIndex &index, int expected_num_matches, | ||
llvm::ArrayRef<DWARFDeclContext::Entry> ctx_entries) { | ||
DWARFDeclContext ctx(ctx_entries); | ||
int num_matches = 0; | ||
|
||
index.GetFullyQualifiedType(ctx, [&](DWARFDIE die) { | ||
num_matches++; | ||
return true; | ||
}); | ||
ASSERT_EQ(num_matches, expected_num_matches); | ||
} | ||
|
||
static DWARFDeclContext::Entry make_entry(const char *c) { | ||
return DWARFDeclContext::Entry(dwarf::DW_TAG_class_type, c); | ||
} | ||
|
||
TEST(DWARFDebugNamesIndexTest, FullyQualifiedQueryWithIDXParent) { | ||
const char *yamldata = R"( | ||
--- !ELF | ||
FileHeader: | ||
Class: ELFCLASS64 | ||
Data: ELFDATA2LSB | ||
Type: ET_EXEC | ||
Machine: EM_386 | ||
DWARF: | ||
debug_str: | ||
- '1' | ||
- '2' | ||
- '3' | ||
debug_abbrev: | ||
- Table: | ||
# We intentionally don't nest types in debug_info: if the nesting is not | ||
# inferred from debug_names, we want the test to fail. | ||
- Code: 0x1 | ||
Tag: DW_TAG_compile_unit | ||
Children: DW_CHILDREN_yes | ||
- Code: 0x2 | ||
Tag: DW_TAG_class_type | ||
Children: DW_CHILDREN_no | ||
Attributes: | ||
- Attribute: DW_AT_name | ||
Form: DW_FORM_strp | ||
debug_info: | ||
- Version: 4 | ||
AddrSize: 8 | ||
Entries: | ||
- AbbrCode: 0x1 | ||
- AbbrCode: 0x2 | ||
Values: | ||
- Value: 0x0 # Name "1" | ||
- AbbrCode: 0x2 | ||
Values: | ||
- Value: 0x2 # Name "2" | ||
- AbbrCode: 0x2 | ||
Values: | ||
- Value: 0x4 # Name "3" | ||
- AbbrCode: 0x0 | ||
debug_names: | ||
Abbreviations: | ||
- Code: 0x11 | ||
Tag: DW_TAG_class_type | ||
Indices: | ||
- Idx: DW_IDX_parent | ||
Form: DW_FORM_flag_present | ||
- Idx: DW_IDX_die_offset | ||
Form: DW_FORM_ref4 | ||
- Code: 0x22 | ||
Tag: DW_TAG_class_type | ||
Indices: | ||
- Idx: DW_IDX_parent | ||
Form: DW_FORM_ref4 | ||
- Idx: DW_IDX_die_offset | ||
Form: DW_FORM_ref4 | ||
Entries: | ||
- Name: 0x0 # strp to Name1 | ||
Code: 0x11 | ||
Values: | ||
- 0xc # Die offset to entry named "1" | ||
- Name: 0x2 # strp to Name2 | ||
Code: 0x22 | ||
Values: | ||
- 0x0 # Parent = First entry ("1") | ||
- 0x11 # Die offset to entry named "1:2" | ||
- Name: 0x4 # strp to Name3 | ||
Code: 0x22 | ||
Values: | ||
- 0x6 # Parent = Second entry ("1::2") | ||
- 0x16 # Die offset to entry named "1::2::3" | ||
- Name: 0x4 # strp to Name3 | ||
Code: 0x11 | ||
Values: | ||
- 0x16 # Die offset to entry named "3" | ||
)"; | ||
|
||
YAMLModuleTester t(yamldata); | ||
auto *symbol_file = | ||
llvm::cast<SymbolFileDWARF>(t.GetModule()->GetSymbolFile()); | ||
auto *index = static_cast<DebugNamesDWARFIndex *>(symbol_file->getIndex()); | ||
ASSERT_NE(index, nullptr); | ||
|
||
check_num_matches(*index, 1, {make_entry("1")}); | ||
check_num_matches(*index, 1, {make_entry("2"), make_entry("1")}); | ||
check_num_matches(*index, 1, | ||
{make_entry("3"), make_entry("2"), make_entry("1")}); | ||
check_num_matches(*index, 0, {make_entry("2")}); | ||
check_num_matches(*index, 1, {make_entry("3")}); | ||
} | ||
|
||
TEST(DWARFDebugNamesIndexTest, FullyQualifiedQueryWithoutIDXParent) { | ||
const char *yamldata = R"( | ||
--- !ELF | ||
FileHeader: | ||
Class: ELFCLASS64 | ||
Data: ELFDATA2LSB | ||
Type: ET_EXEC | ||
Machine: EM_386 | ||
DWARF: | ||
debug_str: | ||
- '1' | ||
- '2' | ||
debug_abbrev: | ||
- Table: | ||
- Code: 0x1 | ||
Tag: DW_TAG_compile_unit | ||
Children: DW_CHILDREN_yes | ||
- Code: 0x2 | ||
Tag: DW_TAG_class_type | ||
Children: DW_CHILDREN_yes | ||
Attributes: | ||
- Attribute: DW_AT_name | ||
Form: DW_FORM_strp | ||
- Code: 0x3 | ||
Tag: DW_TAG_class_type | ||
Children: DW_CHILDREN_no | ||
Attributes: | ||
- Attribute: DW_AT_name | ||
Form: DW_FORM_strp | ||
debug_info: | ||
- Version: 4 | ||
AddrSize: 8 | ||
Entries: | ||
- AbbrCode: 0x1 | ||
- AbbrCode: 0x2 | ||
Values: | ||
- Value: 0x0 # Name "1" | ||
- AbbrCode: 0x3 | ||
Values: | ||
- Value: 0x2 # Name "2" | ||
- AbbrCode: 0x0 | ||
- AbbrCode: 0x3 | ||
Values: | ||
- Value: 0x2 # Name "2" | ||
- AbbrCode: 0x0 | ||
debug_names: | ||
Abbreviations: | ||
- Code: 0x1 | ||
Tag: DW_TAG_class_type | ||
Indices: | ||
- Idx: DW_IDX_die_offset | ||
Form: DW_FORM_ref4 | ||
Entries: | ||
- Name: 0x0 # strp to Name1 | ||
Code: 0x1 | ||
Values: | ||
- 0xc # Die offset to entry named "1" | ||
- Name: 0x2 # strp to Name2 | ||
Code: 0x1 | ||
Values: | ||
- 0x11 # Die offset to entry named "1::2" | ||
- Name: 0x2 # strp to Name2 | ||
Code: 0x1 | ||
Values: | ||
- 0x17 # Die offset to entry named "2" | ||
)"; | ||
|
||
YAMLModuleTester t(yamldata); | ||
auto *symbol_file = | ||
llvm::cast<SymbolFileDWARF>(t.GetModule()->GetSymbolFile()); | ||
auto *index = static_cast<DebugNamesDWARFIndex *>(symbol_file->getIndex()); | ||
ASSERT_NE(index, nullptr); | ||
|
||
check_num_matches(*index, 1, {make_entry("1")}); | ||
check_num_matches(*index, 1, {make_entry("2"), make_entry("1")}); | ||
check_num_matches(*index, 1, {make_entry("2")}); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.