xref: /linux/tools/testing/selftests/bpf/prog_tests/build_id.c (revision 55d0969c451159cff86949b38c39171cab962069)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
3 #include <test_progs.h>
4 
5 #include "test_build_id.skel.h"
6 
7 static char build_id[BPF_BUILD_ID_SIZE];
8 static int build_id_sz;
9 
10 static void print_stack(struct bpf_stack_build_id *stack, int frame_cnt)
11 {
12 	int i, j;
13 
14 	for (i = 0; i < frame_cnt; i++) {
15 		printf("FRAME #%02d: ", i);
16 		switch (stack[i].status) {
17 		case BPF_STACK_BUILD_ID_EMPTY:
18 			printf("<EMPTY>\n");
19 			break;
20 		case BPF_STACK_BUILD_ID_VALID:
21 			printf("BUILD ID = ");
22 			for (j = 0; j < BPF_BUILD_ID_SIZE; j++)
23 				printf("%02hhx", (unsigned)stack[i].build_id[j]);
24 			printf(" OFFSET = %llx", (unsigned long long)stack[i].offset);
25 			break;
26 		case BPF_STACK_BUILD_ID_IP:
27 			printf("IP = %llx", (unsigned long long)stack[i].ip);
28 			break;
29 		default:
30 			printf("UNEXPECTED STATUS %d ", stack[i].status);
31 			break;
32 		}
33 		printf("\n");
34 	}
35 }
36 
37 static void subtest_nofault(bool build_id_resident)
38 {
39 	struct test_build_id *skel;
40 	struct bpf_stack_build_id *stack;
41 	int frame_cnt;
42 
43 	skel = test_build_id__open_and_load();
44 	if (!ASSERT_OK_PTR(skel, "skel_open"))
45 		return;
46 
47 	skel->links.uprobe_nofault = bpf_program__attach(skel->progs.uprobe_nofault);
48 	if (!ASSERT_OK_PTR(skel->links.uprobe_nofault, "link"))
49 		goto cleanup;
50 
51 	if (build_id_resident)
52 		ASSERT_OK(system("./uprobe_multi uprobe-paged-in"), "trigger_uprobe");
53 	else
54 		ASSERT_OK(system("./uprobe_multi uprobe-paged-out"), "trigger_uprobe");
55 
56 	if (!ASSERT_GT(skel->bss->res_nofault, 0, "res"))
57 		goto cleanup;
58 
59 	stack = skel->bss->stack_nofault;
60 	frame_cnt = skel->bss->res_nofault / sizeof(struct bpf_stack_build_id);
61 	if (env.verbosity >= VERBOSE_NORMAL)
62 		print_stack(stack, frame_cnt);
63 
64 	if (build_id_resident) {
65 		ASSERT_EQ(stack[0].status, BPF_STACK_BUILD_ID_VALID, "build_id_status");
66 		ASSERT_EQ(memcmp(stack[0].build_id, build_id, build_id_sz), 0, "build_id_match");
67 	} else {
68 		ASSERT_EQ(stack[0].status, BPF_STACK_BUILD_ID_IP, "build_id_status");
69 	}
70 
71 cleanup:
72 	test_build_id__destroy(skel);
73 }
74 
75 static void subtest_sleepable(void)
76 {
77 	struct test_build_id *skel;
78 	struct bpf_stack_build_id *stack;
79 	int frame_cnt;
80 
81 	skel = test_build_id__open_and_load();
82 	if (!ASSERT_OK_PTR(skel, "skel_open"))
83 		return;
84 
85 	skel->links.uprobe_sleepable = bpf_program__attach(skel->progs.uprobe_sleepable);
86 	if (!ASSERT_OK_PTR(skel->links.uprobe_sleepable, "link"))
87 		goto cleanup;
88 
89 	/* force build ID to not be paged in */
90 	ASSERT_OK(system("./uprobe_multi uprobe-paged-out"), "trigger_uprobe");
91 
92 	if (!ASSERT_GT(skel->bss->res_sleepable, 0, "res"))
93 		goto cleanup;
94 
95 	stack = skel->bss->stack_sleepable;
96 	frame_cnt = skel->bss->res_sleepable / sizeof(struct bpf_stack_build_id);
97 	if (env.verbosity >= VERBOSE_NORMAL)
98 		print_stack(stack, frame_cnt);
99 
100 	ASSERT_EQ(stack[0].status, BPF_STACK_BUILD_ID_VALID, "build_id_status");
101 	ASSERT_EQ(memcmp(stack[0].build_id, build_id, build_id_sz), 0, "build_id_match");
102 
103 cleanup:
104 	test_build_id__destroy(skel);
105 }
106 
107 void serial_test_build_id(void)
108 {
109 	build_id_sz = read_build_id("uprobe_multi", build_id, sizeof(build_id));
110 	ASSERT_EQ(build_id_sz, BPF_BUILD_ID_SIZE, "parse_build_id");
111 
112 	if (test__start_subtest("nofault-paged-out"))
113 		subtest_nofault(false /* not resident */);
114 	if (test__start_subtest("nofault-paged-in"))
115 		subtest_nofault(true /* resident */);
116 	if (test__start_subtest("sleepable"))
117 		subtest_sleepable();
118 }
119