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