xref: /linux/tools/testing/selftests/bpf/prog_tests/tracing_multi.c (revision 1b938f42f5fa1789d0dcc2b9aa6262edba3a7f51)
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 "trace_helpers.h"
11 
12 static __u64 bpf_fentry_test_cookies[] = {
13 	8,  /* bpf_fentry_test1 */
14 	9,  /* bpf_fentry_test2 */
15 	7,  /* bpf_fentry_test3 */
16 	5,  /* bpf_fentry_test4 */
17 	4,  /* bpf_fentry_test5 */
18 	2,  /* bpf_fentry_test6 */
19 	3,  /* bpf_fentry_test7 */
20 	1,  /* bpf_fentry_test8 */
21 	10, /* bpf_fentry_test9 */
22 	6,  /* bpf_fentry_test10 */
23 };
24 
25 static const char * const bpf_fentry_test[] = {
26 	"bpf_fentry_test1",
27 	"bpf_fentry_test2",
28 	"bpf_fentry_test3",
29 	"bpf_fentry_test4",
30 	"bpf_fentry_test5",
31 	"bpf_fentry_test6",
32 	"bpf_fentry_test7",
33 	"bpf_fentry_test8",
34 	"bpf_fentry_test9",
35 	"bpf_fentry_test10",
36 };
37 
38 static const char * const bpf_testmod_fentry_test[] = {
39 	"bpf_testmod_fentry_test1",
40 	"bpf_testmod_fentry_test2",
41 	"bpf_testmod_fentry_test3",
42 	"bpf_testmod_fentry_test7",
43 	"bpf_testmod_fentry_test11",
44 };
45 
46 #define FUNCS_CNT (ARRAY_SIZE(bpf_fentry_test))
47 
48 static int get_random_funcs(const char **funcs)
49 {
50 	int i, cnt = 0;
51 
52 	for (i = 0; i < FUNCS_CNT; i++) {
53 		if (rand() % 2)
54 			funcs[cnt++] = bpf_fentry_test[i];
55 	}
56 	/* we always need at least one.. */
57 	if (!cnt)
58 		funcs[cnt++] = bpf_fentry_test[rand() % FUNCS_CNT];
59 	return cnt;
60 }
61 
62 static int compare(const void *ppa, const void *ppb)
63 {
64 	const char *pa = *(const char **) ppa;
65 	const char *pb = *(const char **) ppb;
66 
67 	return strcmp(pa, pb);
68 }
69 
70 static void tdestroy_free_nop(void *ptr)
71 {
72 }
73 
74 static __u32 *get_ids(const char * const funcs[], int funcs_cnt, const char *mod)
75 {
76 	struct btf *btf, *vmlinux_btf = NULL;
77 	__u32 nr, type_id, cnt = 0;
78 	void *root = NULL;
79 	__u32 *ids = NULL;
80 	int i, err = 0;
81 
82 	btf = btf__load_vmlinux_btf();
83 	if (!ASSERT_OK_PTR(btf, "btf__load_vmlinux_btf"))
84 		return NULL;
85 
86 	if (mod) {
87 		vmlinux_btf = btf;
88 		btf = btf__load_module_btf(mod, vmlinux_btf);
89 		if (!ASSERT_OK_PTR(btf, "btf__load_module_btf")) {
90 			btf__free(vmlinux_btf);
91 			return NULL;
92 		}
93 	}
94 
95 	ids = calloc(funcs_cnt, sizeof(ids[0]));
96 	if (!ids)
97 		goto out;
98 
99 	/*
100 	 * We sort function names by name and search them
101 	 * below for each function.
102 	 */
103 	for (i = 0; i < funcs_cnt; i++) {
104 		if (!tsearch(&funcs[i], &root, compare)) {
105 			ASSERT_FAIL("tsearch failed");
106 			err = -1;
107 			goto error;
108 		}
109 	}
110 
111 	nr = btf__type_cnt(btf);
112 	for (type_id = 1; type_id < nr && cnt < funcs_cnt; type_id++) {
113 		const struct btf_type *type;
114 		const char *str, ***val;
115 		unsigned int idx;
116 
117 		type = btf__type_by_id(btf, type_id);
118 		if (!type) {
119 			err = -1;
120 			break;
121 		}
122 
123 		if (BTF_INFO_KIND(type->info) != BTF_KIND_FUNC)
124 			continue;
125 
126 		str = btf__name_by_offset(btf, type->name_off);
127 		if (!str) {
128 			err = -1;
129 			break;
130 		}
131 
132 		val = tfind(&str, &root, compare);
133 		if (!val)
134 			continue;
135 
136 		/*
137 		 * We keep pointer for each function name so we can get the original
138 		 * array index and have the resulting ids array matching the original
139 		 * function array.
140 		 *
141 		 * Doing it this way allow us to easily test the cookies support,
142 		 * because each cookie is attached to particular function/id.
143 		 */
144 		idx = *val - funcs;
145 		ids[idx] = type_id;
146 		cnt++;
147 	}
148 
149 error:
150 	if (err) {
151 		free(ids);
152 		ids = NULL;
153 	}
154 
155 out:
156 	tdestroy(root, tdestroy_free_nop);
157 	btf__free(vmlinux_btf);
158 	btf__free(btf);
159 	return ids;
160 }
161 
162 static void tracing_multi_test_run(struct tracing_multi *skel)
163 {
164 	LIBBPF_OPTS(bpf_test_run_opts, topts);
165 	int err, prog_fd;
166 
167 	prog_fd = bpf_program__fd(skel->progs.test_fentry);
168 	err = bpf_prog_test_run_opts(prog_fd, &topts);
169 	ASSERT_OK(err, "test_run");
170 
171 	/* extra +1 count for sleepable programs */
172 	ASSERT_EQ(skel->bss->test_result_fentry, FUNCS_CNT + 1, "test_result_fentry");
173 	ASSERT_EQ(skel->bss->test_result_fexit, FUNCS_CNT + 1, "test_result_fexit");
174 }
175 
176 static void test_skel_api(void)
177 {
178 	struct tracing_multi *skel;
179 	int err;
180 
181 	skel = tracing_multi__open_and_load();
182 	if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load"))
183 		return;
184 
185 	skel->bss->pid = getpid();
186 
187 	err = tracing_multi__attach(skel);
188 	if (!ASSERT_OK(err, "tracing_multi__attach"))
189 		goto cleanup;
190 
191 	tracing_multi_test_run(skel);
192 
193 cleanup:
194 	tracing_multi__destroy(skel);
195 }
196 
197 static void test_link_api_pattern(void)
198 {
199 	struct tracing_multi *skel;
200 
201 	skel = tracing_multi__open_and_load();
202 	if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load"))
203 		return;
204 
205 	skel->bss->pid = getpid();
206 
207 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
208 					"bpf_fentry_test*", NULL);
209 	if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
210 		goto cleanup;
211 
212 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
213 					"bpf_fentry_test*", NULL);
214 	if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
215 		goto cleanup;
216 
217 	skel->links.test_fentry_s = bpf_program__attach_tracing_multi(skel->progs.test_fentry_s,
218 					"bpf_fentry_test1", NULL);
219 	if (!ASSERT_OK_PTR(skel->links.test_fentry_s, "bpf_program__attach_tracing_multi"))
220 		goto cleanup;
221 
222 	skel->links.test_fexit_s = bpf_program__attach_tracing_multi(skel->progs.test_fexit_s,
223 					"bpf_fentry_test1", NULL);
224 	if (!ASSERT_OK_PTR(skel->links.test_fexit_s, "bpf_program__attach_tracing_multi"))
225 		goto cleanup;
226 
227 	tracing_multi_test_run(skel);
228 
229 cleanup:
230 	tracing_multi__destroy(skel);
231 }
232 
233 static void test_link_api_ids(bool test_cookies)
234 {
235 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
236 	struct tracing_multi *skel;
237 	size_t cnt = FUNCS_CNT;
238 	__u32 *ids;
239 
240 	skel = tracing_multi__open_and_load();
241 	if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load"))
242 		return;
243 
244 	skel->bss->pid = getpid();
245 	skel->bss->test_cookies = test_cookies;
246 
247 	ids = get_ids(bpf_fentry_test, cnt, NULL);
248 	if (!ASSERT_OK_PTR(ids, "get_ids"))
249 		goto cleanup;
250 
251 	opts.ids = ids;
252 	opts.cnt = cnt;
253 
254 	if (test_cookies)
255 		opts.cookies = bpf_fentry_test_cookies;
256 
257 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
258 						NULL, &opts);
259 	if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
260 		goto cleanup;
261 
262 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
263 						NULL, &opts);
264 	if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
265 		goto cleanup;
266 
267 	/* Only bpf_fentry_test1 is allowed for sleepable programs. */
268 	opts.cnt = 1;
269 	skel->links.test_fentry_s = bpf_program__attach_tracing_multi(skel->progs.test_fentry_s,
270 						NULL, &opts);
271 	if (!ASSERT_OK_PTR(skel->links.test_fentry_s, "bpf_program__attach_tracing_multi"))
272 		goto cleanup;
273 
274 	skel->links.test_fexit_s = bpf_program__attach_tracing_multi(skel->progs.test_fexit_s,
275 						NULL, &opts);
276 	if (!ASSERT_OK_PTR(skel->links.test_fexit_s, "bpf_program__attach_tracing_multi"))
277 		goto cleanup;
278 
279 	tracing_multi_test_run(skel);
280 
281 cleanup:
282 	tracing_multi__destroy(skel);
283 	free(ids);
284 }
285 
286 static void test_module_skel_api(void)
287 {
288 	struct tracing_multi_module *skel = NULL;
289 	int err;
290 
291 	skel = tracing_multi_module__open_and_load();
292 	if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load"))
293 		return;
294 
295 	skel->bss->pid = getpid();
296 
297 	err = tracing_multi_module__attach(skel);
298 	if (!ASSERT_OK(err, "tracing_multi__attach"))
299 		goto cleanup;
300 
301 	ASSERT_OK(trigger_module_test_read(1), "trigger_read");
302 	ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry");
303 	ASSERT_EQ(skel->bss->test_result_fexit, 5, "test_result_fexit");
304 
305 cleanup:
306 	tracing_multi_module__destroy(skel);
307 }
308 
309 static void test_module_link_api_pattern(void)
310 {
311 	struct tracing_multi_module *skel = NULL;
312 
313 	skel = tracing_multi_module__open_and_load();
314 	if (!ASSERT_OK_PTR(skel, "tracing_multi_module__open_and_load"))
315 		return;
316 
317 	skel->bss->pid = getpid();
318 
319 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
320 					"bpf_testmod:bpf_testmod_fentry_test*", NULL);
321 	if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
322 		goto cleanup;
323 
324 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
325 					"bpf_testmod:bpf_testmod_fentry_test*", NULL);
326 	if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
327 		goto cleanup;
328 
329 	ASSERT_OK(trigger_module_test_read(1), "trigger_read");
330 	ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry");
331 	ASSERT_EQ(skel->bss->test_result_fexit, 5, "test_result_fexit");
332 
333 cleanup:
334 	tracing_multi_module__destroy(skel);
335 }
336 
337 static void test_module_link_api_ids(void)
338 {
339 	size_t cnt = ARRAY_SIZE(bpf_testmod_fentry_test);
340 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
341 	struct tracing_multi_module *skel = NULL;
342 	__u32 *ids;
343 
344 	skel = tracing_multi_module__open_and_load();
345 	if (!ASSERT_OK_PTR(skel, "tracing_multi_module__open_and_load"))
346 		return;
347 
348 	skel->bss->pid = getpid();
349 
350 	ids = get_ids(bpf_testmod_fentry_test, cnt, "bpf_testmod");
351 	if (!ASSERT_OK_PTR(ids, "get_ids"))
352 		goto cleanup;
353 
354 	opts.ids = ids;
355 	opts.cnt = cnt;
356 
357 	skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
358 						NULL, &opts);
359 	if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi"))
360 		goto cleanup;
361 
362 	skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit,
363 						NULL, &opts);
364 	if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi"))
365 		goto cleanup;
366 
367 	ASSERT_OK(trigger_module_test_read(1), "trigger_read");
368 	ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry");
369 	ASSERT_EQ(skel->bss->test_result_fexit, 5, "test_result_fexit");
370 
371 cleanup:
372 	tracing_multi_module__destroy(skel);
373 	free(ids);
374 }
375 
376 static bool is_set(__u32 mask, __u32 bit)
377 {
378 	return (1 << bit) & mask;
379 }
380 
381 static void __test_intersect(__u32 mask, const struct bpf_program *progs[4], __u64 *test_results[4])
382 {
383 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
384 	LIBBPF_OPTS(bpf_test_run_opts, topts);
385 	struct bpf_link *links[4] = { NULL };
386 	const char *funcs[FUNCS_CNT];
387 	__u64 expected[4];
388 	__u32 *ids, i;
389 	int err, cnt;
390 
391 	/*
392 	 * We have 4 programs in progs and the mask bits pick which
393 	 * of them gets attached to randomly chosen functions.
394 	 */
395 	for (i = 0; i < 4; i++) {
396 		if (!is_set(mask, i))
397 			continue;
398 
399 		cnt = get_random_funcs(funcs);
400 		ids = get_ids(funcs, cnt, NULL);
401 		if (!ASSERT_OK_PTR(ids, "get_ids"))
402 			goto cleanup;
403 
404 		opts.ids = ids;
405 		opts.cnt = cnt;
406 		links[i] = bpf_program__attach_tracing_multi(progs[i], NULL, &opts);
407 		free(ids);
408 
409 		if (!ASSERT_OK_PTR(links[i], "bpf_program__attach_tracing_multi"))
410 			goto cleanup;
411 
412 		expected[i] = *test_results[i] + cnt;
413 	}
414 
415 	err = bpf_prog_test_run_opts(bpf_program__fd(progs[0]), &topts);
416 	ASSERT_OK(err, "test_run");
417 
418 	for (i = 0; i < 4; i++) {
419 		if (!is_set(mask, i))
420 			continue;
421 		ASSERT_EQ(*test_results[i], expected[i], "test_results");
422 	}
423 
424 cleanup:
425 	for (i = 0; i < 4; i++)
426 		bpf_link__destroy(links[i]);
427 }
428 
429 static void test_intersect(void)
430 {
431 	struct tracing_multi_intersect *skel;
432 	const struct bpf_program *progs[4];
433 	__u64 *test_results[4];
434 	__u32 i;
435 
436 	skel = tracing_multi_intersect__open_and_load();
437 	if (!ASSERT_OK_PTR(skel, "tracing_multi_intersect__open_and_load"))
438 		return;
439 
440 	skel->bss->pid = getpid();
441 
442 	progs[0] = skel->progs.fentry_1;
443 	progs[1] = skel->progs.fexit_1;
444 	progs[2] = skel->progs.fentry_2;
445 	progs[3] = skel->progs.fexit_2;
446 
447 	test_results[0] = &skel->bss->test_result_fentry_1;
448 	test_results[1] = &skel->bss->test_result_fexit_1;
449 	test_results[2] = &skel->bss->test_result_fentry_2;
450 	test_results[3] = &skel->bss->test_result_fexit_2;
451 
452 	for (i = 1; i < 16; i++)
453 		__test_intersect(i, progs, test_results);
454 
455 	tracing_multi_intersect__destroy(skel);
456 }
457 
458 void test_tracing_multi_test(void)
459 {
460 #ifndef __x86_64__
461 	test__skip();
462 	return;
463 #endif
464 
465 	if (test__start_subtest("skel_api"))
466 		test_skel_api();
467 	if (test__start_subtest("link_api_pattern"))
468 		test_link_api_pattern();
469 	if (test__start_subtest("link_api_ids"))
470 		test_link_api_ids(false);
471 	if (test__start_subtest("module_skel_api"))
472 		test_module_skel_api();
473 	if (test__start_subtest("module_link_api_pattern"))
474 		test_module_link_api_pattern();
475 	if (test__start_subtest("module_link_api_ids"))
476 		test_module_link_api_ids();
477 	if (test__start_subtest("intersect"))
478 		test_intersect();
479 	if (test__start_subtest("cookies"))
480 		test_link_api_ids(true);
481 }
482