1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
15 #include <sys/ioctl.h>
16 #include <sys/types.h>
18 #include <sys/syscall.h>
20 #include <linux/err.h>
21 #include <linux/perf_event.h>
22 #include <linux/sizes.h>
26 #include <bpf/libbpf.h>
30 #include "xlated_dumper.h"
32 #define BPF_METADATA_PREFIX "bpf_metadata_"
33 #define BPF_METADATA_PREFIX_LEN (sizeof(BPF_METADATA_PREFIX) - 1)
35 const char * const prog_type_name
[] = {
36 [BPF_PROG_TYPE_UNSPEC
] = "unspec",
37 [BPF_PROG_TYPE_SOCKET_FILTER
] = "socket_filter",
38 [BPF_PROG_TYPE_KPROBE
] = "kprobe",
39 [BPF_PROG_TYPE_SCHED_CLS
] = "sched_cls",
40 [BPF_PROG_TYPE_SCHED_ACT
] = "sched_act",
41 [BPF_PROG_TYPE_TRACEPOINT
] = "tracepoint",
42 [BPF_PROG_TYPE_XDP
] = "xdp",
43 [BPF_PROG_TYPE_PERF_EVENT
] = "perf_event",
44 [BPF_PROG_TYPE_CGROUP_SKB
] = "cgroup_skb",
45 [BPF_PROG_TYPE_CGROUP_SOCK
] = "cgroup_sock",
46 [BPF_PROG_TYPE_LWT_IN
] = "lwt_in",
47 [BPF_PROG_TYPE_LWT_OUT
] = "lwt_out",
48 [BPF_PROG_TYPE_LWT_XMIT
] = "lwt_xmit",
49 [BPF_PROG_TYPE_SOCK_OPS
] = "sock_ops",
50 [BPF_PROG_TYPE_SK_SKB
] = "sk_skb",
51 [BPF_PROG_TYPE_CGROUP_DEVICE
] = "cgroup_device",
52 [BPF_PROG_TYPE_SK_MSG
] = "sk_msg",
53 [BPF_PROG_TYPE_RAW_TRACEPOINT
] = "raw_tracepoint",
54 [BPF_PROG_TYPE_CGROUP_SOCK_ADDR
] = "cgroup_sock_addr",
55 [BPF_PROG_TYPE_LWT_SEG6LOCAL
] = "lwt_seg6local",
56 [BPF_PROG_TYPE_LIRC_MODE2
] = "lirc_mode2",
57 [BPF_PROG_TYPE_SK_REUSEPORT
] = "sk_reuseport",
58 [BPF_PROG_TYPE_FLOW_DISSECTOR
] = "flow_dissector",
59 [BPF_PROG_TYPE_CGROUP_SYSCTL
] = "cgroup_sysctl",
60 [BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE
] = "raw_tracepoint_writable",
61 [BPF_PROG_TYPE_CGROUP_SOCKOPT
] = "cgroup_sockopt",
62 [BPF_PROG_TYPE_TRACING
] = "tracing",
63 [BPF_PROG_TYPE_STRUCT_OPS
] = "struct_ops",
64 [BPF_PROG_TYPE_EXT
] = "ext",
65 [BPF_PROG_TYPE_LSM
] = "lsm",
66 [BPF_PROG_TYPE_SK_LOOKUP
] = "sk_lookup",
69 const size_t prog_type_name_size
= ARRAY_SIZE(prog_type_name
);
76 static const char * const attach_type_strings
[] = {
77 [BPF_SK_SKB_STREAM_PARSER
] = "stream_parser",
78 [BPF_SK_SKB_STREAM_VERDICT
] = "stream_verdict",
79 [BPF_SK_MSG_VERDICT
] = "msg_verdict",
80 [BPF_FLOW_DISSECTOR
] = "flow_dissector",
81 [__MAX_BPF_ATTACH_TYPE
] = NULL
,
84 static enum bpf_attach_type
parse_attach_type(const char *str
)
86 enum bpf_attach_type type
;
88 for (type
= 0; type
< __MAX_BPF_ATTACH_TYPE
; type
++) {
89 if (attach_type_strings
[type
] &&
90 is_prefix(str
, attach_type_strings
[type
]))
94 return __MAX_BPF_ATTACH_TYPE
;
97 static void print_boot_time(__u64 nsecs
, char *buf
, unsigned int size
)
99 struct timespec real_time_ts
, boot_time_ts
;
100 time_t wallclock_secs
;
105 if (clock_gettime(CLOCK_REALTIME
, &real_time_ts
) ||
106 clock_gettime(CLOCK_BOOTTIME
, &boot_time_ts
)) {
107 perror("Can't read clocks");
108 snprintf(buf
, size
, "%llu", nsecs
/ 1000000000);
112 wallclock_secs
= (real_time_ts
.tv_sec
- boot_time_ts
.tv_sec
) +
113 (real_time_ts
.tv_nsec
- boot_time_ts
.tv_nsec
+ nsecs
) /
117 if (!localtime_r(&wallclock_secs
, &load_tm
)) {
118 snprintf(buf
, size
, "%llu", nsecs
/ 1000000000);
123 strftime(buf
, size
, "%s", &load_tm
);
125 strftime(buf
, size
, "%FT%T%z", &load_tm
);
128 static void show_prog_maps(int fd
, __u32 num_maps
)
130 struct bpf_prog_info info
= {};
131 __u32 len
= sizeof(info
);
132 __u32 map_ids
[num_maps
];
136 info
.nr_map_ids
= num_maps
;
137 info
.map_ids
= ptr_to_u64(map_ids
);
139 err
= bpf_obj_get_info_by_fd(fd
, &info
, &len
);
140 if (err
|| !info
.nr_map_ids
)
144 jsonw_name(json_wtr
, "map_ids");
145 jsonw_start_array(json_wtr
);
146 for (i
= 0; i
< info
.nr_map_ids
; i
++)
147 jsonw_uint(json_wtr
, map_ids
[i
]);
148 jsonw_end_array(json_wtr
);
151 for (i
= 0; i
< info
.nr_map_ids
; i
++)
152 printf("%u%s", map_ids
[i
],
153 i
== info
.nr_map_ids
- 1 ? "" : ",");
157 static void *find_metadata(int prog_fd
, struct bpf_map_info
*map_info
)
159 struct bpf_prog_info prog_info
;
170 memset(&prog_info
, 0, sizeof(prog_info
));
171 prog_info_len
= sizeof(prog_info
);
172 ret
= bpf_obj_get_info_by_fd(prog_fd
, &prog_info
, &prog_info_len
);
176 if (!prog_info
.nr_map_ids
)
179 map_ids
= calloc(prog_info
.nr_map_ids
, sizeof(__u32
));
183 nr_maps
= prog_info
.nr_map_ids
;
184 memset(&prog_info
, 0, sizeof(prog_info
));
185 prog_info
.nr_map_ids
= nr_maps
;
186 prog_info
.map_ids
= ptr_to_u64(map_ids
);
187 prog_info_len
= sizeof(prog_info
);
189 ret
= bpf_obj_get_info_by_fd(prog_fd
, &prog_info
, &prog_info_len
);
193 for (i
= 0; i
< prog_info
.nr_map_ids
; i
++) {
194 map_fd
= bpf_map_get_fd_by_id(map_ids
[i
]);
198 memset(map_info
, 0, sizeof(*map_info
));
199 map_info_len
= sizeof(*map_info
);
200 ret
= bpf_obj_get_info_by_fd(map_fd
, map_info
, &map_info_len
);
206 if (map_info
->type
!= BPF_MAP_TYPE_ARRAY
||
207 map_info
->key_size
!= sizeof(int) ||
208 map_info
->max_entries
!= 1 ||
209 !map_info
->btf_value_type_id
||
210 !strstr(map_info
->name
, ".rodata")) {
215 value
= malloc(map_info
->value_size
);
221 if (bpf_map_lookup_elem(map_fd
, &key
, value
)) {
237 static bool has_metadata_prefix(const char *s
)
239 return strncmp(s
, BPF_METADATA_PREFIX
, BPF_METADATA_PREFIX_LEN
) == 0;
242 static void show_prog_metadata(int fd
, __u32 num_maps
)
244 const struct btf_type
*t_datasec
, *t_var
;
245 struct bpf_map_info map_info
;
246 struct btf_var_secinfo
*vsi
;
247 bool printed_header
= false;
248 struct btf
*btf
= NULL
;
249 unsigned int i
, vlen
;
257 memset(&map_info
, 0, sizeof(map_info
));
258 value
= find_metadata(fd
, &map_info
);
262 err
= btf__get_from_id(map_info
.btf_id
, &btf
);
266 t_datasec
= btf__type_by_id(btf
, map_info
.btf_value_type_id
);
267 if (!btf_is_datasec(t_datasec
))
270 vlen
= btf_vlen(t_datasec
);
271 vsi
= btf_var_secinfos(t_datasec
);
273 /* We don't proceed to check the kinds of the elements of the DATASEC.
274 * The verifier enforces them to be BTF_KIND_VAR.
278 struct btf_dumper d
= {
281 .is_plain_text
= false,
284 for (i
= 0; i
< vlen
; i
++, vsi
++) {
285 t_var
= btf__type_by_id(btf
, vsi
->type
);
286 name
= btf__name_by_offset(btf
, t_var
->name_off
);
288 if (!has_metadata_prefix(name
))
291 if (!printed_header
) {
292 jsonw_name(json_wtr
, "metadata");
293 jsonw_start_object(json_wtr
);
294 printed_header
= true;
297 jsonw_name(json_wtr
, name
+ BPF_METADATA_PREFIX_LEN
);
298 err
= btf_dumper_type(&d
, t_var
->type
, value
+ vsi
->offset
);
300 p_err("btf dump failed: %d", err
);
305 jsonw_end_object(json_wtr
);
307 json_writer_t
*btf_wtr
= jsonw_new(stdout
);
308 struct btf_dumper d
= {
311 .is_plain_text
= true,
315 p_err("jsonw alloc failed");
319 for (i
= 0; i
< vlen
; i
++, vsi
++) {
320 t_var
= btf__type_by_id(btf
, vsi
->type
);
321 name
= btf__name_by_offset(btf
, t_var
->name_off
);
323 if (!has_metadata_prefix(name
))
326 if (!printed_header
) {
327 printf("\tmetadata:");
328 printed_header
= true;
331 printf("\n\t\t%s = ", name
+ BPF_METADATA_PREFIX_LEN
);
333 jsonw_reset(btf_wtr
);
334 err
= btf_dumper_type(&d
, t_var
->type
, value
+ vsi
->offset
);
336 p_err("btf dump failed: %d", err
);
341 jsonw_destroy(&btf_wtr
);
349 static void print_prog_header_json(struct bpf_prog_info
*info
)
351 jsonw_uint_field(json_wtr
, "id", info
->id
);
352 if (info
->type
< ARRAY_SIZE(prog_type_name
))
353 jsonw_string_field(json_wtr
, "type",
354 prog_type_name
[info
->type
]);
356 jsonw_uint_field(json_wtr
, "type", info
->type
);
359 jsonw_string_field(json_wtr
, "name", info
->name
);
361 jsonw_name(json_wtr
, "tag");
362 jsonw_printf(json_wtr
, "\"" BPF_TAG_FMT
"\"",
363 info
->tag
[0], info
->tag
[1], info
->tag
[2], info
->tag
[3],
364 info
->tag
[4], info
->tag
[5], info
->tag
[6], info
->tag
[7]);
366 jsonw_bool_field(json_wtr
, "gpl_compatible", info
->gpl_compatible
);
367 if (info
->run_time_ns
) {
368 jsonw_uint_field(json_wtr
, "run_time_ns", info
->run_time_ns
);
369 jsonw_uint_field(json_wtr
, "run_cnt", info
->run_cnt
);
373 static void print_prog_json(struct bpf_prog_info
*info
, int fd
)
377 jsonw_start_object(json_wtr
);
378 print_prog_header_json(info
);
379 print_dev_json(info
->ifindex
, info
->netns_dev
, info
->netns_ino
);
381 if (info
->load_time
) {
384 print_boot_time(info
->load_time
, buf
, sizeof(buf
));
386 /* Piggy back on load_time, since 0 uid is a valid one */
387 jsonw_name(json_wtr
, "loaded_at");
388 jsonw_printf(json_wtr
, "%s", buf
);
389 jsonw_uint_field(json_wtr
, "uid", info
->created_by_uid
);
392 jsonw_uint_field(json_wtr
, "bytes_xlated", info
->xlated_prog_len
);
394 if (info
->jited_prog_len
) {
395 jsonw_bool_field(json_wtr
, "jited", true);
396 jsonw_uint_field(json_wtr
, "bytes_jited", info
->jited_prog_len
);
398 jsonw_bool_field(json_wtr
, "jited", false);
401 memlock
= get_fdinfo(fd
, "memlock");
403 jsonw_int_field(json_wtr
, "bytes_memlock", atoi(memlock
));
406 if (info
->nr_map_ids
)
407 show_prog_maps(fd
, info
->nr_map_ids
);
410 jsonw_int_field(json_wtr
, "btf_id", info
->btf_id
);
412 if (!hash_empty(prog_table
.table
)) {
413 struct pinned_obj
*obj
;
415 jsonw_name(json_wtr
, "pinned");
416 jsonw_start_array(json_wtr
);
417 hash_for_each_possible(prog_table
.table
, obj
, hash
, info
->id
) {
418 if (obj
->id
== info
->id
)
419 jsonw_string(json_wtr
, obj
->path
);
421 jsonw_end_array(json_wtr
);
424 emit_obj_refs_json(&refs_table
, info
->id
, json_wtr
);
426 show_prog_metadata(fd
, info
->nr_map_ids
);
428 jsonw_end_object(json_wtr
);
431 static void print_prog_header_plain(struct bpf_prog_info
*info
)
433 printf("%u: ", info
->id
);
434 if (info
->type
< ARRAY_SIZE(prog_type_name
))
435 printf("%s ", prog_type_name
[info
->type
]);
437 printf("type %u ", info
->type
);
440 printf("name %s ", info
->name
);
443 fprint_hex(stdout
, info
->tag
, BPF_TAG_SIZE
, "");
444 print_dev_plain(info
->ifindex
, info
->netns_dev
, info
->netns_ino
);
445 printf("%s", info
->gpl_compatible
? " gpl" : "");
446 if (info
->run_time_ns
)
447 printf(" run_time_ns %lld run_cnt %lld",
448 info
->run_time_ns
, info
->run_cnt
);
452 static void print_prog_plain(struct bpf_prog_info
*info
, int fd
)
456 print_prog_header_plain(info
);
458 if (info
->load_time
) {
461 print_boot_time(info
->load_time
, buf
, sizeof(buf
));
463 /* Piggy back on load_time, since 0 uid is a valid one */
464 printf("\tloaded_at %s uid %u\n", buf
, info
->created_by_uid
);
467 printf("\txlated %uB", info
->xlated_prog_len
);
469 if (info
->jited_prog_len
)
470 printf(" jited %uB", info
->jited_prog_len
);
472 printf(" not jited");
474 memlock
= get_fdinfo(fd
, "memlock");
476 printf(" memlock %sB", memlock
);
479 if (info
->nr_map_ids
)
480 show_prog_maps(fd
, info
->nr_map_ids
);
482 if (!hash_empty(prog_table
.table
)) {
483 struct pinned_obj
*obj
;
485 hash_for_each_possible(prog_table
.table
, obj
, hash
, info
->id
) {
486 if (obj
->id
== info
->id
)
487 printf("\n\tpinned %s", obj
->path
);
492 printf("\n\tbtf_id %d", info
->btf_id
);
494 emit_obj_refs_plain(&refs_table
, info
->id
, "\n\tpids ");
498 show_prog_metadata(fd
, info
->nr_map_ids
);
501 static int show_prog(int fd
)
503 struct bpf_prog_info info
= {};
504 __u32 len
= sizeof(info
);
507 err
= bpf_obj_get_info_by_fd(fd
, &info
, &len
);
509 p_err("can't get prog info: %s", strerror(errno
));
514 print_prog_json(&info
, fd
);
516 print_prog_plain(&info
, fd
);
521 static int do_show_subset(int argc
, char **argv
)
527 fds
= malloc(sizeof(int));
529 p_err("mem alloc failed");
532 nb_fds
= prog_parse_fds(&argc
, &argv
, &fds
);
536 if (json_output
&& nb_fds
> 1)
537 jsonw_start_array(json_wtr
); /* root array */
538 for (i
= 0; i
< nb_fds
; i
++) {
539 err
= show_prog(fds
[i
]);
541 for (; i
< nb_fds
; i
++)
547 if (json_output
&& nb_fds
> 1)
548 jsonw_end_array(json_wtr
); /* root array */
555 static int do_show(int argc
, char **argv
)
562 build_pinned_obj_table(&prog_table
, BPF_OBJ_PROG
);
563 build_obj_refs_table(&refs_table
, BPF_OBJ_PROG
);
566 return do_show_subset(argc
, argv
);
572 jsonw_start_array(json_wtr
);
574 err
= bpf_prog_get_next_id(id
, &id
);
576 if (errno
== ENOENT
) {
580 p_err("can't get next program: %s%s", strerror(errno
),
581 errno
== EINVAL
? " -- kernel too old?" : "");
586 fd
= bpf_prog_get_fd_by_id(id
);
590 p_err("can't get prog by id (%u): %s",
591 id
, strerror(errno
));
603 jsonw_end_array(json_wtr
);
605 delete_obj_refs_table(&refs_table
);
611 prog_dump(struct bpf_prog_info
*info
, enum dump_mode mode
,
612 char *filepath
, bool opcodes
, bool visual
, bool linum
)
614 struct bpf_prog_linfo
*prog_linfo
= NULL
;
615 const char *disasm_opt
= NULL
;
616 struct dump_data dd
= {};
617 void *func_info
= NULL
;
618 struct btf
*btf
= NULL
;
625 if (mode
== DUMP_JITED
) {
626 if (info
->jited_prog_len
== 0 || !info
->jited_prog_insns
) {
627 p_info("no instructions returned");
630 buf
= u64_to_ptr(info
->jited_prog_insns
);
631 member_len
= info
->jited_prog_len
;
632 } else { /* DUMP_XLATED */
633 if (info
->xlated_prog_len
== 0 || !info
->xlated_prog_insns
) {
634 p_err("error retrieving insn dump: kernel.kptr_restrict set?");
637 buf
= u64_to_ptr(info
->xlated_prog_insns
);
638 member_len
= info
->xlated_prog_len
;
641 if (info
->btf_id
&& btf__get_from_id(info
->btf_id
, &btf
)) {
642 p_err("failed to get btf");
646 func_info
= u64_to_ptr(info
->func_info
);
648 if (info
->nr_line_info
) {
649 prog_linfo
= bpf_prog_linfo__new(info
);
651 p_info("error in processing bpf_line_info. continue without it.");
655 fd
= open(filepath
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
657 p_err("can't open file %s: %s", filepath
,
662 n
= write(fd
, buf
, member_len
);
664 if (n
!= (ssize_t
)member_len
) {
665 p_err("error writing output file: %s",
666 n
< 0 ? strerror(errno
) : "short write");
671 jsonw_null(json_wtr
);
672 } else if (mode
== DUMP_JITED
) {
673 const char *name
= NULL
;
676 name
= ifindex_to_bfd_params(info
->ifindex
,
684 if (info
->nr_jited_func_lens
&& info
->jited_func_lens
) {
685 struct kernel_sym
*sym
= NULL
;
686 struct bpf_func_info
*record
;
687 char sym_name
[SYM_MAX_NAME
];
688 unsigned char *img
= buf
;
692 if (info
->nr_jited_ksyms
) {
693 kernel_syms_load(&dd
);
694 ksyms
= u64_to_ptr(info
->jited_ksyms
);
698 jsonw_start_array(json_wtr
);
700 lens
= u64_to_ptr(info
->jited_func_lens
);
701 for (i
= 0; i
< info
->nr_jited_func_lens
; i
++) {
703 sym
= kernel_syms_search(&dd
, ksyms
[i
]);
705 sprintf(sym_name
, "%s", sym
->name
);
707 sprintf(sym_name
, "0x%016llx", ksyms
[i
]);
709 strcpy(sym_name
, "unknown");
713 record
= func_info
+ i
* info
->func_info_rec_size
;
714 btf_dumper_type_only(btf
, record
->type_id
,
720 jsonw_start_object(json_wtr
);
721 if (func_info
&& func_sig
[0] != '\0') {
722 jsonw_name(json_wtr
, "proto");
723 jsonw_string(json_wtr
, func_sig
);
725 jsonw_name(json_wtr
, "name");
726 jsonw_string(json_wtr
, sym_name
);
727 jsonw_name(json_wtr
, "insns");
729 if (func_info
&& func_sig
[0] != '\0')
730 printf("%s:\n", func_sig
);
731 printf("%s:\n", sym_name
);
734 disasm_print_insn(img
, lens
[i
], opcodes
,
735 name
, disasm_opt
, btf
,
736 prog_linfo
, ksyms
[i
], i
,
742 jsonw_end_object(json_wtr
);
748 jsonw_end_array(json_wtr
);
750 disasm_print_insn(buf
, member_len
, opcodes
, name
,
751 disasm_opt
, btf
, NULL
, 0, 0, false);
755 jsonw_null(json_wtr
);
757 dump_xlated_cfg(buf
, member_len
);
759 kernel_syms_load(&dd
);
760 dd
.nr_jited_ksyms
= info
->nr_jited_ksyms
;
761 dd
.jited_ksyms
= u64_to_ptr(info
->jited_ksyms
);
763 dd
.func_info
= func_info
;
764 dd
.finfo_rec_size
= info
->func_info_rec_size
;
765 dd
.prog_linfo
= prog_linfo
;
768 dump_xlated_json(&dd
, buf
, member_len
, opcodes
,
771 dump_xlated_plain(&dd
, buf
, member_len
, opcodes
,
773 kernel_syms_destroy(&dd
);
779 static int do_dump(int argc
, char **argv
)
781 struct bpf_prog_info_linear
*info_linear
;
782 char *filepath
= NULL
;
783 bool opcodes
= false;
792 if (is_prefix(*argv
, "jited")) {
796 } else if (is_prefix(*argv
, "xlated")) {
799 p_err("expected 'xlated' or 'jited', got: %s", *argv
);
807 fds
= malloc(sizeof(int));
809 p_err("mem alloc failed");
812 nb_fds
= prog_parse_fds(&argc
, &argv
, &fds
);
816 if (is_prefix(*argv
, "file")) {
819 p_err("expected file path");
823 p_err("several programs matched");
829 } else if (is_prefix(*argv
, "opcodes")) {
832 } else if (is_prefix(*argv
, "visual")) {
834 p_err("several programs matched");
840 } else if (is_prefix(*argv
, "linum")) {
850 if (mode
== DUMP_JITED
)
851 arrays
= 1UL << BPF_PROG_INFO_JITED_INSNS
;
853 arrays
= 1UL << BPF_PROG_INFO_XLATED_INSNS
;
855 arrays
|= 1UL << BPF_PROG_INFO_JITED_KSYMS
;
856 arrays
|= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS
;
857 arrays
|= 1UL << BPF_PROG_INFO_FUNC_INFO
;
858 arrays
|= 1UL << BPF_PROG_INFO_LINE_INFO
;
859 arrays
|= 1UL << BPF_PROG_INFO_JITED_LINE_INFO
;
861 if (json_output
&& nb_fds
> 1)
862 jsonw_start_array(json_wtr
); /* root array */
863 for (i
= 0; i
< nb_fds
; i
++) {
864 info_linear
= bpf_program__get_prog_info_linear(fds
[i
], arrays
);
865 if (IS_ERR_OR_NULL(info_linear
)) {
866 p_err("can't get prog info: %s", strerror(errno
));
870 if (json_output
&& nb_fds
> 1) {
871 jsonw_start_object(json_wtr
); /* prog object */
872 print_prog_header_json(&info_linear
->info
);
873 jsonw_name(json_wtr
, "insns");
874 } else if (nb_fds
> 1) {
875 print_prog_header_plain(&info_linear
->info
);
878 err
= prog_dump(&info_linear
->info
, mode
, filepath
, opcodes
,
881 if (json_output
&& nb_fds
> 1)
882 jsonw_end_object(json_wtr
); /* prog object */
883 else if (i
!= nb_fds
- 1 && nb_fds
> 1)
891 if (json_output
&& nb_fds
> 1)
892 jsonw_end_array(json_wtr
); /* root array */
895 for (; i
< nb_fds
; i
++)
902 static int do_pin(int argc
, char **argv
)
906 err
= do_pin_any(argc
, argv
, prog_parse_fd
);
907 if (!err
&& json_output
)
908 jsonw_null(json_wtr
);
918 static int map_replace_compar(const void *p1
, const void *p2
)
920 const struct map_replace
*a
= p1
, *b
= p2
;
922 return a
->idx
- b
->idx
;
925 static int parse_attach_detach_args(int argc
, char **argv
, int *progfd
,
926 enum bpf_attach_type
*attach_type
,
932 *progfd
= prog_parse_fd(&argc
, &argv
);
936 *attach_type
= parse_attach_type(*argv
);
937 if (*attach_type
== __MAX_BPF_ATTACH_TYPE
) {
938 p_err("invalid attach/detach type");
942 if (*attach_type
== BPF_FLOW_DISSECTOR
) {
951 *mapfd
= map_parse_fd(&argc
, &argv
);
958 static int do_attach(int argc
, char **argv
)
960 enum bpf_attach_type attach_type
;
964 err
= parse_attach_detach_args(argc
, argv
,
965 &progfd
, &attach_type
, &mapfd
);
969 err
= bpf_prog_attach(progfd
, mapfd
, attach_type
, 0);
971 p_err("failed prog attach to map");
976 jsonw_null(json_wtr
);
980 static int do_detach(int argc
, char **argv
)
982 enum bpf_attach_type attach_type
;
986 err
= parse_attach_detach_args(argc
, argv
,
987 &progfd
, &attach_type
, &mapfd
);
991 err
= bpf_prog_detach2(progfd
, mapfd
, attach_type
);
993 p_err("failed prog detach from map");
998 jsonw_null(json_wtr
);
1002 static int check_single_stdin(char *file_data_in
, char *file_ctx_in
)
1004 if (file_data_in
&& file_ctx_in
&&
1005 !strcmp(file_data_in
, "-") && !strcmp(file_ctx_in
, "-")) {
1006 p_err("cannot use standard input for both data_in and ctx_in");
1013 static int get_run_data(const char *fname
, void **data_ptr
, unsigned int *size
)
1015 size_t block_size
= 256;
1016 size_t buf_size
= block_size
;
1027 if (!strcmp(fname
, "-"))
1030 f
= fopen(fname
, "r");
1032 p_err("failed to open %s: %s", fname
, strerror(errno
));
1036 *data_ptr
= malloc(block_size
);
1038 p_err("failed to allocate memory for data_in/ctx_in: %s",
1043 while ((nb_read
+= fread(*data_ptr
+ nb_read
, 1, block_size
, f
))) {
1047 p_err("failed to read data_in/ctx_in from %s: %s",
1048 fname
, strerror(errno
));
1051 if (nb_read
> buf_size
- block_size
) {
1052 if (buf_size
== UINT32_MAX
) {
1053 p_err("data_in/ctx_in is too long (max: %d)",
1057 /* No space for fread()-ing next chunk; realloc() */
1059 tmp
= realloc(*data_ptr
, buf_size
);
1061 p_err("failed to reallocate data_in/ctx_in: %s",
1083 static void hex_print(void *data
, unsigned int size
, FILE *f
)
1088 for (i
= 0; i
< size
; i
+= 16) {
1090 fprintf(f
, "%07zx\t", i
);
1092 /* Hexadecimal values */
1093 for (j
= i
; j
< i
+ 16 && j
< size
; j
++)
1094 fprintf(f
, "%02x%s", *(uint8_t *)(data
+ j
),
1096 for (; j
< i
+ 16; j
++)
1097 fprintf(f
, " %s", j
% 2 ? " " : "");
1099 /* ASCII values (if relevant), '.' otherwise */
1101 for (j
= i
; j
< i
+ 16 && j
< size
; j
++) {
1102 c
= *(char *)(data
+ j
);
1103 if (c
< ' ' || c
> '~')
1105 fprintf(f
, "%c%s", c
, j
== i
+ 7 ? " " : "");
1113 print_run_output(void *data
, unsigned int size
, const char *fname
,
1114 const char *json_key
)
1122 if (!strcmp(fname
, "-")) {
1125 jsonw_name(json_wtr
, json_key
);
1126 print_data_json(data
, size
);
1128 hex_print(data
, size
, f
);
1133 f
= fopen(fname
, "w");
1135 p_err("failed to open %s: %s", fname
, strerror(errno
));
1139 nb_written
= fwrite(data
, 1, size
, f
);
1141 if (nb_written
!= size
) {
1142 p_err("failed to write output data/ctx: %s", strerror(errno
));
1149 static int alloc_run_data(void **data_ptr
, unsigned int size_out
)
1151 *data_ptr
= calloc(size_out
, 1);
1153 p_err("failed to allocate memory for output data/ctx: %s",
1161 static int do_run(int argc
, char **argv
)
1163 char *data_fname_in
= NULL
, *data_fname_out
= NULL
;
1164 char *ctx_fname_in
= NULL
, *ctx_fname_out
= NULL
;
1165 struct bpf_prog_test_run_attr test_attr
= {0};
1166 const unsigned int default_size
= SZ_32K
;
1167 void *data_in
= NULL
, *data_out
= NULL
;
1168 void *ctx_in
= NULL
, *ctx_out
= NULL
;
1169 unsigned int repeat
= 1;
1175 fd
= prog_parse_fd(&argc
, &argv
);
1180 if (detect_common_prefix(*argv
, "data_in", "data_out",
1181 "data_size_out", NULL
))
1183 if (detect_common_prefix(*argv
, "ctx_in", "ctx_out",
1184 "ctx_size_out", NULL
))
1187 if (is_prefix(*argv
, "data_in")) {
1192 data_fname_in
= GET_ARG();
1193 if (check_single_stdin(data_fname_in
, ctx_fname_in
))
1195 } else if (is_prefix(*argv
, "data_out")) {
1200 data_fname_out
= GET_ARG();
1201 } else if (is_prefix(*argv
, "data_size_out")) {
1208 test_attr
.data_size_out
= strtoul(*argv
, &endptr
, 0);
1210 p_err("can't parse %s as output data size",
1215 } else if (is_prefix(*argv
, "ctx_in")) {
1220 ctx_fname_in
= GET_ARG();
1221 if (check_single_stdin(data_fname_in
, ctx_fname_in
))
1223 } else if (is_prefix(*argv
, "ctx_out")) {
1228 ctx_fname_out
= GET_ARG();
1229 } else if (is_prefix(*argv
, "ctx_size_out")) {
1236 test_attr
.ctx_size_out
= strtoul(*argv
, &endptr
, 0);
1238 p_err("can't parse %s as output context size",
1243 } else if (is_prefix(*argv
, "repeat")) {
1250 repeat
= strtoul(*argv
, &endptr
, 0);
1252 p_err("can't parse %s as repeat number",
1258 p_err("expected no more arguments, 'data_in', 'data_out', 'data_size_out', 'ctx_in', 'ctx_out', 'ctx_size_out' or 'repeat', got: '%s'?",
1264 err
= get_run_data(data_fname_in
, &data_in
, &test_attr
.data_size_in
);
1269 if (!test_attr
.data_size_out
)
1270 test_attr
.data_size_out
= default_size
;
1271 err
= alloc_run_data(&data_out
, test_attr
.data_size_out
);
1276 err
= get_run_data(ctx_fname_in
, &ctx_in
, &test_attr
.ctx_size_in
);
1281 if (!test_attr
.ctx_size_out
)
1282 test_attr
.ctx_size_out
= default_size
;
1283 err
= alloc_run_data(&ctx_out
, test_attr
.ctx_size_out
);
1288 test_attr
.prog_fd
= fd
;
1289 test_attr
.repeat
= repeat
;
1290 test_attr
.data_in
= data_in
;
1291 test_attr
.data_out
= data_out
;
1292 test_attr
.ctx_in
= ctx_in
;
1293 test_attr
.ctx_out
= ctx_out
;
1295 err
= bpf_prog_test_run_xattr(&test_attr
);
1297 p_err("failed to run program: %s", strerror(errno
));
1304 jsonw_start_object(json_wtr
); /* root */
1306 /* Do not exit on errors occurring when printing output data/context,
1307 * we still want to print return value and duration for program run.
1309 if (test_attr
.data_size_out
)
1310 err
+= print_run_output(test_attr
.data_out
,
1311 test_attr
.data_size_out
,
1312 data_fname_out
, "data_out");
1313 if (test_attr
.ctx_size_out
)
1314 err
+= print_run_output(test_attr
.ctx_out
,
1315 test_attr
.ctx_size_out
,
1316 ctx_fname_out
, "ctx_out");
1319 jsonw_uint_field(json_wtr
, "retval", test_attr
.retval
);
1320 jsonw_uint_field(json_wtr
, "duration", test_attr
.duration
);
1321 jsonw_end_object(json_wtr
); /* root */
1323 fprintf(stdout
, "Return value: %u, duration%s: %uns\n",
1325 repeat
> 1 ? " (average)" : "", test_attr
.duration
);
1341 get_prog_type_by_name(const char *name
, enum bpf_prog_type
*prog_type
,
1342 enum bpf_attach_type
*expected_attach_type
)
1344 libbpf_print_fn_t print_backup
;
1347 ret
= libbpf_prog_type_by_name(name
, prog_type
, expected_attach_type
);
1351 /* libbpf_prog_type_by_name() failed, let's re-run with debug level */
1352 print_backup
= libbpf_set_print(print_all_levels
);
1353 ret
= libbpf_prog_type_by_name(name
, prog_type
, expected_attach_type
);
1354 libbpf_set_print(print_backup
);
1359 static int load_with_options(int argc
, char **argv
, bool first_prog_only
)
1361 enum bpf_prog_type common_prog_type
= BPF_PROG_TYPE_UNSPEC
;
1362 DECLARE_LIBBPF_OPTS(bpf_object_open_opts
, open_opts
,
1363 .relaxed_maps
= relaxed_maps
,
1365 struct bpf_object_load_attr load_attr
= { 0 };
1366 enum bpf_attach_type expected_attach_type
;
1367 struct map_replace
*map_replace
= NULL
;
1368 struct bpf_program
*prog
= NULL
, *pos
;
1369 unsigned int old_map_fds
= 0;
1370 const char *pinmaps
= NULL
;
1371 struct bpf_object
*obj
;
1372 struct bpf_map
*map
;
1373 const char *pinfile
;
1383 pinfile
= GET_ARG();
1386 if (is_prefix(*argv
, "type")) {
1391 if (common_prog_type
!= BPF_PROG_TYPE_UNSPEC
) {
1392 p_err("program type already specified");
1393 goto err_free_reuse_maps
;
1396 goto err_free_reuse_maps
;
1398 /* Put a '/' at the end of type to appease libbpf */
1399 type
= malloc(strlen(*argv
) + 2);
1401 p_err("mem alloc failed");
1402 goto err_free_reuse_maps
;
1405 strcat(type
, *argv
);
1408 err
= get_prog_type_by_name(type
, &common_prog_type
,
1409 &expected_attach_type
);
1412 goto err_free_reuse_maps
;
1415 } else if (is_prefix(*argv
, "map")) {
1416 void *new_map_replace
;
1417 char *endptr
, *name
;
1423 goto err_free_reuse_maps
;
1425 if (is_prefix(*argv
, "idx")) {
1428 idx
= strtoul(*argv
, &endptr
, 0);
1430 p_err("can't parse %s as IDX", *argv
);
1431 goto err_free_reuse_maps
;
1434 } else if (is_prefix(*argv
, "name")) {
1440 p_err("expected 'idx' or 'name', got: '%s'?",
1442 goto err_free_reuse_maps
;
1446 fd
= map_parse_fd(&argc
, &argv
);
1448 goto err_free_reuse_maps
;
1450 new_map_replace
= reallocarray(map_replace
,
1452 sizeof(*map_replace
));
1453 if (!new_map_replace
) {
1454 p_err("mem alloc failed");
1455 goto err_free_reuse_maps
;
1457 map_replace
= new_map_replace
;
1459 map_replace
[old_map_fds
].idx
= idx
;
1460 map_replace
[old_map_fds
].name
= name
;
1461 map_replace
[old_map_fds
].fd
= fd
;
1463 } else if (is_prefix(*argv
, "dev")) {
1467 p_err("offload device already specified");
1468 goto err_free_reuse_maps
;
1471 goto err_free_reuse_maps
;
1473 ifindex
= if_nametoindex(*argv
);
1475 p_err("unrecognized netdevice '%s': %s",
1476 *argv
, strerror(errno
));
1477 goto err_free_reuse_maps
;
1480 } else if (is_prefix(*argv
, "pinmaps")) {
1484 goto err_free_reuse_maps
;
1486 pinmaps
= GET_ARG();
1488 p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
1490 goto err_free_reuse_maps
;
1496 obj
= bpf_object__open_file(file
, &open_opts
);
1497 if (IS_ERR_OR_NULL(obj
)) {
1498 p_err("failed to open object file");
1499 goto err_free_reuse_maps
;
1502 bpf_object__for_each_program(pos
, obj
) {
1503 enum bpf_prog_type prog_type
= common_prog_type
;
1505 if (prog_type
== BPF_PROG_TYPE_UNSPEC
) {
1506 const char *sec_name
= bpf_program__section_name(pos
);
1508 err
= get_prog_type_by_name(sec_name
, &prog_type
,
1509 &expected_attach_type
);
1514 bpf_program__set_ifindex(pos
, ifindex
);
1515 bpf_program__set_type(pos
, prog_type
);
1516 bpf_program__set_expected_attach_type(pos
, expected_attach_type
);
1519 qsort(map_replace
, old_map_fds
, sizeof(*map_replace
),
1520 map_replace_compar
);
1522 /* After the sort maps by name will be first on the list, because they
1523 * have idx == -1. Resolve them.
1526 while (j
< old_map_fds
&& map_replace
[j
].name
) {
1528 bpf_object__for_each_map(map
, obj
) {
1529 if (!strcmp(bpf_map__name(map
), map_replace
[j
].name
)) {
1530 map_replace
[j
].idx
= i
;
1535 if (map_replace
[j
].idx
== -1) {
1536 p_err("unable to find map '%s'", map_replace
[j
].name
);
1541 /* Resort if any names were resolved */
1543 qsort(map_replace
, old_map_fds
, sizeof(*map_replace
),
1544 map_replace_compar
);
1546 /* Set ifindex and name reuse */
1549 bpf_object__for_each_map(map
, obj
) {
1550 if (!bpf_map__is_offload_neutral(map
))
1551 bpf_map__set_ifindex(map
, ifindex
);
1553 if (j
< old_map_fds
&& idx
== map_replace
[j
].idx
) {
1554 err
= bpf_map__reuse_fd(map
, map_replace
[j
++].fd
);
1556 p_err("unable to set up map reuse: %d", err
);
1560 /* Next reuse wants to apply to the same map */
1561 if (j
< old_map_fds
&& map_replace
[j
].idx
== idx
) {
1562 p_err("replacement for map idx %d specified more than once",
1570 if (j
< old_map_fds
) {
1571 p_err("map idx '%d' not used", map_replace
[j
].idx
);
1575 load_attr
.obj
= obj
;
1577 /* log_level1 + log_level2 + stats, but not stable UAPI */
1578 load_attr
.log_level
= 1 + 2 + 4;
1580 err
= bpf_object__load_xattr(&load_attr
);
1582 p_err("failed to load object file");
1586 err
= mount_bpffs_for_pin(pinfile
);
1590 if (first_prog_only
) {
1591 prog
= bpf_program__next(NULL
, obj
);
1593 p_err("object file doesn't contain any bpf program");
1597 err
= bpf_obj_pin(bpf_program__fd(prog
), pinfile
);
1599 p_err("failed to pin program %s",
1600 bpf_program__section_name(prog
));
1604 err
= bpf_object__pin_programs(obj
, pinfile
);
1606 p_err("failed to pin all programs");
1612 err
= bpf_object__pin_maps(obj
, pinmaps
);
1614 p_err("failed to pin all maps");
1620 jsonw_null(json_wtr
);
1622 bpf_object__close(obj
);
1623 for (i
= 0; i
< old_map_fds
; i
++)
1624 close(map_replace
[i
].fd
);
1630 if (first_prog_only
)
1633 bpf_object__unpin_programs(obj
, pinfile
);
1635 bpf_object__close(obj
);
1636 err_free_reuse_maps
:
1637 for (i
= 0; i
< old_map_fds
; i
++)
1638 close(map_replace
[i
].fd
);
1643 static int do_load(int argc
, char **argv
)
1645 return load_with_options(argc
, argv
, true);
1648 static int do_loadall(int argc
, char **argv
)
1650 return load_with_options(argc
, argv
, false);
1653 #ifdef BPFTOOL_WITHOUT_SKELETONS
1655 static int do_profile(int argc
, char **argv
)
1657 p_err("bpftool prog profile command is not supported. Please build bpftool with clang >= 10.0.0");
1661 #else /* BPFTOOL_WITHOUT_SKELETONS */
1663 #include "profiler.skel.h"
1665 struct profile_metric
{
1667 struct bpf_perf_event_value val
;
1668 struct perf_event_attr attr
;
1671 /* calculate ratios like instructions per cycle */
1672 const int ratio_metric
; /* 0 for N/A, 1 for index 0 (cycles) */
1673 const char *ratio_desc
;
1674 const float ratio_mul
;
1679 .type
= PERF_TYPE_HARDWARE
,
1680 .config
= PERF_COUNT_HW_CPU_CYCLES
,
1685 .name
= "instructions",
1687 .type
= PERF_TYPE_HARDWARE
,
1688 .config
= PERF_COUNT_HW_INSTRUCTIONS
,
1692 .ratio_desc
= "insns per cycle",
1696 .name
= "l1d_loads",
1698 .type
= PERF_TYPE_HW_CACHE
,
1700 PERF_COUNT_HW_CACHE_L1D
|
1701 (PERF_COUNT_HW_CACHE_OP_READ
<< 8) |
1702 (PERF_COUNT_HW_CACHE_RESULT_ACCESS
<< 16),
1707 .name
= "llc_misses",
1709 .type
= PERF_TYPE_HW_CACHE
,
1711 PERF_COUNT_HW_CACHE_LL
|
1712 (PERF_COUNT_HW_CACHE_OP_READ
<< 8) |
1713 (PERF_COUNT_HW_CACHE_RESULT_MISS
<< 16),
1717 .ratio_desc
= "LLC misses per million insns",
1721 .name
= "itlb_misses",
1723 .type
= PERF_TYPE_HW_CACHE
,
1725 PERF_COUNT_HW_CACHE_ITLB
|
1726 (PERF_COUNT_HW_CACHE_OP_READ
<< 8) |
1727 (PERF_COUNT_HW_CACHE_RESULT_MISS
<< 16),
1731 .ratio_desc
= "itlb misses per million insns",
1735 .name
= "dtlb_misses",
1737 .type
= PERF_TYPE_HW_CACHE
,
1739 PERF_COUNT_HW_CACHE_DTLB
|
1740 (PERF_COUNT_HW_CACHE_OP_READ
<< 8) |
1741 (PERF_COUNT_HW_CACHE_RESULT_MISS
<< 16),
1745 .ratio_desc
= "dtlb misses per million insns",
1750 static __u64 profile_total_count
;
1752 #define MAX_NUM_PROFILE_METRICS 4
1754 static int profile_parse_metrics(int argc
, char **argv
)
1756 unsigned int metric_cnt
;
1757 int selected_cnt
= 0;
1760 metric_cnt
= sizeof(metrics
) / sizeof(struct profile_metric
);
1763 for (i
= 0; i
< metric_cnt
; i
++) {
1764 if (is_prefix(argv
[0], metrics
[i
].name
)) {
1765 if (!metrics
[i
].selected
)
1767 metrics
[i
].selected
= true;
1771 if (i
== metric_cnt
) {
1772 p_err("unknown metric %s", argv
[0]);
1777 if (selected_cnt
> MAX_NUM_PROFILE_METRICS
) {
1778 p_err("too many (%d) metrics, please specify no more than %d metrics at at time",
1779 selected_cnt
, MAX_NUM_PROFILE_METRICS
);
1782 return selected_cnt
;
1785 static void profile_read_values(struct profiler_bpf
*obj
)
1787 __u32 m
, cpu
, num_cpu
= obj
->rodata
->num_cpu
;
1788 int reading_map_fd
, count_map_fd
;
1789 __u64 counts
[num_cpu
];
1793 reading_map_fd
= bpf_map__fd(obj
->maps
.accum_readings
);
1794 count_map_fd
= bpf_map__fd(obj
->maps
.counts
);
1795 if (reading_map_fd
< 0 || count_map_fd
< 0) {
1796 p_err("failed to get fd for map");
1800 err
= bpf_map_lookup_elem(count_map_fd
, &key
, counts
);
1802 p_err("failed to read count_map: %s", strerror(errno
));
1806 profile_total_count
= 0;
1807 for (cpu
= 0; cpu
< num_cpu
; cpu
++)
1808 profile_total_count
+= counts
[cpu
];
1810 for (m
= 0; m
< ARRAY_SIZE(metrics
); m
++) {
1811 struct bpf_perf_event_value values
[num_cpu
];
1813 if (!metrics
[m
].selected
)
1816 err
= bpf_map_lookup_elem(reading_map_fd
, &key
, values
);
1818 p_err("failed to read reading_map: %s",
1822 for (cpu
= 0; cpu
< num_cpu
; cpu
++) {
1823 metrics
[m
].val
.counter
+= values
[cpu
].counter
;
1824 metrics
[m
].val
.enabled
+= values
[cpu
].enabled
;
1825 metrics
[m
].val
.running
+= values
[cpu
].running
;
1831 static void profile_print_readings_json(void)
1835 jsonw_start_array(json_wtr
);
1836 for (m
= 0; m
< ARRAY_SIZE(metrics
); m
++) {
1837 if (!metrics
[m
].selected
)
1839 jsonw_start_object(json_wtr
);
1840 jsonw_string_field(json_wtr
, "metric", metrics
[m
].name
);
1841 jsonw_lluint_field(json_wtr
, "run_cnt", profile_total_count
);
1842 jsonw_lluint_field(json_wtr
, "value", metrics
[m
].val
.counter
);
1843 jsonw_lluint_field(json_wtr
, "enabled", metrics
[m
].val
.enabled
);
1844 jsonw_lluint_field(json_wtr
, "running", metrics
[m
].val
.running
);
1846 jsonw_end_object(json_wtr
);
1848 jsonw_end_array(json_wtr
);
1851 static void profile_print_readings_plain(void)
1855 printf("\n%18llu %-20s\n", profile_total_count
, "run_cnt");
1856 for (m
= 0; m
< ARRAY_SIZE(metrics
); m
++) {
1857 struct bpf_perf_event_value
*val
= &metrics
[m
].val
;
1860 if (!metrics
[m
].selected
)
1862 printf("%18llu %-20s", val
->counter
, metrics
[m
].name
);
1864 r
= metrics
[m
].ratio_metric
- 1;
1865 if (r
>= 0 && metrics
[r
].selected
&&
1866 metrics
[r
].val
.counter
> 0) {
1867 printf("# %8.2f %-30s",
1868 val
->counter
* metrics
[m
].ratio_mul
/
1869 metrics
[r
].val
.counter
,
1870 metrics
[m
].ratio_desc
);
1872 printf("%-41s", "");
1875 if (val
->enabled
> val
->running
)
1877 val
->running
* 100.0 / val
->enabled
);
1882 static void profile_print_readings(void)
1885 profile_print_readings_json();
1887 profile_print_readings_plain();
1890 static char *profile_target_name(int tgt_fd
)
1892 struct bpf_prog_info_linear
*info_linear
;
1893 struct bpf_func_info
*func_info
;
1894 const struct btf_type
*t
;
1898 info_linear
= bpf_program__get_prog_info_linear(
1899 tgt_fd
, 1UL << BPF_PROG_INFO_FUNC_INFO
);
1900 if (IS_ERR_OR_NULL(info_linear
)) {
1901 p_err("failed to get info_linear for prog FD %d", tgt_fd
);
1905 if (info_linear
->info
.btf_id
== 0 ||
1906 btf__get_from_id(info_linear
->info
.btf_id
, &btf
)) {
1907 p_err("prog FD %d doesn't have valid btf", tgt_fd
);
1911 func_info
= u64_to_ptr(info_linear
->info
.func_info
);
1912 t
= btf__type_by_id(btf
, func_info
[0].type_id
);
1914 p_err("btf %d doesn't have type %d",
1915 info_linear
->info
.btf_id
, func_info
[0].type_id
);
1918 name
= strdup(btf__name_by_offset(btf
, t
->name_off
));
1924 static struct profiler_bpf
*profile_obj
;
1925 static int profile_tgt_fd
= -1;
1926 static char *profile_tgt_name
;
1927 static int *profile_perf_events
;
1928 static int profile_perf_event_cnt
;
1930 static void profile_close_perf_events(struct profiler_bpf
*obj
)
1934 for (i
= profile_perf_event_cnt
- 1; i
>= 0; i
--)
1935 close(profile_perf_events
[i
]);
1937 free(profile_perf_events
);
1938 profile_perf_event_cnt
= 0;
1941 static int profile_open_perf_events(struct profiler_bpf
*obj
)
1943 unsigned int cpu
, m
;
1946 profile_perf_events
= calloc(
1947 sizeof(int), obj
->rodata
->num_cpu
* obj
->rodata
->num_metric
);
1948 if (!profile_perf_events
) {
1949 p_err("failed to allocate memory for perf_event array: %s",
1953 map_fd
= bpf_map__fd(obj
->maps
.events
);
1955 p_err("failed to get fd for events map");
1959 for (m
= 0; m
< ARRAY_SIZE(metrics
); m
++) {
1960 if (!metrics
[m
].selected
)
1962 for (cpu
= 0; cpu
< obj
->rodata
->num_cpu
; cpu
++) {
1963 pmu_fd
= syscall(__NR_perf_event_open
, &metrics
[m
].attr
,
1964 -1/*pid*/, cpu
, -1/*group_fd*/, 0);
1966 bpf_map_update_elem(map_fd
, &profile_perf_event_cnt
,
1967 &pmu_fd
, BPF_ANY
) ||
1968 ioctl(pmu_fd
, PERF_EVENT_IOC_ENABLE
, 0)) {
1969 p_err("failed to create event %s on cpu %d",
1970 metrics
[m
].name
, cpu
);
1973 profile_perf_events
[profile_perf_event_cnt
++] = pmu_fd
;
1979 static void profile_print_and_cleanup(void)
1981 profile_close_perf_events(profile_obj
);
1982 profile_read_values(profile_obj
);
1983 profile_print_readings();
1984 profiler_bpf__destroy(profile_obj
);
1986 close(profile_tgt_fd
);
1987 free(profile_tgt_name
);
1990 static void int_exit(int signo
)
1992 profile_print_and_cleanup();
1996 static int do_profile(int argc
, char **argv
)
1998 int num_metric
, num_cpu
, err
= -1;
1999 struct bpf_program
*prog
;
2000 unsigned long duration
;
2003 /* we at least need two args for the prog and one metric */
2007 /* parse target fd */
2008 profile_tgt_fd
= prog_parse_fd(&argc
, &argv
);
2009 if (profile_tgt_fd
< 0) {
2010 p_err("failed to parse fd");
2014 /* parse profiling optional duration */
2015 if (argc
> 2 && is_prefix(argv
[0], "duration")) {
2017 duration
= strtoul(*argv
, &endptr
, 0);
2022 duration
= UINT_MAX
;
2025 num_metric
= profile_parse_metrics(argc
, argv
);
2026 if (num_metric
<= 0)
2029 num_cpu
= libbpf_num_possible_cpus();
2031 p_err("failed to identify number of CPUs");
2035 profile_obj
= profiler_bpf__open();
2037 p_err("failed to open and/or load BPF object");
2041 profile_obj
->rodata
->num_cpu
= num_cpu
;
2042 profile_obj
->rodata
->num_metric
= num_metric
;
2044 /* adjust map sizes */
2045 bpf_map__resize(profile_obj
->maps
.events
, num_metric
* num_cpu
);
2046 bpf_map__resize(profile_obj
->maps
.fentry_readings
, num_metric
);
2047 bpf_map__resize(profile_obj
->maps
.accum_readings
, num_metric
);
2048 bpf_map__resize(profile_obj
->maps
.counts
, 1);
2050 /* change target name */
2051 profile_tgt_name
= profile_target_name(profile_tgt_fd
);
2052 if (!profile_tgt_name
)
2055 bpf_object__for_each_program(prog
, profile_obj
->obj
) {
2056 err
= bpf_program__set_attach_target(prog
, profile_tgt_fd
,
2059 p_err("failed to set attach target\n");
2065 err
= profiler_bpf__load(profile_obj
);
2067 p_err("failed to load profile_obj");
2071 err
= profile_open_perf_events(profile_obj
);
2075 err
= profiler_bpf__attach(profile_obj
);
2077 p_err("failed to attach profile_obj");
2080 signal(SIGINT
, int_exit
);
2083 profile_print_and_cleanup();
2087 profile_close_perf_events(profile_obj
);
2089 profiler_bpf__destroy(profile_obj
);
2090 close(profile_tgt_fd
);
2091 free(profile_tgt_name
);
2095 #endif /* BPFTOOL_WITHOUT_SKELETONS */
2097 static int do_help(int argc
, char **argv
)
2100 jsonw_null(json_wtr
);
2105 "Usage: %1$s %2$s { show | list } [PROG]\n"
2106 " %1$s %2$s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n"
2107 " %1$s %2$s dump jited PROG [{ file FILE | opcodes | linum }]\n"
2108 " %1$s %2$s pin PROG FILE\n"
2109 " %1$s %2$s { load | loadall } OBJ PATH \\\n"
2110 " [type TYPE] [dev NAME] \\\n"
2111 " [map { idx IDX | name NAME } MAP]\\\n"
2112 " [pinmaps MAP_DIR]\n"
2113 " %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n"
2114 " %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n"
2115 " %1$s %2$s run PROG \\\n"
2116 " data_in FILE \\\n"
2117 " [data_out FILE [data_size_out L]] \\\n"
2118 " [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
2120 " %1$s %2$s profile PROG [duration DURATION] METRICs\n"
2121 " %1$s %2$s tracelog\n"
2124 " " HELP_SPEC_MAP
"\n"
2125 " " HELP_SPEC_PROGRAM
"\n"
2126 " TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
2127 " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
2128 " cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
2129 " lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
2130 " sk_reuseport | flow_dissector | cgroup/sysctl |\n"
2131 " cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
2132 " cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
2133 " cgroup/getpeername4 | cgroup/getpeername6 |\n"
2134 " cgroup/getsockname4 | cgroup/getsockname6 | cgroup/sendmsg4 |\n"
2135 " cgroup/sendmsg6 | cgroup/recvmsg4 | cgroup/recvmsg6 |\n"
2136 " cgroup/getsockopt | cgroup/setsockopt |\n"
2137 " struct_ops | fentry | fexit | freplace | sk_lookup }\n"
2138 " ATTACH_TYPE := { msg_verdict | stream_verdict | stream_parser |\n"
2139 " flow_dissector }\n"
2140 " METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
2141 " " HELP_SPEC_OPTIONS
"\n"
2143 bin_name
, argv
[-2]);
2148 static const struct cmd cmds
[] = {
2149 { "show", do_show
},
2150 { "list", do_show
},
2151 { "help", do_help
},
2152 { "dump", do_dump
},
2154 { "load", do_load
},
2155 { "loadall", do_loadall
},
2156 { "attach", do_attach
},
2157 { "detach", do_detach
},
2158 { "tracelog", do_tracelog
},
2160 { "profile", do_profile
},
2164 int do_prog(int argc
, char **argv
)
2166 return cmd_select(cmds
, argc
, argv
, do_help
);