Skip to content

Commit

Permalink
Implement lldb formattter for "clang encoded" enums (LLDB 18.1+)
Browse files Browse the repository at this point in the history
Summary:
I landed a fix last year to enable `DW_TAG_variant_part` encoding in LLDBs (https://reviews.llvm.org/D149213). This PR is a corresponding fix in synthetic formatters to decode that information.
This is in no way perfect implementation but at least it improves the status quo. But most types of enums will be visible and debuggable in some way.
I've also updated most of the existing tests that touch enums and re-enabled test cases based on LLDB for enums.

Test Plan:
ran tests `./x test tests/debuginfo/`. Also tested manually in LLDB CLI and LLDB VSCode

Other Thoughs
A better approach would probably be adopting [formatters from codelldb](https://github.com/vadimcn/codelldb/blob/master/formatters/rust.py). There is some neat hack that hooks up summary provider via synthetic provider which can ultimately fix more display issues for Rust types and enums too. But getting it to work well might take more time that I have right now.
  • Loading branch information
Vladimir Makayev committed May 4, 2024
1 parent 7f2fc33 commit 43e6600
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 24 deletions.
3 changes: 2 additions & 1 deletion src/etc/lldb_lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ def synthetic_lookup(valobj, dict):
return synthetic_lookup(valobj.GetChildAtIndex(discriminant), dict)
if rust_type == RustType.SINGLETON_ENUM:
return synthetic_lookup(valobj.GetChildAtIndex(0), dict)

if rust_type == RustType.ENUM:
return ClangEncodedEnumProvider(valobj, dict)
if rust_type == RustType.STD_VEC:
return StdVecSyntheticProvider(valobj, dict)
if rust_type == RustType.STD_VEC_DEQUE:
Expand Down
52 changes: 52 additions & 0 deletions src/etc/lldb_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,58 @@ def has_children(self):
# type: () -> bool
return True

class ClangEncodedEnumProvider:
"""Pretty-printer for 'clang-encoded' enums support implemented in LLDB"""
DISCRIMINANT_MEMBER_NAME = "$discr$"
VALUE_MEMBER_NAME = "value"

def __init__(self, valobj, dict):
self.valobj = valobj
self.update()

def has_children(self):
return True

def num_children(self):
if self.is_default:
return 1
return 2

def get_child_index(self, name):
if name == ClangEncodedEnumProvider.VALUE_MEMBER_NAME:
return 0
if name == ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME:
return 1
return -1

def get_child_at_index(self, index):
if index == 0:
return self.variant.GetChildMemberWithName(ClangEncodedEnumProvider.VALUE_MEMBER_NAME)
if index == 1:
return self.variant.GetChildMemberWithName(
ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME)


def update(self):
all_variants = self.valobj.GetChildAtIndex(0)
index = self._getCurrentVariantIndex(all_variants)
self.variant = all_variants.GetChildAtIndex(index)
self.is_default = self.variant.GetIndexOfChildWithName(
ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME) == -1

def _getCurrentVariantIndex(self, all_variants):
default_index = 0
for i in range(all_variants.GetNumChildren()):
variant = all_variants.GetChildAtIndex(i)
discr = variant.GetChildMemberWithName(
ClangEncodedEnumProvider.DISCRIMINANT_MEMBER_NAME)
if discr.IsValid():
discr_unsigned_value = discr.GetValueAsUnsigned()
if variant.GetName() == f"$variant${discr_unsigned_value}":
return i
else:
default_index = i
return default_index

class TupleSyntheticProvider:
"""Pretty-printer for tuples and tuple enum variants"""
Expand Down
7 changes: 6 additions & 1 deletion src/etc/rust_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class RustType(object):

ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$"
ENUM_DISR_FIELD_NAME = "<<variant>>"
ENUM_LLDB_ENCODED_VARIANTS = "$variants$"

STD_TYPE_TO_REGEX = {
RustType.STD_STRING: STD_STRING_REGEX,
Expand Down Expand Up @@ -96,7 +97,11 @@ def classify_struct(name, fields):
if regex.match(name):
return ty

if fields[0].name == ENUM_DISR_FIELD_NAME:
# <<variant>> is emitted by GDB while LLDB(18.1+) emits "$variants$"
if (
fields[0].name == ENUM_DISR_FIELD_NAME
or fields[0].name == ENUM_LLDB_ENCODED_VARIANTS
):
return RustType.ENUM

if is_tuple_fields(fields):
Expand Down
5 changes: 4 additions & 1 deletion tests/debuginfo/borrowed-enum.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Require a gdb or lldb that can read DW_TAG_variant_part.
//@ min-gdb-version: 8.2
//@ needs-rust-lldb
//@ min-lldb-version: 1800

//@ compile-flags:-g

Expand All @@ -23,10 +23,13 @@
// lldb-command:run

// lldb-command:v *the_a_ref
// lldbg-check:(borrowed_enum::ABC) *the_a_ref = { value = { x = 0 y = 8970181431921507452 } $discr$ = 0 }
// lldbr-check:(borrowed_enum::ABC::TheA) *the_a_ref = TheA { TheA: 0, TheB: 8970181431921507452 }
// lldb-command:v *the_b_ref
// lldbg-check:(borrowed_enum::ABC) *the_b_ref = { value = { 0 = 0 1 = 286331153 2 = 286331153 } $discr$ = 1 }
// lldbr-check:(borrowed_enum::ABC::TheB) *the_b_ref = { = 0 = 286331153 = 286331153 }
// lldb-command:v *univariant_ref
// lldbg-check:(borrowed_enum::Univariant) *univariant_ref = { value = { 0 = 4820353753753434 } }
// lldbr-check:(borrowed_enum::Univariant) *univariant_ref = { TheOnlyCase = { = 4820353753753434 } }

#![allow(unused_variables)]
Expand Down
16 changes: 4 additions & 12 deletions tests/debuginfo/coroutine-objects.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Require a gdb that can read DW_TAG_variant_part.
//@ min-gdb-version: 8.2
//@ min-lldb-version: 1800

// LLDB without native Rust support cannot read DW_TAG_variant_part,
// so it prints nothing for coroutines. But those tests are kept to
// LLDB (18.1+) now supports DW_TAG_variant_part, but there is some bug in either compiler or LLDB
// with memory layout of discriminant for this particular enum
// ensure that LLDB won't crash at least (like #57822).

//@ compile-flags:-g
Expand All @@ -26,16 +27,7 @@

// lldb-command:run
// lldb-command:v b
// lldbg-check:(coroutine_objects::main::{coroutine_env#0}) b =
// lldb-command:continue
// lldb-command:v b
// lldbg-check:(coroutine_objects::main::{coroutine_env#0}) b =
// lldb-command:continue
// lldb-command:v b
// lldbg-check:(coroutine_objects::main::{coroutine_env#0}) b =
// lldb-command:continue
// lldb-command:v b
// lldbg-check:(coroutine_objects::main::{coroutine_env#0}) b =
// lldb-check:(coroutine_objects::main::{coroutine_env#0}) b = { value = { _ref__a = 0x[...] } $discr$ = ',' }

// === CDB TESTS ===================================================================================

Expand Down
4 changes: 2 additions & 2 deletions tests/debuginfo/enum-thinlto.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Require a gdb that can read DW_TAG_variant_part.
//@ min-gdb-version: 8.2

//@ min-lldb-version: 1800
//@ compile-flags:-g -Z thinlto

// === GDB TESTS ===================================================================================
Expand All @@ -15,7 +15,7 @@
// lldb-command:run

// lldb-command:v *abc
// lldbg-check:(enum_thinlto::ABC) *abc =
// lldbg-check:(enum_thinlto::ABC) *abc = { value = { x = 0 y = 8970181431921507452 } $discr$ = 0 }
// lldbr-check:(enum_thinlto::ABC) *abc = (x = 0, y = 8970181431921507452)

#![allow(unused_variables)]
Expand Down
4 changes: 2 additions & 2 deletions tests/debuginfo/issue-57822.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// Require a gdb that can read DW_TAG_variant_part.
//@ min-gdb-version: 8.2

//@ min-lldb-version: 1800
//@ compile-flags:-g

// === GDB TESTS ===================================================================================
Expand All @@ -24,7 +24,7 @@
// lldbg-check:(issue_57822::main::{closure_env#1}) g = { f = { x = 1 } }

// lldb-command:v b
// lldbg-check:(issue_57822::main::{coroutine_env#3}) b =
// lldbg-check:(issue_57822::main::{coroutine_env#3}) b = { value = { a = { value = { y = 2 } $discr$ = '\x02' } } $discr$ = '\x02' }

#![feature(omit_gdb_pretty_printer_section, coroutines, coroutine_trait)]
#![omit_gdb_pretty_printer_section]
Expand Down
81 changes: 80 additions & 1 deletion tests/debuginfo/msvc-pretty-enums.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,84 @@
//@ only-cdb
//@ min-lldb-version: 1800
//@ ignore-gdb
//@ compile-flags:-g

// === LLDB TESTS ==================================================================================

// lldb-command:run
// lldb-command:v a
// lldbg-check:(core::option::Option<msvc_pretty_enums::CStyleEnum>) a = { value = { 0 = Low } }

// lldb-command:v b
// lldbg-check:(core::option::Option<msvc_pretty_enums::CStyleEnum>) b = { value = $discr$ = '\x01' }

// lldb-command:v c
// lldbg-check:(msvc_pretty_enums::NicheLayoutEnum) c = { value = $discr$ = '\x11' }

// lldb-command:v d
// lldbg-check:(msvc_pretty_enums::NicheLayoutEnum) d = { value = { my_data = High } }

// lldb-command:v e
// lldbg-check:(msvc_pretty_enums::NicheLayoutEnum) e = { value = $discr$ = '\x13' }

// lldb-command:v h
// lldbg-check:(core::option::Option<u32>) h = { value = { 0 = 12 } $discr$ = 1 }

// lldb-command:v i
// lldbg-check:(core::option::Option<u32>) i = { value = $discr$ = 0 }

// lldb-command:v j
// lldbg-check:(msvc_pretty_enums::CStyleEnum) j = High

// lldb-command:v k
// lldbg-check:(core::option::Option<alloc::string::String>) k = { value = { 0 = "IAMA optional string!" { vec = size=21 { [0] = 'I' [1] = 'A' [2] = 'M' [3] = 'A' [4] = ' ' [5] = 'o' [6] = 'p' [7] = 't' [8] = 'i' [9] = 'o' [10] = 'n' [11] = 'a' [12] = 'l' [13] = ' ' [14] = 's' [15] = 't' [16] = 'r' [17] = 'i' [18] = 'n' [19] = 'g' [20] = '!' } } } }

// lldb-command:v l
// lldbg-check:(core::result::Result<u32, msvc_pretty_enums::Empty>) l = { value = { 0 = {} } }

// lldb-command:v niche128_some
// lldbg-check:(core::option::Option<core::num::nonzero::NonZero<i128>>) niche128_some = { value = $discr$ = 123456 }

// lldb-command:v niche128_none
// lldbg-check:(core::option::Option<core::num::nonzero::NonZero<i128>>) niche128_none = { value = $discr$ = 0 }

// lldb-command:v wrapping_niche128_untagged
// lldbg-check:(msvc_pretty_enums::Wrapping128Niche) wrapping_niche128_untagged = { value = { 0 = { 0 = 340282366920938463463374607431768211454 } } }

// lldb-command:v wrapping_niche128_none1
// lldbg-check:(msvc_pretty_enums::Wrapping128Niche) wrapping_niche128_none1 = { value = { 0 = { 0 = 2 } } }

// lldb-command:v direct_tag_128_a
// lldbg-check:(msvc_pretty_enums::DirectTag128) direct_tag_128_a = { value = { 0 = 42 } $discr$ = 0 }

// lldb-command:v direct_tag_128_b
// lldbg-check:(msvc_pretty_enums::DirectTag128) direct_tag_128_b = { value = { 0 = 137 } $discr$ = 1 }

// &u32 is incorrectly formatted and LLDB thinks it's a char* so skipping niche_w_fields_1_some

// lldb-command:v niche_w_fields_1_none
// lldbg-check:(msvc_pretty_enums::NicheLayoutWithFields1) niche_w_fields_1_none = { value = { 0 = 99 } $discr$ = 1 }

// lldb-command:v niche_w_fields_2_some
// lldbg-check:(msvc_pretty_enums::NicheLayoutWithFields2) niche_w_fields_2_some = { value = { 0 = 800 { __0 = { 0 = 800 } } 1 = 900 } $discr$ = 0 }

// lldb-command:v niche_w_fields_3_some
// lldbg-check:(msvc_pretty_enums::NicheLayoutWithFields3) niche_w_fields_3_some = { value = { 0 = '\x89' 1 = true } }

// lldb-command:v niche_w_fields_3_niche3
// lldbg-check:(msvc_pretty_enums::NicheLayoutWithFields3) niche_w_fields_3_niche3 = { value = { 0 = '"' } $discr$ = '\x04' }

// lldb-command:v arbitrary_discr1
// lldbg-check:(msvc_pretty_enums::ArbitraryDiscr) arbitrary_discr1 = { value = { 0 = 1234 } $discr$ = 1000 }

// lldb-command:v arbitrary_discr2
// lldbg-check:(msvc_pretty_enums::ArbitraryDiscr) arbitrary_discr2 = { value = { 0 = 5678 } $discr$ = 5000000 }

// === CDB TESTS ==================================================================================
// cdb-command: g
//
// cdb-command: dx a
// cdb-check:a : Some [Type: enum2$<core::option::Option<msvc_pretty_enums::CStyleEnum> >]
// cdb-check: [+0x000] __0 : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum]
//
// cdb-command: g
//
Expand Down
7 changes: 5 additions & 2 deletions tests/debuginfo/struct-style-enum.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Require a gdb or lldb that can read DW_TAG_variant_part.
//@ min-gdb-version: 8.2
//@ needs-rust-lldb

//@ min-lldb-version: 1800
//@ compile-flags:-g

// === GDB TESTS ===================================================================================
Expand All @@ -27,15 +26,19 @@
// lldb-command:run

// lldb-command:v case1
// lldbg-check:(struct_style_enum::Regular) case1 = { value = { a = 0 b = 31868 c = 31868 d = 31868 e = 31868 } $discr$ = 0 }
// lldbr-check:(struct_style_enum::Regular::Case1) case1 = { a = 0 b = 31868 c = 31868 d = 31868 e = 31868 }

// lldb-command:v case2
// lldbg-check:(struct_style_enum::Regular) case2 = { value = { a = 0 b = 286331153 c = 286331153 } $discr$ = 1 }
// lldbr-check:(struct_style_enum::Regular::Case2) case2 = Case2 { Case1: 0, Case2: 286331153, Case3: 286331153 }

// lldb-command:v case3
// lldbg-check:(struct_style_enum::Regular) case3 = { value = { a = 0 b = 6438275382588823897 } $discr$ = 2 }
// lldbr-check:(struct_style_enum::Regular::Case3) case3 = Case3 { Case1: 0, Case2: 6438275382588823897 }

// lldb-command:v univariant
// lldbg-check:(struct_style_enum::Univariant) univariant = { value = { a = -1 } }
// lldbr-check:(struct_style_enum::Univariant) univariant = Univariant { TheOnlyCase: TheOnlyCase { a: -1 } }

#![allow(unused_variables)]
Expand Down
6 changes: 5 additions & 1 deletion tests/debuginfo/tuple-style-enum.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Require a gdb or lldb that can read DW_TAG_variant_part.
//@ min-gdb-version: 8.2
//@ needs-rust-lldb
//@ min-lldb-version: 1800

//@ compile-flags:-g

Expand All @@ -27,15 +27,19 @@
// lldb-command:run

// lldb-command:v case1
// lldbg-check:(tuple_style_enum::Regular) case1 = { value = { 0 = 0 1 = 31868 2 = 31868 3 = 31868 4 = 31868 } $discr$ = 0 }
// lldbr-check:(tuple_style_enum::Regular::Case1) case1 = { = 0 = 31868 = 31868 = 31868 = 31868 }

// lldb-command:v case2
// lldbg-check:(tuple_style_enum::Regular) case2 = { value = { 0 = 0 1 = 286331153 2 = 286331153 } $discr$ = 1 }
// lldbr-check:(tuple_style_enum::Regular::Case2) case2 = Case2 { Case1: 0, Case2: 286331153, Case3: 286331153 }

// lldb-command:v case3
// lldbg-check:(tuple_style_enum::Regular) case3 = { value = { 0 = 0 1 = 6438275382588823897 } $discr$ = 2 }
// lldbr-check:(tuple_style_enum::Regular::Case3) case3 = Case3 { Case1: 0, Case2: 6438275382588823897 }

// lldb-command:v univariant
// lldbg-check:(tuple_style_enum::Univariant) univariant = { value = { 0 = -1 } }
// lldbr-check:(tuple_style_enum::Univariant) univariant = { TheOnlyCase = { = -1 } }

#![allow(unused_variables)]
Expand Down
5 changes: 4 additions & 1 deletion tests/debuginfo/unique-enum.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Require a gdb or lldb that can read DW_TAG_variant_part.
//@ min-gdb-version: 8.2
//@ needs-rust-lldb
//@ min-lldb-version: 1800

//@ compile-flags:-g

Expand All @@ -23,12 +23,15 @@
// lldb-command:run

// lldb-command:v *the_a
// lldbg-check:(unique_enum::ABC) *the_a = { value = { x = 0 y = 8970181431921507452 } $discr$ = 0 }
// lldbr-check:(unique_enum::ABC::TheA) *the_a = TheA { TheA: 0, TheB: 8970181431921507452 }

// lldb-command:v *the_b
// lldbg-check:(unique_enum::ABC) *the_b = { value = { 0 = 0 1 = 286331153 2 = 286331153 } $discr$ = 1 }
// lldbr-check:(unique_enum::ABC::TheB) *the_b = { = 0 = 286331153 = 286331153 }

// lldb-command:v *univariant
// lldbg-check:(unique_enum::Univariant) *univariant = { value = { 0 = 123234 } }
// lldbr-check:(unique_enum::Univariant) *univariant = { TheOnlyCase = { = 123234 } }

#![allow(unused_variables)]
Expand Down

0 comments on commit 43e6600

Please sign in to comment.