Message ID | Y+atpJV5rqo08dQJ@kernel.org (mailing list archive) |
---|---|
State | Not Applicable |
Delegated to: | BPF |
Headers | show |
Series | [1/1] pahole/Rust: Check that we're adding DW_TAG_member sorted by byte offset | expand |
Em Fri, Feb 10, 2023 at 05:48:36PM -0300, Arnaldo Carvalho de Melo escreveu: > I'll go thru the others to see if they are easy (or at least restricted > to Rust CUs) as this one. The namespace.o seems to be ok: ⬢[acme@toolbox pahole]$ cat ../pahole-rust-cases/namespace.rs pub struct S { pub a: i32, } pub static S: (i32, S) = (42, S { a: 42 }); ⬢[acme@toolbox pahole]$ ⬢[acme@toolbox pahole]$ pahole --show_private_classes ../pahole-rust-cases/namespace.o struct S { i32 a __attribute__((__aligned__(4))); /* 0 4 */ /* size: 4, cachelines: 1, members: 1 */ /* forced alignments: 1 */ /* last cacheline: 4 bytes */ } __attribute__((__aligned__(4))); struct (i32, namespace::S) { i32 __0 __attribute__((__aligned__(4))); /* 0 4 */ struct S __1 __attribute__((__aligned__(4))); /* 4 4 */ /* size: 8, cachelines: 1, members: 2 */ /* forced alignments: 2 */ /* last cacheline: 8 bytes */ } __attribute__((__aligned__(4))); ⬢[acme@toolbox pahole]$ And encoding/decoding BTF for it: ⬢[acme@toolbox pahole]$ cp ../pahole-rust-cases/namespace.o . ⬢[acme@toolbox pahole]$ pahole --btf_encode namespace.o ⬢[acme@toolbox pahole]$ pahole -F btf namespace.o struct S { i32 a; /* 0 4 */ /* size: 4, cachelines: 1, members: 1 */ /* last cacheline: 4 bytes */ }; struct (i32, namespace::S) { i32 __0; /* 0 4 */ struct S __1; /* 4 4 */ /* size: 8, cachelines: 1, members: 2 */ /* last cacheline: 8 bytes */ }; ⬢[acme@toolbox pahole]$ readelf -SW namespace.o | grep BTF [18] .BTF PROGBITS 0000000000000000 00065c 000089 00 0 0 1 ⬢[acme@toolbox pahole]$ The core one needs work: ⬢[acme@toolbox pahole]$ pahole ../pahole-rust-cases/core.o |& head die__process_class: tag not supported 0x2f (template_type_parameter)! die__process_class: tag not supported 0x33 (variant_part)! die__create_new_enumeration: DW_TAG_subprogram (0x2e) @ <0x2fd8d> not handled! die__create_new_enumeration: DW_TAG_subprogram (0x2e) @ <0x2fdf7> not handled! die__create_new_enumeration: DW_TAG_subprogram (0x2e) @ <0x2fe61> not handled! die__create_new_enumeration: DW_TAG_subprogram (0x2e) @ <0x2fecb> not handled! die__create_new_enumeration: DW_TAG_subprogram (0x2e) @ <0x2ff35> not handled! die__create_new_enumeration: DW_TAG_subprogram (0x2e) @ <0x2ff9f> not handled! die__create_new_enumeration: DW_TAG_subprogram (0x2e) @ <0x30009> not handled! die__create_new_enumeration: DW_TAG_subprogram (0x2e) @ <0x30073> not handled! ⬢[acme@toolbox pahole]$ <1><90>: Abbrev Number: 7 (DW_TAG_namespace) <91> DW_AT_name : (indirect string, offset: 0x147): core <2><95>: Abbrev Number: 7 (DW_TAG_namespace) <96> DW_AT_name : (indirect string, offset: 0x14c): str <3><9a>: Abbrev Number: 7 (DW_TAG_namespace) <9b> DW_AT_name : (indirect string, offset: 0x150): iter <4><9f>: Abbrev Number: 8 (DW_TAG_structure_type) <a0> DW_AT_name : (indirect string, offset: 0x2e1): Split<core::str::IsWhitespace> <a4> DW_AT_byte_size : 64 <a5> DW_AT_alignment : 8 <5><a6>: Abbrev Number: 9 (DW_TAG_template_type_param) <a7> DW_AT_type : <0x41fc> <ab> DW_AT_name : (indirect string, offset: 0x162): P <5><af>: Abbrev Number: 4 (DW_TAG_member) <b0> DW_AT_name : (indirect string, offset: 0x164): __0 <b4> DW_AT_type : <0xbb> <b8> DW_AT_alignment : 8 <b9> DW_AT_data_member_location: 0 <5><ba>: Abbrev Number: 0 <4><bb>: Abbrev Number: 8 (DW_TAG_structure_type) <bc> DW_AT_name : (indirect string, offset: 0x2ba): SplitInternal<core::str::IsWhitespace> <c0> DW_AT_byte_size : 64 <c1> DW_AT_alignment : 8 <5><c2>: Abbrev Number: 9 (DW_TAG_template_type_param) <c3> DW_AT_type : <0x41fc> <c7> DW_AT_name : (indirect string, offset: 0x162): P - Arnaldo
Hi Arnaldo, On Mon, Feb 13, 2023 at 1:09 PM Arnaldo Carvalho de Melo <arnaldo.melo@gmail.com> wrote: > > The namespace.o seems to be ok: I saw the other message too -- this looks great, thanks a ton. > The core one needs work: If `core.o` works, then I think it is likely other things will work :) I can try to extract the cases for those into simpler `.o` files, if you would find simpler test cases useful (perhaps for the test suite etc.). Cheers, Miguel
On Mon, 13 Feb 2023 at 12:45, Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote: > > Hi Arnaldo, > > On Mon, Feb 13, 2023 at 1:09 PM Arnaldo Carvalho de Melo > <arnaldo.melo@gmail.com> wrote: > > > > The namespace.o seems to be ok: > > I saw the other message too -- this looks great, thanks a ton. > > > The core one needs work: > > If `core.o` works, then I think it is likely other things will work :) Hi Guys, I'll leave this to the experts, but if we get this to the point where we are happy to enable again for Rust CUs, could we request another version bump? It just makes it easier to integrate with the kernel scripts when we want to enable again. > > I can try to extract the cases for those into simpler `.o` files, if > you would find simpler test cases useful (perhaps for the test suite > etc.). > > Cheers, > Miguel >
Em Mon, Feb 13, 2023 at 01:45:02PM +0100, Miguel Ojeda escreveu: > On Mon, Feb 13, 2023 at 1:09 PM Arnaldo Carvalho de Melo <arnaldo.melo@gmail.com> wrote: > > The namespace.o seems to be ok: > I saw the other message too -- this looks great, thanks a ton. > > The core one needs work: > If `core.o` works, then I think it is likely other things will work :) > I can try to extract the cases for those into simpler `.o` files, if > you would find simpler test cases useful (perhaps for the test suite > etc.). That would be great! I tried starting this path with this, spitted out by ChatGPT (minus the 'pub' in from of main): ⬢[acme@toolbox pahole-rust-cases]$ cat template_type.rs // Provided by ChatGPT use std::ops::Add; // Define a generic struct template with a single type parameter `T`. struct Point<T> { x: T, y: T, } // Implement a method for the `Point` struct that adds two points together. // The method uses the `Add` trait to ensure that the type `T` supports addition. impl<T: Add<Output=T>> Point<T> { fn add_points(self, other: Point<T>) -> Point<T> { Point { x: self.x + other.x, y: self.y + other.y, } } } pub fn main() { // Create two `Point` instances, using `i32` as the type parameter. let p1 = Point { x: 1, y: 2 }; let p2 = Point { x: 3, y: 4 }; // Add the two points together using the `add_points` method. let p3 = p1.add_points(p2); // Print the result. println!("Point: ({}, {})", p3.x, p3.y); } ⬢[acme@toolbox pahole-rust-cases]$ And then handle the DW_TAG_template_type_parameter for a DW_TAG_subroutine: <3><12d>: Abbrev Number: 5 (DW_TAG_structure_type) <12e> DW_AT_name : (indirect string, offset: 0x299): ArgumentV1 <132> DW_AT_byte_size : 16 <133> DW_AT_alignment : 8 <4><134>: Abbrev Number: 6 (DW_TAG_member) <135> DW_AT_name : (indirect string, offset: 0xd7): value <139> DW_AT_type : <0x43a> <13d> DW_AT_alignment : 8 <13e> DW_AT_data_member_location: 0 <4><13f>: Abbrev Number: 6 (DW_TAG_member) <140> DW_AT_name : (indirect string, offset: 0x10e): formatter <144> DW_AT_type : <0x447> <148> DW_AT_alignment : 8 <149> DW_AT_data_member_location: 8 <4><14a>: Abbrev Number: 11 (DW_TAG_subprogram) <14b> DW_AT_linkage_name: (indirect string, offset: 0x2a8): _ZN4core3fmt10ArgumentV13new17h167b8c43a2ee7614E <14f> DW_AT_name : (indirect string, offset: 0x2d9): new<i32> <153> DW_AT_decl_file : 2 <154> DW_AT_decl_line : 333 <156> DW_AT_type : <0x12d> <15a> DW_AT_inline : 1 (inlined) <5><15b>: Abbrev Number: 12 (DW_TAG_template_type_param) <15c> DW_AT_type : <0x4e3> <160> DW_AT_name : (indirect string, offset: 0x125): T <5><164>: Abbrev Number: 13 (DW_TAG_variable) I'll have to stop now, but was at the point of changing ftype__fprintf() somehow to show that template type parameter, with the patch at the end of this message I get this: ⬢[acme@toolbox pahole]$ pahole --show_private_classes ../pahole-rust-cases/template_type.o die__process_class: tag not supported 0x33 (variant_part)! die__process_class: tag not supported 0x2f (template_type_parameter)! struct Argument { usize position __attribute__((__aligned__(8))); /* 0 8 */ struct FormatSpec format __attribute__((__aligned__(8))); /* 8 48 */ /* XXX last struct has 7 bytes of padding */ /* size: 56, cachelines: 1, members: 2 */ /* paddings: 1, sum paddings: 7 */ /* forced alignments: 2 */ /* last cacheline: 56 bytes */ } __attribute__((__aligned__(8))); struct FormatSpec { struct Count precision __attribute__((__aligned__(8))); /* 0 16 */ /* XXX last struct has 16 bytes of padding */ struct Count width __attribute__((__aligned__(8))); /* 16 16 */ /* XXX last struct has 16 bytes of padding */ char fill __attribute__((__aligned__(4))); /* 32 4 */ u32 flags __attribute__((__aligned__(4))); /* 36 4 */ enum Alignment align __attribute__((__aligned__(1))); /* 40 1 */ /* size: 48, cachelines: 1, members: 5 */ /* padding: 7 */ /* paddings: 2, sum paddings: 32 */ /* forced alignments: 5 */ /* last cacheline: 48 bytes */ } __attribute__((__aligned__(8))); struct Is { /* XXX 8 bytes hole, try to pack */ usize __0 __attribute__((__aligned__(8))); /* 8 8 */ /* size: 16, cachelines: 1, members: 1 */ /* sum members: 8, holes: 1, sum holes: 8 */ /* forced alignments: 1, forced holes: 1, sum forced holes: 8 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct Param { /* XXX 8 bytes hole, try to pack */ usize __0 __attribute__((__aligned__(8))); /* 8 8 */ /* size: 16, cachelines: 1, members: 1 */ /* sum members: 8, holes: 1, sum holes: 8 */ /* forced alignments: 1, forced holes: 1, sum forced holes: 8 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct Implied { /* size: 16, cachelines: 1, members: 0 */ /* padding: 16 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct Count { /* size: 16, cachelines: 1, members: 0 */ /* padding: 16 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct ArgumentV1 { struct Opaque * value __attribute__((__aligned__(8))); /* 0 8 */ struct Result<(), core::fmt::Error> (*formatter)(struct Opaque *, struct Formatter *) __attribute__((__aligned__(8))); /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* forced alignments: 2 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct Opaque { /* size: 0, cachelines: 0, members: 0 */ } __attribute__((__aligned__(1))); struct Error { /* size: 0, cachelines: 0, members: 0 */ } __attribute__((__aligned__(1))); struct Formatter { struct Option<usize> width __attribute__((__aligned__(8))); /* 0 16 */ /* XXX last struct has 16 bytes of padding */ struct Option<usize> precision __attribute__((__aligned__(8))); /* 16 16 */ /* XXX last struct has 16 bytes of padding */ struct &mut dyn core::fmt::Write buf __attribute__((__aligned__(8))); /* 32 16 */ u32 flags __attribute__((__aligned__(4))); /* 48 4 */ char fill __attribute__((__aligned__(4))); /* 52 4 */ enum Alignment align __attribute__((__aligned__(1))); /* 56 1 */ /* size: 64, cachelines: 1, members: 6 */ /* padding: 7 */ /* paddings: 2, sum paddings: 32 */ /* forced alignments: 6 */ } __attribute__((__aligned__(8))); struct Arguments { struct &[&str] pieces __attribute__((__aligned__(8))); /* 0 16 */ struct Option<&[core::fmt::rt::v1::Argument]> fmt __attribute__((__aligned__(8))); /* 16 16 */ /* XXX last struct has 16 bytes of padding */ struct &[core::fmt::ArgumentV1] args __attribute__((__aligned__(8))); /* 32 16 */ /* size: 48, cachelines: 1, members: 3 */ /* paddings: 1, sum paddings: 16 */ /* forced alignments: 3 */ /* last cacheline: 48 bytes */ } __attribute__((__aligned__(8))); struct Ok { /* XXX 1 byte hole, try to pack */ () __0 __attribute__((__aligned__(1))); /* 1 0 */ /* size: 1, cachelines: 1, members: 1 */ /* forced alignments: 1, forced holes: 1, sum forced holes: 1 */ /* last cacheline: 1 bytes */ } __attribute__((__aligned__(1))); struct Err { /* XXX 1 byte hole, try to pack */ struct Error __0 __attribute__((__aligned__(1))); /* 1 0 */ /* size: 1, cachelines: 1, members: 1 */ /* forced alignments: 1, forced holes: 1, sum forced holes: 1 */ /* last cacheline: 1 bytes */ } __attribute__((__aligned__(1))); struct Result<(), core::fmt::Error> { /* size: 1, cachelines: 0, members: 0 */ /* padding: 1 */ /* last cacheline: 1 bytes */ } __attribute__((__aligned__(1))); struct None { /* size: 16, cachelines: 1, members: 0 */ /* padding: 16 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct Some { /* XXX 8 bytes hole, try to pack */ usize __0 __attribute__((__aligned__(8))); /* 8 8 */ /* size: 16, cachelines: 1, members: 1 */ /* sum members: 8, holes: 1, sum holes: 8 */ /* forced alignments: 1, forced holes: 1, sum forced holes: 8 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct Option<usize> { /* size: 16, cachelines: 1, members: 0 */ /* padding: 16 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct Some { struct &[core::fmt::rt::v1::Argument] __0 __attribute__((__aligned__(8))); /* 0 16 */ /* size: 16, cachelines: 1, members: 1 */ /* forced alignments: 1 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct Option<&[core::fmt::rt::v1::Argument]> { /* size: 16, cachelines: 1, members: 0 */ /* padding: 16 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct &mut dyn core::fmt::Write { struct dyn core::fmt::Write * pointer __attribute__((__aligned__(8))); /* 0 8 */ usize * vtable __attribute__((__aligned__(8))); /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* forced alignments: 2 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct dyn core::fmt::Write { /* size: 0, cachelines: 0, members: 0 */ } __attribute__((__aligned__(1))); struct &[&str] { struct &str * data_ptr __attribute__((__aligned__(8))); /* 0 8 */ usize length __attribute__((__aligned__(8))); /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* forced alignments: 2 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct &str { u8 * data_ptr __attribute__((__aligned__(8))); /* 0 8 */ usize length __attribute__((__aligned__(8))); /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* forced alignments: 2 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct &[core::fmt::rt::v1::Argument] { struct Argument * data_ptr __attribute__((__aligned__(8))); /* 0 8 */ usize length __attribute__((__aligned__(8))); /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* forced alignments: 2 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct &[core::fmt::ArgumentV1] { struct ArgumentV1 * data_ptr __attribute__((__aligned__(8))); /* 0 8 */ usize length __attribute__((__aligned__(8))); /* 8 8 */ /* size: 16, cachelines: 1, members: 2 */ /* forced alignments: 2 */ /* last cacheline: 16 bytes */ } __attribute__((__aligned__(8))); struct Point<i32> { i32 x __attribute__((__aligned__(4))); /* 0 4 */ i32 y __attribute__((__aligned__(4))); /* 4 4 */ /* size: 8, cachelines: 1, members: 2 */ /* forced alignments: 2 */ /* last cacheline: 8 bytes */ } __attribute__((__aligned__(4))); ⬢[acme@toolbox pahole]$ I'll cut version 1.25 with what is in the 'master' and 'next' now as binutils 2.40 is out and emitting DW_TAG_unspecified_type, that isn't supported by pahole 1.24 and also Alan's work on optimized functions, etc. - Arnaldo diff --git a/dwarf_loader.c b/dwarf_loader.c index a77598dc3affca88..b767bcaa9322c95a 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -1093,6 +1093,18 @@ static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu, return parm; } +static struct template_type_parameter *template_type_parameter__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) +{ + struct template_type_parameter *parm = tag__alloc(cu, sizeof(*parm)); + + if (parm != NULL) { + tag__init(&parm->tag, cu, die); + parm->name = attr_string(die, DW_AT_name, conf); + } + + return parm; +} + static struct inline_expansion *inline_expansion__new(Dwarf_Die *die, struct cu *cu, struct conf_load *conf) { struct inline_expansion *exp = tag__alloc(cu, sizeof(*exp)); @@ -1211,6 +1223,7 @@ static void ftype__init(struct ftype *ftype, Dwarf_Die *die, struct cu *cu) #endif tag__init(&ftype->tag, cu, die); INIT_LIST_HEAD(&ftype->parms); + INIT_LIST_HEAD(&ftype->template_type_parms); ftype->nr_parms = 0; ftype->unspec_parms = 0; } @@ -1566,6 +1579,19 @@ static struct tag *die__create_new_parameter(Dwarf_Die *die, return &parm->tag; } +static struct tag *die__create_new_template_type_parameter(Dwarf_Die *die, struct ftype *ftype, + struct cu *cu, struct conf_load *conf) +{ + struct template_type_parameter *parm = template_type_parameter__new(die, cu, conf); + + if (parm == NULL) + return NULL; + + ftype__add_template_type_parameter(ftype, parm); + + return &parm->tag; +} + static struct tag *die__create_new_label(Dwarf_Die *die, struct lexblock *lexblock, struct cu *cu, struct conf_load *conf) @@ -1989,6 +2015,8 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype, case DW_TAG_GNU_template_template_param: #endif case DW_TAG_template_type_parameter: + tag = die__create_new_template_type_parameter(die, ftype, cu, conf); + break; case DW_TAG_template_value_parameter: /* FIXME: probably we'll have to attach this as a list of * template parameters to use at class__fprintf time... diff --git a/dwarves.c b/dwarves.c index b43031c93c5cab58..6a8feccc2d45c60d 100644 --- a/dwarves.c +++ b/dwarves.c @@ -1391,6 +1391,12 @@ void ftype__add_parameter(struct ftype *ftype, struct parameter *parm) list_add_tail(&parm->tag.node, &ftype->parms); } +void ftype__add_template_type_parameter(struct ftype *ftype, struct template_type_parameter *parm) +{ + ++ftype->nr_template_type_parms; + list_add_tail(&parm->tag.node, &ftype->template_type_parms); +} + void lexblock__add_tag(struct lexblock *block, struct tag *tag) { list_add_tail(&tag->node, &block->tags); diff --git a/dwarves.h b/dwarves.h index 24a1909a60389dee..5c9952bfddaed301 100644 --- a/dwarves.h +++ b/dwarves.h @@ -825,13 +825,30 @@ static inline const char *parameter__name(const struct parameter *parm) return parm->name; } +struct template_type_parameter { + struct tag tag; + const char *name; +}; + +static inline struct template_type_parameter *tag__template_type_parameter(const struct tag *tag) +{ + return (struct template_type_parameter *)tag; +} + +static inline const char *template_type_parameter__name(const struct template_type_parameter *parm) +{ + return parm->name; +} + /* * tag.tag can be DW_TAG_subprogram_type or DW_TAG_subroutine_type. */ struct ftype { struct tag tag; struct list_head parms; + struct list_head template_type_parms; uint16_t nr_parms; + uint16_t nr_template_type_parms; uint8_t unspec_parms:1; /* just one bit is needed */ uint8_t optimized_parms:1; uint8_t processed:1; @@ -872,6 +889,8 @@ void ftype__delete(struct ftype *ftype); list_for_each_entry_safe_reverse(pos, n, &(ftype)->parms, tag.node) void ftype__add_parameter(struct ftype *ftype, struct parameter *parm); +void ftype__add_template_type_parameter(struct ftype *ftype, struct template_type_parameter *parm); + size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu, const char *name, const int inlined, const int is_pointer, const int type_spacing, bool is_prototype,
Em Mon, Feb 13, 2023 at 12:53:38PM +0000, Eric Curtin escreveu: > On Mon, 13 Feb 2023 at 12:45, Miguel Ojeda > <miguel.ojeda.sandonis@gmail.com> wrote: > > > > Hi Arnaldo, > > > > On Mon, Feb 13, 2023 at 1:09 PM Arnaldo Carvalho de Melo > > <arnaldo.melo@gmail.com> wrote: > > > > > > The namespace.o seems to be ok: > > > > I saw the other message too -- this looks great, thanks a ton. > > > > > The core one needs work: > > > > If `core.o` works, then I think it is likely other things will work :) > > Hi Guys, > > I'll leave this to the experts, but if we get this to the point where > we are happy to enable again for Rust CUs, could we request another > version bump? It just makes it easier to integrate with the kernel Sure, as we improve the support for encoding BTF from DWARF generated for Rust code, and the subset that is used in the kernel is handled, we will just adjust scripts/pahole-flags.sh to skip Rust DWARF if the version is >= the version where excluding DWARF generated from some languages (Rust in this case) but < the version where we're confortable with generating BTF for Rust DWARF. - Arnaldo > scripts when we want to enable again. > > > > > I can try to extract the cases for those into simpler `.o` files, if > > you would find simpler test cases useful (perhaps for the test suite > > etc.). > > > > Cheers, > > Miguel > > >
Em Fri, Feb 10, 2023 at 05:48:36PM -0300, Arnaldo Carvalho de Melo escreveu: > Hi Miguel, after a long winter, I'm trying to get Rust properly > supported on pahole, please check that this specific use case is working > for you as well. > > I'll go thru the others to see if they are easy (or at least restricted > to Rust CUs) as this one. I needed to add this one on top: From 1231b6b9b4d88e0084bef4254eb1a05eb9935c99 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo <acme@redhat.com> Date: Tue, 14 Feb 2023 18:13:59 -0300 Subject: [PATCH 1/1] dwarf_loader: Fix sorting of Rust structs We may have structs with fields on the same offset, don't move anything in that case, otherwise we get into an eternal loop, doh. Tested with the Linux kernel built with CONFIG_RUST + all the code examples, first Rust struct where this happened was: (gdb) p type->namespace.name $2 = 0x7fffda938497 "((), char)" (gdb) p type->nr_members $3 = 2 (gdb) p current_member->name $4 = 0x7fffda918f36 "__1" (gdb) p next_member->name $5 = 0x7fffda91765c "__0" (gdb) p current_member->byte_offset $6 = 0 (gdb) p next_member->byte_offset $7 = 0 But this shows that --lang_exclude should be better positioned as we're now needlessly loading all the tags for Rust DWARF to then just trow it away at the cu__filter() in pahole :-\ Too late in the 1.25 release cycle for that, optimize it in 1.26. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> --- dwarf_loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwarf_loader.c b/dwarf_loader.c index a77598dc3affca88..acdb68d5dd33f148 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -2857,7 +2857,7 @@ restart: struct class_member *next_member = list_entry(current_member->tag.node.next, typeof(*current_member), tag.node); - if (current_member->byte_offset < next_member->byte_offset) + if (current_member->byte_offset <= next_member->byte_offset) continue; list_del(¤t_member->tag.node);
diff --git a/dwarf_loader.c b/dwarf_loader.c index 253c5efaf3b55a93..a77598dc3affca88 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -2835,9 +2835,51 @@ static int class_member__cache_byte_size(struct tag *tag, struct cu *cu, return 0; } +static bool cu__language_reorders_offsets(const struct cu *cu) +{ + return cu->language == DW_LANG_Rust; +} + +static int type__sort_by_offset(struct tag *tag, struct cu *cu, void *cookie __maybe_unused) +{ + if (!tag__is_type(tag)) + return 0; + + struct type *type = tag__type(tag); + struct class_member *current_member; + + // There may be more than DW_TAG_members entries in the type tags, so do a simple + // bubble sort for now, so that the other non tags stay where they are. +restart: + type__for_each_data_member(type, current_member) { + if (list_is_last(¤t_member->tag.node, &type->namespace.tags)) + break; + + struct class_member *next_member = list_entry(current_member->tag.node.next, typeof(*current_member), tag.node); + + if (current_member->byte_offset < next_member->byte_offset) + continue; + + list_del(¤t_member->tag.node); + list_add(¤t_member->tag.node, &next_member->tag.node); + goto restart; + } + + return 0; +} + +static void cu__sort_types_by_offset(struct cu *cu, struct conf_load *conf) +{ + cu__for_all_tags(cu, type__sort_by_offset, conf); +} + static int cu__finalize(struct cu *cu, struct conf_load *conf, void *thr_data) { cu__for_all_tags(cu, class_member__cache_byte_size, conf); + + if (cu__language_reorders_offsets(cu)) + cu__sort_types_by_offset(cu, conf); + if (conf && conf->steal) { return conf->steal(cu, conf, thr_data); }
Hi Miguel, after a long winter, I'm trying to get Rust properly supported on pahole, please check that this specific use case is working for you as well. I'll go thru the others to see if they are easy (or at least restricted to Rust CUs) as this one. Thanks, - Arnaldo --- Rust may reorder struct fields and pahole assumes them to be in order, as is the case for languages like C and C++, etc. So after having the class member bit and byte offsets sorted out, sort Rust CU types by offset. Using: https://github.com/Rust-for-Linux/pahole-rust-cases/blob/main/inverted.o Before: $ pahole --show_private_classes ../pahole-rust-cases/inverted.o struct S { /* XXX 4 bytes hole, try to pack */ bool a __attribute__((__aligned__(1))); /* 4 1 */ /* XXX 65531 bytes hole, try to pack */ /* Bitfield combined with previous fields */ u32 b __attribute__((__aligned__(4))); /* 0 4 */ /* size: 8, cachelines: 1, members: 2 */ /* sum members: 5, holes: 2, sum holes: 65535 */ /* padding: 4 */ /* forced alignments: 2, forced holes: 2, sum forced holes: 65535 */ /* last cacheline: 8 bytes */ /* BRAIN FART ALERT! 8 bytes != 5 (member bytes) + 0 (member bits) + 65535 (byte holes) + 0 (bit holes), diff = -524288 bits */ } __attribute__((__aligned__(4))); $ After: $ readelf -wi ../pahole-rust-cases/inverted.o | grep DW_TAG_compile_unit -A9 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit) <c> DW_AT_producer : (indirect string, offset: 0x0): clang LLVM (rustc version 1.60.0 (7737e0b5c 2022-04-04)) <10> DW_AT_language : 28 (Rust) <12> DW_AT_name : (indirect string, offset: 0x39): inverted.rs/@/inverted.c4dda47b-cgu.0 <16> DW_AT_stmt_list : 0x0 <1a> DW_AT_comp_dir : (indirect string, offset: 0x5f): /root/pahole-rust <1e> DW_AT_GNU_pubnames: 1 <1e> DW_AT_low_pc : 0x0 <26> DW_AT_high_pc : 0x62 <1><2a>: Abbrev Number: 2 (DW_TAG_namespace) $ pahole --show_private_classes ../pahole-rust-cases/inverted.o struct S { u32 b __attribute__((__aligned__(4))); /* 0 4 */ bool a __attribute__((__aligned__(1))); /* 4 1 */ /* size: 8, cachelines: 1, members: 2 */ /* padding: 3 */ /* forced alignments: 2 */ /* last cacheline: 8 bytes */ } __attribute__((__aligned__(4))); $ $ cp ../pahole-rust-cases/inverted.o . $ pahole --btf_encode inverted.o $ readelf -SW inverted.o | grep -i BTF [26] .BTF PROGBITS 0000000000000000 000922 00006c 00 0 0 1 $ $ pahole -F btf inverted.o struct S { u32 b; /* 0 4 */ bool a; /* 4 1 */ /* size: 8, cachelines: 1, members: 2 */ /* padding: 3 */ /* last cacheline: 8 bytes */ }; $ Reported-by: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Andrii Nakryiko <andrii@kernel.org> Cc: Eric Curtin <ecurtin@redhat.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Martin Rodriguez Reboredo <yakoyoku@gmail.com> Cc: Neal Gompa <neal@gompa.dev> Cc: Yonghong Song <yhs@fb.com> Cc: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> --- dwarf_loader.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)