Skip to content

Commit 0c50be2

Browse files
mykyta5qmonnet
authored andcommitted
bpftool: Introduce btf c dump sorting
Sort bpftool c dump output; aiming to simplify vmlinux.h diffing and forcing more natural type definitions ordering. Definitions are sorted first by their BTF kind ranks, then by their base type name and by their own name. Type ranks Assign ranks to btf kinds (defined in function btf_type_rank) to set next order: 1. Anonymous enums/enums64 2. Named enums/enums64 3. Trivial types typedefs (ints, then floats) 4. Structs/Unions 5. Function prototypes 6. Forward declarations Type rank is set to maximum for unnamed reference types, structs and unions to avoid emitting those types early. They will be emitted as part of the type chain starting with named type. Lexicographical ordering Each type is assigned a sort_name and own_name. sort_name is the resolved name of the final base type for reference types (typedef, pointer, array etc). Sorting by sort_name allows to group typedefs of the same base type. sort_name for non-reference type is the same as own_name. own_name is a direct name of particular type, is used as final sorting step. Signed-off-by: Mykyta Yatsenko <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Tested-by: Andrii Nakryiko <[email protected]> Reviewed-by: Quentin Monnet <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 3c36aa1 commit 0c50be2

File tree

3 files changed

+140
-7
lines changed

3 files changed

+140
-7
lines changed

bash-completion/bpftool

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,9 @@ _bpftool()
930930
format)
931931
COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
932932
;;
933+
c)
934+
COMPREPLY=( $( compgen -W "unsorted" -- "$cur" ) )
935+
;;
933936
*)
934937
# emit extra options
935938
case ${words[3]} in

docs/bpftool-btf.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ BTF COMMANDS
2828
| **bpftool** **btf help**
2929
|
3030
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
31-
| *FORMAT* := { **raw** | **c** }
31+
| *FORMAT* := { **raw** | **c** [**unsorted**] }
3232
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
3333
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* }
3434
@@ -63,7 +63,9 @@ bpftool btf dump *BTF_SRC*
6363
pahole.
6464

6565
**format** option can be used to override default (raw) output format. Raw
66-
(**raw**) or C-syntax (**c**) output formats are supported.
66+
(**raw**) or C-syntax (**c**) output formats are supported. With C-style
67+
formatting, the output is sorted by default. Use the **unsorted** option
68+
to avoid sorting the output.
6769

6870
bpftool btf help
6971
Print short help message.

src/btf.c

Lines changed: 133 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
4343
[BTF_KIND_ENUM64] = "ENUM64",
4444
};
4545

46+
struct sort_datum {
47+
int index;
48+
int type_rank;
49+
const char *sort_name;
50+
const char *own_name;
51+
};
52+
4653
static const char *btf_int_enc_str(__u8 encoding)
4754
{
4855
switch (encoding) {
@@ -460,9 +467,122 @@ static void __printf(2, 0) btf_dump_printf(void *ctx,
460467
vfprintf(stdout, fmt, args);
461468
}
462469

470+
static int btf_type_rank(const struct btf *btf, __u32 index, bool has_name)
471+
{
472+
const struct btf_type *t = btf__type_by_id(btf, index);
473+
const int kind = btf_kind(t);
474+
const int max_rank = 10;
475+
476+
if (t->name_off)
477+
has_name = true;
478+
479+
switch (kind) {
480+
case BTF_KIND_ENUM:
481+
case BTF_KIND_ENUM64:
482+
return has_name ? 1 : 0;
483+
case BTF_KIND_INT:
484+
case BTF_KIND_FLOAT:
485+
return 2;
486+
case BTF_KIND_STRUCT:
487+
case BTF_KIND_UNION:
488+
return has_name ? 3 : max_rank;
489+
case BTF_KIND_FUNC_PROTO:
490+
return has_name ? 4 : max_rank;
491+
case BTF_KIND_ARRAY:
492+
if (has_name)
493+
return btf_type_rank(btf, btf_array(t)->type, has_name);
494+
return max_rank;
495+
case BTF_KIND_TYPE_TAG:
496+
case BTF_KIND_CONST:
497+
case BTF_KIND_PTR:
498+
case BTF_KIND_VOLATILE:
499+
case BTF_KIND_RESTRICT:
500+
case BTF_KIND_TYPEDEF:
501+
case BTF_KIND_DECL_TAG:
502+
if (has_name)
503+
return btf_type_rank(btf, t->type, has_name);
504+
return max_rank;
505+
default:
506+
return max_rank;
507+
}
508+
}
509+
510+
static const char *btf_type_sort_name(const struct btf *btf, __u32 index, bool from_ref)
511+
{
512+
const struct btf_type *t = btf__type_by_id(btf, index);
513+
514+
switch (btf_kind(t)) {
515+
case BTF_KIND_ENUM:
516+
case BTF_KIND_ENUM64: {
517+
int name_off = t->name_off;
518+
519+
/* Use name of the first element for anonymous enums if allowed */
520+
if (!from_ref && !t->name_off && btf_vlen(t))
521+
name_off = btf_enum(t)->name_off;
522+
523+
return btf__name_by_offset(btf, name_off);
524+
}
525+
case BTF_KIND_ARRAY:
526+
return btf_type_sort_name(btf, btf_array(t)->type, true);
527+
case BTF_KIND_TYPE_TAG:
528+
case BTF_KIND_CONST:
529+
case BTF_KIND_PTR:
530+
case BTF_KIND_VOLATILE:
531+
case BTF_KIND_RESTRICT:
532+
case BTF_KIND_TYPEDEF:
533+
case BTF_KIND_DECL_TAG:
534+
return btf_type_sort_name(btf, t->type, true);
535+
default:
536+
return btf__name_by_offset(btf, t->name_off);
537+
}
538+
return NULL;
539+
}
540+
541+
static int btf_type_compare(const void *left, const void *right)
542+
{
543+
const struct sort_datum *d1 = (const struct sort_datum *)left;
544+
const struct sort_datum *d2 = (const struct sort_datum *)right;
545+
int r;
546+
547+
if (d1->type_rank != d2->type_rank)
548+
return d1->type_rank < d2->type_rank ? -1 : 1;
549+
550+
r = strcmp(d1->sort_name, d2->sort_name);
551+
if (r)
552+
return r;
553+
554+
return strcmp(d1->own_name, d2->own_name);
555+
}
556+
557+
static struct sort_datum *sort_btf_c(const struct btf *btf)
558+
{
559+
struct sort_datum *datums;
560+
int n;
561+
562+
n = btf__type_cnt(btf);
563+
datums = malloc(sizeof(struct sort_datum) * n);
564+
if (!datums)
565+
return NULL;
566+
567+
for (int i = 0; i < n; ++i) {
568+
struct sort_datum *d = datums + i;
569+
const struct btf_type *t = btf__type_by_id(btf, i);
570+
571+
d->index = i;
572+
d->type_rank = btf_type_rank(btf, i, false);
573+
d->sort_name = btf_type_sort_name(btf, i, false);
574+
d->own_name = btf__name_by_offset(btf, t->name_off);
575+
}
576+
577+
qsort(datums, n, sizeof(struct sort_datum), btf_type_compare);
578+
579+
return datums;
580+
}
581+
463582
static int dump_btf_c(const struct btf *btf,
464-
__u32 *root_type_ids, int root_type_cnt)
583+
__u32 *root_type_ids, int root_type_cnt, bool sort_dump)
465584
{
585+
struct sort_datum *datums = NULL;
466586
struct btf_dump *d;
467587
int err = 0, i;
468588

@@ -486,8 +606,12 @@ static int dump_btf_c(const struct btf *btf,
486606
} else {
487607
int cnt = btf__type_cnt(btf);
488608

609+
if (sort_dump)
610+
datums = sort_btf_c(btf);
489611
for (i = 1; i < cnt; i++) {
490-
err = btf_dump__dump_type(d, i);
612+
int idx = datums ? datums[i].index : i;
613+
614+
err = btf_dump__dump_type(d, idx);
491615
if (err)
492616
goto done;
493617
}
@@ -500,6 +624,7 @@ static int dump_btf_c(const struct btf *btf,
500624
printf("#endif /* __VMLINUX_H__ */\n");
501625

502626
done:
627+
free(datums);
503628
btf_dump__free(d);
504629
return err;
505630
}
@@ -549,10 +674,10 @@ static bool btf_is_kernel_module(__u32 btf_id)
549674

550675
static int do_dump(int argc, char **argv)
551676
{
677+
bool dump_c = false, sort_dump_c = true;
552678
struct btf *btf = NULL, *base = NULL;
553679
__u32 root_type_ids[2];
554680
int root_type_cnt = 0;
555-
bool dump_c = false;
556681
__u32 btf_id = -1;
557682
const char *src;
558683
int fd = -1;
@@ -663,6 +788,9 @@ static int do_dump(int argc, char **argv)
663788
goto done;
664789
}
665790
NEXT_ARG();
791+
} else if (is_prefix(*argv, "unsorted")) {
792+
sort_dump_c = false;
793+
NEXT_ARG();
666794
} else {
667795
p_err("unrecognized option: '%s'", *argv);
668796
err = -EINVAL;
@@ -691,7 +819,7 @@ static int do_dump(int argc, char **argv)
691819
err = -ENOTSUP;
692820
goto done;
693821
}
694-
err = dump_btf_c(btf, root_type_ids, root_type_cnt);
822+
err = dump_btf_c(btf, root_type_ids, root_type_cnt, sort_dump_c);
695823
} else {
696824
err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
697825
}
@@ -1063,7 +1191,7 @@ static int do_help(int argc, char **argv)
10631191
" %1$s %2$s help\n"
10641192
"\n"
10651193
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
1066-
" FORMAT := { raw | c }\n"
1194+
" FORMAT := { raw | c [unsorted] }\n"
10671195
" " HELP_SPEC_MAP "\n"
10681196
" " HELP_SPEC_PROGRAM "\n"
10691197
" " HELP_SPEC_OPTIONS " |\n"

0 commit comments

Comments
 (0)