1 /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2 /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
6 #include <linux/errno.h>
7 #include "bpf_helpers.h"
8 #include "bpf_tracing.h"
10 /* Below types and maps are internal implementation details of libbpf's USDT
11 * support and are subjects to change. Also, bpf_usdt_xxx() API helpers should
12 * be considered an unstable API as well and might be adjusted based on user
13 * feedback from using libbpf's USDT support in production.
16 /* User can override BPF_USDT_MAX_SPEC_CNT to change default size of internal
17 * map that keeps track of USDT argument specifications. This might be
18 * necessary if there are a lot of USDT attachments.
20 #ifndef BPF_USDT_MAX_SPEC_CNT
21 #define BPF_USDT_MAX_SPEC_CNT 256
23 /* User can override BPF_USDT_MAX_IP_CNT to change default size of internal
24 * map that keeps track of IP (memory address) mapping to USDT argument
26 * Note, if kernel supports BPF cookies, this map is not used and could be
27 * resized all the way to 1 to save a bit of memory.
29 #ifndef BPF_USDT_MAX_IP_CNT
30 #define BPF_USDT_MAX_IP_CNT (4 * BPF_USDT_MAX_SPEC_CNT)
33 enum __bpf_usdt_arg_type
{
36 BPF_USDT_ARG_REG_DEREF
,
39 struct __bpf_usdt_arg_spec
{
40 /* u64 scalar interpreted depending on arg_type, see below */
42 /* arg location case, see bpf_usdt_arg() for details */
43 enum __bpf_usdt_arg_type arg_type
;
44 /* offset of referenced register within struct pt_regs */
46 /* whether arg should be interpreted as signed value */
48 /* number of bits that need to be cleared and, optionally,
49 * sign-extended to cast arguments that are 1, 2, or 4 bytes
50 * long into final 8-byte u64/s64 value returned to user
55 /* should match USDT_MAX_ARG_CNT in usdt.c exactly */
56 #define BPF_USDT_MAX_ARG_CNT 12
57 struct __bpf_usdt_spec
{
58 struct __bpf_usdt_arg_spec args
[BPF_USDT_MAX_ARG_CNT
];
64 __uint(type
, BPF_MAP_TYPE_ARRAY
);
65 __uint(max_entries
, BPF_USDT_MAX_SPEC_CNT
);
67 __type(value
, struct __bpf_usdt_spec
);
68 } __bpf_usdt_specs
SEC(".maps") __weak
;
71 __uint(type
, BPF_MAP_TYPE_HASH
);
72 __uint(max_entries
, BPF_USDT_MAX_IP_CNT
);
75 } __bpf_usdt_ip_to_spec_id
SEC(".maps") __weak
;
77 extern const _Bool LINUX_HAS_BPF_COOKIE __kconfig
;
79 static __always_inline
80 int __bpf_usdt_spec_id(struct pt_regs
*ctx
)
82 if (!LINUX_HAS_BPF_COOKIE
) {
83 long ip
= PT_REGS_IP(ctx
);
86 spec_id_ptr
= bpf_map_lookup_elem(&__bpf_usdt_ip_to_spec_id
, &ip
);
87 return spec_id_ptr
? *spec_id_ptr
: -ESRCH
;
90 return bpf_get_attach_cookie(ctx
);
93 /* Return number of USDT arguments defined for currently traced USDT. */
95 int bpf_usdt_arg_cnt(struct pt_regs
*ctx
)
97 struct __bpf_usdt_spec
*spec
;
100 spec_id
= __bpf_usdt_spec_id(ctx
);
104 spec
= bpf_map_lookup_elem(&__bpf_usdt_specs
, &spec_id
);
108 return spec
->arg_cnt
;
111 /* Fetch USDT argument #*arg_num* (zero-indexed) and put its value into *res.
112 * Returns 0 on success; negative error, otherwise.
113 * On error *res is guaranteed to be set to zero.
116 int bpf_usdt_arg(struct pt_regs
*ctx
, __u64 arg_num
, long *res
)
118 struct __bpf_usdt_spec
*spec
;
119 struct __bpf_usdt_arg_spec
*arg_spec
;
125 spec_id
= __bpf_usdt_spec_id(ctx
);
129 spec
= bpf_map_lookup_elem(&__bpf_usdt_specs
, &spec_id
);
133 if (arg_num
>= BPF_USDT_MAX_ARG_CNT
)
135 barrier_var(arg_num
);
136 if (arg_num
>= spec
->arg_cnt
)
139 arg_spec
= &spec
->args
[arg_num
];
140 switch (arg_spec
->arg_type
) {
141 case BPF_USDT_ARG_CONST
:
142 /* Arg is just a constant ("-4@$-9" in USDT arg spec).
143 * value is recorded in arg_spec->val_off directly.
145 val
= arg_spec
->val_off
;
147 case BPF_USDT_ARG_REG
:
148 /* Arg is in a register (e.g, "8@%rax" in USDT arg spec),
149 * so we read the contents of that register directly from
150 * struct pt_regs. To keep things simple user-space parts
151 * record offsetof(struct pt_regs, <regname>) in arg_spec->reg_off.
153 err
= bpf_probe_read_kernel(&val
, sizeof(val
), (void *)ctx
+ arg_spec
->reg_off
);
157 case BPF_USDT_ARG_REG_DEREF
:
158 /* Arg is in memory addressed by register, plus some offset
159 * (e.g., "-4@-1204(%rbp)" in USDT arg spec). Register is
160 * identified like with BPF_USDT_ARG_REG case, and the offset
161 * is in arg_spec->val_off. We first fetch register contents
162 * from pt_regs, then do another user-space probe read to
163 * fetch argument value itself.
165 err
= bpf_probe_read_kernel(&val
, sizeof(val
), (void *)ctx
+ arg_spec
->reg_off
);
168 err
= bpf_probe_read_user(&val
, sizeof(val
), (void *)val
+ arg_spec
->val_off
);
171 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
172 val
>>= arg_spec
->arg_bitshift
;
179 /* cast arg from 1, 2, or 4 bytes to final 8 byte size clearing
180 * necessary upper arg_bitshift bits, with sign extension if argument
183 val
<<= arg_spec
->arg_bitshift
;
184 if (arg_spec
->arg_signed
)
185 val
= ((long)val
) >> arg_spec
->arg_bitshift
;
187 val
= val
>> arg_spec
->arg_bitshift
;
192 /* Retrieve user-specified cookie value provided during attach as
193 * bpf_usdt_opts.usdt_cookie. This serves the same purpose as BPF cookie
194 * returned by bpf_get_attach_cookie(). Libbpf's support for USDT is itself
195 * utilizing BPF cookies internally, so user can't use BPF cookie directly
196 * for USDT programs and has to use bpf_usdt_cookie() API instead.
199 long bpf_usdt_cookie(struct pt_regs
*ctx
)
201 struct __bpf_usdt_spec
*spec
;
204 spec_id
= __bpf_usdt_spec_id(ctx
);
208 spec
= bpf_map_lookup_elem(&__bpf_usdt_specs
, &spec_id
);
212 return spec
->usdt_cookie
;
215 /* we rely on ___bpf_apply() and ___bpf_narg() macros already defined in bpf_tracing.h */
216 #define ___bpf_usdt_args0() ctx
217 #define ___bpf_usdt_args1(x) ___bpf_usdt_args0(), ({ long _x; bpf_usdt_arg(ctx, 0, &_x); _x; })
218 #define ___bpf_usdt_args2(x, args...) ___bpf_usdt_args1(args), ({ long _x; bpf_usdt_arg(ctx, 1, &_x); _x; })
219 #define ___bpf_usdt_args3(x, args...) ___bpf_usdt_args2(args), ({ long _x; bpf_usdt_arg(ctx, 2, &_x); _x; })
220 #define ___bpf_usdt_args4(x, args...) ___bpf_usdt_args3(args), ({ long _x; bpf_usdt_arg(ctx, 3, &_x); _x; })
221 #define ___bpf_usdt_args5(x, args...) ___bpf_usdt_args4(args), ({ long _x; bpf_usdt_arg(ctx, 4, &_x); _x; })
222 #define ___bpf_usdt_args6(x, args...) ___bpf_usdt_args5(args), ({ long _x; bpf_usdt_arg(ctx, 5, &_x); _x; })
223 #define ___bpf_usdt_args7(x, args...) ___bpf_usdt_args6(args), ({ long _x; bpf_usdt_arg(ctx, 6, &_x); _x; })
224 #define ___bpf_usdt_args8(x, args...) ___bpf_usdt_args7(args), ({ long _x; bpf_usdt_arg(ctx, 7, &_x); _x; })
225 #define ___bpf_usdt_args9(x, args...) ___bpf_usdt_args8(args), ({ long _x; bpf_usdt_arg(ctx, 8, &_x); _x; })
226 #define ___bpf_usdt_args10(x, args...) ___bpf_usdt_args9(args), ({ long _x; bpf_usdt_arg(ctx, 9, &_x); _x; })
227 #define ___bpf_usdt_args11(x, args...) ___bpf_usdt_args10(args), ({ long _x; bpf_usdt_arg(ctx, 10, &_x); _x; })
228 #define ___bpf_usdt_args12(x, args...) ___bpf_usdt_args11(args), ({ long _x; bpf_usdt_arg(ctx, 11, &_x); _x; })
229 #define ___bpf_usdt_args(args...) ___bpf_apply(___bpf_usdt_args, ___bpf_narg(args))(args)
232 * BPF_USDT serves the same purpose for USDT handlers as BPF_PROG for
233 * tp_btf/fentry/fexit BPF programs and BPF_KPROBE for kprobes.
234 * Original struct pt_regs * context is preserved as 'ctx' argument.
236 #define BPF_USDT(name, args...) \
237 name(struct pt_regs *ctx); \
238 static __always_inline typeof(name(0)) \
239 ____##name(struct pt_regs *ctx, ##args); \
240 typeof(name(0)) name(struct pt_regs *ctx) \
242 _Pragma("GCC diagnostic push") \
243 _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
244 return ____##name(___bpf_usdt_args(args)); \
245 _Pragma("GCC diagnostic pop") \
247 static __always_inline typeof(name(0)) \
248 ____##name(struct pt_regs *ctx, ##args)
250 #endif /* __USDT_BPF_H__ */