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