1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2017 Facebook |
3 | */ |
4 | #include <stdio.h> |
5 | #include <unistd.h> |
6 | #include <fcntl.h> |
7 | #include <stdlib.h> |
8 | #include <string.h> |
9 | #include <linux/perf_event.h> |
10 | #include <errno.h> |
11 | #include <bpf/libbpf.h> |
12 | #include <bpf/bpf.h> |
13 | |
14 | /* This program verifies bpf attachment to tracepoint sys_enter_* and sys_exit_*. |
15 | * This requires kernel CONFIG_FTRACE_SYSCALLS to be set. |
16 | */ |
17 | |
18 | static void usage(const char *cmd) |
19 | { |
20 | printf(format: "USAGE: %s [-i nr_tests] [-h]\n" , cmd); |
21 | printf(format: " -i nr_tests # rounds of test to run\n" ); |
22 | printf(format: " -h # help\n" ); |
23 | } |
24 | |
25 | static void verify_map(int map_id) |
26 | { |
27 | __u32 key = 0; |
28 | __u32 val; |
29 | |
30 | if (bpf_map_lookup_elem(map_id, &key, &val) != 0) { |
31 | fprintf(stderr, format: "map_lookup failed: %s\n" , strerror(errno)); |
32 | return; |
33 | } |
34 | if (val == 0) { |
35 | fprintf(stderr, format: "failed: map #%d returns value 0\n" , map_id); |
36 | return; |
37 | } |
38 | |
39 | printf(format: "verify map:%d val: %d\n" , map_id, val); |
40 | |
41 | val = 0; |
42 | if (bpf_map_update_elem(map_id, &key, &val, BPF_ANY) != 0) { |
43 | fprintf(stderr, format: "map_update failed: %s\n" , strerror(errno)); |
44 | return; |
45 | } |
46 | } |
47 | |
48 | static int test(char *filename, int nr_tests) |
49 | { |
50 | int map0_fds[nr_tests], map1_fds[nr_tests], fd, i, j = 0; |
51 | struct bpf_link **links = NULL; |
52 | struct bpf_object *objs[nr_tests]; |
53 | struct bpf_program *prog; |
54 | |
55 | for (i = 0; i < nr_tests; i++) { |
56 | objs[i] = bpf_object__open_file(filename, NULL); |
57 | if (libbpf_get_error(objs[i])) { |
58 | fprintf(stderr, format: "opening BPF object file failed\n" ); |
59 | objs[i] = NULL; |
60 | goto cleanup; |
61 | } |
62 | |
63 | /* One-time initialization */ |
64 | if (!links) { |
65 | int nr_progs = 0; |
66 | |
67 | bpf_object__for_each_program(prog, objs[i]) |
68 | nr_progs += 1; |
69 | |
70 | links = calloc(nmemb: nr_progs * nr_tests, size: sizeof(struct bpf_link *)); |
71 | |
72 | if (!links) |
73 | goto cleanup; |
74 | } |
75 | |
76 | /* load BPF program */ |
77 | if (bpf_object__load(objs[i])) { |
78 | fprintf(stderr, format: "loading BPF object file failed\n" ); |
79 | goto cleanup; |
80 | } |
81 | |
82 | map0_fds[i] = bpf_object__find_map_fd_by_name(objs[i], |
83 | "enter_open_map" ); |
84 | map1_fds[i] = bpf_object__find_map_fd_by_name(objs[i], |
85 | "exit_open_map" ); |
86 | if (map0_fds[i] < 0 || map1_fds[i] < 0) { |
87 | fprintf(stderr, format: "finding a map in obj file failed\n" ); |
88 | goto cleanup; |
89 | } |
90 | |
91 | bpf_object__for_each_program(prog, objs[i]) { |
92 | links[j] = bpf_program__attach(prog); |
93 | if (libbpf_get_error(links[j])) { |
94 | fprintf(stderr, format: "bpf_program__attach failed\n" ); |
95 | links[j] = NULL; |
96 | goto cleanup; |
97 | } |
98 | j++; |
99 | } |
100 | printf(format: "prog #%d: map ids %d %d\n" , i, map0_fds[i], map1_fds[i]); |
101 | } |
102 | |
103 | /* current load_bpf_file has perf_event_open default pid = -1 |
104 | * and cpu = 0, which permits attached bpf execution on |
105 | * all cpus for all pid's. bpf program execution ignores |
106 | * cpu affinity. |
107 | */ |
108 | /* trigger some "open" operations */ |
109 | fd = open(file: filename, O_RDONLY); |
110 | if (fd < 0) { |
111 | fprintf(stderr, format: "open failed: %s\n" , strerror(errno)); |
112 | return 1; |
113 | } |
114 | close(fd: fd); |
115 | |
116 | /* verify the map */ |
117 | for (i = 0; i < nr_tests; i++) { |
118 | verify_map(map_id: map0_fds[i]); |
119 | verify_map(map_id: map1_fds[i]); |
120 | } |
121 | |
122 | cleanup: |
123 | if (links) { |
124 | for (j--; j >= 0; j--) |
125 | bpf_link__destroy(links[j]); |
126 | |
127 | free(ptr: links); |
128 | } |
129 | |
130 | for (i--; i >= 0; i--) |
131 | bpf_object__close(objs[i]); |
132 | return 0; |
133 | } |
134 | |
135 | int main(int argc, char **argv) |
136 | { |
137 | int opt, nr_tests = 1; |
138 | char filename[256]; |
139 | |
140 | while ((opt = getopt(argc: argc, argv: argv, shortopts: "i:h" )) != -1) { |
141 | switch (opt) { |
142 | case 'i': |
143 | nr_tests = atoi(nptr: optarg); |
144 | break; |
145 | case 'h': |
146 | default: |
147 | usage(cmd: argv[0]); |
148 | return 0; |
149 | } |
150 | } |
151 | |
152 | snprintf(s: filename, maxlen: sizeof(filename), format: "%s_kern.o" , argv[0]); |
153 | |
154 | return test(filename, nr_tests); |
155 | } |
156 | |