]> git.ipfire.org Git - thirdparty/kernel/linux.git/blame - 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
CommitLineData
45178a92
SL
1// SPDX-License-Identifier: GPL-2.0
2#include <errno.h>
7b612e29 3#include <stdlib.h>
45178a92 4#include <bpf/bpf.h>
7b612e29 5#include <bpf/btf.h>
a742258a 6#include <bpf/libbpf.h>
7b612e29 7#include <linux/btf.h>
a742258a 8#include <linux/err.h>
45178a92
SL
9#include "bpf-event.h"
10#include "debug.h"
11#include "symbol.h"
811184fb 12#include "machine.h"
e4378f0c 13#include "env.h"
e5416950 14#include "session.h"
45178a92 15
7b612e29
SL
16#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
17
18static 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
45178a92
SL
28int 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}
7b612e29
SL
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 */
e5416950 47static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
7b612e29
SL
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;
a742258a 56 struct bpf_prog_info_linear *info_linear;
e5416950 57 struct perf_tool *tool = session->tool;
e4378f0c 58 struct bpf_prog_info_node *info_node;
a742258a 59 struct bpf_prog_info *info;
7b612e29 60 struct btf *btf = NULL;
7b612e29 61 bool has_btf = false;
e4378f0c 62 struct perf_env *env;
a742258a 63 u32 sub_prog_cnt, i;
7b612e29 64 int err = 0;
a742258a 65 u64 arrays;
7b612e29 66
e4378f0c
SL
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
a742258a
SL
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;
e4378f0c
SL
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;
7b612e29 80
a742258a
SL
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__);
7b612e29
SL
85 return -1;
86 }
a742258a
SL
87
88 if (info_linear->info_len < offsetof(struct bpf_prog_info, prog_tags)) {
7b612e29
SL
89 pr_debug("%s: the kernel is too old, aborting\n", __func__);
90 return -2;
91 }
92
a742258a
SL
93 info = &info_linear->info;
94
7b612e29 95 /* number of ksyms, func_lengths, and tags should match */
a742258a
SL
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)
7b612e29
SL
99 return -1;
100
101 /* check BTF func info support */
a742258a 102 if (info->btf_id && info->nr_func_info && info->func_info_rec_size) {
7b612e29 103 /* btf func info number should be same as sub_prog_cnt */
a742258a 104 if (sub_prog_cnt != info->nr_func_info) {
7b612e29 105 pr_debug("%s: mismatch in BPF sub program count and BTF function info count, aborting\n", __func__);
a742258a
SL
106 err = -1;
107 goto out;
7b612e29 108 }
a742258a
SL
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;
7b612e29
SL
114 }
115 has_btf = true;
116 }
117
7b612e29
SL
118 /* Synthesize PERF_RECORD_KSYMBOL */
119 for (i = 0; i < sub_prog_cnt; i++) {
a742258a
SL
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);
7b612e29
SL
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,
811184fb 132 .size = offsetof(struct ksymbol_event, name),
7b612e29
SL
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) {
a742258a 145 finfo = func_infos + i * info->func_info_rec_size;
7b612e29
SL
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 */
a742258a
SL
150 if (info->name[0])
151 short_name = info->name;
7b612e29
SL
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));
811184fb
SL
161
162 memset((void *)event + event->header.size, 0, machine->id_hdr_size);
163 event->header.size += machine->id_hdr_size;
7b612e29
SL
164 err = perf_tool__process_synth_event(tool, event,
165 machine, process);
166 }
167
71184c6a 168 if (!opts->no_bpf_event) {
e4378f0c 169 /* Synthesize PERF_RECORD_BPF_EVENT */
7b612e29
SL
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,
a742258a 177 .id = info->id,
7b612e29 178 };
a742258a 179 memcpy(bpf_event->tag, info->tag, BPF_TAG_SIZE);
811184fb
SL
180 memset((void *)event + event->header.size, 0, machine->id_hdr_size);
181 event->header.size += machine->id_hdr_size;
e4378f0c
SL
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 */
7b612e29
SL
198 err = perf_tool__process_synth_event(tool, event,
199 machine, process);
200 }
201
202out:
a742258a 203 free(info_linear);
7b612e29
SL
204 free(btf);
205 return err ? -1 : 0;
206}
207
e5416950 208int perf_event__synthesize_bpf_events(struct perf_session *session,
7b612e29
SL
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
811184fb 218 event = malloc(sizeof(event->bpf_event) + KSYM_NAME_LEN + machine->id_hdr_size);
7b612e29
SL
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 }
b20fe106 228 pr_debug("%s: can't get next program: %s%s\n",
7b612e29
SL
229 __func__, strerror(errno),
230 errno == EINVAL ? " -- kernel too old?" : "");
39f4a913
SL
231 /* don't report error on old kernel or EPERM */
232 err = (errno == EINVAL || errno == EPERM) ? 0 : -1;
7b612e29
SL
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
e5416950 242 err = perf_event__synthesize_one_bpf_prog(session, process,
7b612e29
SL
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}