]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
45178a92 SL |
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 | } | |
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 | 47 | static 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 | ||
202 | out: | |
a742258a | 203 | free(info_linear); |
7b612e29 SL |
204 | free(btf); |
205 | return err ? -1 : 0; | |
206 | } | |
207 | ||
e5416950 | 208 | int 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 | } |