From: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Add btf_find_struct_member() API to search a member of a given data structure
or union from the member's name.
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Reviewed-by: Alan Maguire <alan.maguire@oracle.com>
---
Changes in v3:
- Remove simple input check.
- Fix unneeded IS_ERR_OR_NULL() check for btf_type_by_id().
- Move the code next to btf_get_func_param().
- Use for_each_member() macro instead of for-loop.
- Use btf_type_skip_modifiers() instead of btf_type_by_id().
Changes in v4:
- Use a stack for searching in anonymous members instead of nested call.
Changes in v5:
- Move the generic functions into a new file, trace_btf.c, for avoiding
tree-merge conflict.
- Return anon_offset for calculating correct offset from root struct.
---
kernel/trace/trace_btf.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++
kernel/trace/trace_btf.h | 4 +++
2 files changed, 61 insertions(+)
diff --git a/kernel/trace/trace_btf.c b/kernel/trace/trace_btf.c
index 82cbbeeda071..109a238437cf 100644
--- a/kernel/trace/trace_btf.c
+++ b/kernel/trace/trace_btf.c
@@ -50,3 +50,60 @@ const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, s3
return NULL;
}
+#define BTF_ANON_STACK_MAX 16
+
+/*
+ * Find a member of data structure/union by name and return it.
+ * Return NULL if not found, or -EINVAL if parameter is invalid.
+ * If the member is an member of anonymous union/structure, the offset
+ * of that anonymous union/structure is stored into @anon_offset. Caller
+ * can calculate the correct offset from the root data structure by
+ * adding anon_offset to the member's offset.
+ */
+const struct btf_member *btf_find_struct_member(struct btf *btf,
+ const struct btf_type *type,
+ const char *member_name,
+ u32 *anon_offset)
+{
+ struct {
+ u32 tid;
+ u32 offset;
+ } anon_stack[BTF_ANON_STACK_MAX];
+ const struct btf_member *member;
+ u32 tid, cur_offset = 0;
+ const char *name;
+ int i, top = 0;
+
+retry:
+ if (!btf_type_is_struct(type))
+ return ERR_PTR(-EINVAL);
+
+ for_each_member(i, type, member) {
+ if (!member->name_off) {
+ /* Anonymous union/struct: push it for later use */
+ type = btf_type_skip_modifiers(btf, member->type, &tid);
+ if (type && top < BTF_ANON_STACK_MAX) {
+ anon_stack[top].tid = tid;
+ anon_stack[top++].offset =
+ cur_offset + member->offset;
+ }
+ } else {
+ name = btf_name_by_offset(btf, member->name_off);
+ if (name && !strcmp(member_name, name)) {
+ if (anon_offset)
+ *anon_offset = cur_offset;
+ return member;
+ }
+ }
+ }
+ if (top > 0) {
+ /* Pop from the anonymous stack and retry */
+ tid = anon_stack[--top].tid;
+ cur_offset = anon_stack[top].offset;
+ type = btf_type_by_id(btf, tid);
+ goto retry;
+ }
+
+ return NULL;
+}
+
diff --git a/kernel/trace/trace_btf.h b/kernel/trace/trace_btf.h
index 2ef1b2367e1e..fb2919a74225 100644
--- a/kernel/trace/trace_btf.h
+++ b/kernel/trace/trace_btf.h
@@ -5,3 +5,7 @@ const struct btf_type *btf_find_func_proto(const char *func_name,
struct btf **btf_p);
const struct btf_param *btf_get_func_param(const struct btf_type *func_proto,
s32 *nr);
+const struct btf_member *btf_find_struct_member(struct btf *btf,
+ const struct btf_type *type,
+ const char *member_name,
+ u32 *anon_offset);
On Mon, 7 Aug 2023 11:54:58 +0900 "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > --- a/kernel/trace/trace_btf.c > +++ b/kernel/trace/trace_btf.c > @@ -50,3 +50,60 @@ const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, s3 > return NULL; > } > > +#define BTF_ANON_STACK_MAX 16 > + > +/* > + * Find a member of data structure/union by name and return it. > + * Return NULL if not found, or -EINVAL if parameter is invalid. > + * If the member is an member of anonymous union/structure, the offset > + * of that anonymous union/structure is stored into @anon_offset. Caller > + * can calculate the correct offset from the root data structure by > + * adding anon_offset to the member's offset. > + */ > +const struct btf_member *btf_find_struct_member(struct btf *btf, > + const struct btf_type *type, > + const char *member_name, > + u32 *anon_offset) > +{ > + struct { > + u32 tid; > + u32 offset; > + } anon_stack[BTF_ANON_STACK_MAX]; Where is this called as the above is 128 bytes, which is a bit large for the stack. It may not be bad if it's not that generic of a function. But if the stack is getting tight, this could still be an issue. -- Steve > + const struct btf_member *member; > + u32 tid, cur_offset = 0; > + const char *name; > + int i, top = 0; > + > +retry: > + if (!btf_type_is_struct(type)) > + return ERR_PTR(-EINVAL); > + > + for_each_member(i, type, member) { > + if (!member->name_off) { > + /* Anonymous union/struct: push it for later use */ > + type = btf_type_skip_modifiers(btf, member->type, &tid); > + if (type && top < BTF_ANON_STACK_MAX) { > + anon_stack[top].tid = tid; > + anon_stack[top++].offset = > + cur_offset + member->offset; > + } > + } else { > + name = btf_name_by_offset(btf, member->name_off); > + if (name && !strcmp(member_name, name)) { > + if (anon_offset) > + *anon_offset = cur_offset; > + return member; > + } > + } > + } > + if (top > 0) { > + /* Pop from the anonymous stack and retry */ > + tid = anon_stack[--top].tid; > + cur_offset = anon_stack[top].offset; > + type = btf_type_by_id(btf, tid); > + goto retry; > + } > + > + return NULL; > +} > +
On Tue, 22 Aug 2023 09:37:20 -0400 Steven Rostedt <rostedt@goodmis.org> wrote: > On Mon, 7 Aug 2023 11:54:58 +0900 > "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote: > > > --- a/kernel/trace/trace_btf.c > > +++ b/kernel/trace/trace_btf.c > > @@ -50,3 +50,60 @@ const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, s3 > > return NULL; > > } > > > > +#define BTF_ANON_STACK_MAX 16 > > + > > +/* > > + * Find a member of data structure/union by name and return it. > > + * Return NULL if not found, or -EINVAL if parameter is invalid. > > + * If the member is an member of anonymous union/structure, the offset > > + * of that anonymous union/structure is stored into @anon_offset. Caller > > + * can calculate the correct offset from the root data structure by > > + * adding anon_offset to the member's offset. > > + */ > > +const struct btf_member *btf_find_struct_member(struct btf *btf, > > + const struct btf_type *type, > > + const char *member_name, > > + u32 *anon_offset) > > +{ > > + struct { > > + u32 tid; > > + u32 offset; > > + } anon_stack[BTF_ANON_STACK_MAX]; > > Where is this called as the above is 128 bytes, which is a bit large > for the stack. It may not be bad if it's not that generic of a > function. But if the stack is getting tight, this could still be an > issue. OK, let me allocate an array then. Thank you, > > -- Steve > > > > + const struct btf_member *member; > > + u32 tid, cur_offset = 0; > > + const char *name; > > + int i, top = 0; > > + > > +retry: > > + if (!btf_type_is_struct(type)) > > + return ERR_PTR(-EINVAL); > > + > > + for_each_member(i, type, member) { > > + if (!member->name_off) { > > + /* Anonymous union/struct: push it for later use */ > > + type = btf_type_skip_modifiers(btf, member->type, &tid); > > + if (type && top < BTF_ANON_STACK_MAX) { > > + anon_stack[top].tid = tid; > > + anon_stack[top++].offset = > > + cur_offset + member->offset; > > + } > > + } else { > > + name = btf_name_by_offset(btf, member->name_off); > > + if (name && !strcmp(member_name, name)) { > > + if (anon_offset) > > + *anon_offset = cur_offset; > > + return member; > > + } > > + } > > + } > > + if (top > 0) { > > + /* Pop from the anonymous stack and retry */ > > + tid = anon_stack[--top].tid; > > + cur_offset = anon_stack[top].offset; > > + type = btf_type_by_id(btf, tid); > > + goto retry; > > + } > > + > > + return NULL; > > +} > > + -- Masami Hiramatsu (Google) <mhiramat@kernel.org>
© 2016 - 2025 Red Hat, Inc.