907 lines · c
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 24static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,25 __u8 bit_offset, const void *data);26 27static 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 31static 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(d->btf, prog_str, func_proto, NULL, 0,48 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(&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 86print:87 if (!prog_id)88 snprintf(&prog_str[func_sig_len],89 sizeof(prog_str) - func_sig_len, " 0");90 else if (prog_name)91 snprintf(&prog_str[func_sig_len],92 sizeof(prog_str) - func_sig_len,93 " %s/prog_id:%u", prog_name, prog_id);94 else95 snprintf(&prog_str[func_sig_len],96 sizeof(prog_str) - func_sig_len,97 " <unknown_prog_name>/prog_id:%u", prog_id);98 99 prog_str[sizeof(prog_str) - 1] = '\0';100 jsonw_string(d->jw, prog_str);101 btf__free(prog_btf);102 if (prog_fd >= 0)103 close(prog_fd);104 return 0;105}106 107static 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, ptr_type, value))126 return;127 128print_ptr_value:129 if (d->is_plain_text)130 jsonw_printf(d->jw, "\"%p\"", (void *)value);131 else132 jsonw_printf(d->jw, "%lu", value);133}134 135static 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, actual_type_id, bit_offset, data);145}146 147static 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(d->jw,175 btf__name_by_offset(d->btf,176 enums[i].name_off));177 return 0;178 }179 }180 181 jsonw_int(d->jw, value);182 return 0;183}184 185static 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(d->jw,201 btf__name_by_offset(d->btf,202 enums[i].name_off));203 return 0;204 }205 }206 207 jsonw_int(d->jw, value);208 return 0;209}210 211static 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 as222 * 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(elem_type) || elem_type->size != 1)228 return false;229 230 if (btf_int_encoding(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 247static 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(d->btf, arr, data)) {257 jsonw_string(d->jw, 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(d->jw);266 for (i = 0; i < arr->nelems; i++) {267 ret = btf_dumper_do_type(d, arr->type, 0,268 data + i * elem_size);269 if (ret)270 break;271 }272 273 jsonw_end_array(d->jw);274 return ret;275}276 277static 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 * Suppose282 * 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_BITFIELD290 upper_num = *(__u64 *)data;291 lower_num = *(__u64 *)(data + 8);292#else293 upper_num = *(__u64 *)(data + 8);294 lower_num = *(__u64 *)data;295#endif296 297 if (is_plain_text) {298 if (upper_num == 0)299 jsonw_printf(jw, "0x%llx", lower_num);300 else301 jsonw_printf(jw, "0x%llx%016llx", upper_num, lower_num);302 } else {303 if (upper_num == 0)304 jsonw_printf(jw, "\"0x%llx\"", lower_num);305 else306 jsonw_printf(jw, "\"0x%llx%016llx\"", upper_num, lower_num);307 }308}309 310static 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_BITFIELD316 upper_num = print_num[0];317 lower_num = print_num[1];318#else319 upper_num = print_num[1];320 lower_num = print_num[0];321#endif322 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_BITFIELD343 print_num[0] = upper_num;344 print_num[1] = lower_num;345#else346 print_num[0] = lower_num;347 print_num[1] = upper_num;348#endif349}350 351static 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#else369#error neither big nor little endian370#endif371 right_shift_bits = 128 - nr_bits;372 373 btf_int128_shift(print_num, left_shift_bits, right_shift_bits);374 btf_int128_print(jw, print_num, is_plain_text);375}376 377 378static 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 395static 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, 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(jw, "%llu", *(__u64 *)data);421 else if (BTF_INT_BITS(*int_type) == 32)422 jsonw_printf(jw, "%u", *(__u32 *)data);423 else if (BTF_INT_BITS(*int_type) == 16)424 jsonw_printf(jw, "%hu", *(__u16 *)data);425 else if (BTF_INT_BITS(*int_type) == 8)426 jsonw_printf(jw, "%hhu", *(__u8 *)data);427 else428 btf_dumper_int_bits(*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(jw, "%lld", *(long long *)data);434 else if (BTF_INT_BITS(*int_type) == 32)435 jsonw_printf(jw, "%d", *(int *)data);436 else if (BTF_INT_BITS(*int_type) == 16)437 jsonw_printf(jw, "%hd", *(short *)data);438 else if (BTF_INT_BITS(*int_type) == 8)439 jsonw_printf(jw, "%hhd", *(char *)data);440 else441 btf_dumper_int_bits(*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(jw, "\"%c\"", *(char *)data);447 else448 if (is_plain_text)449 jsonw_printf(jw, "0x%hhx", *(char *)data);450 else451 jsonw_printf(jw, "\"\\u00%02hhx\"",452 *(char *)data);453 break;454 case BTF_INT_BOOL:455 jsonw_bool(jw, *(bool *)data);456 break;457 default:458 /* shouldn't happen */459 return -EINVAL;460 }461 462 return 0;463}464 465static 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(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(d->jw, 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(bitfield_size,497 BITS_PER_BYTE_MASKED(bit_offset),498 data_off, d->jw, d->is_plain_text);499 } else {500 ret = btf_dumper_do_type(d, m[i].type,501 BITS_PER_BYTE_MASKED(bit_offset),502 data_off);503 if (ret)504 break;505 }506 }507 508 jsonw_end_object(d->jw);509 510 return ret;511}512 513static 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(d->jw);520 jsonw_name(d->jw, btf__name_by_offset(d->btf, t->name_off));521 ret = btf_dumper_do_type(d, t->type, bit_offset, data);522 jsonw_end_object(d->jw);523 524 return ret;525}526 527static 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(d->jw);542 jsonw_name(d->jw, btf__name_by_offset(d->btf, t->name_off));543 jsonw_start_array(d->jw);544 for (i = 0; i < vlen; i++) {545 ret = btf_dumper_do_type(d, vsi[i].type, 0, data + vsi[i].offset);546 if (ret)547 break;548 }549 jsonw_end_array(d->jw);550 jsonw_end_object(d->jw);551 552 return ret;553}554 555static 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, d->jw,563 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(d->jw, "(unknown)");578 return 0;579 case BTF_KIND_FWD:580 /* map key or value can't be forward */581 jsonw_printf(d->jw, "(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(d->jw, "(unsupported-kind");594 return -EINVAL;595 }596}597 598int 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, 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 619static 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, 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, proto_type, 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 710static 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 else720 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 for734 * FUNC_PROTO that does not have735 * arg->name_off736 */737 func_sig[--pos] = '\0';738 } else {739 BTF_PRINT_ARG("...");740 }741 }742 BTF_PRINT_ARG(")");743 744 return pos;745}746 747void 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, 0, size);757 if (err < 0)758 func_sig[0] = '\0';759}760 761static const char *ltrim(const char *s)762{763 while (isspace(*s))764 s++;765 766 return s;767}768 769void 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(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 is786 * expected to provide more info than the already787 * 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 801void 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(json_wtr, "src", ltrim(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(json_wtr, "file", file);814 815 if (BPF_LINE_INFO_LINE_NUM(linfo->line_col))816 jsonw_int_field(json_wtr, "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(json_wtr, "line_col",821 BPF_LINE_INFO_LINE_COL(linfo->line_col));822 }823}824 825static 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 845static 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'll859 * 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 874void 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(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 is888 * expected to provide more info than the already889 * available src line.890 */891 if (!file)892 shortfile = "";893 else894 shortfile = shorten_path(file);895 896 printf("; [%s", shortfile > file ? "..." : "");897 dotlabel_puts(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(line);905 printf("\\l\\\n");906}907