]> git.ipfire.org Git - thirdparty/kernel/linux.git/blob - tools/perf/util/bpf-event.c
perf bpf: Save bpf_prog_info in a rbtree in perf_env
[thirdparty/kernel/linux.git] / tools / perf / util / bpf-event.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include <stdlib.h>
4 #include <bpf/bpf.h>
5 #include <bpf/btf.h>
6 #include <bpf/libbpf.h>
7 #include <linux/btf.h>
8 #include <linux/err.h>
9 #include "bpf-event.h"
10 #include "debug.h"
11 #include "symbol.h"
12 #include "machine.h"
13 #include "env.h"
14 #include "session.h"
15
16 #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
17
18 static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len)
19 {
20 int ret = 0;
21 size_t i;
22
23 for (i = 0; i < len; i++)
24 ret += snprintf(buf + ret, size - ret, "%02x", data[i]);
25 return ret;
26 }
27
28 int machine__process_bpf_event(struct machine *machine __maybe_unused,
29 union perf_event *event,
30 struct perf_sample *sample __maybe_unused)
31 {
32 if (dump_trace)
33 perf_event__fprintf_bpf_event(event, stdout);
34 return 0;
35 }
36
37 /*
38 * Synthesize PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT for one bpf
39 * program. One PERF_RECORD_BPF_EVENT is generated for the program. And
40 * one PERF_RECORD_KSYMBOL is generated for each sub program.
41 *
42 * Returns:
43 * 0 for success;
44 * -1 for failures;
45 * -2 for lack of kernel support.
46 */
47 static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
48 perf_event__handler_t process,
49 struct machine *machine,
50 int fd,
51 union perf_event *event,
52 struct record_opts *opts)
53 {
54 struct ksymbol_event *ksymbol_event = &event->ksymbol_event;
55 struct bpf_event *bpf_event = &event->bpf_event;
56 struct bpf_prog_info_linear *info_linear;
57 struct perf_tool *tool = session->tool;
58 struct bpf_prog_info_node *info_node;
59 struct bpf_prog_info *info;
60 struct btf *btf = NULL;
61 bool has_btf = false;
62 struct perf_env *env;
63 u32 sub_prog_cnt, i;
64 int err = 0;
65 u64 arrays;
66
67 /*
68 * for perf-record and perf-report use header.env;
69 * otherwise, use global perf_env.
70 */
71 env = session->data ? &session->header.env : &perf_env;
72
73 arrays = 1UL << BPF_PROG_INFO_JITED_KSYMS;
74 arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS;
75 arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO;
76 arrays |= 1UL << BPF_PROG_INFO_PROG_TAGS;
77 arrays |= 1UL << BPF_PROG_INFO_JITED_INSNS;
78 arrays |= 1UL << BPF_PROG_INFO_LINE_INFO;
79 arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO;
80
81 info_linear = bpf_program__get_prog_info_linear(fd, arrays);
82 if (IS_ERR_OR_NULL(info_linear)) {
83 info_linear = NULL;
84 pr_debug("%s: failed to get BPF program info. aborting\n", __func__);
85 return -1;
86 }
87
88 if (info_linear->info_len < offsetof(struct bpf_prog_info, prog_tags)) {
89 pr_debug("%s: the kernel is too old, aborting\n", __func__);
90 return -2;
91 }
92
93 info = &info_linear->info;
94
95 /* number of ksyms, func_lengths, and tags should match */
96 sub_prog_cnt = info->nr_jited_ksyms;
97 if (sub_prog_cnt != info->nr_prog_tags ||
98 sub_prog_cnt != info->nr_jited_func_lens)
99 return -1;
100
101 /* check BTF func info support */
102 if (info->btf_id && info->nr_func_info && info->func_info_rec_size) {
103 /* btf func info number should be same as sub_prog_cnt */
104 if (sub_prog_cnt != info->nr_func_info) {
105 pr_debug("%s: mismatch in BPF sub program count and BTF function info count, aborting\n", __func__);
106 err = -1;
107 goto out;
108 }
109 if (btf__get_from_id(info->btf_id, &btf)) {
110 pr_debug("%s: failed to get BTF of id %u, aborting\n", __func__, info->btf_id);
111 err = -1;
112 btf = NULL;
113 goto out;
114 }
115 has_btf = true;
116 }
117
118 /* Synthesize PERF_RECORD_KSYMBOL */
119 for (i = 0; i < sub_prog_cnt; i++) {
120 u8 (*prog_tags)[BPF_TAG_SIZE] = (void *)(uintptr_t)(info->prog_tags);
121 __u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens);
122 __u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms);
123 void *func_infos = (void *)(uintptr_t)(info->func_info);
124 const struct bpf_func_info *finfo;
125 const char *short_name = NULL;
126 const struct btf_type *t;
127 int name_len;
128
129 *ksymbol_event = (struct ksymbol_event){
130 .header = {
131 .type = PERF_RECORD_KSYMBOL,
132 .size = offsetof(struct ksymbol_event, name),
133 },
134 .addr = prog_addrs[i],
135 .len = prog_lens[i],
136 .ksym_type = PERF_RECORD_KSYMBOL_TYPE_BPF,
137 .flags = 0,
138 };
139 name_len = snprintf(ksymbol_event->name, KSYM_NAME_LEN,
140 "bpf_prog_");
141 name_len += snprintf_hex(ksymbol_event->name + name_len,
142 KSYM_NAME_LEN - name_len,
143 prog_tags[i], BPF_TAG_SIZE);
144 if (has_btf) {
145 finfo = func_infos + i * info->func_info_rec_size;
146 t = btf__type_by_id(btf, finfo->type_id);
147 short_name = btf__name_by_offset(btf, t->name_off);
148 } else if (i == 0 && sub_prog_cnt == 1) {
149 /* no subprog */
150 if (info->name[0])
151 short_name = info->name;
152 } else
153 short_name = "F";
154 if (short_name)
155 name_len += snprintf(ksymbol_event->name + name_len,
156 KSYM_NAME_LEN - name_len,
157 "_%s", short_name);
158
159 ksymbol_event->header.size += PERF_ALIGN(name_len + 1,
160 sizeof(u64));
161
162 memset((void *)event + event->header.size, 0, machine->id_hdr_size);
163 event->header.size += machine->id_hdr_size;
164 err = perf_tool__process_synth_event(tool, event,
165 machine, process);
166 }
167
168 if (!opts->no_bpf_event) {
169 /* Synthesize PERF_RECORD_BPF_EVENT */
170 *bpf_event = (struct bpf_event){
171 .header = {
172 .type = PERF_RECORD_BPF_EVENT,
173 .size = sizeof(struct bpf_event),
174 },
175 .type = PERF_BPF_EVENT_PROG_LOAD,
176 .flags = 0,
177 .id = info->id,
178 };
179 memcpy(bpf_event->tag, info->tag, BPF_TAG_SIZE);
180 memset((void *)event + event->header.size, 0, machine->id_hdr_size);
181 event->header.size += machine->id_hdr_size;
182
183 /* save bpf_prog_info to env */
184 info_node = malloc(sizeof(struct bpf_prog_info_node));
185 if (!info_node) {
186 err = -1;
187 goto out;
188 }
189
190 info_node->info_linear = info_linear;
191 perf_env__insert_bpf_prog_info(env, info_node);
192 info_linear = NULL;
193
194 /*
195 * process after saving bpf_prog_info to env, so that
196 * required information is ready for look up
197 */
198 err = perf_tool__process_synth_event(tool, event,
199 machine, process);
200 }
201
202 out:
203 free(info_linear);
204 free(btf);
205 return err ? -1 : 0;
206 }
207
208 int perf_event__synthesize_bpf_events(struct perf_session *session,
209 perf_event__handler_t process,
210 struct machine *machine,
211 struct record_opts *opts)
212 {
213 union perf_event *event;
214 __u32 id = 0;
215 int err;
216 int fd;
217
218 event = malloc(sizeof(event->bpf_event) + KSYM_NAME_LEN + machine->id_hdr_size);
219 if (!event)
220 return -1;
221 while (true) {
222 err = bpf_prog_get_next_id(id, &id);
223 if (err) {
224 if (errno == ENOENT) {
225 err = 0;
226 break;
227 }
228 pr_debug("%s: can't get next program: %s%s\n",
229 __func__, strerror(errno),
230 errno == EINVAL ? " -- kernel too old?" : "");
231 /* don't report error on old kernel or EPERM */
232 err = (errno == EINVAL || errno == EPERM) ? 0 : -1;
233 break;
234 }
235 fd = bpf_prog_get_fd_by_id(id);
236 if (fd < 0) {
237 pr_debug("%s: failed to get fd for prog_id %u\n",
238 __func__, id);
239 continue;
240 }
241
242 err = perf_event__synthesize_one_bpf_prog(session, process,
243 machine, fd,
244 event, opts);
245 close(fd);
246 if (err) {
247 /* do not return error for old kernel */
248 if (err == -2)
249 err = 0;
250 break;
251 }
252 }
253 free(event);
254 return err;
255 }