summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQuentin Monnet <quentin.monnet@netronome.com>2019-08-20 05:31:54 -0400
committerAlexei Starovoitov <ast@kernel.org>2019-08-20 12:51:06 -0400
commit4d374ba0bf30a2a372167ee4b7cdd527e7b47b3b (patch)
tree30a08257f3e2b40204d52143a4219d022675a82e
parent09d7c2e32b6e06d58fe7a5aa38847f4719ab4cc7 (diff)
tools: bpftool: implement "bpftool btf show|list"
Add a "btf list" (alias: "btf show") subcommand to bpftool in order to dump all BTF objects loaded on a system. When running the command, hash tables are built in bpftool to retrieve all the associations between BTF objects and BPF maps and programs. This allows for printing all such associations when listing the BTF objects. The command is added at the top of the subcommands for "bpftool btf", so that typing only "bpftool btf" also comes down to listing the programs. We could not have this with the previous command ("dump"), which required a BTF object id, so it should not break any previous behaviour. This also makes the "btf" command behaviour consistent with "prog" or "map". Bash completion is updated to use "bpftool btf" instead of "bpftool prog" to list the BTF ids, as it looks more consistent. Example output (plain): # bpftool btf show 9: size 2989B prog_ids 21 map_ids 15 17: size 2847B prog_ids 36 map_ids 30,29,28 26: size 2847B Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-btf.rst7
-rw-r--r--tools/bpf/bpftool/bash-completion/bpftool20
-rw-r--r--tools/bpf/bpftool/btf.c342
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
19BTF COMMANDS 19BTF 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
30DESCRIPTION 31DESCRIPTION
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
39struct btf_attach_table {
40 DECLARE_HASHTABLE(table, 16);
41};
42
43struct btf_attach_point {
44 __u32 obj_id;
45 __u32 btf_id;
46 struct hlist_node hash;
47};
48
38static const char *btf_int_enc_str(__u8 encoding) 49static 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
536static 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
563static 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
576static int
577build_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
672err_free:
673 delete_btf_table(tab);
674 return err;
675}
676
677static int
678build_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
702static void
703show_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
730static void
731show_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
761static int
762show_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
783static 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
853exit_free:
854 delete_btf_table(&btf_prog_table);
855 delete_btf_table(&btf_map_table);
856
857 return err;
858}
859
525static int do_help(int argc, char **argv) 860static 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
547static const struct cmd cmds[] = { 883static 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 }