summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 }