diff options
-rw-r--r-- | tools/bpf/bpftool/Documentation/bpftool-btf.rst | 7 | ||||
-rw-r--r-- | tools/bpf/bpftool/bash-completion/bpftool | 20 | ||||
-rw-r--r-- | tools/bpf/bpftool/btf.c | 342 |
3 files changed, 363 insertions, 6 deletions
diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst index 6694a0fc8f99..39615f8e145b 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst | |||
@@ -19,6 +19,7 @@ SYNOPSIS | |||
19 | BTF COMMANDS | 19 | BTF COMMANDS |
20 | ============= | 20 | ============= |
21 | 21 | ||
22 | | **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*] | ||
22 | | **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] | 23 | | **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] |
23 | | **bpftool** **btf help** | 24 | | **bpftool** **btf help** |
24 | | | 25 | | |
@@ -29,6 +30,12 @@ BTF COMMANDS | |||
29 | 30 | ||
30 | DESCRIPTION | 31 | DESCRIPTION |
31 | =========== | 32 | =========== |
33 | **bpftool btf { show | list }** [**id** *BTF_ID*] | ||
34 | Show information about loaded BTF objects. If a BTF ID is | ||
35 | specified, show information only about given BTF object, | ||
36 | otherwise list all BTF objects currently loaded on the | ||
37 | system. | ||
38 | |||
32 | **bpftool btf dump** *BTF_SRC* | 39 | **bpftool btf dump** *BTF_SRC* |
33 | Dump BTF entries from a given *BTF_SRC*. | 40 | Dump BTF entries from a given *BTF_SRC*. |
34 | 41 | ||
diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool index 4549fd424069..2ffd351f9dbf 100644 --- a/tools/bpf/bpftool/bash-completion/bpftool +++ b/tools/bpf/bpftool/bash-completion/bpftool | |||
@@ -73,8 +73,8 @@ _bpftool_get_prog_tags() | |||
73 | 73 | ||
74 | _bpftool_get_btf_ids() | 74 | _bpftool_get_btf_ids() |
75 | { | 75 | { |
76 | COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \ | 76 | COMPREPLY+=( $( compgen -W "$( bpftool -jp btf 2>&1 | \ |
77 | command sed -n 's/.*"btf_id": \(.*\),\?$/\1/p' )" -- "$cur" ) ) | 77 | command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) ) |
78 | } | 78 | } |
79 | 79 | ||
80 | _bpftool_get_obj_map_names() | 80 | _bpftool_get_obj_map_names() |
@@ -670,7 +670,7 @@ _bpftool() | |||
670 | map) | 670 | map) |
671 | _bpftool_get_map_ids | 671 | _bpftool_get_map_ids |
672 | ;; | 672 | ;; |
673 | dump) | 673 | $command) |
674 | _bpftool_get_btf_ids | 674 | _bpftool_get_btf_ids |
675 | ;; | 675 | ;; |
676 | esac | 676 | esac |
@@ -698,9 +698,21 @@ _bpftool() | |||
698 | ;; | 698 | ;; |
699 | esac | 699 | esac |
700 | ;; | 700 | ;; |
701 | show|list) | ||
702 | case $prev in | ||
703 | $command) | ||
704 | COMPREPLY+=( $( compgen -W "id" -- "$cur" ) ) | ||
705 | ;; | ||
706 | id) | ||
707 | _bpftool_get_btf_ids | ||
708 | ;; | ||
709 | esac | ||
710 | return 0 | ||
711 | ;; | ||
701 | *) | 712 | *) |
702 | [[ $prev == $object ]] && \ | 713 | [[ $prev == $object ]] && \ |
703 | COMPREPLY=( $( compgen -W 'dump help' -- "$cur" ) ) | 714 | COMPREPLY=( $( compgen -W 'dump help show list' \ |
715 | -- "$cur" ) ) | ||
704 | ;; | 716 | ;; |
705 | esac | 717 | esac |
706 | ;; | 718 | ;; |
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c index 8805637f1a7e..9a9376d1d3df 100644 --- a/tools/bpf/bpftool/btf.c +++ b/tools/bpf/bpftool/btf.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <bpf.h> | 11 | #include <bpf.h> |
12 | #include <libbpf.h> | 12 | #include <libbpf.h> |
13 | #include <linux/btf.h> | 13 | #include <linux/btf.h> |
14 | #include <linux/hashtable.h> | ||
14 | 15 | ||
15 | #include "btf.h" | 16 | #include "btf.h" |
16 | #include "json_writer.h" | 17 | #include "json_writer.h" |
@@ -35,6 +36,16 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = { | |||
35 | [BTF_KIND_DATASEC] = "DATASEC", | 36 | [BTF_KIND_DATASEC] = "DATASEC", |
36 | }; | 37 | }; |
37 | 38 | ||
39 | struct btf_attach_table { | ||
40 | DECLARE_HASHTABLE(table, 16); | ||
41 | }; | ||
42 | |||
43 | struct btf_attach_point { | ||
44 | __u32 obj_id; | ||
45 | __u32 btf_id; | ||
46 | struct hlist_node hash; | ||
47 | }; | ||
48 | |||
38 | static const char *btf_int_enc_str(__u8 encoding) | 49 | static const char *btf_int_enc_str(__u8 encoding) |
39 | { | 50 | { |
40 | switch (encoding) { | 51 | switch (encoding) { |
@@ -522,6 +533,330 @@ done: | |||
522 | return err; | 533 | return err; |
523 | } | 534 | } |
524 | 535 | ||
536 | static int btf_parse_fd(int *argc, char ***argv) | ||
537 | { | ||
538 | unsigned int id; | ||
539 | char *endptr; | ||
540 | int fd; | ||
541 | |||
542 | if (!is_prefix(*argv[0], "id")) { | ||
543 | p_err("expected 'id', got: '%s'?", **argv); | ||
544 | return -1; | ||
545 | } | ||
546 | NEXT_ARGP(); | ||
547 | |||
548 | id = strtoul(**argv, &endptr, 0); | ||
549 | if (*endptr) { | ||
550 | p_err("can't parse %s as ID", **argv); | ||
551 | return -1; | ||
552 | } | ||
553 | NEXT_ARGP(); | ||
554 | |||
555 | fd = bpf_btf_get_fd_by_id(id); | ||
556 | if (fd < 0) | ||
557 | p_err("can't get BTF object by id (%u): %s", | ||
558 | id, strerror(errno)); | ||
559 | |||
560 | return fd; | ||
561 | } | ||
562 | |||
563 | static void delete_btf_table(struct btf_attach_table *tab) | ||
564 | { | ||
565 | struct btf_attach_point *obj; | ||
566 | struct hlist_node *tmp; | ||
567 | |||
568 | unsigned int bkt; | ||
569 | |||
570 | hash_for_each_safe(tab->table, bkt, tmp, obj, hash) { | ||
571 | hash_del(&obj->hash); | ||
572 | free(obj); | ||
573 | } | ||
574 | } | ||
575 | |||
576 | static int | ||
577 | build_btf_type_table(struct btf_attach_table *tab, enum bpf_obj_type type, | ||
578 | void *info, __u32 *len) | ||
579 | { | ||
580 | static const char * const names[] = { | ||
581 | [BPF_OBJ_UNKNOWN] = "unknown", | ||
582 | [BPF_OBJ_PROG] = "prog", | ||
583 | [BPF_OBJ_MAP] = "map", | ||
584 | }; | ||
585 | struct btf_attach_point *obj_node; | ||
586 | __u32 btf_id, id = 0; | ||
587 | int err; | ||
588 | int fd; | ||
589 | |||
590 | while (true) { | ||
591 | switch (type) { | ||
592 | case BPF_OBJ_PROG: | ||
593 | err = bpf_prog_get_next_id(id, &id); | ||
594 | break; | ||
595 | case BPF_OBJ_MAP: | ||
596 | err = bpf_map_get_next_id(id, &id); | ||
597 | break; | ||
598 | default: | ||
599 | err = -1; | ||
600 | p_err("unexpected object type: %d", type); | ||
601 | goto err_free; | ||
602 | } | ||
603 | if (err) { | ||
604 | if (errno == ENOENT) { | ||
605 | err = 0; | ||
606 | break; | ||
607 | } | ||
608 | p_err("can't get next %s: %s%s", names[type], | ||
609 | strerror(errno), | ||
610 | errno == EINVAL ? " -- kernel too old?" : ""); | ||
611 | goto err_free; | ||
612 | } | ||
613 | |||
614 | switch (type) { | ||
615 | case BPF_OBJ_PROG: | ||
616 | fd = bpf_prog_get_fd_by_id(id); | ||
617 | break; | ||
618 | case BPF_OBJ_MAP: | ||
619 | fd = bpf_map_get_fd_by_id(id); | ||
620 | break; | ||
621 | default: | ||
622 | err = -1; | ||
623 | p_err("unexpected object type: %d", type); | ||
624 | goto err_free; | ||
625 | } | ||
626 | if (fd < 0) { | ||
627 | if (errno == ENOENT) | ||
628 | continue; | ||
629 | p_err("can't get %s by id (%u): %s", names[type], id, | ||
630 | strerror(errno)); | ||
631 | err = -1; | ||
632 | goto err_free; | ||
633 | } | ||
634 | |||
635 | memset(info, 0, *len); | ||
636 | err = bpf_obj_get_info_by_fd(fd, info, len); | ||
637 | close(fd); | ||
638 | if (err) { | ||
639 | p_err("can't get %s info: %s", names[type], | ||
640 | strerror(errno)); | ||
641 | goto err_free; | ||
642 | } | ||
643 | |||
644 | switch (type) { | ||
645 | case BPF_OBJ_PROG: | ||
646 | btf_id = ((struct bpf_prog_info *)info)->btf_id; | ||
647 | break; | ||
648 | case BPF_OBJ_MAP: | ||
649 | btf_id = ((struct bpf_map_info *)info)->btf_id; | ||
650 | break; | ||
651 | default: | ||
652 | err = -1; | ||
653 | p_err("unexpected object type: %d", type); | ||
654 | goto err_free; | ||
655 | } | ||
656 | if (!btf_id) | ||
657 | continue; | ||
658 | |||
659 | obj_node = calloc(1, sizeof(*obj_node)); | ||
660 | if (!obj_node) { | ||
661 | p_err("failed to allocate memory: %s", strerror(errno)); | ||
662 | goto err_free; | ||
663 | } | ||
664 | |||
665 | obj_node->obj_id = id; | ||
666 | obj_node->btf_id = btf_id; | ||
667 | hash_add(tab->table, &obj_node->hash, obj_node->btf_id); | ||
668 | } | ||
669 | |||
670 | return 0; | ||
671 | |||
672 | err_free: | ||
673 | delete_btf_table(tab); | ||
674 | return err; | ||
675 | } | ||
676 | |||
677 | static int | ||
678 | build_btf_tables(struct btf_attach_table *btf_prog_table, | ||
679 | struct btf_attach_table *btf_map_table) | ||
680 | { | ||
681 | struct bpf_prog_info prog_info; | ||
682 | __u32 prog_len = sizeof(prog_info); | ||
683 | struct bpf_map_info map_info; | ||
684 | __u32 map_len = sizeof(map_info); | ||
685 | int err = 0; | ||
686 | |||
687 | err = build_btf_type_table(btf_prog_table, BPF_OBJ_PROG, &prog_info, | ||
688 | &prog_len); | ||
689 | if (err) | ||
690 | return err; | ||
691 | |||
692 | err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info, | ||
693 | &map_len); | ||
694 | if (err) { | ||
695 | delete_btf_table(btf_prog_table); | ||
696 | return err; | ||
697 | } | ||
698 | |||
699 | return 0; | ||
700 | } | ||
701 | |||
702 | static void | ||
703 | show_btf_plain(struct bpf_btf_info *info, int fd, | ||
704 | struct btf_attach_table *btf_prog_table, | ||
705 | struct btf_attach_table *btf_map_table) | ||
706 | { | ||
707 | struct btf_attach_point *obj; | ||
708 | int n; | ||
709 | |||
710 | printf("%u: ", info->id); | ||
711 | printf("size %uB", info->btf_size); | ||
712 | |||
713 | n = 0; | ||
714 | hash_for_each_possible(btf_prog_table->table, obj, hash, info->id) { | ||
715 | if (obj->btf_id == info->id) | ||
716 | printf("%s%u", n++ == 0 ? " prog_ids " : ",", | ||
717 | obj->obj_id); | ||
718 | } | ||
719 | |||
720 | n = 0; | ||
721 | hash_for_each_possible(btf_map_table->table, obj, hash, info->id) { | ||
722 | if (obj->btf_id == info->id) | ||
723 | printf("%s%u", n++ == 0 ? " map_ids " : ",", | ||
724 | obj->obj_id); | ||
725 | } | ||
726 | |||
727 | printf("\n"); | ||
728 | } | ||
729 | |||
730 | static void | ||
731 | show_btf_json(struct bpf_btf_info *info, int fd, | ||
732 | struct btf_attach_table *btf_prog_table, | ||
733 | struct btf_attach_table *btf_map_table) | ||
734 | { | ||
735 | struct btf_attach_point *obj; | ||
736 | |||
737 | jsonw_start_object(json_wtr); /* btf object */ | ||
738 | jsonw_uint_field(json_wtr, "id", info->id); | ||
739 | jsonw_uint_field(json_wtr, "size", info->btf_size); | ||
740 | |||
741 | jsonw_name(json_wtr, "prog_ids"); | ||
742 | jsonw_start_array(json_wtr); /* prog_ids */ | ||
743 | hash_for_each_possible(btf_prog_table->table, obj, hash, | ||
744 | info->id) { | ||
745 | if (obj->btf_id == info->id) | ||
746 | jsonw_uint(json_wtr, obj->obj_id); | ||
747 | } | ||
748 | jsonw_end_array(json_wtr); /* prog_ids */ | ||
749 | |||
750 | jsonw_name(json_wtr, "map_ids"); | ||
751 | jsonw_start_array(json_wtr); /* map_ids */ | ||
752 | hash_for_each_possible(btf_map_table->table, obj, hash, | ||
753 | info->id) { | ||
754 | if (obj->btf_id == info->id) | ||
755 | jsonw_uint(json_wtr, obj->obj_id); | ||
756 | } | ||
757 | jsonw_end_array(json_wtr); /* map_ids */ | ||
758 | jsonw_end_object(json_wtr); /* btf object */ | ||
759 | } | ||
760 | |||
761 | static int | ||
762 | show_btf(int fd, struct btf_attach_table *btf_prog_table, | ||
763 | struct btf_attach_table *btf_map_table) | ||
764 | { | ||
765 | struct bpf_btf_info info = {}; | ||
766 | __u32 len = sizeof(info); | ||
767 | int err; | ||
768 | |||
769 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | ||
770 | if (err) { | ||
771 | p_err("can't get BTF object info: %s", strerror(errno)); | ||
772 | return -1; | ||
773 | } | ||
774 | |||
775 | if (json_output) | ||
776 | show_btf_json(&info, fd, btf_prog_table, btf_map_table); | ||
777 | else | ||
778 | show_btf_plain(&info, fd, btf_prog_table, btf_map_table); | ||
779 | |||
780 | return 0; | ||
781 | } | ||
782 | |||
783 | static int do_show(int argc, char **argv) | ||
784 | { | ||
785 | struct btf_attach_table btf_prog_table; | ||
786 | struct btf_attach_table btf_map_table; | ||
787 | int err, fd = -1; | ||
788 | __u32 id = 0; | ||
789 | |||
790 | if (argc == 2) { | ||
791 | fd = btf_parse_fd(&argc, &argv); | ||
792 | if (fd < 0) | ||
793 | return -1; | ||
794 | } | ||
795 | |||
796 | if (argc) { | ||
797 | if (fd >= 0) | ||
798 | close(fd); | ||
799 | return BAD_ARG(); | ||
800 | } | ||
801 | |||
802 | hash_init(btf_prog_table.table); | ||
803 | hash_init(btf_map_table.table); | ||
804 | err = build_btf_tables(&btf_prog_table, &btf_map_table); | ||
805 | if (err) { | ||
806 | if (fd >= 0) | ||
807 | close(fd); | ||
808 | return err; | ||
809 | } | ||
810 | |||
811 | if (fd >= 0) { | ||
812 | err = show_btf(fd, &btf_prog_table, &btf_map_table); | ||
813 | close(fd); | ||
814 | goto exit_free; | ||
815 | } | ||
816 | |||
817 | if (json_output) | ||
818 | jsonw_start_array(json_wtr); /* root array */ | ||
819 | |||
820 | while (true) { | ||
821 | err = bpf_btf_get_next_id(id, &id); | ||
822 | if (err) { | ||
823 | if (errno == ENOENT) { | ||
824 | err = 0; | ||
825 | break; | ||
826 | } | ||
827 | p_err("can't get next BTF object: %s%s", | ||
828 | strerror(errno), | ||
829 | errno == EINVAL ? " -- kernel too old?" : ""); | ||
830 | err = -1; | ||
831 | break; | ||
832 | } | ||
833 | |||
834 | fd = bpf_btf_get_fd_by_id(id); | ||
835 | if (fd < 0) { | ||
836 | if (errno == ENOENT) | ||
837 | continue; | ||
838 | p_err("can't get BTF object by id (%u): %s", | ||
839 | id, strerror(errno)); | ||
840 | err = -1; | ||
841 | break; | ||
842 | } | ||
843 | |||
844 | err = show_btf(fd, &btf_prog_table, &btf_map_table); | ||
845 | close(fd); | ||
846 | if (err) | ||
847 | break; | ||
848 | } | ||
849 | |||
850 | if (json_output) | ||
851 | jsonw_end_array(json_wtr); /* root array */ | ||
852 | |||
853 | exit_free: | ||
854 | delete_btf_table(&btf_prog_table); | ||
855 | delete_btf_table(&btf_map_table); | ||
856 | |||
857 | return err; | ||
858 | } | ||
859 | |||
525 | static int do_help(int argc, char **argv) | 860 | static int do_help(int argc, char **argv) |
526 | { | 861 | { |
527 | if (json_output) { | 862 | if (json_output) { |
@@ -530,7 +865,8 @@ static int do_help(int argc, char **argv) | |||
530 | } | 865 | } |
531 | 866 | ||
532 | fprintf(stderr, | 867 | fprintf(stderr, |
533 | "Usage: %s btf dump BTF_SRC [format FORMAT]\n" | 868 | "Usage: %s btf { show | list } [id BTF_ID]\n" |
869 | " %s btf dump BTF_SRC [format FORMAT]\n" | ||
534 | " %s btf help\n" | 870 | " %s btf help\n" |
535 | "\n" | 871 | "\n" |
536 | " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n" | 872 | " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n" |
@@ -539,12 +875,14 @@ static int do_help(int argc, char **argv) | |||
539 | " " HELP_SPEC_PROGRAM "\n" | 875 | " " HELP_SPEC_PROGRAM "\n" |
540 | " " HELP_SPEC_OPTIONS "\n" | 876 | " " HELP_SPEC_OPTIONS "\n" |
541 | "", | 877 | "", |
542 | bin_name, bin_name); | 878 | bin_name, bin_name, bin_name); |
543 | 879 | ||
544 | return 0; | 880 | return 0; |
545 | } | 881 | } |
546 | 882 | ||
547 | static const struct cmd cmds[] = { | 883 | static const struct cmd cmds[] = { |
884 | { "show", do_show }, | ||
885 | { "list", do_show }, | ||
548 | { "help", do_help }, | 886 | { "help", do_help }, |
549 | { "dump", do_dump }, | 887 | { "dump", do_dump }, |
550 | { 0 } | 888 | { 0 } |