On Thu, 31 Jul 2025 12:44:49 +0100
Douglas Raillard <douglas.raillard@xxxxxxx> wrote:
The delimiter is '.' and the first item is the structure name. Then the
member of the structure to get the offset of. If that member is an
embedded structure, another '.MEMBER' may be added to get the offset of
its members with respect to the original value.
"+kmem_cache.size($arg1)" is equivalent to:
(*(struct kmem_cache *)$arg1).size
Anonymous structures are also handled:
# echo 'e:xmit net.net_dev_xmit +net_device.name(+sk_buff.dev($skbaddr)):string' >> dynamic_events
Not sure how hard that would be but the type of the expression could probably be inferred from
BTF as well in some cases. Some cases may be ambiguous (like char* that could be either a buffer
to display as hex or a null-terminated ASCII string) but BTF would still allow to restrict
to something sensible (e.g. prevent u32 for a char*).
Hmm, should be possible, but would require passing that information back to
the caller of the BTF lookup function.
diff --git a/kernel/trace/trace_btf.c b/kernel/trace/trace_btf.c
index 5bbdbcbbde3c..b69404451410 100644
--- a/kernel/trace/trace_btf.c
+++ b/kernel/trace/trace_btf.c
@@ -120,3 +120,109 @@ const struct btf_member *btf_find_struct_member(struct btf *btf,
return member;
}
+#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)
+
+static int find_member(const char *ptr, struct btf *btf,
+ const struct btf_type **type, int level)
+{
+ const struct btf_member *member;
+ const struct btf_type *t = *type;
+ int i;
+
+ /* Max of 3 depth of anonymous structures */
+ if (level > 3)
+ return -1;
+
+ for_each_member(i, t, member) {
+ const char *tname = btf_name_by_offset(btf, member->name_off);
+
+ if (strcmp(ptr, tname) == 0) {
+ *type = btf_type_by_id(btf, member->type);
+ return BITS_ROUNDDOWN_BYTES(member->offset);
member->offset does not only contain the offset, and the offset may not be
a multiple of 8:
https://elixir.bootlin.com/linux/v6.16/source/include/uapi/linux/btf.h#L126
From the BTF spec (https://docs.kernel.org/bpf/btf.html):
If the kind_flag is set, the btf_member.offset contains
both member bitfield size and bit offset.
The bitfield size and bit offset are calculated as below.:
#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24)
#define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff)
So basically just need to change that to:
if (strcmp(ptr, tname) == 0) {
int offset = BTF_MEMBER_BIT_OFFSET(member->offset);
*type = btf_type_by_id(btf, member->type);
return BITS_ROUNDDOWN_BYTES(offset);
?
+ }
+
+ /* Handle anonymous structures */
+ if (strlen(tname))
+ continue;
+
+ *type = btf_type_by_id(btf, member->type);
+ if (btf_type_is_struct(*type)) {
+ int offset = find_member(ptr, btf, type, level + 1);
+
+ if (offset < 0)
+ continue;
+
+ return offset + BITS_ROUNDDOWN_BYTES(member->offset);
And here too.
-- Steve
+ }
+ }
+
+ return -1;
+}
+