Commit 302db0f5 authored by Masami Hiramatsu (Google)'s avatar Masami Hiramatsu (Google)
Browse files

tracing/probes: Add a function to search a member of a struct/union

Add btf_find_struct_member() API to search a member of a given data structure
or union from the member's name.

Link: https://lore.kernel.org/all/169272156248.160970.8868479822371129043.stgit@devnote2/



Signed-off-by: default avatarMasami Hiramatsu (Google) <mhiramat@kernel.org>
Reviewed-by: default avatarAlan Maguire <alan.maguire@oracle.com>
Acked-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
parent ebeed8d4
Loading
Loading
Loading
Loading
+69 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include <linux/btf.h>
#include <linux/kernel.h>
#include <linux/slab.h>

#include "trace_btf.h"

@@ -51,3 +52,71 @@ const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, s3
		return NULL;
}

#define BTF_ANON_STACK_MAX	16

struct btf_anon_stack {
	u32 tid;
	u32 offset;
};

/*
 * 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 btf_anon_stack *anon_stack;
	const struct btf_member *member;
	u32 tid, cur_offset = 0;
	const char *name;
	int i, top = 0;

	anon_stack = kcalloc(BTF_ANON_STACK_MAX, sizeof(*anon_stack), GFP_KERNEL);
	if (!anon_stack)
		return ERR_PTR(-ENOMEM);

retry:
	if (!btf_type_is_struct(type)) {
		member = ERR_PTR(-EINVAL);
		goto out;
	}

	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;
				goto out;
			}
		}
	}
	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;
	}
	member = NULL;

out:
	kfree(anon_stack);
	return member;
}
+4 −0
Original line number Diff line number Diff line
@@ -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);