1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (c) 2018 Facebook */ |
3 | |
4 | #include <ctype.h> |
5 | #include <stdio.h> /* for (FILE *) used by json_writer */ |
6 | #include <string.h> |
7 | #include <unistd.h> |
8 | #include <asm/byteorder.h> |
9 | #include <linux/bitops.h> |
10 | #include <linux/btf.h> |
11 | #include <linux/err.h> |
12 | #include <bpf/btf.h> |
13 | #include <bpf/bpf.h> |
14 | |
15 | #include "json_writer.h" |
16 | #include "main.h" |
17 | |
18 | #define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1) |
19 | #define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK) |
20 | #define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3) |
21 | #define BITS_ROUNDUP_BYTES(bits) \ |
22 | (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits)) |
23 | |
24 | static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id, |
25 | __u8 bit_offset, const void *data); |
26 | |
27 | static int btf_dump_func(const struct btf *btf, char *func_sig, |
28 | const struct btf_type *func_proto, |
29 | const struct btf_type *func, int pos, int size); |
30 | |
31 | static int dump_prog_id_as_func_ptr(const struct btf_dumper *d, |
32 | const struct btf_type *func_proto, |
33 | __u32 prog_id) |
34 | { |
35 | const struct btf_type *func_type; |
36 | int prog_fd = -1, func_sig_len; |
37 | struct bpf_prog_info info = {}; |
38 | __u32 info_len = sizeof(info); |
39 | const char *prog_name = NULL; |
40 | struct btf *prog_btf = NULL; |
41 | struct bpf_func_info finfo; |
42 | __u32 finfo_rec_size; |
43 | char prog_str[1024]; |
44 | int err; |
45 | |
46 | /* Get the ptr's func_proto */ |
47 | func_sig_len = btf_dump_func(btf: d->btf, func_sig: prog_str, func_proto, NULL, pos: 0, |
48 | size: sizeof(prog_str)); |
49 | if (func_sig_len == -1) |
50 | return -1; |
51 | |
52 | if (!prog_id) |
53 | goto print; |
54 | |
55 | /* Get the bpf_prog's name. Obtain from func_info. */ |
56 | prog_fd = bpf_prog_get_fd_by_id(prog_id); |
57 | if (prog_fd < 0) |
58 | goto print; |
59 | |
60 | err = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len); |
61 | if (err) |
62 | goto print; |
63 | |
64 | if (!info.btf_id || !info.nr_func_info) |
65 | goto print; |
66 | |
67 | finfo_rec_size = info.func_info_rec_size; |
68 | memset(&info, 0, sizeof(info)); |
69 | info.nr_func_info = 1; |
70 | info.func_info_rec_size = finfo_rec_size; |
71 | info.func_info = ptr_to_u64(ptr: &finfo); |
72 | |
73 | err = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len); |
74 | if (err) |
75 | goto print; |
76 | |
77 | prog_btf = btf__load_from_kernel_by_id(info.btf_id); |
78 | if (!prog_btf) |
79 | goto print; |
80 | func_type = btf__type_by_id(prog_btf, finfo.type_id); |
81 | if (!func_type || !btf_is_func(func_type)) |
82 | goto print; |
83 | |
84 | prog_name = btf__name_by_offset(prog_btf, func_type->name_off); |
85 | |
86 | print: |
87 | if (!prog_id) |
88 | snprintf(buf: &prog_str[func_sig_len], |
89 | size: sizeof(prog_str) - func_sig_len, fmt: " 0" ); |
90 | else if (prog_name) |
91 | snprintf(buf: &prog_str[func_sig_len], |
92 | size: sizeof(prog_str) - func_sig_len, |
93 | fmt: " %s/prog_id:%u" , prog_name, prog_id); |
94 | else |
95 | snprintf(buf: &prog_str[func_sig_len], |
96 | size: sizeof(prog_str) - func_sig_len, |
97 | fmt: " <unknown_prog_name>/prog_id:%u" , prog_id); |
98 | |
99 | prog_str[sizeof(prog_str) - 1] = '\0'; |
100 | jsonw_string(self: d->jw, value: prog_str); |
101 | btf__free(prog_btf); |
102 | if (prog_fd >= 0) |
103 | close(prog_fd); |
104 | return 0; |
105 | } |
106 | |
107 | static void btf_dumper_ptr(const struct btf_dumper *d, |
108 | const struct btf_type *t, |
109 | const void *data) |
110 | { |
111 | unsigned long value = *(unsigned long *)data; |
112 | const struct btf_type *ptr_type; |
113 | __s32 ptr_type_id; |
114 | |
115 | if (!d->prog_id_as_func_ptr || value > UINT32_MAX) |
116 | goto print_ptr_value; |
117 | |
118 | ptr_type_id = btf__resolve_type(d->btf, t->type); |
119 | if (ptr_type_id < 0) |
120 | goto print_ptr_value; |
121 | ptr_type = btf__type_by_id(d->btf, ptr_type_id); |
122 | if (!ptr_type || !btf_is_func_proto(ptr_type)) |
123 | goto print_ptr_value; |
124 | |
125 | if (!dump_prog_id_as_func_ptr(d, func_proto: ptr_type, prog_id: value)) |
126 | return; |
127 | |
128 | print_ptr_value: |
129 | if (d->is_plain_text) |
130 | jsonw_printf(self: d->jw, fmt: "\"%p\"" , (void *)value); |
131 | else |
132 | jsonw_printf(self: d->jw, fmt: "%lu" , value); |
133 | } |
134 | |
135 | static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id, |
136 | __u8 bit_offset, const void *data) |
137 | { |
138 | int actual_type_id; |
139 | |
140 | actual_type_id = btf__resolve_type(d->btf, type_id); |
141 | if (actual_type_id < 0) |
142 | return actual_type_id; |
143 | |
144 | return btf_dumper_do_type(d, type_id: actual_type_id, bit_offset, data); |
145 | } |
146 | |
147 | static int btf_dumper_enum(const struct btf_dumper *d, |
148 | const struct btf_type *t, |
149 | const void *data) |
150 | { |
151 | const struct btf_enum *enums = btf_enum(t); |
152 | __s64 value; |
153 | __u16 i; |
154 | |
155 | switch (t->size) { |
156 | case 8: |
157 | value = *(__s64 *)data; |
158 | break; |
159 | case 4: |
160 | value = *(__s32 *)data; |
161 | break; |
162 | case 2: |
163 | value = *(__s16 *)data; |
164 | break; |
165 | case 1: |
166 | value = *(__s8 *)data; |
167 | break; |
168 | default: |
169 | return -EINVAL; |
170 | } |
171 | |
172 | for (i = 0; i < btf_vlen(t); i++) { |
173 | if (value == enums[i].val) { |
174 | jsonw_string(self: d->jw, |
175 | value: btf__name_by_offset(d->btf, |
176 | enums[i].name_off)); |
177 | return 0; |
178 | } |
179 | } |
180 | |
181 | jsonw_int(self: d->jw, number: value); |
182 | return 0; |
183 | } |
184 | |
185 | static int btf_dumper_enum64(const struct btf_dumper *d, |
186 | const struct btf_type *t, |
187 | const void *data) |
188 | { |
189 | const struct btf_enum64 *enums = btf_enum64(t); |
190 | __u32 val_lo32, val_hi32; |
191 | __u64 value; |
192 | __u16 i; |
193 | |
194 | value = *(__u64 *)data; |
195 | val_lo32 = (__u32)value; |
196 | val_hi32 = value >> 32; |
197 | |
198 | for (i = 0; i < btf_vlen(t); i++) { |
199 | if (val_lo32 == enums[i].val_lo32 && val_hi32 == enums[i].val_hi32) { |
200 | jsonw_string(self: d->jw, |
201 | value: btf__name_by_offset(d->btf, |
202 | enums[i].name_off)); |
203 | return 0; |
204 | } |
205 | } |
206 | |
207 | jsonw_int(self: d->jw, number: value); |
208 | return 0; |
209 | } |
210 | |
211 | static bool is_str_array(const struct btf *btf, const struct btf_array *arr, |
212 | const char *s) |
213 | { |
214 | const struct btf_type *elem_type; |
215 | const char *end_s; |
216 | |
217 | if (!arr->nelems) |
218 | return false; |
219 | |
220 | elem_type = btf__type_by_id(btf, arr->type); |
221 | /* Not skipping typedef. typedef to char does not count as |
222 | * a string now. |
223 | */ |
224 | while (elem_type && btf_is_mod(elem_type)) |
225 | elem_type = btf__type_by_id(btf, elem_type->type); |
226 | |
227 | if (!elem_type || !btf_is_int(t: elem_type) || elem_type->size != 1) |
228 | return false; |
229 | |
230 | if (btf_int_encoding(t: elem_type) != BTF_INT_CHAR && |
231 | strcmp("char" , btf__name_by_offset(btf, elem_type->name_off))) |
232 | return false; |
233 | |
234 | end_s = s + arr->nelems; |
235 | while (s < end_s) { |
236 | if (!*s) |
237 | return true; |
238 | if (*s <= 0x1f || *s >= 0x7f) |
239 | return false; |
240 | s++; |
241 | } |
242 | |
243 | /* '\0' is not found */ |
244 | return false; |
245 | } |
246 | |
247 | static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id, |
248 | const void *data) |
249 | { |
250 | const struct btf_type *t = btf__type_by_id(d->btf, type_id); |
251 | struct btf_array *arr = (struct btf_array *)(t + 1); |
252 | long long elem_size; |
253 | int ret = 0; |
254 | __u32 i; |
255 | |
256 | if (is_str_array(btf: d->btf, arr, s: data)) { |
257 | jsonw_string(self: d->jw, value: data); |
258 | return 0; |
259 | } |
260 | |
261 | elem_size = btf__resolve_size(d->btf, arr->type); |
262 | if (elem_size < 0) |
263 | return elem_size; |
264 | |
265 | jsonw_start_array(self: d->jw); |
266 | for (i = 0; i < arr->nelems; i++) { |
267 | ret = btf_dumper_do_type(d, type_id: arr->type, bit_offset: 0, |
268 | data: data + i * elem_size); |
269 | if (ret) |
270 | break; |
271 | } |
272 | |
273 | jsonw_end_array(self: d->jw); |
274 | return ret; |
275 | } |
276 | |
277 | static void btf_int128_print(json_writer_t *jw, const void *data, |
278 | bool is_plain_text) |
279 | { |
280 | /* data points to a __int128 number. |
281 | * Suppose |
282 | * int128_num = *(__int128 *)data; |
283 | * The below formulas shows what upper_num and lower_num represents: |
284 | * upper_num = int128_num >> 64; |
285 | * lower_num = int128_num & 0xffffffffFFFFFFFFULL; |
286 | */ |
287 | __u64 upper_num, lower_num; |
288 | |
289 | #ifdef __BIG_ENDIAN_BITFIELD |
290 | upper_num = *(__u64 *)data; |
291 | lower_num = *(__u64 *)(data + 8); |
292 | #else |
293 | upper_num = *(__u64 *)(data + 8); |
294 | lower_num = *(__u64 *)data; |
295 | #endif |
296 | |
297 | if (is_plain_text) { |
298 | if (upper_num == 0) |
299 | jsonw_printf(self: jw, fmt: "0x%llx" , lower_num); |
300 | else |
301 | jsonw_printf(self: jw, fmt: "0x%llx%016llx" , upper_num, lower_num); |
302 | } else { |
303 | if (upper_num == 0) |
304 | jsonw_printf(self: jw, fmt: "\"0x%llx\"" , lower_num); |
305 | else |
306 | jsonw_printf(self: jw, fmt: "\"0x%llx%016llx\"" , upper_num, lower_num); |
307 | } |
308 | } |
309 | |
310 | static void btf_int128_shift(__u64 *print_num, __u16 left_shift_bits, |
311 | __u16 right_shift_bits) |
312 | { |
313 | __u64 upper_num, lower_num; |
314 | |
315 | #ifdef __BIG_ENDIAN_BITFIELD |
316 | upper_num = print_num[0]; |
317 | lower_num = print_num[1]; |
318 | #else |
319 | upper_num = print_num[1]; |
320 | lower_num = print_num[0]; |
321 | #endif |
322 | |
323 | /* shake out un-needed bits by shift/or operations */ |
324 | if (left_shift_bits >= 64) { |
325 | upper_num = lower_num << (left_shift_bits - 64); |
326 | lower_num = 0; |
327 | } else { |
328 | upper_num = (upper_num << left_shift_bits) | |
329 | (lower_num >> (64 - left_shift_bits)); |
330 | lower_num = lower_num << left_shift_bits; |
331 | } |
332 | |
333 | if (right_shift_bits >= 64) { |
334 | lower_num = upper_num >> (right_shift_bits - 64); |
335 | upper_num = 0; |
336 | } else { |
337 | lower_num = (lower_num >> right_shift_bits) | |
338 | (upper_num << (64 - right_shift_bits)); |
339 | upper_num = upper_num >> right_shift_bits; |
340 | } |
341 | |
342 | #ifdef __BIG_ENDIAN_BITFIELD |
343 | print_num[0] = upper_num; |
344 | print_num[1] = lower_num; |
345 | #else |
346 | print_num[0] = lower_num; |
347 | print_num[1] = upper_num; |
348 | #endif |
349 | } |
350 | |
351 | static void btf_dumper_bitfield(__u32 nr_bits, __u8 bit_offset, |
352 | const void *data, json_writer_t *jw, |
353 | bool is_plain_text) |
354 | { |
355 | int left_shift_bits, right_shift_bits; |
356 | __u64 print_num[2] = {}; |
357 | int bytes_to_copy; |
358 | int bits_to_copy; |
359 | |
360 | bits_to_copy = bit_offset + nr_bits; |
361 | bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy); |
362 | |
363 | memcpy(print_num, data, bytes_to_copy); |
364 | #if defined(__BIG_ENDIAN_BITFIELD) |
365 | left_shift_bits = bit_offset; |
366 | #elif defined(__LITTLE_ENDIAN_BITFIELD) |
367 | left_shift_bits = 128 - bits_to_copy; |
368 | #else |
369 | #error neither big nor little endian |
370 | #endif |
371 | right_shift_bits = 128 - nr_bits; |
372 | |
373 | btf_int128_shift(print_num, left_shift_bits, right_shift_bits); |
374 | btf_int128_print(jw, data: print_num, is_plain_text); |
375 | } |
376 | |
377 | |
378 | static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset, |
379 | const void *data, json_writer_t *jw, |
380 | bool is_plain_text) |
381 | { |
382 | int nr_bits = BTF_INT_BITS(int_type); |
383 | int total_bits_offset; |
384 | |
385 | /* bits_offset is at most 7. |
386 | * BTF_INT_OFFSET() cannot exceed 128 bits. |
387 | */ |
388 | total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type); |
389 | data += BITS_ROUNDDOWN_BYTES(total_bits_offset); |
390 | bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset); |
391 | btf_dumper_bitfield(nr_bits, bit_offset, data, jw, |
392 | is_plain_text); |
393 | } |
394 | |
395 | static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset, |
396 | const void *data, json_writer_t *jw, |
397 | bool is_plain_text) |
398 | { |
399 | __u32 *int_type; |
400 | __u32 nr_bits; |
401 | |
402 | int_type = (__u32 *)(t + 1); |
403 | nr_bits = BTF_INT_BITS(*int_type); |
404 | /* if this is bit field */ |
405 | if (bit_offset || BTF_INT_OFFSET(*int_type) || |
406 | BITS_PER_BYTE_MASKED(nr_bits)) { |
407 | btf_dumper_int_bits(int_type: *int_type, bit_offset, data, jw, |
408 | is_plain_text); |
409 | return 0; |
410 | } |
411 | |
412 | if (nr_bits == 128) { |
413 | btf_int128_print(jw, data, is_plain_text); |
414 | return 0; |
415 | } |
416 | |
417 | switch (BTF_INT_ENCODING(*int_type)) { |
418 | case 0: |
419 | if (BTF_INT_BITS(*int_type) == 64) |
420 | jsonw_printf(self: jw, fmt: "%llu" , *(__u64 *)data); |
421 | else if (BTF_INT_BITS(*int_type) == 32) |
422 | jsonw_printf(self: jw, fmt: "%u" , *(__u32 *)data); |
423 | else if (BTF_INT_BITS(*int_type) == 16) |
424 | jsonw_printf(self: jw, fmt: "%hu" , *(__u16 *)data); |
425 | else if (BTF_INT_BITS(*int_type) == 8) |
426 | jsonw_printf(self: jw, fmt: "%hhu" , *(__u8 *)data); |
427 | else |
428 | btf_dumper_int_bits(int_type: *int_type, bit_offset, data, jw, |
429 | is_plain_text); |
430 | break; |
431 | case BTF_INT_SIGNED: |
432 | if (BTF_INT_BITS(*int_type) == 64) |
433 | jsonw_printf(self: jw, fmt: "%lld" , *(long long *)data); |
434 | else if (BTF_INT_BITS(*int_type) == 32) |
435 | jsonw_printf(self: jw, fmt: "%d" , *(int *)data); |
436 | else if (BTF_INT_BITS(*int_type) == 16) |
437 | jsonw_printf(self: jw, fmt: "%hd" , *(short *)data); |
438 | else if (BTF_INT_BITS(*int_type) == 8) |
439 | jsonw_printf(self: jw, fmt: "%hhd" , *(char *)data); |
440 | else |
441 | btf_dumper_int_bits(int_type: *int_type, bit_offset, data, jw, |
442 | is_plain_text); |
443 | break; |
444 | case BTF_INT_CHAR: |
445 | if (isprint(*(char *)data)) |
446 | jsonw_printf(self: jw, fmt: "\"%c\"" , *(char *)data); |
447 | else |
448 | if (is_plain_text) |
449 | jsonw_printf(self: jw, fmt: "0x%hhx" , *(char *)data); |
450 | else |
451 | jsonw_printf(self: jw, fmt: "\"\\u00%02hhx\"" , |
452 | *(char *)data); |
453 | break; |
454 | case BTF_INT_BOOL: |
455 | jsonw_bool(self: jw, value: *(bool *)data); |
456 | break; |
457 | default: |
458 | /* shouldn't happen */ |
459 | return -EINVAL; |
460 | } |
461 | |
462 | return 0; |
463 | } |
464 | |
465 | static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id, |
466 | const void *data) |
467 | { |
468 | const struct btf_type *t; |
469 | struct btf_member *m; |
470 | const void *data_off; |
471 | int kind_flag; |
472 | int ret = 0; |
473 | int i, vlen; |
474 | |
475 | t = btf__type_by_id(d->btf, type_id); |
476 | if (!t) |
477 | return -EINVAL; |
478 | |
479 | kind_flag = BTF_INFO_KFLAG(t->info); |
480 | vlen = BTF_INFO_VLEN(t->info); |
481 | jsonw_start_object(self: d->jw); |
482 | m = (struct btf_member *)(t + 1); |
483 | |
484 | for (i = 0; i < vlen; i++) { |
485 | __u32 bit_offset = m[i].offset; |
486 | __u32 bitfield_size = 0; |
487 | |
488 | if (kind_flag) { |
489 | bitfield_size = BTF_MEMBER_BITFIELD_SIZE(bit_offset); |
490 | bit_offset = BTF_MEMBER_BIT_OFFSET(bit_offset); |
491 | } |
492 | |
493 | jsonw_name(self: d->jw, name: btf__name_by_offset(d->btf, m[i].name_off)); |
494 | data_off = data + BITS_ROUNDDOWN_BYTES(bit_offset); |
495 | if (bitfield_size) { |
496 | btf_dumper_bitfield(nr_bits: bitfield_size, |
497 | BITS_PER_BYTE_MASKED(bit_offset), |
498 | data: data_off, jw: d->jw, is_plain_text: d->is_plain_text); |
499 | } else { |
500 | ret = btf_dumper_do_type(d, type_id: m[i].type, |
501 | BITS_PER_BYTE_MASKED(bit_offset), |
502 | data: data_off); |
503 | if (ret) |
504 | break; |
505 | } |
506 | } |
507 | |
508 | jsonw_end_object(self: d->jw); |
509 | |
510 | return ret; |
511 | } |
512 | |
513 | static int btf_dumper_var(const struct btf_dumper *d, __u32 type_id, |
514 | __u8 bit_offset, const void *data) |
515 | { |
516 | const struct btf_type *t = btf__type_by_id(d->btf, type_id); |
517 | int ret; |
518 | |
519 | jsonw_start_object(self: d->jw); |
520 | jsonw_name(self: d->jw, name: btf__name_by_offset(d->btf, t->name_off)); |
521 | ret = btf_dumper_do_type(d, type_id: t->type, bit_offset, data); |
522 | jsonw_end_object(self: d->jw); |
523 | |
524 | return ret; |
525 | } |
526 | |
527 | static int btf_dumper_datasec(const struct btf_dumper *d, __u32 type_id, |
528 | const void *data) |
529 | { |
530 | struct btf_var_secinfo *vsi; |
531 | const struct btf_type *t; |
532 | int ret = 0, i, vlen; |
533 | |
534 | t = btf__type_by_id(d->btf, type_id); |
535 | if (!t) |
536 | return -EINVAL; |
537 | |
538 | vlen = BTF_INFO_VLEN(t->info); |
539 | vsi = (struct btf_var_secinfo *)(t + 1); |
540 | |
541 | jsonw_start_object(self: d->jw); |
542 | jsonw_name(self: d->jw, name: btf__name_by_offset(d->btf, t->name_off)); |
543 | jsonw_start_array(self: d->jw); |
544 | for (i = 0; i < vlen; i++) { |
545 | ret = btf_dumper_do_type(d, type_id: vsi[i].type, bit_offset: 0, data: data + vsi[i].offset); |
546 | if (ret) |
547 | break; |
548 | } |
549 | jsonw_end_array(self: d->jw); |
550 | jsonw_end_object(self: d->jw); |
551 | |
552 | return ret; |
553 | } |
554 | |
555 | static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id, |
556 | __u8 bit_offset, const void *data) |
557 | { |
558 | const struct btf_type *t = btf__type_by_id(d->btf, type_id); |
559 | |
560 | switch (BTF_INFO_KIND(t->info)) { |
561 | case BTF_KIND_INT: |
562 | return btf_dumper_int(t, bit_offset, data, jw: d->jw, |
563 | is_plain_text: d->is_plain_text); |
564 | case BTF_KIND_STRUCT: |
565 | case BTF_KIND_UNION: |
566 | return btf_dumper_struct(d, type_id, data); |
567 | case BTF_KIND_ARRAY: |
568 | return btf_dumper_array(d, type_id, data); |
569 | case BTF_KIND_ENUM: |
570 | return btf_dumper_enum(d, t, data); |
571 | case BTF_KIND_ENUM64: |
572 | return btf_dumper_enum64(d, t, data); |
573 | case BTF_KIND_PTR: |
574 | btf_dumper_ptr(d, t, data); |
575 | return 0; |
576 | case BTF_KIND_UNKN: |
577 | jsonw_printf(self: d->jw, fmt: "(unknown)" ); |
578 | return 0; |
579 | case BTF_KIND_FWD: |
580 | /* map key or value can't be forward */ |
581 | jsonw_printf(self: d->jw, fmt: "(fwd-kind-invalid)" ); |
582 | return -EINVAL; |
583 | case BTF_KIND_TYPEDEF: |
584 | case BTF_KIND_VOLATILE: |
585 | case BTF_KIND_CONST: |
586 | case BTF_KIND_RESTRICT: |
587 | return btf_dumper_modifier(d, type_id, bit_offset, data); |
588 | case BTF_KIND_VAR: |
589 | return btf_dumper_var(d, type_id, bit_offset, data); |
590 | case BTF_KIND_DATASEC: |
591 | return btf_dumper_datasec(d, type_id, data); |
592 | default: |
593 | jsonw_printf(self: d->jw, fmt: "(unsupported-kind" ); |
594 | return -EINVAL; |
595 | } |
596 | } |
597 | |
598 | int btf_dumper_type(const struct btf_dumper *d, __u32 type_id, |
599 | const void *data) |
600 | { |
601 | return btf_dumper_do_type(d, type_id, bit_offset: 0, data); |
602 | } |
603 | |
604 | #define BTF_PRINT_ARG(...) \ |
605 | do { \ |
606 | pos += snprintf(func_sig + pos, size - pos, \ |
607 | __VA_ARGS__); \ |
608 | if (pos >= size) \ |
609 | return -1; \ |
610 | } while (0) |
611 | #define BTF_PRINT_TYPE(type) \ |
612 | do { \ |
613 | pos = __btf_dumper_type_only(btf, type, func_sig, \ |
614 | pos, size); \ |
615 | if (pos == -1) \ |
616 | return -1; \ |
617 | } while (0) |
618 | |
619 | static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id, |
620 | char *func_sig, int pos, int size) |
621 | { |
622 | const struct btf_type *proto_type; |
623 | const struct btf_array *array; |
624 | const struct btf_var *var; |
625 | const struct btf_type *t; |
626 | |
627 | if (!type_id) { |
628 | BTF_PRINT_ARG("void " ); |
629 | return pos; |
630 | } |
631 | |
632 | t = btf__type_by_id(btf, type_id); |
633 | |
634 | switch (BTF_INFO_KIND(t->info)) { |
635 | case BTF_KIND_INT: |
636 | case BTF_KIND_TYPEDEF: |
637 | case BTF_KIND_FLOAT: |
638 | BTF_PRINT_ARG("%s " , btf__name_by_offset(btf, t->name_off)); |
639 | break; |
640 | case BTF_KIND_STRUCT: |
641 | BTF_PRINT_ARG("struct %s " , |
642 | btf__name_by_offset(btf, t->name_off)); |
643 | break; |
644 | case BTF_KIND_UNION: |
645 | BTF_PRINT_ARG("union %s " , |
646 | btf__name_by_offset(btf, t->name_off)); |
647 | break; |
648 | case BTF_KIND_ENUM: |
649 | case BTF_KIND_ENUM64: |
650 | BTF_PRINT_ARG("enum %s " , |
651 | btf__name_by_offset(btf, t->name_off)); |
652 | break; |
653 | case BTF_KIND_ARRAY: |
654 | array = (struct btf_array *)(t + 1); |
655 | BTF_PRINT_TYPE(array->type); |
656 | BTF_PRINT_ARG("[%d]" , array->nelems); |
657 | break; |
658 | case BTF_KIND_PTR: |
659 | BTF_PRINT_TYPE(t->type); |
660 | BTF_PRINT_ARG("* " ); |
661 | break; |
662 | case BTF_KIND_FWD: |
663 | BTF_PRINT_ARG("%s %s " , |
664 | BTF_INFO_KFLAG(t->info) ? "union" : "struct" , |
665 | btf__name_by_offset(btf, t->name_off)); |
666 | break; |
667 | case BTF_KIND_VOLATILE: |
668 | BTF_PRINT_ARG("volatile " ); |
669 | BTF_PRINT_TYPE(t->type); |
670 | break; |
671 | case BTF_KIND_CONST: |
672 | BTF_PRINT_ARG("const " ); |
673 | BTF_PRINT_TYPE(t->type); |
674 | break; |
675 | case BTF_KIND_RESTRICT: |
676 | BTF_PRINT_ARG("restrict " ); |
677 | BTF_PRINT_TYPE(t->type); |
678 | break; |
679 | case BTF_KIND_FUNC_PROTO: |
680 | pos = btf_dump_func(btf, func_sig, func_proto: t, NULL, pos, size); |
681 | if (pos == -1) |
682 | return -1; |
683 | break; |
684 | case BTF_KIND_FUNC: |
685 | proto_type = btf__type_by_id(btf, t->type); |
686 | pos = btf_dump_func(btf, func_sig, func_proto: proto_type, func: t, pos, size); |
687 | if (pos == -1) |
688 | return -1; |
689 | break; |
690 | case BTF_KIND_VAR: |
691 | var = (struct btf_var *)(t + 1); |
692 | if (var->linkage == BTF_VAR_STATIC) |
693 | BTF_PRINT_ARG("static " ); |
694 | BTF_PRINT_TYPE(t->type); |
695 | BTF_PRINT_ARG(" %s" , |
696 | btf__name_by_offset(btf, t->name_off)); |
697 | break; |
698 | case BTF_KIND_DATASEC: |
699 | BTF_PRINT_ARG("section (\"%s\") " , |
700 | btf__name_by_offset(btf, t->name_off)); |
701 | break; |
702 | case BTF_KIND_UNKN: |
703 | default: |
704 | return -1; |
705 | } |
706 | |
707 | return pos; |
708 | } |
709 | |
710 | static int btf_dump_func(const struct btf *btf, char *func_sig, |
711 | const struct btf_type *func_proto, |
712 | const struct btf_type *func, int pos, int size) |
713 | { |
714 | int i, vlen; |
715 | |
716 | BTF_PRINT_TYPE(func_proto->type); |
717 | if (func) |
718 | BTF_PRINT_ARG("%s(" , btf__name_by_offset(btf, func->name_off)); |
719 | else |
720 | BTF_PRINT_ARG("(" ); |
721 | vlen = BTF_INFO_VLEN(func_proto->info); |
722 | for (i = 0; i < vlen; i++) { |
723 | struct btf_param *arg = &((struct btf_param *)(func_proto + 1))[i]; |
724 | |
725 | if (i) |
726 | BTF_PRINT_ARG(", " ); |
727 | if (arg->type) { |
728 | BTF_PRINT_TYPE(arg->type); |
729 | if (arg->name_off) |
730 | BTF_PRINT_ARG("%s" , |
731 | btf__name_by_offset(btf, arg->name_off)); |
732 | else if (pos && func_sig[pos - 1] == ' ') |
733 | /* Remove unnecessary space for |
734 | * FUNC_PROTO that does not have |
735 | * arg->name_off |
736 | */ |
737 | func_sig[--pos] = '\0'; |
738 | } else { |
739 | BTF_PRINT_ARG("..." ); |
740 | } |
741 | } |
742 | BTF_PRINT_ARG(")" ); |
743 | |
744 | return pos; |
745 | } |
746 | |
747 | void btf_dumper_type_only(const struct btf *btf, __u32 type_id, char *func_sig, |
748 | int size) |
749 | { |
750 | int err; |
751 | |
752 | func_sig[0] = '\0'; |
753 | if (!btf) |
754 | return; |
755 | |
756 | err = __btf_dumper_type_only(btf, type_id, func_sig, pos: 0, size); |
757 | if (err < 0) |
758 | func_sig[0] = '\0'; |
759 | } |
760 | |
761 | static const char *ltrim(const char *s) |
762 | { |
763 | while (isspace(*s)) |
764 | s++; |
765 | |
766 | return s; |
767 | } |
768 | |
769 | void btf_dump_linfo_plain(const struct btf *btf, |
770 | const struct bpf_line_info *linfo, |
771 | const char *prefix, bool linum) |
772 | { |
773 | const char *line = btf__name_by_offset(btf, linfo->line_off); |
774 | |
775 | if (!line) |
776 | return; |
777 | line = ltrim(s: line); |
778 | |
779 | if (!prefix) |
780 | prefix = "" ; |
781 | |
782 | if (linum) { |
783 | const char *file = btf__name_by_offset(btf, linfo->file_name_off); |
784 | |
785 | /* More forgiving on file because linum option is |
786 | * expected to provide more info than the already |
787 | * available src line. |
788 | */ |
789 | if (!file) |
790 | file = "" ; |
791 | |
792 | printf("%s%s [file:%s line_num:%u line_col:%u]\n" , |
793 | prefix, line, file, |
794 | BPF_LINE_INFO_LINE_NUM(linfo->line_col), |
795 | BPF_LINE_INFO_LINE_COL(linfo->line_col)); |
796 | } else { |
797 | printf("%s%s\n" , prefix, line); |
798 | } |
799 | } |
800 | |
801 | void btf_dump_linfo_json(const struct btf *btf, |
802 | const struct bpf_line_info *linfo, bool linum) |
803 | { |
804 | const char *line = btf__name_by_offset(btf, linfo->line_off); |
805 | |
806 | if (line) |
807 | jsonw_string_field(self: json_wtr, prop: "src" , val: ltrim(s: line)); |
808 | |
809 | if (linum) { |
810 | const char *file = btf__name_by_offset(btf, linfo->file_name_off); |
811 | |
812 | if (file) |
813 | jsonw_string_field(self: json_wtr, prop: "file" , val: file); |
814 | |
815 | if (BPF_LINE_INFO_LINE_NUM(linfo->line_col)) |
816 | jsonw_int_field(self: json_wtr, prop: "line_num" , |
817 | BPF_LINE_INFO_LINE_NUM(linfo->line_col)); |
818 | |
819 | if (BPF_LINE_INFO_LINE_COL(linfo->line_col)) |
820 | jsonw_int_field(self: json_wtr, prop: "line_col" , |
821 | BPF_LINE_INFO_LINE_COL(linfo->line_col)); |
822 | } |
823 | } |
824 | |
825 | static void dotlabel_puts(const char *s) |
826 | { |
827 | for (; *s; ++s) { |
828 | switch (*s) { |
829 | case '\\': |
830 | case '"': |
831 | case '{': |
832 | case '}': |
833 | case '<': |
834 | case '>': |
835 | case '|': |
836 | case ' ': |
837 | putchar('\\'); |
838 | fallthrough; |
839 | default: |
840 | putchar(*s); |
841 | } |
842 | } |
843 | } |
844 | |
845 | static const char *shorten_path(const char *path) |
846 | { |
847 | const unsigned int MAX_PATH_LEN = 32; |
848 | size_t len = strlen(path); |
849 | const char *shortpath; |
850 | |
851 | if (len <= MAX_PATH_LEN) |
852 | return path; |
853 | |
854 | /* Search for last '/' under the MAX_PATH_LEN limit */ |
855 | shortpath = strchr(path + len - MAX_PATH_LEN, '/'); |
856 | if (shortpath) { |
857 | if (shortpath < path + strlen("..." )) |
858 | /* We removed a very short prefix, e.g. "/w", and we'll |
859 | * make the path longer by prefixing with the ellipsis. |
860 | * Not worth it, keep initial path. |
861 | */ |
862 | return path; |
863 | return shortpath; |
864 | } |
865 | |
866 | /* File base name length is > MAX_PATH_LEN, search for last '/' */ |
867 | shortpath = strrchr(path, '/'); |
868 | if (shortpath) |
869 | return shortpath; |
870 | |
871 | return path; |
872 | } |
873 | |
874 | void btf_dump_linfo_dotlabel(const struct btf *btf, |
875 | const struct bpf_line_info *linfo, bool linum) |
876 | { |
877 | const char *line = btf__name_by_offset(btf, linfo->line_off); |
878 | |
879 | if (!line || !strlen(line)) |
880 | return; |
881 | line = ltrim(s: line); |
882 | |
883 | if (linum) { |
884 | const char *file = btf__name_by_offset(btf, linfo->file_name_off); |
885 | const char *shortfile; |
886 | |
887 | /* More forgiving on file because linum option is |
888 | * expected to provide more info than the already |
889 | * available src line. |
890 | */ |
891 | if (!file) |
892 | shortfile = "" ; |
893 | else |
894 | shortfile = shorten_path(path: file); |
895 | |
896 | printf("; [%s" , shortfile > file ? "..." : "" ); |
897 | dotlabel_puts(s: shortfile); |
898 | printf(" line:%u col:%u]\\l\\\n" , |
899 | BPF_LINE_INFO_LINE_NUM(linfo->line_col), |
900 | BPF_LINE_INFO_LINE_COL(linfo->line_col)); |
901 | } |
902 | |
903 | printf("; " ); |
904 | dotlabel_puts(s: line); |
905 | printf("\\l\\\n" ); |
906 | } |
907 | |