On 31-07-2025 14:29, Steven Rostedt wrote:
On Thu, 31 Jul 2025 12:44:49 +0100
Douglas Raillard <douglas.raill...@arm.com> 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);
?
This would work in practice for now, but strictly speaking you should check
the kind_flag field in btf_type.info (bit 31) of the parent struct/union:
https://elixir.bootlin.com/linux/v6.16/source/include/uapi/linux/btf.h#L38
__btf_member_bit_offset() seems to do exactly that.
While writing that, I realized there is another subtlety: BTF encodes int
member offsets in 2 different ways:
1. Either their bit offset is encoded struct btf_member, and the btf_type of
the member is an integer type with no leading padding bits.
2. Or the rounded-down offset is encoded in struct btf_member and the integer
type contains some leading padding bits information:
https://docs.kernel.org/bpf/btf.html#btf-kind-int
The 2nd case is somewhat surprising but BTF_KIND_INT has 3 pieces of
information:
1. The C signedness of the type.
2. The number of value bits of the type.
3. The offset of the 1st bit to interpret as being the value. Anything before
is leading padding.
That means that the actual bit offset of an int member's value in a parent
struct is:
<offset of the member> + <offset of the type of the member>
You could technically have all members with btf_member.offset == 0 and then
encode the actual values offsets in the btf_type of the members.
+ }
+
+ /* 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;
+}
+