1 /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2 /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
3 #ifndef __USDT_BPF_H__
4 #define __USDT_BPF_H__
5
6 #include <linux/errno.h>
7 #include <bpf/bpf_helpers.h>
8 #include <bpf/bpf_tracing.h>
9
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.
14 */
15
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.
19 */
20 #ifndef BPF_USDT_MAX_SPEC_CNT
21 #define BPF_USDT_MAX_SPEC_CNT 256
22 #endif
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
25 * specification.
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.
28 */
29 #ifndef BPF_USDT_MAX_IP_CNT
30 #define BPF_USDT_MAX_IP_CNT (4 * BPF_USDT_MAX_SPEC_CNT)
31 #endif
32
33 enum __bpf_usdt_arg_type {
34 BPF_USDT_ARG_CONST,
35 BPF_USDT_ARG_REG,
36 BPF_USDT_ARG_REG_DEREF,
37 };
38
39 struct __bpf_usdt_arg_spec {
40 /* u64 scalar interpreted depending on arg_type, see below */
41 __u64 val_off;
42 /* arg location case, see bpf_udst_arg() for details */
43 enum __bpf_usdt_arg_type arg_type;
44 /* offset of referenced register within struct pt_regs */
45 short reg_off;
46 /* whether arg should be interpreted as signed value */
47 bool arg_signed;
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
51 */
52 char arg_bitshift;
53 };
54
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];
59 __u64 usdt_cookie;
60 short arg_cnt;
61 };
62
63 struct {
64 __uint(type, BPF_MAP_TYPE_ARRAY);
65 __uint(max_entries, BPF_USDT_MAX_SPEC_CNT);
66 __type(key, int);
67 __type(value, struct __bpf_usdt_spec);
68 } __bpf_usdt_specs SEC(".maps") __weak;
69
70 struct {
71 __uint(type, BPF_MAP_TYPE_HASH);
72 __uint(max_entries, BPF_USDT_MAX_IP_CNT);
73 __type(key, long);
74 __type(value, __u32);
75 } __bpf_usdt_ip_to_spec_id SEC(".maps") __weak;
76
77 extern const _Bool LINUX_HAS_BPF_COOKIE __kconfig;
78
79 static __always_inline
__bpf_usdt_spec_id(struct pt_regs * ctx)80 int __bpf_usdt_spec_id(struct pt_regs *ctx)
81 {
82 if (!LINUX_HAS_BPF_COOKIE) {
83 long ip = PT_REGS_IP(ctx);
84 int *spec_id_ptr;
85
86 spec_id_ptr = bpf_map_lookup_elem(&__bpf_usdt_ip_to_spec_id, &ip);
87 return spec_id_ptr ? *spec_id_ptr : -ESRCH;
88 }
89
90 return bpf_get_attach_cookie(ctx);
91 }
92
93 /* Return number of USDT arguments defined for currently traced USDT. */
94 __weak __hidden
bpf_usdt_arg_cnt(struct pt_regs * ctx)95 int bpf_usdt_arg_cnt(struct pt_regs *ctx)
96 {
97 struct __bpf_usdt_spec *spec;
98 int spec_id;
99
100 spec_id = __bpf_usdt_spec_id(ctx);
101 if (spec_id < 0)
102 return -ESRCH;
103
104 spec = bpf_map_lookup_elem(&__bpf_usdt_specs, &spec_id);
105 if (!spec)
106 return -ESRCH;
107
108 return spec->arg_cnt;
109 }
110
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.
114 */
115 __weak __hidden
bpf_usdt_arg(struct pt_regs * ctx,__u64 arg_num,long * res)116 int bpf_usdt_arg(struct pt_regs *ctx, __u64 arg_num, long *res)
117 {
118 struct __bpf_usdt_spec *spec;
119 struct __bpf_usdt_arg_spec *arg_spec;
120 unsigned long val;
121 int err, spec_id;
122
123 *res = 0;
124
125 spec_id = __bpf_usdt_spec_id(ctx);
126 if (spec_id < 0)
127 return -ESRCH;
128
129 spec = bpf_map_lookup_elem(&__bpf_usdt_specs, &spec_id);
130 if (!spec)
131 return -ESRCH;
132
133 if (arg_num >= BPF_USDT_MAX_ARG_CNT || arg_num >= spec->arg_cnt)
134 return -ENOENT;
135
136 arg_spec = &spec->args[arg_num];
137 switch (arg_spec->arg_type) {
138 case BPF_USDT_ARG_CONST:
139 /* Arg is just a constant ("-4@$-9" in USDT arg spec).
140 * value is recorded in arg_spec->val_off directly.
141 */
142 val = arg_spec->val_off;
143 break;
144 case BPF_USDT_ARG_REG:
145 /* Arg is in a register (e.g, "8@%rax" in USDT arg spec),
146 * so we read the contents of that register directly from
147 * struct pt_regs. To keep things simple user-space parts
148 * record offsetof(struct pt_regs, <regname>) in arg_spec->reg_off.
149 */
150 err = bpf_probe_read_kernel(&val, sizeof(val), (void *)ctx + arg_spec->reg_off);
151 if (err)
152 return err;
153 break;
154 case BPF_USDT_ARG_REG_DEREF:
155 /* Arg is in memory addressed by register, plus some offset
156 * (e.g., "-4@-1204(%rbp)" in USDT arg spec). Register is
157 * identified like with BPF_USDT_ARG_REG case, and the offset
158 * is in arg_spec->val_off. We first fetch register contents
159 * from pt_regs, then do another user-space probe read to
160 * fetch argument value itself.
161 */
162 err = bpf_probe_read_kernel(&val, sizeof(val), (void *)ctx + arg_spec->reg_off);
163 if (err)
164 return err;
165 err = bpf_probe_read_user(&val, sizeof(val), (void *)val + arg_spec->val_off);
166 if (err)
167 return err;
168 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
169 val >>= arg_spec->arg_bitshift;
170 #endif
171 break;
172 default:
173 return -EINVAL;
174 }
175
176 /* cast arg from 1, 2, or 4 bytes to final 8 byte size clearing
177 * necessary upper arg_bitshift bits, with sign extension if argument
178 * is signed
179 */
180 val <<= arg_spec->arg_bitshift;
181 if (arg_spec->arg_signed)
182 val = ((long)val) >> arg_spec->arg_bitshift;
183 else
184 val = val >> arg_spec->arg_bitshift;
185 *res = val;
186 return 0;
187 }
188
189 /* Retrieve user-specified cookie value provided during attach as
190 * bpf_usdt_opts.usdt_cookie. This serves the same purpose as BPF cookie
191 * returned by bpf_get_attach_cookie(). Libbpf's support for USDT is itself
192 * utilizing BPF cookies internally, so user can't use BPF cookie directly
193 * for USDT programs and has to use bpf_usdt_cookie() API instead.
194 */
195 __weak __hidden
bpf_usdt_cookie(struct pt_regs * ctx)196 long bpf_usdt_cookie(struct pt_regs *ctx)
197 {
198 struct __bpf_usdt_spec *spec;
199 int spec_id;
200
201 spec_id = __bpf_usdt_spec_id(ctx);
202 if (spec_id < 0)
203 return 0;
204
205 spec = bpf_map_lookup_elem(&__bpf_usdt_specs, &spec_id);
206 if (!spec)
207 return 0;
208
209 return spec->usdt_cookie;
210 }
211
212 /* we rely on ___bpf_apply() and ___bpf_narg() macros already defined in bpf_tracing.h */
213 #define ___bpf_usdt_args0() ctx
214 #define ___bpf_usdt_args1(x) ___bpf_usdt_args0(), ({ long _x; bpf_usdt_arg(ctx, 0, &_x); (void *)_x; })
215 #define ___bpf_usdt_args2(x, args...) ___bpf_usdt_args1(args), ({ long _x; bpf_usdt_arg(ctx, 1, &_x); (void *)_x; })
216 #define ___bpf_usdt_args3(x, args...) ___bpf_usdt_args2(args), ({ long _x; bpf_usdt_arg(ctx, 2, &_x); (void *)_x; })
217 #define ___bpf_usdt_args4(x, args...) ___bpf_usdt_args3(args), ({ long _x; bpf_usdt_arg(ctx, 3, &_x); (void *)_x; })
218 #define ___bpf_usdt_args5(x, args...) ___bpf_usdt_args4(args), ({ long _x; bpf_usdt_arg(ctx, 4, &_x); (void *)_x; })
219 #define ___bpf_usdt_args6(x, args...) ___bpf_usdt_args5(args), ({ long _x; bpf_usdt_arg(ctx, 5, &_x); (void *)_x; })
220 #define ___bpf_usdt_args7(x, args...) ___bpf_usdt_args6(args), ({ long _x; bpf_usdt_arg(ctx, 6, &_x); (void *)_x; })
221 #define ___bpf_usdt_args8(x, args...) ___bpf_usdt_args7(args), ({ long _x; bpf_usdt_arg(ctx, 7, &_x); (void *)_x; })
222 #define ___bpf_usdt_args9(x, args...) ___bpf_usdt_args8(args), ({ long _x; bpf_usdt_arg(ctx, 8, &_x); (void *)_x; })
223 #define ___bpf_usdt_args10(x, args...) ___bpf_usdt_args9(args), ({ long _x; bpf_usdt_arg(ctx, 9, &_x); (void *)_x; })
224 #define ___bpf_usdt_args11(x, args...) ___bpf_usdt_args10(args), ({ long _x; bpf_usdt_arg(ctx, 10, &_x); (void *)_x; })
225 #define ___bpf_usdt_args12(x, args...) ___bpf_usdt_args11(args), ({ long _x; bpf_usdt_arg(ctx, 11, &_x); (void *)_x; })
226 #define ___bpf_usdt_args(args...) ___bpf_apply(___bpf_usdt_args, ___bpf_narg(args))(args)
227
228 /*
229 * BPF_USDT serves the same purpose for USDT handlers as BPF_PROG for
230 * tp_btf/fentry/fexit BPF programs and BPF_KPROBE for kprobes.
231 * Original struct pt_regs * context is preserved as 'ctx' argument.
232 */
233 #define BPF_USDT(name, args...) \
234 name(struct pt_regs *ctx); \
235 static __always_inline typeof(name(0)) \
236 ____##name(struct pt_regs *ctx, ##args); \
237 typeof(name(0)) name(struct pt_regs *ctx) \
238 { \
239 _Pragma("GCC diagnostic push") \
240 _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
241 return ____##name(___bpf_usdt_args(args)); \
242 _Pragma("GCC diagnostic pop") \
243 } \
244 static __always_inline typeof(name(0)) \
245 ____##name(struct pt_regs *ctx, ##args)
246
247 #endif /* __USDT_BPF_H__ */
248