Skip to content

libbpf-cargo: Relative anonymous type naming for struct fields #1178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

Waujito
Copy link

@Waujito Waujito commented May 22, 2025

Previously, anonymous types were named as _anon{id} with id as a global counter. This approach caused cascading renaming of all subsequent anonymous definitions after any C anonymous struct order/amount change. This was a major design problem not for only development but even portability, since here may be the case when something was changed in vmlinux.h leading to compilation errors.

This PR addresses the issue by localizing cascading effects to the local struct layer rather than the global layer. While the solution is not fully ideal - cascading still occurs within individual structs - it eliminates the need for wide Rust code modifications when the order of anonymous struct definitions changes.

The PR is breaking and should be included in major version update.

The PR fixes #1161

While this approach is ok, I still think the better way will be to use getters, since this is breaking and less intuitive.

@danielocfb
Copy link
Collaborator

Haven't looked at it yet, but this is great. I was considering doing that for 0.25.0, but didn't have the cycles. Will postpone the release until I've gotten a chance to take a look here. Please look into the CI failures, which all seem legit.

Previously, anonymous types were named as __anon_{id} with id as a global counter. This approach caused cascading renaming of all subsequent anonymous definitions after any C anonymous struct order/amount change. This was a major design problem not for only development but even portability, since here may be the case when something was changed in vmlinux.h leading to compilation errors.

This commit addresses the issue by localizing cascading effects to the local struct layer rather than the global layer. While the solution is not fully ideal - cascading still occurs within individual structs - it eliminates the need for wide Rust code modifications when the order of anonymous struct definitions changes.

Note, that the commit does not fully eliminates __anon_{id}, but the most important part of it.

The commit is breaking and should be included in major version update.
@Waujito
Copy link
Author

Waujito commented May 22, 2025

Here are still some plain __anon types like in maps. I'll try to catch more and may be add some defenders of name collisions.

Copy link
Collaborator

@danielocfb danielocfb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general this seems okay to me, but the code seems under documented and under-tested in the sense that I can remove/change various lines and all tests keep passing. Similarly, I am not sure I understand some of the assignments done. Left some comments.

@@ -367,6 +368,13 @@ fn escape_reserved_keyword(identifier: Cow<'_, str>) -> Cow<'_, str> {
}
}

#[derive(Debug, Clone)]
pub struct BtfDependency {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't have to be pub, please restrict visibility. Same for fields.

Comment on lines 388 to 389

dependencies: RefCell<HashMap<TypeId, BtfDependency>>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a doc comment describing the member?

}

impl TypeMap {
pub fn derive_parent<'s>(&self, ty: &BtfType<'s>, parent: &BtfType<'s>) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn derive_parent<'s>(&self, ty: &BtfType<'s>, parent: &BtfType<'s>) {
fn derive_parent<'s>(&self, ty: &BtfType<'s>, parent: &BtfType<'s>) {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the name really fitting? Aren't you more registering a parent relationship rather than "deriving" anything?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assign_parent?

dep.child_counter = Rc::new(RefCell::new(0));
}

let parent_counter = Rc::<RefCell<i32>>::clone(&pdep.child_counter);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let parent_counter = Rc::<RefCell<i32>>::clone(&pdep.child_counter);
let parent_counter = Rc::clone(&pdep.child_counter);

Comment on lines 406 to 408
if ty.name().is_some() {
dep.child_counter = Rc::new(RefCell::new(0));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this code needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment

if ty.name().is_some() {
dep.child_counter = Rc::new(RefCell::new(0));
}
dep.dep_id = 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this value set here and not already during initialization?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment

Comment on lines 423 to 425
if let Some(n) = parent.name() {
dep.name = Some(n.to_string_lossy().to_string());
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this value set here and not during initialization?

Comment on lines 373 to 375
pub name: Option<String>,
pub dep_id: i32,
pub child_counter: Rc<RefCell<i32>>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document what these represent.

@danielocfb danielocfb mentioned this pull request May 22, 2025
@danielocfb
Copy link
Collaborator

Here are still some plain __anon types like in maps. I'll try to catch more and may be add some defenders of name collisions.

What do you mean exactly? Where is "here"?

@Waujito
Copy link
Author

Waujito commented May 23, 2025

In 0e504e6 I simplified the code slightly, modified the global anon counting behavior, added documentation for new structure fields and included a test for depth-aligned anonymous structs.

The global anonymous struct counting now excludes relative anons to make its behavior more representative and easier to use. I also added some fallback protection on it for (im)possible case when global anonymous struct is assigned before a relative one: in such case we will use old type of naming.

Here are still some plain __anon types like in maps. I'll try to catch more and may be add some defenders of name collisions.

What do you mean exactly? Where is "here"?

Nvm. Those anonymous structs are at the global level and should be kept with old naming.

I think the code is ok now, but I'm aware of possible name collisions for relative types.
I can't mind the case, but what if. Maybe I should implement dep id counting in a separate hashmap for parent names as strings, but not with that crazy child_counter? This solution will not be elegant, but more trustworthy...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

libbpf-cargo: Getters for struct fields
2 participants