Skip to content

Commit 9a7262c

Browse files
authored
[lldb][DWARF] Delay struct/class/union definition DIE searching when parsing declaration DIEs. (#90663)
This is the implementation for https://discourse.llvm.org/t/rfc-delay-definition-die-searching-when-parse-a-declaration-die-for-record-type/78526. #### Motivation Currently, lldb eagerly searches for definition DIE when parsing a declaration DIE for struct/class/union definition DIE. It will search for all definition DIEs with the same unqualified name (just `DW_AT_name` ) and then find out those DIEs with same fully qualified name. Then lldb will try to resolve those DIEs to create the Types from definition DIEs. It works fine most time. However, when built with `-gsimple-template-names`, the search graph expands very quickly, because for the specialized-template classes, they don’t have template parameter names encoded inside `DW_AT_name`. They have `DW_TAG_template_type_parameter` to reference the types used as template parameters. In order to identify if a definition DIE matches a declaration DIE, lldb needs to resolve all template parameter types first and those template parameter types might be template classes as well, and so on… So, the search graph explodes, causing a lot unnecessary searching/type-resolving to just get the fully qualified names for a specialized-template class. This causes lldb stack overflow for us internally on template-heavy libraries. #### Implementation Instead of searching for definition DIEs when parsing declaration DIEs, we always construct the record type from the DIE regardless if it's definition or declaration. The process of searching for definition DIE is refactored to `DWARFASTParserClang::FindDefinitionTypeForDIE` which is invoked when 1) completing the type on `SymbolFileDWARF::CompleteType`. 2) the record type needs to start its definition as a containing type so that nested classes can be added into it in `PrepareContextToReceiveMembers`. The key difference is `SymbolFileDWARF::ResolveType` return a `Type*` that might be created from declaration DIE, which means it hasn't starts its definition yet. We also need to change according in places where we want the type to start definition, like `PrepareContextToReceiveMembers` (I'm not aware of any other places, but this should be a simple call to `SymbolFileDWARF::FindDefinitionDIE`) #### Result It fixes the stack overflow of lldb for the internal binary built with simple template name. When constructing the fully qualified name built with `-gsimple-template-names`, it gets the name of the type parameter by resolving the referenced DIE, which might be a declaration (we won't try to search for the definition DIE to just get the name). I got rough measurement about the time using the same commands (set breakpoint, run, expr this, exit). For the binary built without `-gsimple-template-names`, this change has no impact on time, still taking 41 seconds to complete. When built with `-gsimple-template-names`, it also takes about 41 seconds to complete wit this change.
1 parent 099c152 commit 9a7262c

File tree

8 files changed

+438
-383
lines changed

8 files changed

+438
-383
lines changed

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class DWARFASTParser {
6060

6161
virtual ConstString GetDIEClassTemplateParams(const DWARFDIE &die) = 0;
6262

63+
virtual lldb_private::Type *FindDefinitionTypeForDIE(const DWARFDIE &die) = 0;
64+
6365
static std::optional<SymbolFile::ArrayInfo>
6466
ParseChildArrayInfo(const DWARFDIE &parent_die,
6567
const ExecutionContext *exe_ctx = nullptr);

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

+218-179
Large diffs are not rendered by default.

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h

+88-109
Large diffs are not rendered by default.

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp

+25-19
Original file line numberDiff line numberDiff line change
@@ -1632,27 +1632,33 @@ bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) {
16321632
return true;
16331633
}
16341634

1635-
DWARFDIE dwarf_die = GetDIE(die_it->getSecond());
1636-
if (dwarf_die) {
1637-
// Once we start resolving this type, remove it from the forward
1638-
// declaration map in case anyone child members or other types require this
1639-
// type to get resolved. The type will get resolved when all of the calls
1640-
// to SymbolFileDWARF::ResolveClangOpaqueTypeDefinition are done.
1641-
GetForwardDeclCompilerTypeToDIE().erase(die_it);
1642-
1643-
Type *type = GetDIEToType().lookup(dwarf_die.GetDIE());
1635+
// Once we start resolving this type, remove it from the forward
1636+
// declaration map in case anyone's child members or other types require this
1637+
// type to get resolved.
1638+
DWARFDIE dwarf_die = GetDIE(die_it->second);
1639+
GetForwardDeclCompilerTypeToDIE().erase(die_it);
1640+
Type *type = nullptr;
1641+
if (DWARFASTParser *dwarf_ast = GetDWARFParser(*dwarf_die.GetCU()))
1642+
type = dwarf_ast->FindDefinitionTypeForDIE(dwarf_die);
1643+
if (!type)
1644+
return false;
16441645

1645-
Log *log = GetLog(DWARFLog::DebugInfo | DWARFLog::TypeCompletion);
1646-
if (log)
1647-
GetObjectFile()->GetModule()->LogMessageVerboseBacktrace(
1648-
log, "{0:x8}: {1} ({2}) '{3}' resolving forward declaration...",
1649-
dwarf_die.GetID(), DW_TAG_value_to_name(dwarf_die.Tag()),
1650-
dwarf_die.Tag(), type->GetName().AsCString());
1651-
assert(compiler_type);
1652-
if (DWARFASTParser *dwarf_ast = GetDWARFParser(*dwarf_die.GetCU()))
1653-
return dwarf_ast->CompleteTypeFromDWARF(dwarf_die, type, compiler_type);
1646+
die_it = GetForwardDeclCompilerTypeToDIE().find(
1647+
compiler_type_no_qualifiers.GetOpaqueQualType());
1648+
if (die_it != GetForwardDeclCompilerTypeToDIE().end()) {
1649+
dwarf_die = GetDIE(die_it->getSecond());
1650+
GetForwardDeclCompilerTypeToDIE().erase(die_it);
16541651
}
1655-
return false;
1652+
1653+
if (Log *log = GetLog(DWARFLog::DebugInfo | DWARFLog::TypeCompletion))
1654+
GetObjectFile()->GetModule()->LogMessageVerboseBacktrace(
1655+
log, "{0:x8}: {1} ({2}) '{3}' resolving forward declaration...",
1656+
dwarf_die.GetID(), DW_TAG_value_to_name(dwarf_die.Tag()),
1657+
dwarf_die.Tag(), type->GetName().AsCString());
1658+
assert(compiler_type);
1659+
if (DWARFASTParser *dwarf_ast = GetDWARFParser(*dwarf_die.GetCU()))
1660+
return dwarf_ast->CompleteTypeFromDWARF(dwarf_die, type, compiler_type);
1661+
return true;
16561662
}
16571663

16581664
Type *SymbolFileDWARF::ResolveType(const DWARFDIE &die,

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h

+4
Original file line numberDiff line numberDiff line change
@@ -533,8 +533,12 @@ class SymbolFileDWARF : public SymbolFileCommon {
533533
NameToOffsetMap m_function_scope_qualified_name_map;
534534
std::unique_ptr<DWARFDebugRanges> m_ranges;
535535
UniqueDWARFASTTypeMap m_unique_ast_type_map;
536+
// A map from DIE to lldb_private::Type. For record type, the key might be
537+
// either declaration DIE or definition DIE.
536538
DIEToTypePtr m_die_to_type;
537539
DIEToVariableSP m_die_to_variable_sp;
540+
// A map from CompilerType to the struct/class/union/enum DIE (might be a
541+
// declaration or a definition) that is used to construct it.
538542
CompilerTypeToDIE m_forward_decl_compiler_type_to_die;
539543
llvm::DenseMap<dw_offset_t, std::unique_ptr<SupportFileList>>
540544
m_type_unit_support_files;

lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp

+54-53
Original file line numberDiff line numberDiff line change
@@ -13,66 +13,67 @@
1313
using namespace lldb_private::dwarf;
1414
using namespace lldb_private::plugin::dwarf;
1515

16-
bool UniqueDWARFASTTypeList::Find(const DWARFDIE &die,
17-
const lldb_private::Declaration &decl,
18-
const int32_t byte_size,
19-
UniqueDWARFASTType &entry) const {
20-
for (const UniqueDWARFASTType &udt : m_collection) {
16+
UniqueDWARFASTType *UniqueDWARFASTTypeList::Find(
17+
const DWARFDIE &die, const lldb_private::Declaration &decl,
18+
const int32_t byte_size, bool is_forward_declaration) {
19+
for (UniqueDWARFASTType &udt : m_collection) {
2120
// Make sure the tags match
2221
if (udt.m_die.Tag() == die.Tag()) {
23-
// Validate byte sizes of both types only if both are valid.
24-
if (udt.m_byte_size < 0 || byte_size < 0 ||
25-
udt.m_byte_size == byte_size) {
26-
// Make sure the file and line match
27-
if (udt.m_declaration == decl) {
28-
// The type has the same name, and was defined on the same file and
29-
// line. Now verify all of the parent DIEs match.
30-
DWARFDIE parent_arg_die = die.GetParent();
31-
DWARFDIE parent_pos_die = udt.m_die.GetParent();
32-
bool match = true;
33-
bool done = false;
34-
while (!done && match && parent_arg_die && parent_pos_die) {
35-
const dw_tag_t parent_arg_tag = parent_arg_die.Tag();
36-
const dw_tag_t parent_pos_tag = parent_pos_die.Tag();
37-
if (parent_arg_tag == parent_pos_tag) {
38-
switch (parent_arg_tag) {
39-
case DW_TAG_class_type:
40-
case DW_TAG_structure_type:
41-
case DW_TAG_union_type:
42-
case DW_TAG_namespace: {
43-
const char *parent_arg_die_name = parent_arg_die.GetName();
44-
if (parent_arg_die_name ==
45-
nullptr) // Anonymous (i.e. no-name) struct
46-
{
47-
match = false;
48-
} else {
49-
const char *parent_pos_die_name = parent_pos_die.GetName();
50-
if (parent_pos_die_name == nullptr ||
51-
((parent_arg_die_name != parent_pos_die_name) &&
52-
strcmp(parent_arg_die_name, parent_pos_die_name)))
53-
match = false;
54-
}
55-
} break;
56-
57-
case DW_TAG_compile_unit:
58-
case DW_TAG_partial_unit:
59-
done = true;
60-
break;
61-
default:
62-
break;
63-
}
22+
// If they are not both definition DIEs or both declaration DIEs, then
23+
// don't check for byte size and declaration location, because declaration
24+
// DIEs usually don't have those info.
25+
bool matching_size_declaration =
26+
udt.m_is_forward_declaration != is_forward_declaration
27+
? true
28+
: (udt.m_byte_size < 0 || byte_size < 0 ||
29+
udt.m_byte_size == byte_size) &&
30+
udt.m_declaration == decl;
31+
if (!matching_size_declaration)
32+
continue;
33+
// The type has the same name, and was defined on the same file and
34+
// line. Now verify all of the parent DIEs match.
35+
DWARFDIE parent_arg_die = die.GetParent();
36+
DWARFDIE parent_pos_die = udt.m_die.GetParent();
37+
bool match = true;
38+
bool done = false;
39+
while (!done && match && parent_arg_die && parent_pos_die) {
40+
const dw_tag_t parent_arg_tag = parent_arg_die.Tag();
41+
const dw_tag_t parent_pos_tag = parent_pos_die.Tag();
42+
if (parent_arg_tag == parent_pos_tag) {
43+
switch (parent_arg_tag) {
44+
case DW_TAG_class_type:
45+
case DW_TAG_structure_type:
46+
case DW_TAG_union_type:
47+
case DW_TAG_namespace: {
48+
const char *parent_arg_die_name = parent_arg_die.GetName();
49+
if (parent_arg_die_name == nullptr) {
50+
// Anonymous (i.e. no-name) struct
51+
match = false;
52+
} else {
53+
const char *parent_pos_die_name = parent_pos_die.GetName();
54+
if (parent_pos_die_name == nullptr ||
55+
((parent_arg_die_name != parent_pos_die_name) &&
56+
strcmp(parent_arg_die_name, parent_pos_die_name)))
57+
match = false;
6458
}
65-
parent_arg_die = parent_arg_die.GetParent();
66-
parent_pos_die = parent_pos_die.GetParent();
67-
}
59+
} break;
6860

69-
if (match) {
70-
entry = udt;
71-
return true;
61+
case DW_TAG_compile_unit:
62+
case DW_TAG_partial_unit:
63+
done = true;
64+
break;
65+
default:
66+
break;
7267
}
7368
}
69+
parent_arg_die = parent_arg_die.GetParent();
70+
parent_pos_die = parent_pos_die.GetParent();
71+
}
72+
73+
if (match) {
74+
return &udt;
7475
}
7576
}
7677
}
77-
return false;
78+
return nullptr;
7879
}

lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h

+13-23
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,19 @@ class UniqueDWARFASTType {
2323
// Constructors and Destructors
2424
UniqueDWARFASTType() : m_type_sp(), m_die(), m_declaration() {}
2525

26-
UniqueDWARFASTType(lldb::TypeSP &type_sp, const DWARFDIE &die,
27-
const Declaration &decl, int32_t byte_size)
28-
: m_type_sp(type_sp), m_die(die), m_declaration(decl),
29-
m_byte_size(byte_size) {}
30-
3126
UniqueDWARFASTType(const UniqueDWARFASTType &rhs)
3227
: m_type_sp(rhs.m_type_sp), m_die(rhs.m_die),
33-
m_declaration(rhs.m_declaration), m_byte_size(rhs.m_byte_size) {}
28+
m_declaration(rhs.m_declaration), m_byte_size(rhs.m_byte_size),
29+
m_is_forward_declaration(rhs.m_is_forward_declaration) {}
3430

3531
~UniqueDWARFASTType() = default;
3632

37-
UniqueDWARFASTType &operator=(const UniqueDWARFASTType &rhs) {
38-
if (this != &rhs) {
39-
m_type_sp = rhs.m_type_sp;
40-
m_die = rhs.m_die;
41-
m_declaration = rhs.m_declaration;
42-
m_byte_size = rhs.m_byte_size;
43-
}
44-
return *this;
45-
}
46-
4733
lldb::TypeSP m_type_sp;
4834
DWARFDIE m_die;
4935
Declaration m_declaration;
5036
int32_t m_byte_size = -1;
37+
// True if the m_die is a forward declaration DIE.
38+
bool m_is_forward_declaration = true;
5139
};
5240

5341
class UniqueDWARFASTTypeList {
@@ -62,8 +50,9 @@ class UniqueDWARFASTTypeList {
6250
m_collection.push_back(entry);
6351
}
6452

65-
bool Find(const DWARFDIE &die, const Declaration &decl,
66-
const int32_t byte_size, UniqueDWARFASTType &entry) const;
53+
UniqueDWARFASTType *Find(const DWARFDIE &die, const Declaration &decl,
54+
const int32_t byte_size,
55+
bool is_forward_declaration);
6756

6857
protected:
6958
typedef std::vector<UniqueDWARFASTType> collection;
@@ -80,14 +69,15 @@ class UniqueDWARFASTTypeMap {
8069
m_collection[name.GetCString()].Append(entry);
8170
}
8271

83-
bool Find(ConstString name, const DWARFDIE &die, const Declaration &decl,
84-
const int32_t byte_size, UniqueDWARFASTType &entry) const {
72+
UniqueDWARFASTType *Find(ConstString name, const DWARFDIE &die,
73+
const Declaration &decl, const int32_t byte_size,
74+
bool is_forward_declaration) {
8575
const char *unique_name_cstr = name.GetCString();
86-
collection::const_iterator pos = m_collection.find(unique_name_cstr);
76+
collection::iterator pos = m_collection.find(unique_name_cstr);
8777
if (pos != m_collection.end()) {
88-
return pos->second.Find(die, decl, byte_size, entry);
78+
return pos->second.Find(die, decl, byte_size, is_forward_declaration);
8979
}
90-
return false;
80+
return nullptr;
9181
}
9282

9383
protected:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Test definition DIE searching is delayed until complete type is required.
2+
3+
# RUN: split-file %s %t
4+
# RUN: %clangxx_host %t/main.cpp %t/t1_def.cpp -g -o %t.out
5+
# RUN: %lldb -b %t.out -s %t/lldb.cmd | FileCheck %s
6+
7+
# CHECK: (lldb) p v1
8+
# CHECK: DWARFASTParserClang::ParseTypeFromDWARF{{.*}}DW_TAG_structure_type (DW_TAG_structure_type) name = 't2<t1>'
9+
# CHECK: DWARFASTParserClang::ParseTypeFromDWARF{{.*}}DW_TAG_structure_type (DW_TAG_structure_type) name = 't1'
10+
# CHECK: DW_TAG_structure_type (DW_TAG_structure_type) 't2<t1>' resolving forward declaration...
11+
# CHECK: (t2<t1>) {}
12+
# CHECK: (lldb) p v2
13+
# CHECK: DWARFASTParserClang::ParseTypeFromDWARF{{.*}}DW_TAG_structure_type (DW_TAG_structure_type) name = 't1'
14+
# CHECK: DW_TAG_structure_type (DW_TAG_structure_type) 't1' resolving forward declaration...
15+
16+
#--- lldb.cmd
17+
log enable dwarf comp
18+
p v1
19+
p v2
20+
21+
#--- main.cpp
22+
template<typename T>
23+
struct t2 {
24+
};
25+
struct t1;
26+
t2<t1> v1; // this CU doesn't have definition DIE for t1, but only declaration DIE for it.
27+
int main() {
28+
}
29+
30+
#--- t1_def.cpp
31+
struct t1 { // this CU contains definition DIE for t1.
32+
int x;
33+
};
34+
t1 v2;

0 commit comments

Comments
 (0)