xref: /linux/tools/testing/selftests/bpf/benchs/bench_trigger.c (revision b86761ff6374813cdf64ffd6b95ddd1813c435d8)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2020 Facebook */
3 #define _GNU_SOURCE
4 #include <unistd.h>
5 #include "bench.h"
6 #include "trigger_bench.skel.h"
7 #include "trace_helpers.h"
8 
9 /* adjust slot shift in inc_hits() if changing */
10 #define MAX_BUCKETS 256
11 
12 #pragma GCC diagnostic ignored "-Wattributes"
13 
14 /* BPF triggering benchmarks */
15 static struct trigger_ctx {
16 	struct trigger_bench *skel;
17 } ctx;
18 
19 static struct counter base_hits[MAX_BUCKETS];
20 
21 static __always_inline void inc_counter(struct counter *counters)
22 {
23 	static __thread int tid = 0;
24 	unsigned slot;
25 
26 	if (unlikely(tid == 0))
27 		tid = syscall(SYS_gettid);
28 
29 	/* multiplicative hashing, it's fast */
30 	slot = 2654435769U * tid;
31 	slot >>= 24;
32 
33 	atomic_inc(&base_hits[slot].value); /* use highest byte as an index */
34 }
35 
36 static long sum_and_reset_counters(struct counter *counters)
37 {
38 	int i;
39 	long sum = 0;
40 
41 	for (i = 0; i < MAX_BUCKETS; i++)
42 		sum += atomic_swap(&counters[i].value, 0);
43 	return sum;
44 }
45 
46 static void trigger_validate(void)
47 {
48 	if (env.consumer_cnt != 0) {
49 		fprintf(stderr, "benchmark doesn't support consumer!\n");
50 		exit(1);
51 	}
52 }
53 
54 static void *trigger_base_producer(void *input)
55 {
56 	while (true) {
57 		(void)syscall(__NR_getpgid);
58 		inc_counter(base_hits);
59 	}
60 	return NULL;
61 }
62 
63 static void trigger_base_measure(struct bench_res *res)
64 {
65 	res->hits = sum_and_reset_counters(base_hits);
66 }
67 
68 static void *trigger_producer(void *input)
69 {
70 	while (true)
71 		(void)syscall(__NR_getpgid);
72 	return NULL;
73 }
74 
75 static void trigger_measure(struct bench_res *res)
76 {
77 	res->hits = sum_and_reset_counters(ctx.skel->bss->hits);
78 }
79 
80 static void setup_ctx(void)
81 {
82 	setup_libbpf();
83 
84 	ctx.skel = trigger_bench__open_and_load();
85 	if (!ctx.skel) {
86 		fprintf(stderr, "failed to open skeleton\n");
87 		exit(1);
88 	}
89 }
90 
91 static void attach_bpf(struct bpf_program *prog)
92 {
93 	struct bpf_link *link;
94 
95 	link = bpf_program__attach(prog);
96 	if (!link) {
97 		fprintf(stderr, "failed to attach program!\n");
98 		exit(1);
99 	}
100 }
101 
102 static void trigger_tp_setup(void)
103 {
104 	setup_ctx();
105 	attach_bpf(ctx.skel->progs.bench_trigger_tp);
106 }
107 
108 static void trigger_rawtp_setup(void)
109 {
110 	setup_ctx();
111 	attach_bpf(ctx.skel->progs.bench_trigger_raw_tp);
112 }
113 
114 static void trigger_kprobe_setup(void)
115 {
116 	setup_ctx();
117 	attach_bpf(ctx.skel->progs.bench_trigger_kprobe);
118 }
119 
120 static void trigger_kretprobe_setup(void)
121 {
122 	setup_ctx();
123 	attach_bpf(ctx.skel->progs.bench_trigger_kretprobe);
124 }
125 
126 static void trigger_kprobe_multi_setup(void)
127 {
128 	setup_ctx();
129 	attach_bpf(ctx.skel->progs.bench_trigger_kprobe_multi);
130 }
131 
132 static void trigger_kretprobe_multi_setup(void)
133 {
134 	setup_ctx();
135 	attach_bpf(ctx.skel->progs.bench_trigger_kretprobe_multi);
136 }
137 
138 static void trigger_fentry_setup(void)
139 {
140 	setup_ctx();
141 	attach_bpf(ctx.skel->progs.bench_trigger_fentry);
142 }
143 
144 static void trigger_fexit_setup(void)
145 {
146 	setup_ctx();
147 	attach_bpf(ctx.skel->progs.bench_trigger_fexit);
148 }
149 
150 static void trigger_fentry_sleep_setup(void)
151 {
152 	setup_ctx();
153 	attach_bpf(ctx.skel->progs.bench_trigger_fentry_sleep);
154 }
155 
156 static void trigger_fmodret_setup(void)
157 {
158 	setup_ctx();
159 	attach_bpf(ctx.skel->progs.bench_trigger_fmodret);
160 }
161 
162 /* make sure call is not inlined and not avoided by compiler, so __weak and
163  * inline asm volatile in the body of the function
164  *
165  * There is a performance difference between uprobing at nop location vs other
166  * instructions. So use two different targets, one of which starts with nop
167  * and another doesn't.
168  *
169  * GCC doesn't generate stack setup preample for these functions due to them
170  * having no input arguments and doing nothing in the body.
171  */
172 __nocf_check __weak void uprobe_target_nop(void)
173 {
174 	asm volatile ("nop");
175 }
176 
177 __weak void opaque_noop_func(void)
178 {
179 }
180 
181 __nocf_check __weak int uprobe_target_push(void)
182 {
183 	/* overhead of function call is negligible compared to uprobe
184 	 * triggering, so this shouldn't affect benchmark results much
185 	 */
186 	opaque_noop_func();
187 	return 1;
188 }
189 
190 __nocf_check __weak void uprobe_target_ret(void)
191 {
192 	asm volatile ("");
193 }
194 
195 static void *uprobe_base_producer(void *input)
196 {
197 	while (true) {
198 		uprobe_target_nop();
199 		inc_counter(base_hits);
200 	}
201 	return NULL;
202 }
203 
204 static void *uprobe_producer_nop(void *input)
205 {
206 	while (true)
207 		uprobe_target_nop();
208 	return NULL;
209 }
210 
211 static void *uprobe_producer_push(void *input)
212 {
213 	while (true)
214 		uprobe_target_push();
215 	return NULL;
216 }
217 
218 static void *uprobe_producer_ret(void *input)
219 {
220 	while (true)
221 		uprobe_target_ret();
222 	return NULL;
223 }
224 
225 static void usetup(bool use_retprobe, void *target_addr)
226 {
227 	size_t uprobe_offset;
228 	struct bpf_link *link;
229 
230 	setup_libbpf();
231 
232 	ctx.skel = trigger_bench__open_and_load();
233 	if (!ctx.skel) {
234 		fprintf(stderr, "failed to open skeleton\n");
235 		exit(1);
236 	}
237 
238 	uprobe_offset = get_uprobe_offset(target_addr);
239 	link = bpf_program__attach_uprobe(ctx.skel->progs.bench_trigger_uprobe,
240 					  use_retprobe,
241 					  -1 /* all PIDs */,
242 					  "/proc/self/exe",
243 					  uprobe_offset);
244 	if (!link) {
245 		fprintf(stderr, "failed to attach uprobe!\n");
246 		exit(1);
247 	}
248 	ctx.skel->links.bench_trigger_uprobe = link;
249 }
250 
251 static void uprobe_setup_nop(void)
252 {
253 	usetup(false, &uprobe_target_nop);
254 }
255 
256 static void uretprobe_setup_nop(void)
257 {
258 	usetup(true, &uprobe_target_nop);
259 }
260 
261 static void uprobe_setup_push(void)
262 {
263 	usetup(false, &uprobe_target_push);
264 }
265 
266 static void uretprobe_setup_push(void)
267 {
268 	usetup(true, &uprobe_target_push);
269 }
270 
271 static void uprobe_setup_ret(void)
272 {
273 	usetup(false, &uprobe_target_ret);
274 }
275 
276 static void uretprobe_setup_ret(void)
277 {
278 	usetup(true, &uprobe_target_ret);
279 }
280 
281 const struct bench bench_trig_base = {
282 	.name = "trig-base",
283 	.validate = trigger_validate,
284 	.producer_thread = trigger_base_producer,
285 	.measure = trigger_base_measure,
286 	.report_progress = hits_drops_report_progress,
287 	.report_final = hits_drops_report_final,
288 };
289 
290 const struct bench bench_trig_tp = {
291 	.name = "trig-tp",
292 	.validate = trigger_validate,
293 	.setup = trigger_tp_setup,
294 	.producer_thread = trigger_producer,
295 	.measure = trigger_measure,
296 	.report_progress = hits_drops_report_progress,
297 	.report_final = hits_drops_report_final,
298 };
299 
300 const struct bench bench_trig_rawtp = {
301 	.name = "trig-rawtp",
302 	.validate = trigger_validate,
303 	.setup = trigger_rawtp_setup,
304 	.producer_thread = trigger_producer,
305 	.measure = trigger_measure,
306 	.report_progress = hits_drops_report_progress,
307 	.report_final = hits_drops_report_final,
308 };
309 
310 const struct bench bench_trig_kprobe = {
311 	.name = "trig-kprobe",
312 	.validate = trigger_validate,
313 	.setup = trigger_kprobe_setup,
314 	.producer_thread = trigger_producer,
315 	.measure = trigger_measure,
316 	.report_progress = hits_drops_report_progress,
317 	.report_final = hits_drops_report_final,
318 };
319 
320 const struct bench bench_trig_kretprobe = {
321 	.name = "trig-kretprobe",
322 	.validate = trigger_validate,
323 	.setup = trigger_kretprobe_setup,
324 	.producer_thread = trigger_producer,
325 	.measure = trigger_measure,
326 	.report_progress = hits_drops_report_progress,
327 	.report_final = hits_drops_report_final,
328 };
329 
330 const struct bench bench_trig_kprobe_multi = {
331 	.name = "trig-kprobe-multi",
332 	.validate = trigger_validate,
333 	.setup = trigger_kprobe_multi_setup,
334 	.producer_thread = trigger_producer,
335 	.measure = trigger_measure,
336 	.report_progress = hits_drops_report_progress,
337 	.report_final = hits_drops_report_final,
338 };
339 
340 const struct bench bench_trig_kretprobe_multi = {
341 	.name = "trig-kretprobe-multi",
342 	.validate = trigger_validate,
343 	.setup = trigger_kretprobe_multi_setup,
344 	.producer_thread = trigger_producer,
345 	.measure = trigger_measure,
346 	.report_progress = hits_drops_report_progress,
347 	.report_final = hits_drops_report_final,
348 };
349 
350 const struct bench bench_trig_fentry = {
351 	.name = "trig-fentry",
352 	.validate = trigger_validate,
353 	.setup = trigger_fentry_setup,
354 	.producer_thread = trigger_producer,
355 	.measure = trigger_measure,
356 	.report_progress = hits_drops_report_progress,
357 	.report_final = hits_drops_report_final,
358 };
359 
360 const struct bench bench_trig_fexit = {
361 	.name = "trig-fexit",
362 	.validate = trigger_validate,
363 	.setup = trigger_fexit_setup,
364 	.producer_thread = trigger_producer,
365 	.measure = trigger_measure,
366 	.report_progress = hits_drops_report_progress,
367 	.report_final = hits_drops_report_final,
368 };
369 
370 const struct bench bench_trig_fentry_sleep = {
371 	.name = "trig-fentry-sleep",
372 	.validate = trigger_validate,
373 	.setup = trigger_fentry_sleep_setup,
374 	.producer_thread = trigger_producer,
375 	.measure = trigger_measure,
376 	.report_progress = hits_drops_report_progress,
377 	.report_final = hits_drops_report_final,
378 };
379 
380 const struct bench bench_trig_fmodret = {
381 	.name = "trig-fmodret",
382 	.validate = trigger_validate,
383 	.setup = trigger_fmodret_setup,
384 	.producer_thread = trigger_producer,
385 	.measure = trigger_measure,
386 	.report_progress = hits_drops_report_progress,
387 	.report_final = hits_drops_report_final,
388 };
389 
390 const struct bench bench_trig_uprobe_base = {
391 	.name = "trig-uprobe-base",
392 	.setup = NULL, /* no uprobe/uretprobe is attached */
393 	.producer_thread = uprobe_base_producer,
394 	.measure = trigger_base_measure,
395 	.report_progress = hits_drops_report_progress,
396 	.report_final = hits_drops_report_final,
397 };
398 
399 const struct bench bench_trig_uprobe_nop = {
400 	.name = "trig-uprobe-nop",
401 	.setup = uprobe_setup_nop,
402 	.producer_thread = uprobe_producer_nop,
403 	.measure = trigger_measure,
404 	.report_progress = hits_drops_report_progress,
405 	.report_final = hits_drops_report_final,
406 };
407 
408 const struct bench bench_trig_uretprobe_nop = {
409 	.name = "trig-uretprobe-nop",
410 	.setup = uretprobe_setup_nop,
411 	.producer_thread = uprobe_producer_nop,
412 	.measure = trigger_measure,
413 	.report_progress = hits_drops_report_progress,
414 	.report_final = hits_drops_report_final,
415 };
416 
417 const struct bench bench_trig_uprobe_push = {
418 	.name = "trig-uprobe-push",
419 	.setup = uprobe_setup_push,
420 	.producer_thread = uprobe_producer_push,
421 	.measure = trigger_measure,
422 	.report_progress = hits_drops_report_progress,
423 	.report_final = hits_drops_report_final,
424 };
425 
426 const struct bench bench_trig_uretprobe_push = {
427 	.name = "trig-uretprobe-push",
428 	.setup = uretprobe_setup_push,
429 	.producer_thread = uprobe_producer_push,
430 	.measure = trigger_measure,
431 	.report_progress = hits_drops_report_progress,
432 	.report_final = hits_drops_report_final,
433 };
434 
435 const struct bench bench_trig_uprobe_ret = {
436 	.name = "trig-uprobe-ret",
437 	.setup = uprobe_setup_ret,
438 	.producer_thread = uprobe_producer_ret,
439 	.measure = trigger_measure,
440 	.report_progress = hits_drops_report_progress,
441 	.report_final = hits_drops_report_final,
442 };
443 
444 const struct bench bench_trig_uretprobe_ret = {
445 	.name = "trig-uretprobe-ret",
446 	.setup = uretprobe_setup_ret,
447 	.producer_thread = uprobe_producer_ret,
448 	.measure = trigger_measure,
449 	.report_progress = hits_drops_report_progress,
450 	.report_final = hits_drops_report_final,
451 };
452