Skip to content

Commit e84751a

Browse files
[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
1 parent 52c62d4 commit e84751a

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
@@ -2496,8 +2496,163 @@ struct PropertyAttributes {
24962496
/// \see clang::ObjCPropertyAttribute
24972497
uint32_t prop_attributes = 0;
24982498
};
2499+
2500+
struct DiscriminantValue {
2501+
explicit DiscriminantValue(const DWARFDIE &die, ModuleSP module_sp);
2502+
2503+
uint32_t byte_offset;
2504+
uint32_t byte_size;
2505+
DWARFFormValue type_ref;
2506+
};
2507+
2508+
struct VariantMember {
2509+
explicit VariantMember(DWARFDIE &die, ModuleSP module_sp);
2510+
bool IsDefault() const;
2511+
2512+
std::optional<u_int32_t> discr_value;
2513+
DWARFFormValue type_ref;
2514+
ConstString variant_name;
2515+
uint32_t byte_offset;
2516+
ConstString GetName() const;
2517+
};
2518+
2519+
struct VariantPart {
2520+
explicit VariantPart(const DWARFDIE &die, const DWARFDIE &parent_die,
2521+
ModuleSP module_sp);
2522+
2523+
std::vector<VariantMember> &members();
2524+
2525+
DiscriminantValue &discriminant();
2526+
2527+
private:
2528+
std::vector<VariantMember> _members;
2529+
DiscriminantValue _discriminant;
2530+
};
2531+
24992532
} // namespace
25002533

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

3179+
case DW_TAG_variant_part:
3180+
if (die.GetCU()->GetDWARFLanguageType() == eLanguageTypeRust) {
3181+
ParseRustVariantPart(die, parent_die, class_clang_type,
3182+
default_accessibility, layout_info);
3183+
}
3184+
break;
3185+
30243186
case DW_TAG_member:
30253187
ParseSingleMember(die, parent_die, class_clang_type,
30263188
default_accessibility, layout_info, last_field_info);
@@ -3728,3 +3890,76 @@ bool DWARFASTParserClang::ShouldCreateUnnamedBitfield(
37283890

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

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

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

322337
/// 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)