Skip to content

Commit 4a194b3

Browse files
VladimirMakaevadrian-prantl
authored andcommitted
[lldb] Add basic support to Rust enums in TypeSystemClang
LLDB doesn't yet have a TypeSystemRust implemented however it is used to debug Rust applications. Most of the types map well enough to Clang types and there are python formatters implemented to display those types reasonably well in a debugger. However, Rust enums are completely ignored by LLDB as Clang never emits DW_TAG_variant_part inside DW_TAG_structure_type This diff adds a parser for DW_TAG_variant_part (Rust-only) that creates a matching valid Clang declaration to the Rust enum. As long as there is enough information and all fields have correct offsets synthetic/summary providers can be implemented to display it correctly when debugging Rust code Differential Revision: https://reviews.llvm.org/D149213 (cherry picked from commit e84751a)
1 parent e47653c commit 4a194b3

File tree

6 files changed

+3775
-0
lines changed

6 files changed

+3775
-0
lines changed

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

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2501,8 +2501,163 @@ struct PropertyAttributes {
25012501
/// \see clang::ObjCPropertyAttribute
25022502
uint32_t prop_attributes = 0;
25032503
};
2504+
2505+
struct DiscriminantValue {
2506+
explicit DiscriminantValue(const DWARFDIE &die, ModuleSP module_sp);
2507+
2508+
uint32_t byte_offset;
2509+
uint32_t byte_size;
2510+
DWARFFormValue type_ref;
2511+
};
2512+
2513+
struct VariantMember {
2514+
explicit VariantMember(DWARFDIE &die, ModuleSP module_sp);
2515+
bool IsDefault() const;
2516+
2517+
std::optional<u_int32_t> discr_value;
2518+
DWARFFormValue type_ref;
2519+
ConstString variant_name;
2520+
uint32_t byte_offset;
2521+
ConstString GetName() const;
2522+
};
2523+
2524+
struct VariantPart {
2525+
explicit VariantPart(const DWARFDIE &die, const DWARFDIE &parent_die,
2526+
ModuleSP module_sp);
2527+
2528+
std::vector<VariantMember> &members();
2529+
2530+
DiscriminantValue &discriminant();
2531+
2532+
private:
2533+
std::vector<VariantMember> _members;
2534+
DiscriminantValue _discriminant;
2535+
};
2536+
25042537
} // namespace
25052538

2539+
ConstString VariantMember::GetName() const { return this->variant_name; }
2540+
2541+
bool VariantMember::IsDefault() const { return !discr_value; }
2542+
2543+
VariantMember::VariantMember(DWARFDIE &die, lldb::ModuleSP module_sp) {
2544+
assert(die.Tag() == llvm::dwarf::DW_TAG_variant);
2545+
this->discr_value =
2546+
die.GetAttributeValueAsOptionalUnsigned(DW_AT_discr_value);
2547+
2548+
for (auto child_die : die.children()) {
2549+
switch (child_die.Tag()) {
2550+
case llvm::dwarf::DW_TAG_member: {
2551+
DWARFAttributes attributes = child_die.GetAttributes();
2552+
for (std::size_t i = 0; i < attributes.Size(); ++i) {
2553+
DWARFFormValue form_value;
2554+
const dw_attr_t attr = attributes.AttributeAtIndex(i);
2555+
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
2556+
switch (attr) {
2557+
case DW_AT_name:
2558+
variant_name = ConstString(form_value.AsCString());
2559+
break;
2560+
case DW_AT_type:
2561+
type_ref = form_value;
2562+
break;
2563+
2564+
case DW_AT_data_member_location:
2565+
if (form_value.BlockData()) {
2566+
Value initialValue(0);
2567+
Value memberOffset(0);
2568+
const DWARFDataExtractor &debug_info_data = die.GetData();
2569+
uint32_t block_length = form_value.Unsigned();
2570+
uint32_t block_offset =
2571+
form_value.BlockData() - debug_info_data.GetDataStart();
2572+
if (DWARFExpression::Evaluate(
2573+
nullptr, // ExecutionContext *
2574+
nullptr, // RegisterContext *
2575+
module_sp,
2576+
DataExtractor(debug_info_data, block_offset,
2577+
block_length),
2578+
die.GetCU(), eRegisterKindDWARF, &initialValue, nullptr,
2579+
memberOffset, nullptr)) {
2580+
byte_offset = memberOffset.ResolveValue(nullptr).UInt();
2581+
}
2582+
} else {
2583+
// With DWARF 3 and later, if the value is an integer constant,
2584+
// this form value is the offset in bytes from the beginning of
2585+
// the containing entity.
2586+
byte_offset = form_value.Unsigned();
2587+
}
2588+
break;
2589+
2590+
default:
2591+
break;
2592+
}
2593+
}
2594+
}
2595+
break;
2596+
}
2597+
default:
2598+
break;
2599+
}
2600+
break;
2601+
}
2602+
}
2603+
2604+
DiscriminantValue::DiscriminantValue(const DWARFDIE &die, ModuleSP module_sp) {
2605+
auto referenced_die = die.GetReferencedDIE(DW_AT_discr);
2606+
DWARFAttributes attributes = referenced_die.GetAttributes();
2607+
for (std::size_t i = 0; i < attributes.Size(); ++i) {
2608+
const dw_attr_t attr = attributes.AttributeAtIndex(i);
2609+
DWARFFormValue form_value;
2610+
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
2611+
switch (attr) {
2612+
case DW_AT_type:
2613+
type_ref = form_value;
2614+
break;
2615+
case DW_AT_data_member_location:
2616+
if (form_value.BlockData()) {
2617+
Value initialValue(0);
2618+
Value memberOffset(0);
2619+
const DWARFDataExtractor &debug_info_data = die.GetData();
2620+
uint32_t block_length = form_value.Unsigned();
2621+
uint32_t block_offset =
2622+
form_value.BlockData() - debug_info_data.GetDataStart();
2623+
if (DWARFExpression::Evaluate(
2624+
nullptr, // ExecutionContext *
2625+
nullptr, // RegisterContext *
2626+
module_sp,
2627+
DataExtractor(debug_info_data, block_offset, block_length),
2628+
die.GetCU(), eRegisterKindDWARF, &initialValue, nullptr,
2629+
memberOffset, nullptr)) {
2630+
byte_offset = memberOffset.ResolveValue(nullptr).UInt();
2631+
}
2632+
} else {
2633+
// With DWARF 3 and later, if the value is an integer constant,
2634+
// this form value is the offset in bytes from the beginning of
2635+
// the containing entity.
2636+
byte_offset = form_value.Unsigned();
2637+
}
2638+
break;
2639+
default:
2640+
break;
2641+
}
2642+
}
2643+
}
2644+
}
2645+
2646+
VariantPart::VariantPart(const DWARFDIE &die, const DWARFDIE &parent_die,
2647+
lldb::ModuleSP module_sp)
2648+
: _members(), _discriminant(die, module_sp) {
2649+
2650+
for (auto child : die.children()) {
2651+
if (child.Tag() == llvm::dwarf::DW_TAG_variant) {
2652+
_members.push_back(VariantMember(child, module_sp));
2653+
}
2654+
}
2655+
}
2656+
2657+
std::vector<VariantMember> &VariantPart::members() { return this->_members; }
2658+
2659+
DiscriminantValue &VariantPart::discriminant() { return this->_discriminant; }
2660+
25062661
MemberAttributes::MemberAttributes(const DWARFDIE &die,
25072662
const DWARFDIE &parent_die,
25082663
ModuleSP module_sp) {
@@ -3037,6 +3192,13 @@ bool DWARFASTParserClang::ParseChildMembers(
30373192
ParseObjCProperty(die, parent_die, class_clang_type, delayed_properties);
30383193
break;
30393194

3195+
case DW_TAG_variant_part:
3196+
if (die.GetCU()->GetDWARFLanguageType() == eLanguageTypeRust) {
3197+
ParseRustVariantPart(die, parent_die, class_clang_type,
3198+
default_accessibility, layout_info);
3199+
}
3200+
break;
3201+
30403202
case DW_TAG_member:
30413203
ParseSingleMember(die, parent_die, class_clang_type,
30423204
default_accessibility, layout_info, last_field_info);
@@ -3721,3 +3883,76 @@ bool DWARFASTParserClang::ShouldCreateUnnamedBitfield(
37213883

37223884
return true;
37233885
}
3886+
3887+
void DWARFASTParserClang::ParseRustVariantPart(
3888+
DWARFDIE &die, const DWARFDIE &parent_die, CompilerType &class_clang_type,
3889+
const lldb::AccessType default_accesibility,
3890+
ClangASTImporter::LayoutInfo &layout_info) {
3891+
assert(die.Tag() == llvm::dwarf::DW_TAG_variant_part);
3892+
assert(SymbolFileDWARF::GetLanguage(*die.GetCU()) ==
3893+
LanguageType::eLanguageTypeRust);
3894+
3895+
ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
3896+
3897+
VariantPart variants(die, parent_die, module_sp);
3898+
3899+
auto discriminant_type =
3900+
die.ResolveTypeUID(variants.discriminant().type_ref.Reference());
3901+
3902+
auto decl_context = m_ast.GetDeclContextForType(class_clang_type);
3903+
3904+
auto inner_holder = m_ast.CreateRecordType(
3905+
decl_context, OptionalClangModuleID(), lldb::eAccessPublic,
3906+
std::string(
3907+
llvm::formatv("{0}$Inner", class_clang_type.GetTypeName(false))),
3908+
clang::TTK_Union, lldb::eLanguageTypeRust);
3909+
m_ast.StartTagDeclarationDefinition(inner_holder);
3910+
m_ast.SetIsPacked(inner_holder);
3911+
3912+
for (auto member : variants.members()) {
3913+
3914+
auto has_discriminant = !member.IsDefault();
3915+
3916+
auto member_type = die.ResolveTypeUID(member.type_ref.Reference());
3917+
3918+
auto field_type = m_ast.CreateRecordType(
3919+
m_ast.GetDeclContextForType(inner_holder), OptionalClangModuleID(),
3920+
lldb::eAccessPublic,
3921+
std::string(llvm::formatv("{0}$Variant", member.GetName())),
3922+
clang::TTK_Struct, lldb::eLanguageTypeRust);
3923+
3924+
m_ast.StartTagDeclarationDefinition(field_type);
3925+
auto offset = member.byte_offset;
3926+
3927+
if (has_discriminant) {
3928+
m_ast.AddFieldToRecordType(
3929+
field_type, "$discr$", discriminant_type->GetFullCompilerType(),
3930+
lldb::eAccessPublic, variants.discriminant().byte_offset);
3931+
offset += discriminant_type->GetByteSize(nullptr).value_or(0);
3932+
}
3933+
3934+
m_ast.AddFieldToRecordType(field_type, "value",
3935+
member_type->GetFullCompilerType(),
3936+
lldb::eAccessPublic, offset * 8);
3937+
3938+
m_ast.CompleteTagDeclarationDefinition(field_type);
3939+
3940+
auto name = has_discriminant
3941+
? llvm::formatv("$variant${0}", member.discr_value.value())
3942+
: std::string("$variant$");
3943+
3944+
auto variant_decl =
3945+
m_ast.AddFieldToRecordType(inner_holder, llvm::StringRef(name),
3946+
field_type, default_accesibility, 0);
3947+
3948+
layout_info.field_offsets.insert({variant_decl, 0});
3949+
}
3950+
3951+
auto inner_field = m_ast.AddFieldToRecordType(class_clang_type,
3952+
llvm::StringRef("$variants$"),
3953+
inner_holder, eAccessPublic, 0);
3954+
3955+
m_ast.CompleteTagDeclarationDefinition(inner_holder);
3956+
3957+
layout_info.field_offsets.insert({inner_field, 0});
3958+
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,21 @@ class DWARFASTParserClang : public DWARFASTParser {
319319
const lldb::ModuleSP &module_sp,
320320
std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
321321
lldb_private::ClangASTImporter::LayoutInfo &layout_info);
322+
323+
/// Parses DW_TAG_variant_part DIE into a structure that encodes all variants
324+
/// Note that this is currently being emitted by rustc and not Clang
325+
/// \param die DW_TAG_variant_part DIE to parse
326+
/// \param parent_die The parent DW_TAG_structure_type to parse
327+
/// \param class_clang_type The Rust struct representing parent_die.
328+
/// \param default_accesibility The default accessibility that is given to
329+
/// base classes if they don't have an explicit accessibility set
330+
/// \param layout_info The layout information that will be updated for
331+
// base classes with the base offset
332+
void
333+
ParseRustVariantPart(DWARFDIE &die, const DWARFDIE &parent_die,
334+
lldb_private::CompilerType &class_clang_type,
335+
const lldb::AccessType default_accesibility,
336+
lldb_private::ClangASTImporter::LayoutInfo &layout_info);
322337
};
323338

324339
/// Parsed form of all attributes that are relevant for type reconstruction.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Helper library to traverse data emitted for Rust enums """
2+
from lldbsuite.test.lldbtest import *
3+
4+
DISCRIMINANT_MEMBER_NAME = "$discr$"
5+
VALUE_MEMBER_NAME = "value"
6+
7+
class RustEnumValue:
8+
9+
def __init__(self, value: lldb.SBValue):
10+
self.value = value
11+
12+
def getAllVariantTypes(self):
13+
result = []
14+
for i in range(self._inner().GetNumChildren()):
15+
result.append(self.getVariantByIndex(i).GetDisplayTypeName())
16+
return result
17+
18+
def _inner(self) -> lldb.SBValue:
19+
return self.value.GetChildAtIndex(0)
20+
21+
def getVariantByIndex(self, index):
22+
return self._inner().GetChildAtIndex(index).GetChildMemberWithName(VALUE_MEMBER_NAME)
23+
24+
@staticmethod
25+
def _getDiscriminantValueAsUnsigned(discr_sbvalue: lldb.SBValue):
26+
byte_size = discr_sbvalue.GetType().GetByteSize()
27+
error = lldb.SBError()
28+
29+
# when discriminant is u16 Clang emits 'unsigned char'
30+
# and LLDB seems to treat it as character type disalowing to call GetValueAsUnsigned
31+
if byte_size == 1:
32+
return discr_sbvalue.GetData().GetUnsignedInt8(error, 0)
33+
elif byte_size == 2:
34+
return discr_sbvalue.GetData().GetUnsignedInt16(error, 0)
35+
elif byte_size == 4:
36+
return discr_sbvalue.GetData().GetUnsignedInt32(error, 0)
37+
elif byte_size == 8:
38+
return discr_sbvalue.GetData().GetUnsignedInt64(error, 0)
39+
else:
40+
return discr_sbvalue.GetValueAsUnsigned()
41+
42+
def getCurrentVariantIndex(self):
43+
default_index = 0
44+
for i in range(self._inner().GetNumChildren()):
45+
variant: lldb.SBValue = self._inner().GetChildAtIndex(i);
46+
discr = variant.GetChildMemberWithName(DISCRIMINANT_MEMBER_NAME)
47+
if discr.IsValid():
48+
discr_unsigned_value = RustEnumValue._getDiscriminantValueAsUnsigned(discr)
49+
if variant.GetName() == f"$variant${discr_unsigned_value}":
50+
return discr_unsigned_value
51+
else:
52+
default_index = i
53+
return default_index
54+
55+
def getFields(self):
56+
result = []
57+
for i in range(self._inner().GetNumChildren()):
58+
type: lldb.SBType = self._inner().GetType()
59+
result.append(type.GetFieldAtIndex(i).GetName())
60+
return result
61+
62+
def getCurrentValue(self) -> lldb.SBValue:
63+
return self.getVariantByIndex(self.getCurrentVariantIndex())

0 commit comments

Comments
 (0)