xref: /linux/tools/testing/selftests/bpf/prog_tests/tracing_multi.c (revision c49f336dbcf30ff8622d3725c54fe1c90e8ccd9c)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <test_progs.h>
4 #include <bpf/btf.h>
5 #include <search.h>
6 #include "bpf/libbpf_internal.h"
7 #include "tracing_multi.skel.h"
8 #include "tracing_multi_module.skel.h"
9 #include "tracing_multi_intersect.skel.h"
10 #include "tracing_multi_session.skel.h"
11 #include "tracing_multi_fail.skel.h"
12 #include "tracing_multi_verifier.skel.h"
13 #include "tracing_multi_bench.skel.h"
14 #include "tracing_multi_rollback.skel.h"
15 #include "trace_helpers.h"
16 
17 static __u64 bpf_fentry_test_cookies[] = {
18 	8,  /* bpf_fentry_test1 */
19 	9,  /* bpf_fentry_test2 */
20 	7,  /* bpf_fentry_test3 */
21 	5,  /* bpf_fentry_test4 */
22 	4,  /* bpf_fentry_test5 */
23 	2,  /* bpf_fentry_test6 */
24 	3,  /* bpf_fentry_test7 */
25 	1,  /* bpf_fentry_test8 */
26 	10, /* bpf_fentry_test9 */
27 	6,  /* bpf_fentry_test10 */
28 };
29 
30 static const char * const bpf_fentry_test[] = {
31 	"bpf_fentry_test1",
32 	"bpf_fentry_test2",
33 	"bpf_fentry_test3",
34 	"bpf_fentry_test4",
35 	"bpf_fentry_test5",
36 	"bpf_fentry_test6",
37 	"bpf_fentry_test7",
38 	"bpf_fentry_test8",
39 	"bpf_fentry_test9",
40 	"bpf_fentry_test10",
41 };
42 
43 static const char * const bpf_testmod_fentry_test[] = {
44 	"bpf_testmod_fentry_test1",
45 	"bpf_testmod_fentry_test2",
46 	"bpf_testmod_fentry_test3",
47 	"bpf_testmod_fentry_test7",
48 	"bpf_testmod_fentry_test11",
49 };
50 
51 #define FUNCS_CNT (ARRAY_SIZE(bpf_fentry_test))
52 
53 static int get_random_funcs(const char **funcs)
54 {
55 	int i, cnt = 0;
56 
57 	for (i = 0; i < FUNCS_CNT; i++) {
58 		if (rand() % 2)
59 			funcs[cnt++] = bpf_fentry_test[i];
60 	}
61 	/* we always need at least one.. */
62 	if (!cnt)
63 		funcs[cnt++] = bpf_fentry_test[rand() % FUNCS_CNT];
64 	return cnt;
65 }
66 
67 static int compare(const void *ppa, const void *ppb)
68 {
69 	const char *pa = *(const char **) ppa;
70 	const char *pb = *(const char **) ppb;
71 
72 	return strcmp(pa, pb);
73 }
74 
75 static void tdestroy_free_nop(void *ptr)
76 {
77 }
78 
79 static __u32 *get_ids(const char * const funcs[], int funcs_cnt, const char *mod)
80 {
81 	struct btf *btf, *vmlinux_btf = NULL;
82 	__u32 nr, type_id, cnt = 0;
83 	void *root = NULL;
84 	__u32 *ids = NULL;
85 	int i, err = 0;
86 
87 	btf = btf__load_vmlinux_btf();
88 	if (!ASSERT_OK_PTR(btf, "btf__load_vmlinux_btf"))
89 		return NULL;
90 
91 	if (mod) {
92 		vmlinux_btf = btf;
93 		btf = btf__load_module_btf(mod, vmlinux_btf);
94 		if (!ASSERT_OK_PTR(btf, "btf__load_module_btf")) {
95 			btf__free(vmlinux_btf);
96 			return NULL;
97 		}
98 	}
99 
100 	ids = calloc(funcs_cnt, sizeof(ids[0]));
101 	if (!ids)
102 		goto out;
103 
104 	/*
105 	 * We sort function names by name and search them
106 	 * below for each function.
107 	 */
108 	for (i = 0; i < funcs_cnt; i++) {
109 		if (!tsearch(&funcs[i], &root, compare)) {
110 			ASSERT_FAIL("tsearch failed");
111 			err = -1;
112 			goto error;
113 		}
114 	}
115 
116 	nr = btf__type_cnt(btf);
117 	for (type_id = 1; type_id < nr && cnt < funcs_cnt; type_id++) {
118 		const struct btf_type *type;
119 		const char *str, ***val;
120 		unsigned int idx;
121 
122 		type = btf__type_by_id(btf, type_id);
123 		if (!type) {
124 			err = -1;
125 			break;
126 		}
127 
128 		if (BTF_INFO_KIND(type->info) != BTF_KIND_FUNC)
129 			continue;
130 
131 		str = btf__name_by_offset(btf, type->name_off);
132 		if (!str) {
133 			err = -1;
134 			break;
135 		}
136 
137 		val = tfind(&str, &root, compare);
138 		if (!val)
139 			continue;
140 
141 		/*
142 		 * We keep pointer for each function name so we can get the original
143 		 * array index and have the resulting ids array matching the original
144 		 * function array.
145 		 *
146 		 * Doing it this way allow us to easily test the cookies support,
147 		 * because each cookie is attached to particular function/id.
148 		 */
149 		idx = *val - funcs;
150 		ids[idx] = type_id;
151 		cnt++;
152 	}
153 
154 error:
155 	if (err) {
156 		free(ids);
157 		ids = NULL;
158 	}
159 
160 out:
161 	tdestroy(root, tdestroy_free_nop);
162 	btf__free(vmlinux_btf);
163 	btf__free(btf);
164 	return ids;
165 }
166 
167 static void tracing_multi_test_run(struct tracing_multi *skel)
168 {
169 	LIBBPF_OPTS(bpf_test_run_opts, topts);
170 	int err, prog_fd;
171 
172 	prog_fd = bpf_program__fd(skel->progs.test_fentry);
173 	err = bpf_prog_test_run_opts(prog_fd, &topts);
174 	ASSERT_OK(err, "test_run");
175 
176 	/* extra +1 count for sleepable programs */
177 	ASSERT_EQ(skel->bss->test_result_fentry, FUNCS_CNT + 1, "test_result_fentry");
178 	ASSERT_EQ(skel->bss->test_result_fexit, FUNCS_CNT + 1, "test_result_fexit");
179 }
180 
181 static void test_skel_api(void)
182 {
183 	struct tracing_multi *skel;
184 	int err;
185 
186 	skel = tracing_multi__open_and_load();
187 	if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load"))
188 		return;
189 
190 	skel->bss->pid = getpid();
191 
192 	err = tracing_multi__attach(skel);
193 	if (!ASSERT_OK(err, "tracing_multi__attach"))
194 		goto cleanup;
195 
196 	tracing_multi_test_run(skel);
197 
198 cleanup:
199 	tracing_multi__destroy(skel);
200 }
201 
202 static void test_link_api_pattern(void)
203 {
204 	struct tracing_multi *skel;
205 
206 	skel = tracing_multi__open_and_load();
207 	if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load"))
208 		return;
209 
210 	skel->bss->pid = getpid();
211 
212 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
213 					"bpf_fentry_test*", NULL);
214 	if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
215 		goto cleanup;
216 
217 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
218 					"bpf_fentry_test*", NULL);
219 	if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
220 		goto cleanup;
221 
222 	skel->links.test_fentry_s = bpf_program__attach_tracing_multi(skel->progs.test_fentry_s,
223 					"bpf_fentry_test1", NULL);
224 	if (!ASSERT_OK_PTR(skel->links.test_fentry_s, "bpf_program__attach_tracing_multi"))
225 		goto cleanup;
226 
227 	skel->links.test_fexit_s = bpf_program__attach_tracing_multi(skel->progs.test_fexit_s,
228 					"bpf_fentry_test1", NULL);
229 	if (!ASSERT_OK_PTR(skel->links.test_fexit_s, "bpf_program__attach_tracing_multi"))
230 		goto cleanup;
231 
232 	tracing_multi_test_run(skel);
233 
234 cleanup:
235 	tracing_multi__destroy(skel);
236 }
237 
238 static void test_link_api_ids(bool test_cookies)
239 {
240 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
241 	struct tracing_multi *skel;
242 	size_t cnt = FUNCS_CNT;
243 	__u32 *ids;
244 
245 	skel = tracing_multi__open_and_load();
246 	if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load"))
247 		return;
248 
249 	skel->bss->pid = getpid();
250 	skel->bss->test_cookies = test_cookies;
251 
252 	ids = get_ids(bpf_fentry_test, cnt, NULL);
253 	if (!ASSERT_OK_PTR(ids, "get_ids"))
254 		goto cleanup;
255 
256 	opts.ids = ids;
257 	opts.cnt = cnt;
258 
259 	if (test_cookies)
260 		opts.cookies = bpf_fentry_test_cookies;
261 
262 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
263 						NULL, &opts);
264 	if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
265 		goto cleanup;
266 
267 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
268 						NULL, &opts);
269 	if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
270 		goto cleanup;
271 
272 	/* Only bpf_fentry_test1 is allowed for sleepable programs. */
273 	opts.cnt = 1;
274 	skel->links.test_fentry_s = bpf_program__attach_tracing_multi(skel->progs.test_fentry_s,
275 						NULL, &opts);
276 	if (!ASSERT_OK_PTR(skel->links.test_fentry_s, "bpf_program__attach_tracing_multi"))
277 		goto cleanup;
278 
279 	skel->links.test_fexit_s = bpf_program__attach_tracing_multi(skel->progs.test_fexit_s,
280 						NULL, &opts);
281 	if (!ASSERT_OK_PTR(skel->links.test_fexit_s, "bpf_program__attach_tracing_multi"))
282 		goto cleanup;
283 
284 	tracing_multi_test_run(skel);
285 
286 cleanup:
287 	tracing_multi__destroy(skel);
288 	free(ids);
289 }
290 
291 static void test_module_skel_api(void)
292 {
293 	struct tracing_multi_module *skel = NULL;
294 	int err;
295 
296 	skel = tracing_multi_module__open_and_load();
297 	if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load"))
298 		return;
299 
300 	skel->bss->pid = getpid();
301 
302 	err = tracing_multi_module__attach(skel);
303 	if (!ASSERT_OK(err, "tracing_multi__attach"))
304 		goto cleanup;
305 
306 	ASSERT_OK(trigger_module_test_read(1), "trigger_read");
307 	ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry");
308 	ASSERT_EQ(skel->bss->test_result_fexit, 5, "test_result_fexit");
309 
310 cleanup:
311 	tracing_multi_module__destroy(skel);
312 }
313 
314 static void test_module_link_api_pattern(void)
315 {
316 	struct tracing_multi_module *skel = NULL;
317 
318 	skel = tracing_multi_module__open_and_load();
319 	if (!ASSERT_OK_PTR(skel, "tracing_multi_module__open_and_load"))
320 		return;
321 
322 	skel->bss->pid = getpid();
323 
324 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
325 					"bpf_testmod:bpf_testmod_fentry_test*", NULL);
326 	if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
327 		goto cleanup;
328 
329 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
330 					"bpf_testmod:bpf_testmod_fentry_test*", NULL);
331 	if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
332 		goto cleanup;
333 
334 	ASSERT_OK(trigger_module_test_read(1), "trigger_read");
335 	ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry");
336 	ASSERT_EQ(skel->bss->test_result_fexit, 5, "test_result_fexit");
337 
338 cleanup:
339 	tracing_multi_module__destroy(skel);
340 }
341 
342 static void test_module_link_api_ids(void)
343 {
344 	size_t cnt = ARRAY_SIZE(bpf_testmod_fentry_test);
345 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
346 	struct tracing_multi_module *skel = NULL;
347 	__u32 *ids;
348 
349 	skel = tracing_multi_module__open_and_load();
350 	if (!ASSERT_OK_PTR(skel, "tracing_multi_module__open_and_load"))
351 		return;
352 
353 	skel->bss->pid = getpid();
354 
355 	ids = get_ids(bpf_testmod_fentry_test, cnt, "bpf_testmod");
356 	if (!ASSERT_OK_PTR(ids, "get_ids"))
357 		goto cleanup;
358 
359 	opts.ids = ids;
360 	opts.cnt = cnt;
361 
362 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
363 						NULL, &opts);
364 	if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
365 		goto cleanup;
366 
367 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
368 						NULL, &opts);
369 	if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
370 		goto cleanup;
371 
372 	ASSERT_OK(trigger_module_test_read(1), "trigger_read");
373 	ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry");
374 	ASSERT_EQ(skel->bss->test_result_fexit, 5, "test_result_fexit");
375 
376 cleanup:
377 	tracing_multi_module__destroy(skel);
378 	free(ids);
379 }
380 
381 static bool is_set(__u32 mask, __u32 bit)
382 {
383 	return (1 << bit) & mask;
384 }
385 
386 static void __test_intersect(__u32 mask, const struct bpf_program *progs[4], __u64 *test_results[4])
387 {
388 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
389 	LIBBPF_OPTS(bpf_test_run_opts, topts);
390 	struct bpf_link *links[4] = { NULL };
391 	const char *funcs[FUNCS_CNT];
392 	__u64 expected[4];
393 	__u32 *ids, i;
394 	int err, cnt;
395 
396 	/*
397 	 * We have 4 programs in progs and the mask bits pick which
398 	 * of them gets attached to randomly chosen functions.
399 	 */
400 	for (i = 0; i < 4; i++) {
401 		if (!is_set(mask, i))
402 			continue;
403 
404 		cnt = get_random_funcs(funcs);
405 		ids = get_ids(funcs, cnt, NULL);
406 		if (!ASSERT_OK_PTR(ids, "get_ids"))
407 			goto cleanup;
408 
409 		opts.ids = ids;
410 		opts.cnt = cnt;
411 		links[i] = bpf_program__attach_tracing_multi(progs[i], NULL, &opts);
412 		free(ids);
413 
414 		if (!ASSERT_OK_PTR(links[i], "bpf_program__attach_tracing_multi"))
415 			goto cleanup;
416 
417 		expected[i] = *test_results[i] + cnt;
418 	}
419 
420 	err = bpf_prog_test_run_opts(bpf_program__fd(progs[0]), &topts);
421 	ASSERT_OK(err, "test_run");
422 
423 	for (i = 0; i < 4; i++) {
424 		if (!is_set(mask, i))
425 			continue;
426 		ASSERT_EQ(*test_results[i], expected[i], "test_results");
427 	}
428 
429 cleanup:
430 	for (i = 0; i < 4; i++)
431 		bpf_link__destroy(links[i]);
432 }
433 
434 static void test_intersect(void)
435 {
436 	struct tracing_multi_intersect *skel;
437 	const struct bpf_program *progs[4];
438 	__u64 *test_results[4];
439 	__u32 i;
440 
441 	skel = tracing_multi_intersect__open_and_load();
442 	if (!ASSERT_OK_PTR(skel, "tracing_multi_intersect__open_and_load"))
443 		return;
444 
445 	skel->bss->pid = getpid();
446 
447 	progs[0] = skel->progs.fentry_1;
448 	progs[1] = skel->progs.fexit_1;
449 	progs[2] = skel->progs.fentry_2;
450 	progs[3] = skel->progs.fexit_2;
451 
452 	test_results[0] = &skel->bss->test_result_fentry_1;
453 	test_results[1] = &skel->bss->test_result_fexit_1;
454 	test_results[2] = &skel->bss->test_result_fentry_2;
455 	test_results[3] = &skel->bss->test_result_fexit_2;
456 
457 	for (i = 1; i < 16; i++)
458 		__test_intersect(i, progs, test_results);
459 
460 	tracing_multi_intersect__destroy(skel);
461 }
462 
463 static void test_session(void)
464 {
465 	LIBBPF_OPTS(bpf_test_run_opts, topts);
466 	struct tracing_multi_session *skel;
467 	int err, prog_fd;
468 
469 	skel = tracing_multi_session__open_and_load();
470 	if (!ASSERT_OK_PTR(skel, "tracing_multi_session__open_and_load"))
471 		return;
472 
473 	skel->bss->pid = getpid();
474 
475 	err = tracing_multi_session__attach(skel);
476 	if (!ASSERT_OK(err, "tracing_multi_session__attach"))
477 		goto cleanup;
478 
479 	/* execute kernel session */
480 	prog_fd = bpf_program__fd(skel->progs.test_session_1);
481 	err = bpf_prog_test_run_opts(prog_fd, &topts);
482 	ASSERT_OK(err, "test_run");
483 
484 	/* 10 for test_session_1, 1 for test_fsession_s */
485 	ASSERT_EQ(skel->bss->test_result_fentry, 11, "test_result_fentry");
486 	/* extra count (+1 for each fexit execution) for test_result_fexit cookie check/inc */
487 	ASSERT_EQ(skel->bss->test_result_fexit, 22, "test_result_fexit");
488 
489 	skel->bss->test_result_fentry = 0;
490 	skel->bss->test_result_fexit = 0;
491 
492 	/* execute bpf_testmo.ko session */
493 	ASSERT_OK(trigger_module_test_read(1), "trigger_read");
494 
495 	/* 5 for test_session_2 */
496 	ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry");
497 	/* extra count (+1 for each fexit execution) for test_result_fexit cookie */
498 	ASSERT_EQ(skel->bss->test_result_fexit, 10, "test_result_fexit");
499 
500 
501 cleanup:
502 	tracing_multi_session__destroy(skel);
503 }
504 
505 static void test_attach_api_fails(void)
506 {
507 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
508 	static const char * const func[] = {
509 		"bpf_fentry_test2",
510 	};
511 	struct tracing_multi_fail *skel = NULL;
512 	__u32 ids[2] = {}, *ids2 = NULL;
513 	__u64 cookies[2];
514 
515 	skel = tracing_multi_fail__open_and_load();
516 	if (!ASSERT_OK_PTR(skel, "tracing_multi_fail__open_and_load"))
517 		return;
518 
519 	/* fail#1 (libbpf) pattern and opts NULL */
520 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
521 						NULL, NULL);
522 	if (!ASSERT_EQ(libbpf_get_error(skel->links.test_fentry), -EINVAL, "fail_1"))
523 		goto cleanup;
524 
525 	/* fail#2 (libbpf) pattern and ids */
526 	LIBBPF_OPTS_RESET(opts,
527 		.ids = ids,
528 		.cnt = 2,
529 	);
530 
531 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
532 						"bpf_fentry_test*", &opts);
533 	if (!ASSERT_EQ(libbpf_get_error(skel->links.test_fentry), -EINVAL, "fail_2"))
534 		goto cleanup;
535 
536 	/* fail#3 (libbpf) pattern and cookies */
537 	LIBBPF_OPTS_RESET(opts,
538 		.ids = NULL,
539 		.cnt = 2,
540 		.cookies = cookies,
541 	);
542 
543 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
544 						"bpf_fentry_test*", &opts);
545 	if (!ASSERT_EQ(libbpf_get_error(skel->links.test_fentry), -EINVAL, "fail_3"))
546 		goto cleanup;
547 
548 	/* fail#4 (libbpf) bogus pattern */
549 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
550 						"bpf_not_really_a_function*", NULL);
551 	if (!ASSERT_EQ(libbpf_get_error(skel->links.test_fentry), -EINVAL, "fail_4"))
552 		goto cleanup;
553 
554 	/* fail#5 (kernel) abnormal cnt */
555 	LIBBPF_OPTS_RESET(opts,
556 		.ids = ids,
557 		.cnt = INT_MAX,
558 	);
559 
560 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
561 						NULL, &opts);
562 	if (!ASSERT_EQ(libbpf_get_error(skel->links.test_fentry), -E2BIG, "fail_5"))
563 		goto cleanup;
564 
565 	/* fail#6 (kernel) attach sleepable program to not-allowed function */
566 	ids2 = get_ids(func, 1, NULL);
567 	if (!ASSERT_OK_PTR(ids2, "get_ids"))
568 		goto cleanup;
569 
570 	LIBBPF_OPTS_RESET(opts,
571 		.ids = ids2,
572 		.cnt = 1,
573 	);
574 
575 	skel->links.test_fentry_s = bpf_program__attach_tracing_multi(skel->progs.test_fentry_s,
576 						NULL, &opts);
577 	if (!ASSERT_EQ(libbpf_get_error(skel->links.test_fentry_s), -EINVAL, "fail_6"))
578 		goto cleanup;
579 
580 	/* fail#7 (kernel) attach with duplicate id */
581 	ids[0] = ids2[0];
582 	ids[1] = ids2[0];
583 
584 	LIBBPF_OPTS_RESET(opts,
585 		.ids = ids,
586 		.cnt = 2,
587 	);
588 
589 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
590 						NULL, &opts);
591 	ASSERT_EQ(libbpf_get_error(skel->links.test_fentry), -EINVAL, "fail_7");
592 
593 cleanup:
594 	tracing_multi_fail__destroy(skel);
595 	free(ids2);
596 }
597 
598 void serial_test_tracing_multi_bench_attach(void)
599 {
600 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
601 	struct tracing_multi_bench *skel = NULL;
602 	long attach_start_ns, attach_end_ns;
603 	long detach_start_ns, detach_end_ns;
604 	double attach_delta, detach_delta;
605 	struct bpf_link *link = NULL;
606 	size_t i, cap = 0, cnt = 0;
607 	struct ksyms *ksyms = NULL;
608 	void *root = NULL;
609 	void *dups = NULL;
610 	__u32 *ids = NULL;
611 	__u32 nr, type_id;
612 	struct btf *btf;
613 	int err;
614 
615 #ifndef __x86_64__
616 	test__skip();
617 	return;
618 #endif
619 
620 	btf = btf__load_vmlinux_btf();
621 	if (!ASSERT_OK_PTR(btf, "btf__load_vmlinux_btf"))
622 		return;
623 
624 	skel = tracing_multi_bench__open_and_load();
625 	if (!ASSERT_OK_PTR(skel, "tracing_multi_bench__open_and_load"))
626 		goto cleanup;
627 
628 	if (!ASSERT_OK(bpf_get_ksyms(&ksyms, true), "get_syms"))
629 		goto cleanup;
630 
631 	/* Get all ftrace 'safe' symbols.. */
632 	for (i = 0; i < ksyms->filtered_cnt; i++) {
633 		if (!tsearch(&ksyms->filtered_syms[i], &root, compare)) {
634 			ASSERT_FAIL("tsearch failed");
635 			goto cleanup;
636 		}
637 	}
638 
639 	/*
640 	 * Collect names that are not unique in kallsyms. The kernel resolves a
641 	 * tracing-multi BTF id to an address with kallsyms_lookup_name(), which
642 	 * returns the first symbol of that name. For a duplicate name that may
643 	 * be a different (non-ftrace-able) instance than the ftrace-able one in
644 	 * available_filter_functions, so attaching to it by BTF id fails with
645 	 * -ENOENT (e.g. t_start/t_next/t_stop). ksyms->syms is sorted by name,
646 	 * so equal names are adjacent.
647 	 */
648 	for (i = 1; i < ksyms->sym_cnt; i++) {
649 		if (strcmp(ksyms->syms[i].name, ksyms->syms[i - 1].name))
650 			continue;
651 		if (!tsearch(&ksyms->syms[i].name, &dups, compare)) {
652 			ASSERT_FAIL("tsearch failed");
653 			goto cleanup;
654 		}
655 	}
656 
657 	/* ..and filter them through BTF and btf_type_is_traceable_func. */
658 	nr = btf__type_cnt(btf);
659 	for (type_id = 1; type_id < nr; type_id++) {
660 		const struct btf_type *type;
661 		const char *str;
662 
663 		type = btf__type_by_id(btf, type_id);
664 		if (!type)
665 			break;
666 
667 		if (BTF_INFO_KIND(type->info) != BTF_KIND_FUNC)
668 			continue;
669 
670 		str = btf__name_by_offset(btf, type->name_off);
671 		if (!str)
672 			break;
673 
674 		if (!tfind(&str, &root, compare))
675 			continue;
676 
677 		/* Skip names that are not unique in kallsyms, see above. */
678 		if (tfind(&str, &dups, compare))
679 			continue;
680 
681 		if (!btf_type_is_traceable_func(btf, type))
682 			continue;
683 
684 		err = libbpf_ensure_mem((void **) &ids, &cap, sizeof(*ids), cnt + 1);
685 		if (err)
686 			goto cleanup;
687 
688 		ids[cnt++] = type_id;
689 	}
690 
691 	opts.ids = ids;
692 	opts.cnt = cnt;
693 
694 	attach_start_ns = get_time_ns();
695 	link = bpf_program__attach_tracing_multi(skel->progs.bench, NULL, &opts);
696 	attach_end_ns = get_time_ns();
697 
698 	if (!ASSERT_OK_PTR(link, "bpf_program__attach_tracing_multi"))
699 		goto cleanup;
700 
701 	detach_start_ns = get_time_ns();
702 	bpf_link__destroy(link);
703 	detach_end_ns = get_time_ns();
704 
705 	attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;
706 	detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;
707 
708 	printf("%s: found %lu functions\n", __func__, cnt);
709 	printf("%s: attached in %7.3lfs\n", __func__, attach_delta);
710 	printf("%s: detached in %7.3lfs\n", __func__, detach_delta);
711 
712 cleanup:
713 	tracing_multi_bench__destroy(skel);
714 	tdestroy(root, tdestroy_free_nop);
715 	tdestroy(dups, tdestroy_free_nop);
716 	free_kallsyms_local(ksyms);
717 	free(ids);
718 	btf__free(btf);
719 }
720 
721 static void tracing_multi_rollback_run(struct tracing_multi_rollback *skel)
722 {
723 	LIBBPF_OPTS(bpf_test_run_opts, topts);
724 	int err, prog_fd;
725 
726 	prog_fd = bpf_program__fd(skel->progs.test_fentry);
727 	err = bpf_prog_test_run_opts(prog_fd, &topts);
728 	ASSERT_OK(err, "test_run");
729 
730 	/* make sure the rollback code did not leave any program attached */
731 	ASSERT_EQ(skel->bss->test_result_fentry, 0, "test_result_fentry");
732 	ASSERT_EQ(skel->bss->test_result_fexit, 0, "test_result_fexit");
733 }
734 
735 static void test_rollback_put(void)
736 {
737 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
738 	struct tracing_multi_rollback *skel = NULL;
739 	size_t cnt = FUNCS_CNT;
740 	__u32 *ids = NULL;
741 	int err;
742 
743 	skel = tracing_multi_rollback__open();
744 	if (!ASSERT_OK_PTR(skel, "tracing_multi_rollback__open"))
745 		return;
746 
747 	bpf_program__set_autoload(skel->progs.test_fentry, true);
748 	bpf_program__set_autoload(skel->progs.test_fexit, true);
749 
750 	err = tracing_multi_rollback__load(skel);
751 	if (!ASSERT_OK(err, "tracing_multi_rollback__load"))
752 		goto cleanup;
753 
754 	ids = get_ids(bpf_fentry_test, cnt, NULL);
755 	if (!ASSERT_OK_PTR(ids, "get_ids"))
756 		goto cleanup;
757 
758 	/*
759 	 * Mangle last id to trigger rollback, which needs to do put
760 	 * on get-ed trampolines.
761 	 */
762 	ids[9] = 0;
763 
764 	opts.ids = ids;
765 	opts.cnt = cnt;
766 
767 	skel->bss->pid = getpid();
768 
769 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
770 						NULL, &opts);
771 	if (!ASSERT_ERR_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
772 		goto cleanup;
773 
774 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
775 						NULL, &opts);
776 	if (!ASSERT_ERR_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
777 		goto cleanup;
778 
779 	/* We don't really attach any program, but let's make sure. */
780 	tracing_multi_rollback_run(skel);
781 
782 cleanup:
783 	tracing_multi_rollback__destroy(skel);
784 	free(ids);
785 }
786 
787 static void fillers_cleanup(struct tracing_multi_rollback **skels, int cnt)
788 {
789 	int i;
790 
791 	for (i = 0; i < cnt; i++)
792 		tracing_multi_rollback__destroy(skels[i]);
793 
794 	free(skels);
795 }
796 
797 static struct tracing_multi_rollback *extra_load_and_link(void)
798 {
799 	struct tracing_multi_rollback *skel;
800 	int err;
801 
802 	skel = tracing_multi_rollback__open();
803 	if (!ASSERT_OK_PTR(skel, "tracing_multi_rollback__open"))
804 		goto cleanup;
805 
806 	bpf_program__set_autoload(skel->progs.extra, true);
807 
808 	err = tracing_multi_rollback__load(skel);
809 	if (!ASSERT_OK(err, "tracing_multi_rollback__load"))
810 		goto cleanup;
811 
812 	skel->links.extra = bpf_program__attach_trace(skel->progs.extra);
813 	if (!ASSERT_OK_PTR(skel->links.extra, "bpf_program__attach_trace"))
814 		goto cleanup;
815 
816 	return skel;
817 
818 cleanup:
819 	tracing_multi_rollback__destroy(skel);
820 	return NULL;
821 }
822 
823 static struct tracing_multi_rollback **fillers_load_and_link(int max)
824 {
825 	struct tracing_multi_rollback **skels, *skel;
826 	int i, err;
827 
828 	skels = calloc(max + 1, sizeof(*skels));
829 	if (!ASSERT_OK_PTR(skels, "calloc"))
830 		return NULL;
831 
832 	for (i = 0; i < max; i++) {
833 		skel = skels[i] = tracing_multi_rollback__open();
834 		if (!ASSERT_OK_PTR(skels[i], "tracing_multi_rollback__open"))
835 			goto cleanup;
836 
837 		bpf_program__set_autoload(skel->progs.filler, true);
838 
839 		err = tracing_multi_rollback__load(skel);
840 		if (!ASSERT_OK(err, "tracing_multi_rollback__load"))
841 			goto cleanup;
842 
843 		skel->links.filler = bpf_program__attach_trace(skel->progs.filler);
844 		if (!ASSERT_OK_PTR(skels[i]->links.filler, "bpf_program__attach_trace"))
845 			goto cleanup;
846 	}
847 
848 	return skels;
849 
850 cleanup:
851 	fillers_cleanup(skels, i + 1);
852 	return NULL;
853 }
854 
855 static void test_rollback_unlink(void)
856 {
857 	struct tracing_multi_rollback *skel = NULL, *extra;
858 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
859 	struct tracing_multi_rollback **fillers;
860 	size_t cnt = FUNCS_CNT;
861 	__u32 *ids = NULL;
862 	int err, max;
863 
864 	max = get_bpf_max_tramp_links();
865 	if (!ASSERT_GE(max, 1, "bpf_max_tramp_links"))
866 		return;
867 
868 	/* Attach maximum allowed programs to bpf_fentry_test10 */
869 	fillers = fillers_load_and_link(max);
870 	if (!ASSERT_OK_PTR(fillers, "fillers_load_and_link"))
871 		return;
872 
873 	extra = extra_load_and_link();
874 	if (!ASSERT_OK_PTR(extra, "extra_load_and_link"))
875 		goto cleanup;
876 
877 	skel = tracing_multi_rollback__open();
878 	if (!ASSERT_OK_PTR(skel, "tracing_multi_rollback__open"))
879 		goto cleanup;
880 
881 	bpf_program__set_autoload(skel->progs.test_fentry, true);
882 	bpf_program__set_autoload(skel->progs.test_fexit, true);
883 
884 	/*
885 	 * Attach tracing_multi link on bpf_fentry_test1-10, which will
886 	 * fail on bpf_fentry_test10 function, because it already has
887 	 * maximum allowed programs attached.
888 	 *
889 	 * The rollback needs to unlink already link-ed trampolines and
890 	 * put all of them.
891 	 */
892 	err = tracing_multi_rollback__load(skel);
893 	if (!ASSERT_OK(err, "tracing_multi_rollback__load"))
894 		goto cleanup;
895 
896 	ids = get_ids(bpf_fentry_test, cnt, NULL);
897 	if (!ASSERT_OK_PTR(ids, "get_ids"))
898 		goto cleanup;
899 
900 	opts.ids = ids;
901 	opts.cnt = cnt;
902 
903 	skel->bss->pid = getpid();
904 
905 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
906 						NULL, &opts);
907 	if (!ASSERT_ERR_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
908 		goto cleanup;
909 
910 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
911 						NULL, &opts);
912 	if (!ASSERT_ERR_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
913 		goto cleanup;
914 
915 	tracing_multi_rollback_run(skel);
916 
917 cleanup:
918 	fillers_cleanup(fillers, max);
919 	tracing_multi_rollback__destroy(extra);
920 	tracing_multi_rollback__destroy(skel);
921 	free(ids);
922 }
923 
924 void serial_test_tracing_multi_attach_rollback(void)
925 {
926 	if (test__start_subtest("put"))
927 		test_rollback_put();
928 	if (test__start_subtest("unlink"))
929 		test_rollback_unlink();
930 }
931 
932 void test_tracing_multi_test(void)
933 {
934 #ifndef __x86_64__
935 	test__skip();
936 	return;
937 #endif
938 
939 	if (test__start_subtest("skel_api"))
940 		test_skel_api();
941 	if (test__start_subtest("link_api_pattern"))
942 		test_link_api_pattern();
943 	if (test__start_subtest("link_api_ids"))
944 		test_link_api_ids(false);
945 	if (test__start_subtest("module_skel_api"))
946 		test_module_skel_api();
947 	if (test__start_subtest("module_link_api_pattern"))
948 		test_module_link_api_pattern();
949 	if (test__start_subtest("module_link_api_ids"))
950 		test_module_link_api_ids();
951 	if (test__start_subtest("intersect"))
952 		test_intersect();
953 	if (test__start_subtest("cookies"))
954 		test_link_api_ids(true);
955 	if (test__start_subtest("session"))
956 		test_session();
957 	if (test__start_subtest("attach_api_fails"))
958 		test_attach_api_fails();
959 	RUN_TESTS(tracing_multi_verifier);
960 }
961