102ff58dcSJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 202ff58dcSJakub Kicinski /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ 371bb428fSJakub Kicinski 43ff5a4dcSJakub Kicinski #define _GNU_SOURCE 571bb428fSJakub Kicinski #include <errno.h> 671bb428fSJakub Kicinski #include <fcntl.h> 747c09d6aSSong Liu #include <signal.h> 8c9c35995SJakub Kicinski #include <stdarg.h> 971bb428fSJakub Kicinski #include <stdio.h> 1071bb428fSJakub Kicinski #include <stdlib.h> 1171bb428fSJakub Kicinski #include <string.h> 1271bb428fSJakub Kicinski #include <time.h> 1371bb428fSJakub Kicinski #include <unistd.h> 14ba6dd679SJakub Kicinski #include <net/if.h> 1547c09d6aSSong Liu #include <sys/ioctl.h> 1671bb428fSJakub Kicinski #include <sys/types.h> 1771bb428fSJakub Kicinski #include <sys/stat.h> 1847c09d6aSSong Liu #include <sys/syscall.h> 1971bb428fSJakub Kicinski 20c8406848SJakub Kicinski #include <linux/err.h> 2147c09d6aSSong Liu #include <linux/perf_event.h> 22ba95c745SQuentin Monnet #include <linux/sizes.h> 23c8406848SJakub Kicinski 24229c3b47SToke Høiland-Jørgensen #include <bpf/bpf.h> 25229c3b47SToke Høiland-Jørgensen #include <bpf/btf.h> 26229c3b47SToke Høiland-Jørgensen #include <bpf/libbpf.h> 2771bb428fSJakub Kicinski 28b6c1cedbSJiong Wang #include "cfg.h" 2971bb428fSJakub Kicinski #include "main.h" 3073bb5b4fSJiong Wang #include "xlated_dumper.h" 3171bb428fSJakub Kicinski 329023497dSTobias Klauser const char * const prog_type_name[] = { 339023497dSTobias Klauser [BPF_PROG_TYPE_UNSPEC] = "unspec", 349023497dSTobias Klauser [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", 359023497dSTobias Klauser [BPF_PROG_TYPE_KPROBE] = "kprobe", 369023497dSTobias Klauser [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", 379023497dSTobias Klauser [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", 389023497dSTobias Klauser [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", 399023497dSTobias Klauser [BPF_PROG_TYPE_XDP] = "xdp", 409023497dSTobias Klauser [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", 419023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", 429023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", 439023497dSTobias Klauser [BPF_PROG_TYPE_LWT_IN] = "lwt_in", 449023497dSTobias Klauser [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", 459023497dSTobias Klauser [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", 469023497dSTobias Klauser [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", 479023497dSTobias Klauser [BPF_PROG_TYPE_SK_SKB] = "sk_skb", 489023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device", 499023497dSTobias Klauser [BPF_PROG_TYPE_SK_MSG] = "sk_msg", 509023497dSTobias Klauser [BPF_PROG_TYPE_RAW_TRACEPOINT] = "raw_tracepoint", 519023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr", 529023497dSTobias Klauser [BPF_PROG_TYPE_LWT_SEG6LOCAL] = "lwt_seg6local", 539023497dSTobias Klauser [BPF_PROG_TYPE_LIRC_MODE2] = "lirc_mode2", 549023497dSTobias Klauser [BPF_PROG_TYPE_SK_REUSEPORT] = "sk_reuseport", 559023497dSTobias Klauser [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector", 569023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SYSCTL] = "cgroup_sysctl", 579023497dSTobias Klauser [BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE] = "raw_tracepoint_writable", 589023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SOCKOPT] = "cgroup_sockopt", 599023497dSTobias Klauser [BPF_PROG_TYPE_TRACING] = "tracing", 609023497dSTobias Klauser [BPF_PROG_TYPE_STRUCT_OPS] = "struct_ops", 619023497dSTobias Klauser [BPF_PROG_TYPE_EXT] = "ext", 629a97c9d2SQuentin Monnet [BPF_PROG_TYPE_LSM] = "lsm", 6393a3545dSJakub Sitnicki [BPF_PROG_TYPE_SK_LOOKUP] = "sk_lookup", 649023497dSTobias Klauser }; 659023497dSTobias Klauser 669023497dSTobias Klauser const size_t prog_type_name_size = ARRAY_SIZE(prog_type_name); 679023497dSTobias Klauser 68ec202509SPaul Chaignon enum dump_mode { 69ec202509SPaul Chaignon DUMP_JITED, 70ec202509SPaul Chaignon DUMP_XLATED, 71ec202509SPaul Chaignon }; 72ec202509SPaul Chaignon 73b7d3826cSJohn Fastabend static const char * const attach_type_strings[] = { 74b7d3826cSJohn Fastabend [BPF_SK_SKB_STREAM_PARSER] = "stream_parser", 75b7d3826cSJohn Fastabend [BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict", 76b7d3826cSJohn Fastabend [BPF_SK_MSG_VERDICT] = "msg_verdict", 77092f0892SStanislav Fomichev [BPF_FLOW_DISSECTOR] = "flow_dissector", 78b7d3826cSJohn Fastabend [__MAX_BPF_ATTACH_TYPE] = NULL, 79b7d3826cSJohn Fastabend }; 80b7d3826cSJohn Fastabend 81c101189bSQuentin Monnet static enum bpf_attach_type parse_attach_type(const char *str) 82b7d3826cSJohn Fastabend { 83b7d3826cSJohn Fastabend enum bpf_attach_type type; 84b7d3826cSJohn Fastabend 85b7d3826cSJohn Fastabend for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { 86b7d3826cSJohn Fastabend if (attach_type_strings[type] && 87b7d3826cSJohn Fastabend is_prefix(str, attach_type_strings[type])) 88b7d3826cSJohn Fastabend return type; 89b7d3826cSJohn Fastabend } 90b7d3826cSJohn Fastabend 91b7d3826cSJohn Fastabend return __MAX_BPF_ATTACH_TYPE; 92b7d3826cSJohn Fastabend } 93b7d3826cSJohn Fastabend 9471bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) 9571bb428fSJakub Kicinski { 9671bb428fSJakub Kicinski struct timespec real_time_ts, boot_time_ts; 9771bb428fSJakub Kicinski time_t wallclock_secs; 9871bb428fSJakub Kicinski struct tm load_tm; 9971bb428fSJakub Kicinski 10071bb428fSJakub Kicinski buf[--size] = '\0'; 10171bb428fSJakub Kicinski 10271bb428fSJakub Kicinski if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || 10371bb428fSJakub Kicinski clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { 10471bb428fSJakub Kicinski perror("Can't read clocks"); 10571bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 10671bb428fSJakub Kicinski return; 10771bb428fSJakub Kicinski } 10871bb428fSJakub Kicinski 10971bb428fSJakub Kicinski wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + 11007480cbcSJakub Kicinski (real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) / 11107480cbcSJakub Kicinski 1000000000; 11207480cbcSJakub Kicinski 11371bb428fSJakub Kicinski 11471bb428fSJakub Kicinski if (!localtime_r(&wallclock_secs, &load_tm)) { 11571bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 11671bb428fSJakub Kicinski return; 11771bb428fSJakub Kicinski } 11871bb428fSJakub Kicinski 119a3fe1f6fSQuentin Monnet if (json_output) 120a3fe1f6fSQuentin Monnet strftime(buf, size, "%s", &load_tm); 121a3fe1f6fSQuentin Monnet else 122a3fe1f6fSQuentin Monnet strftime(buf, size, "%FT%T%z", &load_tm); 12371bb428fSJakub Kicinski } 12471bb428fSJakub Kicinski 1256e7e034eSQuentin Monnet static void show_prog_maps(int fd, __u32 num_maps) 12671bb428fSJakub Kicinski { 12771bb428fSJakub Kicinski struct bpf_prog_info info = {}; 12871bb428fSJakub Kicinski __u32 len = sizeof(info); 12971bb428fSJakub Kicinski __u32 map_ids[num_maps]; 13071bb428fSJakub Kicinski unsigned int i; 13171bb428fSJakub Kicinski int err; 13271bb428fSJakub Kicinski 13371bb428fSJakub Kicinski info.nr_map_ids = num_maps; 13471bb428fSJakub Kicinski info.map_ids = ptr_to_u64(map_ids); 13571bb428fSJakub Kicinski 13671bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 13771bb428fSJakub Kicinski if (err || !info.nr_map_ids) 13871bb428fSJakub Kicinski return; 13971bb428fSJakub Kicinski 140743cc665SQuentin Monnet if (json_output) { 141743cc665SQuentin Monnet jsonw_name(json_wtr, "map_ids"); 142743cc665SQuentin Monnet jsonw_start_array(json_wtr); 143743cc665SQuentin Monnet for (i = 0; i < info.nr_map_ids; i++) 144743cc665SQuentin Monnet jsonw_uint(json_wtr, map_ids[i]); 145743cc665SQuentin Monnet jsonw_end_array(json_wtr); 146743cc665SQuentin Monnet } else { 14771bb428fSJakub Kicinski printf(" map_ids "); 14871bb428fSJakub Kicinski for (i = 0; i < info.nr_map_ids; i++) 14971bb428fSJakub Kicinski printf("%u%s", map_ids[i], 15071bb428fSJakub Kicinski i == info.nr_map_ids - 1 ? "" : ","); 15171bb428fSJakub Kicinski } 15271bb428fSJakub Kicinski } 15371bb428fSJakub Kicinski 154ec202509SPaul Chaignon static void print_prog_header_json(struct bpf_prog_info *info) 155743cc665SQuentin Monnet { 156743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "id", info->id); 157743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 158743cc665SQuentin Monnet jsonw_string_field(json_wtr, "type", 159743cc665SQuentin Monnet prog_type_name[info->type]); 16071bb428fSJakub Kicinski else 161743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "type", info->type); 16271bb428fSJakub Kicinski 163743cc665SQuentin Monnet if (*info->name) 164743cc665SQuentin Monnet jsonw_string_field(json_wtr, "name", info->name); 16571bb428fSJakub Kicinski 166743cc665SQuentin Monnet jsonw_name(json_wtr, "tag"); 167743cc665SQuentin Monnet jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", 168743cc665SQuentin Monnet info->tag[0], info->tag[1], info->tag[2], info->tag[3], 169743cc665SQuentin Monnet info->tag[4], info->tag[5], info->tag[6], info->tag[7]); 17071bb428fSJakub Kicinski 1719b984a20SJiri Olsa jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible); 17288ad472bSAlexei Starovoitov if (info->run_time_ns) { 17388ad472bSAlexei Starovoitov jsonw_uint_field(json_wtr, "run_time_ns", info->run_time_ns); 17488ad472bSAlexei Starovoitov jsonw_uint_field(json_wtr, "run_cnt", info->run_cnt); 17588ad472bSAlexei Starovoitov } 176ec202509SPaul Chaignon } 1779b984a20SJiri Olsa 178ec202509SPaul Chaignon static void print_prog_json(struct bpf_prog_info *info, int fd) 179ec202509SPaul Chaignon { 180ec202509SPaul Chaignon char *memlock; 181ec202509SPaul Chaignon 182ec202509SPaul Chaignon jsonw_start_object(json_wtr); 183ec202509SPaul Chaignon print_prog_header_json(info); 18452262210SJakub Kicinski print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); 18552262210SJakub Kicinski 186743cc665SQuentin Monnet if (info->load_time) { 18771bb428fSJakub Kicinski char buf[32]; 18871bb428fSJakub Kicinski 189743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 19071bb428fSJakub Kicinski 19171bb428fSJakub Kicinski /* Piggy back on load_time, since 0 uid is a valid one */ 192a3fe1f6fSQuentin Monnet jsonw_name(json_wtr, "loaded_at"); 193a3fe1f6fSQuentin Monnet jsonw_printf(json_wtr, "%s", buf); 194743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "uid", info->created_by_uid); 19571bb428fSJakub Kicinski } 19671bb428fSJakub Kicinski 197743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); 19871bb428fSJakub Kicinski 199743cc665SQuentin Monnet if (info->jited_prog_len) { 200743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", true); 201743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); 202743cc665SQuentin Monnet } else { 203743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", false); 204743cc665SQuentin Monnet } 205743cc665SQuentin Monnet 206743cc665SQuentin Monnet memlock = get_fdinfo(fd, "memlock"); 207743cc665SQuentin Monnet if (memlock) 208743cc665SQuentin Monnet jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 209743cc665SQuentin Monnet free(memlock); 210743cc665SQuentin Monnet 211743cc665SQuentin Monnet if (info->nr_map_ids) 212743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 213743cc665SQuentin Monnet 214569b0c77SPrashant Bhole if (info->btf_id) 215569b0c77SPrashant Bhole jsonw_int_field(json_wtr, "btf_id", info->btf_id); 216569b0c77SPrashant Bhole 2174990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 2184990f1f4SPrashant Bhole struct pinned_obj *obj; 2194990f1f4SPrashant Bhole 2204990f1f4SPrashant Bhole jsonw_name(json_wtr, "pinned"); 2214990f1f4SPrashant Bhole jsonw_start_array(json_wtr); 2224990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 2234990f1f4SPrashant Bhole if (obj->id == info->id) 2244990f1f4SPrashant Bhole jsonw_string(json_wtr, obj->path); 2254990f1f4SPrashant Bhole } 2264990f1f4SPrashant Bhole jsonw_end_array(json_wtr); 2274990f1f4SPrashant Bhole } 2284990f1f4SPrashant Bhole 229d53dee3fSAndrii Nakryiko emit_obj_refs_json(&refs_table, info->id, json_wtr); 230d53dee3fSAndrii Nakryiko 231743cc665SQuentin Monnet jsonw_end_object(json_wtr); 232743cc665SQuentin Monnet } 233743cc665SQuentin Monnet 234ec202509SPaul Chaignon static void print_prog_header_plain(struct bpf_prog_info *info) 235743cc665SQuentin Monnet { 236743cc665SQuentin Monnet printf("%u: ", info->id); 237743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 238743cc665SQuentin Monnet printf("%s ", prog_type_name[info->type]); 239743cc665SQuentin Monnet else 240743cc665SQuentin Monnet printf("type %u ", info->type); 241743cc665SQuentin Monnet 242743cc665SQuentin Monnet if (*info->name) 243743cc665SQuentin Monnet printf("name %s ", info->name); 244743cc665SQuentin Monnet 245743cc665SQuentin Monnet printf("tag "); 246743cc665SQuentin Monnet fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); 24752262210SJakub Kicinski print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); 2489b984a20SJiri Olsa printf("%s", info->gpl_compatible ? " gpl" : ""); 24988ad472bSAlexei Starovoitov if (info->run_time_ns) 25088ad472bSAlexei Starovoitov printf(" run_time_ns %lld run_cnt %lld", 25188ad472bSAlexei Starovoitov info->run_time_ns, info->run_cnt); 252743cc665SQuentin Monnet printf("\n"); 253ec202509SPaul Chaignon } 254ec202509SPaul Chaignon 255ec202509SPaul Chaignon static void print_prog_plain(struct bpf_prog_info *info, int fd) 256ec202509SPaul Chaignon { 257ec202509SPaul Chaignon char *memlock; 258ec202509SPaul Chaignon 259ec202509SPaul Chaignon print_prog_header_plain(info); 260743cc665SQuentin Monnet 261743cc665SQuentin Monnet if (info->load_time) { 262743cc665SQuentin Monnet char buf[32]; 263743cc665SQuentin Monnet 264743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 265743cc665SQuentin Monnet 266743cc665SQuentin Monnet /* Piggy back on load_time, since 0 uid is a valid one */ 267743cc665SQuentin Monnet printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); 268743cc665SQuentin Monnet } 269743cc665SQuentin Monnet 270743cc665SQuentin Monnet printf("\txlated %uB", info->xlated_prog_len); 271743cc665SQuentin Monnet 272743cc665SQuentin Monnet if (info->jited_prog_len) 273743cc665SQuentin Monnet printf(" jited %uB", info->jited_prog_len); 27471bb428fSJakub Kicinski else 27571bb428fSJakub Kicinski printf(" not jited"); 27671bb428fSJakub Kicinski 27771bb428fSJakub Kicinski memlock = get_fdinfo(fd, "memlock"); 27871bb428fSJakub Kicinski if (memlock) 27971bb428fSJakub Kicinski printf(" memlock %sB", memlock); 28071bb428fSJakub Kicinski free(memlock); 28171bb428fSJakub Kicinski 282743cc665SQuentin Monnet if (info->nr_map_ids) 283743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 28471bb428fSJakub Kicinski 2854990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 2864990f1f4SPrashant Bhole struct pinned_obj *obj; 2874990f1f4SPrashant Bhole 2884990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 2894990f1f4SPrashant Bhole if (obj->id == info->id) 290a8bfd2bcSQuentin Monnet printf("\n\tpinned %s", obj->path); 2914990f1f4SPrashant Bhole } 2924990f1f4SPrashant Bhole } 2934990f1f4SPrashant Bhole 294569b0c77SPrashant Bhole if (info->btf_id) 295031ebc1aSQuentin Monnet printf("\n\tbtf_id %d", info->btf_id); 296569b0c77SPrashant Bhole 297d53dee3fSAndrii Nakryiko emit_obj_refs_plain(&refs_table, info->id, "\n\tpids "); 298d53dee3fSAndrii Nakryiko 29971bb428fSJakub Kicinski printf("\n"); 300743cc665SQuentin Monnet } 301743cc665SQuentin Monnet 302743cc665SQuentin Monnet static int show_prog(int fd) 303743cc665SQuentin Monnet { 304743cc665SQuentin Monnet struct bpf_prog_info info = {}; 305743cc665SQuentin Monnet __u32 len = sizeof(info); 306743cc665SQuentin Monnet int err; 307743cc665SQuentin Monnet 308743cc665SQuentin Monnet err = bpf_obj_get_info_by_fd(fd, &info, &len); 309743cc665SQuentin Monnet if (err) { 3109a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 311743cc665SQuentin Monnet return -1; 312743cc665SQuentin Monnet } 313743cc665SQuentin Monnet 314743cc665SQuentin Monnet if (json_output) 315743cc665SQuentin Monnet print_prog_json(&info, fd); 316743cc665SQuentin Monnet else 317743cc665SQuentin Monnet print_prog_plain(&info, fd); 31871bb428fSJakub Kicinski 31971bb428fSJakub Kicinski return 0; 32071bb428fSJakub Kicinski } 32171bb428fSJakub Kicinski 322ec202509SPaul Chaignon static int do_show_subset(int argc, char **argv) 323ec202509SPaul Chaignon { 324ec202509SPaul Chaignon int *fds = NULL; 325ec202509SPaul Chaignon int nb_fds, i; 326ec202509SPaul Chaignon int err = -1; 327ec202509SPaul Chaignon 328ec202509SPaul Chaignon fds = malloc(sizeof(int)); 329ec202509SPaul Chaignon if (!fds) { 330ec202509SPaul Chaignon p_err("mem alloc failed"); 331ec202509SPaul Chaignon return -1; 332ec202509SPaul Chaignon } 333ec202509SPaul Chaignon nb_fds = prog_parse_fds(&argc, &argv, &fds); 334ec202509SPaul Chaignon if (nb_fds < 1) 335ec202509SPaul Chaignon goto exit_free; 336ec202509SPaul Chaignon 337ec202509SPaul Chaignon if (json_output && nb_fds > 1) 338ec202509SPaul Chaignon jsonw_start_array(json_wtr); /* root array */ 339ec202509SPaul Chaignon for (i = 0; i < nb_fds; i++) { 340ec202509SPaul Chaignon err = show_prog(fds[i]); 341ec202509SPaul Chaignon if (err) { 342ec202509SPaul Chaignon for (; i < nb_fds; i++) 343ec202509SPaul Chaignon close(fds[i]); 344ec202509SPaul Chaignon break; 345ec202509SPaul Chaignon } 346ec202509SPaul Chaignon close(fds[i]); 347ec202509SPaul Chaignon } 348ec202509SPaul Chaignon if (json_output && nb_fds > 1) 349ec202509SPaul Chaignon jsonw_end_array(json_wtr); /* root array */ 350ec202509SPaul Chaignon 351ec202509SPaul Chaignon exit_free: 352ec202509SPaul Chaignon free(fds); 353ec202509SPaul Chaignon return err; 354ec202509SPaul Chaignon } 355ec202509SPaul Chaignon 35671bb428fSJakub Kicinski static int do_show(int argc, char **argv) 357743cc665SQuentin Monnet { 358743cc665SQuentin Monnet __u32 id = 0; 35971bb428fSJakub Kicinski int err; 36071bb428fSJakub Kicinski int fd; 36171bb428fSJakub Kicinski 362c541b734SPrashant Bhole if (show_pinned) 3634990f1f4SPrashant Bhole build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); 364d53dee3fSAndrii Nakryiko build_obj_refs_table(&refs_table, BPF_OBJ_PROG); 3654990f1f4SPrashant Bhole 366ec202509SPaul Chaignon if (argc == 2) 367ec202509SPaul Chaignon return do_show_subset(argc, argv); 36871bb428fSJakub Kicinski 36971bb428fSJakub Kicinski if (argc) 37071bb428fSJakub Kicinski return BAD_ARG(); 37171bb428fSJakub Kicinski 372743cc665SQuentin Monnet if (json_output) 373743cc665SQuentin Monnet jsonw_start_array(json_wtr); 37471bb428fSJakub Kicinski while (true) { 37571bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 37671bb428fSJakub Kicinski if (err) { 3771739c26dSQuentin Monnet if (errno == ENOENT) { 3781739c26dSQuentin Monnet err = 0; 37971bb428fSJakub Kicinski break; 3801739c26dSQuentin Monnet } 3819a5ab8bfSQuentin Monnet p_err("can't get next program: %s%s", strerror(errno), 3829a5ab8bfSQuentin Monnet errno == EINVAL ? " -- kernel too old?" : ""); 383743cc665SQuentin Monnet err = -1; 384743cc665SQuentin Monnet break; 38571bb428fSJakub Kicinski } 38671bb428fSJakub Kicinski 38771bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 38871bb428fSJakub Kicinski if (fd < 0) { 3898207c6ddSJakub Kicinski if (errno == ENOENT) 3908207c6ddSJakub Kicinski continue; 3919a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 39271bb428fSJakub Kicinski id, strerror(errno)); 393743cc665SQuentin Monnet err = -1; 394743cc665SQuentin Monnet break; 39571bb428fSJakub Kicinski } 39671bb428fSJakub Kicinski 39771bb428fSJakub Kicinski err = show_prog(fd); 39871bb428fSJakub Kicinski close(fd); 39971bb428fSJakub Kicinski if (err) 400743cc665SQuentin Monnet break; 40171bb428fSJakub Kicinski } 40271bb428fSJakub Kicinski 403743cc665SQuentin Monnet if (json_output) 404743cc665SQuentin Monnet jsonw_end_array(json_wtr); 405743cc665SQuentin Monnet 406d53dee3fSAndrii Nakryiko delete_obj_refs_table(&refs_table); 407d53dee3fSAndrii Nakryiko 408743cc665SQuentin Monnet return err; 40971bb428fSJakub Kicinski } 41071bb428fSJakub Kicinski 411ec202509SPaul Chaignon static int 412ec202509SPaul Chaignon prog_dump(struct bpf_prog_info *info, enum dump_mode mode, 413ec202509SPaul Chaignon char *filepath, bool opcodes, bool visual, bool linum) 41471bb428fSJakub Kicinski { 415b053b439SMartin KaFai Lau struct bpf_prog_linfo *prog_linfo = NULL; 4163ddeac67SJakub Kicinski const char *disasm_opt = NULL; 4177105e828SDaniel Borkmann struct dump_data dd = {}; 418cae73f23SSong Liu void *func_info = NULL; 419254471e5SYonghong Song struct btf *btf = NULL; 420254471e5SYonghong Song char func_sig[1024]; 42171bb428fSJakub Kicinski unsigned char *buf; 422cae73f23SSong Liu __u32 member_len; 42371bb428fSJakub Kicinski ssize_t n; 42471bb428fSJakub Kicinski int fd; 42571bb428fSJakub Kicinski 426cae73f23SSong Liu if (mode == DUMP_JITED) { 4275b79bcdfSToke Høiland-Jørgensen if (info->jited_prog_len == 0 || !info->jited_prog_insns) { 4289a5ab8bfSQuentin Monnet p_info("no instructions returned"); 429ec202509SPaul Chaignon return -1; 430f84192eeSSandipan Das } 43109f44b75SAndrii Nakryiko buf = u64_to_ptr(info->jited_prog_insns); 432cae73f23SSong Liu member_len = info->jited_prog_len; 433cae73f23SSong Liu } else { /* DUMP_XLATED */ 434d95f1e8bSToke Høiland-Jørgensen if (info->xlated_prog_len == 0 || !info->xlated_prog_insns) { 4357105e828SDaniel Borkmann p_err("error retrieving insn dump: kernel.kptr_restrict set?"); 436ec202509SPaul Chaignon return -1; 4377105e828SDaniel Borkmann } 43809f44b75SAndrii Nakryiko buf = u64_to_ptr(info->xlated_prog_insns); 439cae73f23SSong Liu member_len = info->xlated_prog_len; 440cae73f23SSong Liu } 4417105e828SDaniel Borkmann 442cae73f23SSong Liu if (info->btf_id && btf__get_from_id(info->btf_id, &btf)) { 443254471e5SYonghong Song p_err("failed to get btf"); 444ec202509SPaul Chaignon return -1; 445254471e5SYonghong Song } 446254471e5SYonghong Song 44709f44b75SAndrii Nakryiko func_info = u64_to_ptr(info->func_info); 448cae73f23SSong Liu 449cae73f23SSong Liu if (info->nr_line_info) { 450cae73f23SSong Liu prog_linfo = bpf_prog_linfo__new(info); 451b053b439SMartin KaFai Lau if (!prog_linfo) 45210a5ce98SMartin KaFai Lau p_info("error in processing bpf_line_info. continue without it."); 453b053b439SMartin KaFai Lau } 454b053b439SMartin KaFai Lau 45571bb428fSJakub Kicinski if (filepath) { 45671bb428fSJakub Kicinski fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 45771bb428fSJakub Kicinski if (fd < 0) { 4589a5ab8bfSQuentin Monnet p_err("can't open file %s: %s", filepath, 45971bb428fSJakub Kicinski strerror(errno)); 460ec202509SPaul Chaignon return -1; 46171bb428fSJakub Kicinski } 46271bb428fSJakub Kicinski 463cae73f23SSong Liu n = write(fd, buf, member_len); 46471bb428fSJakub Kicinski close(fd); 46509f44b75SAndrii Nakryiko if (n != (ssize_t)member_len) { 4669a5ab8bfSQuentin Monnet p_err("error writing output file: %s", 46771bb428fSJakub Kicinski n < 0 ? strerror(errno) : "short write"); 468ec202509SPaul Chaignon return -1; 46971bb428fSJakub Kicinski } 47052c84d36SQuentin Monnet 47152c84d36SQuentin Monnet if (json_output) 47252c84d36SQuentin Monnet jsonw_null(json_wtr); 473cae73f23SSong Liu } else if (mode == DUMP_JITED) { 474e6593596SJiong Wang const char *name = NULL; 475e6593596SJiong Wang 476cae73f23SSong Liu if (info->ifindex) { 477cae73f23SSong Liu name = ifindex_to_bfd_params(info->ifindex, 478cae73f23SSong Liu info->netns_dev, 479cae73f23SSong Liu info->netns_ino, 4803ddeac67SJakub Kicinski &disasm_opt); 481e6593596SJiong Wang if (!name) 482ec202509SPaul Chaignon return -1; 483e6593596SJiong Wang } 484e6593596SJiong Wang 485cae73f23SSong Liu if (info->nr_jited_func_lens && info->jited_func_lens) { 486f7f62c71SSandipan Das struct kernel_sym *sym = NULL; 487254471e5SYonghong Song struct bpf_func_info *record; 488f7f62c71SSandipan Das char sym_name[SYM_MAX_NAME]; 489f7f62c71SSandipan Das unsigned char *img = buf; 490f7f62c71SSandipan Das __u64 *ksyms = NULL; 491f7f62c71SSandipan Das __u32 *lens; 492f7f62c71SSandipan Das __u32 i; 493cae73f23SSong Liu if (info->nr_jited_ksyms) { 494f7f62c71SSandipan Das kernel_syms_load(&dd); 49509f44b75SAndrii Nakryiko ksyms = u64_to_ptr(info->jited_ksyms); 496f7f62c71SSandipan Das } 497f7f62c71SSandipan Das 498f7f62c71SSandipan Das if (json_output) 499f7f62c71SSandipan Das jsonw_start_array(json_wtr); 500f7f62c71SSandipan Das 50109f44b75SAndrii Nakryiko lens = u64_to_ptr(info->jited_func_lens); 502cae73f23SSong Liu for (i = 0; i < info->nr_jited_func_lens; i++) { 503f7f62c71SSandipan Das if (ksyms) { 504f7f62c71SSandipan Das sym = kernel_syms_search(&dd, ksyms[i]); 505f7f62c71SSandipan Das if (sym) 506f7f62c71SSandipan Das sprintf(sym_name, "%s", sym->name); 507f7f62c71SSandipan Das else 508f7f62c71SSandipan Das sprintf(sym_name, "0x%016llx", ksyms[i]); 509f7f62c71SSandipan Das } else { 510f7f62c71SSandipan Das strcpy(sym_name, "unknown"); 511f7f62c71SSandipan Das } 512f7f62c71SSandipan Das 513254471e5SYonghong Song if (func_info) { 514cae73f23SSong Liu record = func_info + i * info->func_info_rec_size; 515254471e5SYonghong Song btf_dumper_type_only(btf, record->type_id, 516254471e5SYonghong Song func_sig, 517254471e5SYonghong Song sizeof(func_sig)); 518254471e5SYonghong Song } 519254471e5SYonghong Song 520f7f62c71SSandipan Das if (json_output) { 521f7f62c71SSandipan Das jsonw_start_object(json_wtr); 522254471e5SYonghong Song if (func_info && func_sig[0] != '\0') { 523254471e5SYonghong Song jsonw_name(json_wtr, "proto"); 524254471e5SYonghong Song jsonw_string(json_wtr, func_sig); 525254471e5SYonghong Song } 526f7f62c71SSandipan Das jsonw_name(json_wtr, "name"); 527f7f62c71SSandipan Das jsonw_string(json_wtr, sym_name); 528f7f62c71SSandipan Das jsonw_name(json_wtr, "insns"); 529f7f62c71SSandipan Das } else { 530254471e5SYonghong Song if (func_info && func_sig[0] != '\0') 531254471e5SYonghong Song printf("%s:\n", func_sig); 532f7f62c71SSandipan Das printf("%s:\n", sym_name); 533f7f62c71SSandipan Das } 534f7f62c71SSandipan Das 535b053b439SMartin KaFai Lau disasm_print_insn(img, lens[i], opcodes, 536b053b439SMartin KaFai Lau name, disasm_opt, btf, 537b053b439SMartin KaFai Lau prog_linfo, ksyms[i], i, 538b053b439SMartin KaFai Lau linum); 539b053b439SMartin KaFai Lau 540f7f62c71SSandipan Das img += lens[i]; 541f7f62c71SSandipan Das 542f7f62c71SSandipan Das if (json_output) 543f7f62c71SSandipan Das jsonw_end_object(json_wtr); 544f7f62c71SSandipan Das else 545f7f62c71SSandipan Das printf("\n"); 546f7f62c71SSandipan Das } 547f7f62c71SSandipan Das 548f7f62c71SSandipan Das if (json_output) 549f7f62c71SSandipan Das jsonw_end_array(json_wtr); 550f7f62c71SSandipan Das } else { 551cae73f23SSong Liu disasm_print_insn(buf, member_len, opcodes, name, 552b053b439SMartin KaFai Lau disasm_opt, btf, NULL, 0, 0, false); 553f7f62c71SSandipan Das } 554b6c1cedbSJiong Wang } else if (visual) { 555b6c1cedbSJiong Wang if (json_output) 556b6c1cedbSJiong Wang jsonw_null(json_wtr); 557b6c1cedbSJiong Wang else 558cae73f23SSong Liu dump_xlated_cfg(buf, member_len); 5597105e828SDaniel Borkmann } else { 5607105e828SDaniel Borkmann kernel_syms_load(&dd); 561cae73f23SSong Liu dd.nr_jited_ksyms = info->nr_jited_ksyms; 56209f44b75SAndrii Nakryiko dd.jited_ksyms = u64_to_ptr(info->jited_ksyms); 563254471e5SYonghong Song dd.btf = btf; 564254471e5SYonghong Song dd.func_info = func_info; 565cae73f23SSong Liu dd.finfo_rec_size = info->func_info_rec_size; 566b053b439SMartin KaFai Lau dd.prog_linfo = prog_linfo; 567f84192eeSSandipan Das 568f05e2c32SQuentin Monnet if (json_output) 569cae73f23SSong Liu dump_xlated_json(&dd, buf, member_len, opcodes, 570b053b439SMartin KaFai Lau linum); 571f05e2c32SQuentin Monnet else 572cae73f23SSong Liu dump_xlated_plain(&dd, buf, member_len, opcodes, 573b053b439SMartin KaFai Lau linum); 5747105e828SDaniel Borkmann kernel_syms_destroy(&dd); 5757105e828SDaniel Borkmann } 57671bb428fSJakub Kicinski 57771bb428fSJakub Kicinski return 0; 578ec202509SPaul Chaignon } 57971bb428fSJakub Kicinski 580ec202509SPaul Chaignon static int do_dump(int argc, char **argv) 581ec202509SPaul Chaignon { 582ec202509SPaul Chaignon struct bpf_prog_info_linear *info_linear; 583ec202509SPaul Chaignon char *filepath = NULL; 584ec202509SPaul Chaignon bool opcodes = false; 585ec202509SPaul Chaignon bool visual = false; 586ec202509SPaul Chaignon enum dump_mode mode; 587ec202509SPaul Chaignon bool linum = false; 588ec202509SPaul Chaignon int *fds = NULL; 589ec202509SPaul Chaignon int nb_fds, i = 0; 590ec202509SPaul Chaignon int err = -1; 591ec202509SPaul Chaignon __u64 arrays; 592ec202509SPaul Chaignon 593ec202509SPaul Chaignon if (is_prefix(*argv, "jited")) { 594ec202509SPaul Chaignon if (disasm_init()) 59571bb428fSJakub Kicinski return -1; 596ec202509SPaul Chaignon mode = DUMP_JITED; 597ec202509SPaul Chaignon } else if (is_prefix(*argv, "xlated")) { 598ec202509SPaul Chaignon mode = DUMP_XLATED; 599ec202509SPaul Chaignon } else { 600ec202509SPaul Chaignon p_err("expected 'xlated' or 'jited', got: %s", *argv); 601ec202509SPaul Chaignon return -1; 602ec202509SPaul Chaignon } 603ec202509SPaul Chaignon NEXT_ARG(); 604ec202509SPaul Chaignon 605ec202509SPaul Chaignon if (argc < 2) 606ec202509SPaul Chaignon usage(); 607ec202509SPaul Chaignon 608ec202509SPaul Chaignon fds = malloc(sizeof(int)); 609ec202509SPaul Chaignon if (!fds) { 610ec202509SPaul Chaignon p_err("mem alloc failed"); 611ec202509SPaul Chaignon return -1; 612ec202509SPaul Chaignon } 613ec202509SPaul Chaignon nb_fds = prog_parse_fds(&argc, &argv, &fds); 614ec202509SPaul Chaignon if (nb_fds < 1) 615ec202509SPaul Chaignon goto exit_free; 616ec202509SPaul Chaignon 617ec202509SPaul Chaignon if (is_prefix(*argv, "file")) { 618ec202509SPaul Chaignon NEXT_ARG(); 619ec202509SPaul Chaignon if (!argc) { 620ec202509SPaul Chaignon p_err("expected file path"); 621ec202509SPaul Chaignon goto exit_close; 622ec202509SPaul Chaignon } 623ec202509SPaul Chaignon if (nb_fds > 1) { 624ec202509SPaul Chaignon p_err("several programs matched"); 625ec202509SPaul Chaignon goto exit_close; 626ec202509SPaul Chaignon } 627ec202509SPaul Chaignon 628ec202509SPaul Chaignon filepath = *argv; 629ec202509SPaul Chaignon NEXT_ARG(); 630ec202509SPaul Chaignon } else if (is_prefix(*argv, "opcodes")) { 631ec202509SPaul Chaignon opcodes = true; 632ec202509SPaul Chaignon NEXT_ARG(); 633ec202509SPaul Chaignon } else if (is_prefix(*argv, "visual")) { 634ec202509SPaul Chaignon if (nb_fds > 1) { 635ec202509SPaul Chaignon p_err("several programs matched"); 636ec202509SPaul Chaignon goto exit_close; 637ec202509SPaul Chaignon } 638ec202509SPaul Chaignon 639ec202509SPaul Chaignon visual = true; 640ec202509SPaul Chaignon NEXT_ARG(); 641ec202509SPaul Chaignon } else if (is_prefix(*argv, "linum")) { 642ec202509SPaul Chaignon linum = true; 643ec202509SPaul Chaignon NEXT_ARG(); 644ec202509SPaul Chaignon } 645ec202509SPaul Chaignon 646ec202509SPaul Chaignon if (argc) { 647ec202509SPaul Chaignon usage(); 648ec202509SPaul Chaignon goto exit_close; 649ec202509SPaul Chaignon } 650ec202509SPaul Chaignon 651ec202509SPaul Chaignon if (mode == DUMP_JITED) 652ec202509SPaul Chaignon arrays = 1UL << BPF_PROG_INFO_JITED_INSNS; 653ec202509SPaul Chaignon else 654ec202509SPaul Chaignon arrays = 1UL << BPF_PROG_INFO_XLATED_INSNS; 655ec202509SPaul Chaignon 656ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_JITED_KSYMS; 657ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; 658ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; 659ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; 660ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; 661ec202509SPaul Chaignon 662ec202509SPaul Chaignon if (json_output && nb_fds > 1) 663ec202509SPaul Chaignon jsonw_start_array(json_wtr); /* root array */ 664ec202509SPaul Chaignon for (i = 0; i < nb_fds; i++) { 665ec202509SPaul Chaignon info_linear = bpf_program__get_prog_info_linear(fds[i], arrays); 666ec202509SPaul Chaignon if (IS_ERR_OR_NULL(info_linear)) { 667ec202509SPaul Chaignon p_err("can't get prog info: %s", strerror(errno)); 668ec202509SPaul Chaignon break; 669ec202509SPaul Chaignon } 670ec202509SPaul Chaignon 671ec202509SPaul Chaignon if (json_output && nb_fds > 1) { 672ec202509SPaul Chaignon jsonw_start_object(json_wtr); /* prog object */ 673ec202509SPaul Chaignon print_prog_header_json(&info_linear->info); 674ec202509SPaul Chaignon jsonw_name(json_wtr, "insns"); 675ec202509SPaul Chaignon } else if (nb_fds > 1) { 676ec202509SPaul Chaignon print_prog_header_plain(&info_linear->info); 677ec202509SPaul Chaignon } 678ec202509SPaul Chaignon 679ec202509SPaul Chaignon err = prog_dump(&info_linear->info, mode, filepath, opcodes, 680ec202509SPaul Chaignon visual, linum); 681ec202509SPaul Chaignon 682ec202509SPaul Chaignon if (json_output && nb_fds > 1) 683ec202509SPaul Chaignon jsonw_end_object(json_wtr); /* prog object */ 684ec202509SPaul Chaignon else if (i != nb_fds - 1 && nb_fds > 1) 685ec202509SPaul Chaignon printf("\n"); 686ec202509SPaul Chaignon 687ec202509SPaul Chaignon free(info_linear); 688ec202509SPaul Chaignon if (err) 689ec202509SPaul Chaignon break; 690ec202509SPaul Chaignon close(fds[i]); 691ec202509SPaul Chaignon } 692ec202509SPaul Chaignon if (json_output && nb_fds > 1) 693ec202509SPaul Chaignon jsonw_end_array(json_wtr); /* root array */ 694ec202509SPaul Chaignon 695ec202509SPaul Chaignon exit_close: 696ec202509SPaul Chaignon for (; i < nb_fds; i++) 697ec202509SPaul Chaignon close(fds[i]); 698ec202509SPaul Chaignon exit_free: 699ec202509SPaul Chaignon free(fds); 700ec202509SPaul Chaignon return err; 70171bb428fSJakub Kicinski } 70271bb428fSJakub Kicinski 70371bb428fSJakub Kicinski static int do_pin(int argc, char **argv) 70471bb428fSJakub Kicinski { 705004b45c0SQuentin Monnet int err; 706004b45c0SQuentin Monnet 70775a1e792SQuentin Monnet err = do_pin_any(argc, argv, prog_parse_fd); 708004b45c0SQuentin Monnet if (!err && json_output) 709004b45c0SQuentin Monnet jsonw_null(json_wtr); 710004b45c0SQuentin Monnet return err; 71171bb428fSJakub Kicinski } 71271bb428fSJakub Kicinski 7133ff5a4dcSJakub Kicinski struct map_replace { 7143ff5a4dcSJakub Kicinski int idx; 7153ff5a4dcSJakub Kicinski int fd; 7163ff5a4dcSJakub Kicinski char *name; 7173ff5a4dcSJakub Kicinski }; 7183ff5a4dcSJakub Kicinski 719c101189bSQuentin Monnet static int map_replace_compar(const void *p1, const void *p2) 7203ff5a4dcSJakub Kicinski { 7213ff5a4dcSJakub Kicinski const struct map_replace *a = p1, *b = p2; 7223ff5a4dcSJakub Kicinski 7233ff5a4dcSJakub Kicinski return a->idx - b->idx; 7243ff5a4dcSJakub Kicinski } 7253ff5a4dcSJakub Kicinski 726092f0892SStanislav Fomichev static int parse_attach_detach_args(int argc, char **argv, int *progfd, 727092f0892SStanislav Fomichev enum bpf_attach_type *attach_type, 728092f0892SStanislav Fomichev int *mapfd) 729092f0892SStanislav Fomichev { 730092f0892SStanislav Fomichev if (!REQ_ARGS(3)) 731092f0892SStanislav Fomichev return -EINVAL; 732092f0892SStanislav Fomichev 733092f0892SStanislav Fomichev *progfd = prog_parse_fd(&argc, &argv); 734092f0892SStanislav Fomichev if (*progfd < 0) 735092f0892SStanislav Fomichev return *progfd; 736092f0892SStanislav Fomichev 737092f0892SStanislav Fomichev *attach_type = parse_attach_type(*argv); 738092f0892SStanislav Fomichev if (*attach_type == __MAX_BPF_ATTACH_TYPE) { 739092f0892SStanislav Fomichev p_err("invalid attach/detach type"); 740092f0892SStanislav Fomichev return -EINVAL; 741092f0892SStanislav Fomichev } 742092f0892SStanislav Fomichev 743092f0892SStanislav Fomichev if (*attach_type == BPF_FLOW_DISSECTOR) { 744092f0892SStanislav Fomichev *mapfd = -1; 745092f0892SStanislav Fomichev return 0; 746092f0892SStanislav Fomichev } 747092f0892SStanislav Fomichev 748092f0892SStanislav Fomichev NEXT_ARG(); 749092f0892SStanislav Fomichev if (!REQ_ARGS(2)) 750092f0892SStanislav Fomichev return -EINVAL; 751092f0892SStanislav Fomichev 752092f0892SStanislav Fomichev *mapfd = map_parse_fd(&argc, &argv); 753092f0892SStanislav Fomichev if (*mapfd < 0) 754092f0892SStanislav Fomichev return *mapfd; 755092f0892SStanislav Fomichev 756092f0892SStanislav Fomichev return 0; 757092f0892SStanislav Fomichev } 758092f0892SStanislav Fomichev 759b7d3826cSJohn Fastabend static int do_attach(int argc, char **argv) 760b7d3826cSJohn Fastabend { 761b7d3826cSJohn Fastabend enum bpf_attach_type attach_type; 762092f0892SStanislav Fomichev int err, progfd; 763092f0892SStanislav Fomichev int mapfd; 764b7d3826cSJohn Fastabend 765092f0892SStanislav Fomichev err = parse_attach_detach_args(argc, argv, 766092f0892SStanislav Fomichev &progfd, &attach_type, &mapfd); 767092f0892SStanislav Fomichev if (err) 768092f0892SStanislav Fomichev return err; 769b7d3826cSJohn Fastabend 770b7d3826cSJohn Fastabend err = bpf_prog_attach(progfd, mapfd, attach_type, 0); 771b7d3826cSJohn Fastabend if (err) { 772b7d3826cSJohn Fastabend p_err("failed prog attach to map"); 773b7d3826cSJohn Fastabend return -EINVAL; 774b7d3826cSJohn Fastabend } 775b7d3826cSJohn Fastabend 776b7d3826cSJohn Fastabend if (json_output) 777b7d3826cSJohn Fastabend jsonw_null(json_wtr); 778b7d3826cSJohn Fastabend return 0; 779b7d3826cSJohn Fastabend } 780b7d3826cSJohn Fastabend 781b7d3826cSJohn Fastabend static int do_detach(int argc, char **argv) 782b7d3826cSJohn Fastabend { 783b7d3826cSJohn Fastabend enum bpf_attach_type attach_type; 784092f0892SStanislav Fomichev int err, progfd; 785092f0892SStanislav Fomichev int mapfd; 786b7d3826cSJohn Fastabend 787092f0892SStanislav Fomichev err = parse_attach_detach_args(argc, argv, 788092f0892SStanislav Fomichev &progfd, &attach_type, &mapfd); 789092f0892SStanislav Fomichev if (err) 790092f0892SStanislav Fomichev return err; 791b7d3826cSJohn Fastabend 792b7d3826cSJohn Fastabend err = bpf_prog_detach2(progfd, mapfd, attach_type); 793b7d3826cSJohn Fastabend if (err) { 794b7d3826cSJohn Fastabend p_err("failed prog detach from map"); 795b7d3826cSJohn Fastabend return -EINVAL; 796b7d3826cSJohn Fastabend } 797b7d3826cSJohn Fastabend 798b7d3826cSJohn Fastabend if (json_output) 799b7d3826cSJohn Fastabend jsonw_null(json_wtr); 800b7d3826cSJohn Fastabend return 0; 801b7d3826cSJohn Fastabend } 80277380998SStanislav Fomichev 803ba95c745SQuentin Monnet static int check_single_stdin(char *file_data_in, char *file_ctx_in) 804ba95c745SQuentin Monnet { 805ba95c745SQuentin Monnet if (file_data_in && file_ctx_in && 806ba95c745SQuentin Monnet !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) { 807ba95c745SQuentin Monnet p_err("cannot use standard input for both data_in and ctx_in"); 808ba95c745SQuentin Monnet return -1; 809ba95c745SQuentin Monnet } 810ba95c745SQuentin Monnet 811ba95c745SQuentin Monnet return 0; 812ba95c745SQuentin Monnet } 813ba95c745SQuentin Monnet 814ba95c745SQuentin Monnet static int get_run_data(const char *fname, void **data_ptr, unsigned int *size) 815ba95c745SQuentin Monnet { 816ba95c745SQuentin Monnet size_t block_size = 256; 817ba95c745SQuentin Monnet size_t buf_size = block_size; 818ba95c745SQuentin Monnet size_t nb_read = 0; 819ba95c745SQuentin Monnet void *tmp; 820ba95c745SQuentin Monnet FILE *f; 821ba95c745SQuentin Monnet 822ba95c745SQuentin Monnet if (!fname) { 823ba95c745SQuentin Monnet *data_ptr = NULL; 824ba95c745SQuentin Monnet *size = 0; 825ba95c745SQuentin Monnet return 0; 826ba95c745SQuentin Monnet } 827ba95c745SQuentin Monnet 828ba95c745SQuentin Monnet if (!strcmp(fname, "-")) 829ba95c745SQuentin Monnet f = stdin; 830ba95c745SQuentin Monnet else 831ba95c745SQuentin Monnet f = fopen(fname, "r"); 832ba95c745SQuentin Monnet if (!f) { 833ba95c745SQuentin Monnet p_err("failed to open %s: %s", fname, strerror(errno)); 834ba95c745SQuentin Monnet return -1; 835ba95c745SQuentin Monnet } 836ba95c745SQuentin Monnet 837ba95c745SQuentin Monnet *data_ptr = malloc(block_size); 838ba95c745SQuentin Monnet if (!*data_ptr) { 839ba95c745SQuentin Monnet p_err("failed to allocate memory for data_in/ctx_in: %s", 840ba95c745SQuentin Monnet strerror(errno)); 841ba95c745SQuentin Monnet goto err_fclose; 842ba95c745SQuentin Monnet } 843ba95c745SQuentin Monnet 844ba95c745SQuentin Monnet while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) { 845ba95c745SQuentin Monnet if (feof(f)) 846ba95c745SQuentin Monnet break; 847ba95c745SQuentin Monnet if (ferror(f)) { 848ba95c745SQuentin Monnet p_err("failed to read data_in/ctx_in from %s: %s", 849ba95c745SQuentin Monnet fname, strerror(errno)); 850ba95c745SQuentin Monnet goto err_free; 851ba95c745SQuentin Monnet } 852ba95c745SQuentin Monnet if (nb_read > buf_size - block_size) { 853ba95c745SQuentin Monnet if (buf_size == UINT32_MAX) { 854ba95c745SQuentin Monnet p_err("data_in/ctx_in is too long (max: %d)", 855ba95c745SQuentin Monnet UINT32_MAX); 856ba95c745SQuentin Monnet goto err_free; 857ba95c745SQuentin Monnet } 858ba95c745SQuentin Monnet /* No space for fread()-ing next chunk; realloc() */ 859ba95c745SQuentin Monnet buf_size *= 2; 860ba95c745SQuentin Monnet tmp = realloc(*data_ptr, buf_size); 861ba95c745SQuentin Monnet if (!tmp) { 862ba95c745SQuentin Monnet p_err("failed to reallocate data_in/ctx_in: %s", 863ba95c745SQuentin Monnet strerror(errno)); 864ba95c745SQuentin Monnet goto err_free; 865ba95c745SQuentin Monnet } 866ba95c745SQuentin Monnet *data_ptr = tmp; 867ba95c745SQuentin Monnet } 868ba95c745SQuentin Monnet } 869ba95c745SQuentin Monnet if (f != stdin) 870ba95c745SQuentin Monnet fclose(f); 871ba95c745SQuentin Monnet 872ba95c745SQuentin Monnet *size = nb_read; 873ba95c745SQuentin Monnet return 0; 874ba95c745SQuentin Monnet 875ba95c745SQuentin Monnet err_free: 876ba95c745SQuentin Monnet free(*data_ptr); 877ba95c745SQuentin Monnet *data_ptr = NULL; 878ba95c745SQuentin Monnet err_fclose: 879ba95c745SQuentin Monnet if (f != stdin) 880ba95c745SQuentin Monnet fclose(f); 881ba95c745SQuentin Monnet return -1; 882ba95c745SQuentin Monnet } 883ba95c745SQuentin Monnet 884ba95c745SQuentin Monnet static void hex_print(void *data, unsigned int size, FILE *f) 885ba95c745SQuentin Monnet { 886ba95c745SQuentin Monnet size_t i, j; 887ba95c745SQuentin Monnet char c; 888ba95c745SQuentin Monnet 889ba95c745SQuentin Monnet for (i = 0; i < size; i += 16) { 890ba95c745SQuentin Monnet /* Row offset */ 891ba95c745SQuentin Monnet fprintf(f, "%07zx\t", i); 892ba95c745SQuentin Monnet 893ba95c745SQuentin Monnet /* Hexadecimal values */ 894ba95c745SQuentin Monnet for (j = i; j < i + 16 && j < size; j++) 895ba95c745SQuentin Monnet fprintf(f, "%02x%s", *(uint8_t *)(data + j), 896ba95c745SQuentin Monnet j % 2 ? " " : ""); 897ba95c745SQuentin Monnet for (; j < i + 16; j++) 898ba95c745SQuentin Monnet fprintf(f, " %s", j % 2 ? " " : ""); 899ba95c745SQuentin Monnet 900ba95c745SQuentin Monnet /* ASCII values (if relevant), '.' otherwise */ 901ba95c745SQuentin Monnet fprintf(f, "| "); 902ba95c745SQuentin Monnet for (j = i; j < i + 16 && j < size; j++) { 903ba95c745SQuentin Monnet c = *(char *)(data + j); 904ba95c745SQuentin Monnet if (c < ' ' || c > '~') 905ba95c745SQuentin Monnet c = '.'; 906ba95c745SQuentin Monnet fprintf(f, "%c%s", c, j == i + 7 ? " " : ""); 907ba95c745SQuentin Monnet } 908ba95c745SQuentin Monnet 909ba95c745SQuentin Monnet fprintf(f, "\n"); 910ba95c745SQuentin Monnet } 911ba95c745SQuentin Monnet } 912ba95c745SQuentin Monnet 913ba95c745SQuentin Monnet static int 914ba95c745SQuentin Monnet print_run_output(void *data, unsigned int size, const char *fname, 915ba95c745SQuentin Monnet const char *json_key) 916ba95c745SQuentin Monnet { 917ba95c745SQuentin Monnet size_t nb_written; 918ba95c745SQuentin Monnet FILE *f; 919ba95c745SQuentin Monnet 920ba95c745SQuentin Monnet if (!fname) 921ba95c745SQuentin Monnet return 0; 922ba95c745SQuentin Monnet 923ba95c745SQuentin Monnet if (!strcmp(fname, "-")) { 924ba95c745SQuentin Monnet f = stdout; 925ba95c745SQuentin Monnet if (json_output) { 926ba95c745SQuentin Monnet jsonw_name(json_wtr, json_key); 927ba95c745SQuentin Monnet print_data_json(data, size); 928ba95c745SQuentin Monnet } else { 929ba95c745SQuentin Monnet hex_print(data, size, f); 930ba95c745SQuentin Monnet } 931ba95c745SQuentin Monnet return 0; 932ba95c745SQuentin Monnet } 933ba95c745SQuentin Monnet 934ba95c745SQuentin Monnet f = fopen(fname, "w"); 935ba95c745SQuentin Monnet if (!f) { 936ba95c745SQuentin Monnet p_err("failed to open %s: %s", fname, strerror(errno)); 937ba95c745SQuentin Monnet return -1; 938ba95c745SQuentin Monnet } 939ba95c745SQuentin Monnet 940ba95c745SQuentin Monnet nb_written = fwrite(data, 1, size, f); 941ba95c745SQuentin Monnet fclose(f); 942ba95c745SQuentin Monnet if (nb_written != size) { 943ba95c745SQuentin Monnet p_err("failed to write output data/ctx: %s", strerror(errno)); 944ba95c745SQuentin Monnet return -1; 945ba95c745SQuentin Monnet } 946ba95c745SQuentin Monnet 947ba95c745SQuentin Monnet return 0; 948ba95c745SQuentin Monnet } 949ba95c745SQuentin Monnet 950ba95c745SQuentin Monnet static int alloc_run_data(void **data_ptr, unsigned int size_out) 951ba95c745SQuentin Monnet { 952ba95c745SQuentin Monnet *data_ptr = calloc(size_out, 1); 953ba95c745SQuentin Monnet if (!*data_ptr) { 954ba95c745SQuentin Monnet p_err("failed to allocate memory for output data/ctx: %s", 955ba95c745SQuentin Monnet strerror(errno)); 956ba95c745SQuentin Monnet return -1; 957ba95c745SQuentin Monnet } 958ba95c745SQuentin Monnet 959ba95c745SQuentin Monnet return 0; 960ba95c745SQuentin Monnet } 961ba95c745SQuentin Monnet 962ba95c745SQuentin Monnet static int do_run(int argc, char **argv) 963ba95c745SQuentin Monnet { 964ba95c745SQuentin Monnet char *data_fname_in = NULL, *data_fname_out = NULL; 965ba95c745SQuentin Monnet char *ctx_fname_in = NULL, *ctx_fname_out = NULL; 966ba95c745SQuentin Monnet struct bpf_prog_test_run_attr test_attr = {0}; 967ba95c745SQuentin Monnet const unsigned int default_size = SZ_32K; 968ba95c745SQuentin Monnet void *data_in = NULL, *data_out = NULL; 969ba95c745SQuentin Monnet void *ctx_in = NULL, *ctx_out = NULL; 970ba95c745SQuentin Monnet unsigned int repeat = 1; 971ba95c745SQuentin Monnet int fd, err; 972ba95c745SQuentin Monnet 973ba95c745SQuentin Monnet if (!REQ_ARGS(4)) 974ba95c745SQuentin Monnet return -1; 975ba95c745SQuentin Monnet 976ba95c745SQuentin Monnet fd = prog_parse_fd(&argc, &argv); 977ba95c745SQuentin Monnet if (fd < 0) 978ba95c745SQuentin Monnet return -1; 979ba95c745SQuentin Monnet 980ba95c745SQuentin Monnet while (argc) { 981ba95c745SQuentin Monnet if (detect_common_prefix(*argv, "data_in", "data_out", 982ba95c745SQuentin Monnet "data_size_out", NULL)) 983ba95c745SQuentin Monnet return -1; 984ba95c745SQuentin Monnet if (detect_common_prefix(*argv, "ctx_in", "ctx_out", 985ba95c745SQuentin Monnet "ctx_size_out", NULL)) 986ba95c745SQuentin Monnet return -1; 987ba95c745SQuentin Monnet 988ba95c745SQuentin Monnet if (is_prefix(*argv, "data_in")) { 989ba95c745SQuentin Monnet NEXT_ARG(); 990ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 991ba95c745SQuentin Monnet return -1; 992ba95c745SQuentin Monnet 993ba95c745SQuentin Monnet data_fname_in = GET_ARG(); 994ba95c745SQuentin Monnet if (check_single_stdin(data_fname_in, ctx_fname_in)) 995ba95c745SQuentin Monnet return -1; 996ba95c745SQuentin Monnet } else if (is_prefix(*argv, "data_out")) { 997ba95c745SQuentin Monnet NEXT_ARG(); 998ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 999ba95c745SQuentin Monnet return -1; 1000ba95c745SQuentin Monnet 1001ba95c745SQuentin Monnet data_fname_out = GET_ARG(); 1002ba95c745SQuentin Monnet } else if (is_prefix(*argv, "data_size_out")) { 1003ba95c745SQuentin Monnet char *endptr; 1004ba95c745SQuentin Monnet 1005ba95c745SQuentin Monnet NEXT_ARG(); 1006ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1007ba95c745SQuentin Monnet return -1; 1008ba95c745SQuentin Monnet 1009ba95c745SQuentin Monnet test_attr.data_size_out = strtoul(*argv, &endptr, 0); 1010ba95c745SQuentin Monnet if (*endptr) { 1011ba95c745SQuentin Monnet p_err("can't parse %s as output data size", 1012ba95c745SQuentin Monnet *argv); 1013ba95c745SQuentin Monnet return -1; 1014ba95c745SQuentin Monnet } 1015ba95c745SQuentin Monnet NEXT_ARG(); 1016ba95c745SQuentin Monnet } else if (is_prefix(*argv, "ctx_in")) { 1017ba95c745SQuentin Monnet NEXT_ARG(); 1018ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1019ba95c745SQuentin Monnet return -1; 1020ba95c745SQuentin Monnet 1021ba95c745SQuentin Monnet ctx_fname_in = GET_ARG(); 1022ba95c745SQuentin Monnet if (check_single_stdin(data_fname_in, ctx_fname_in)) 1023ba95c745SQuentin Monnet return -1; 1024ba95c745SQuentin Monnet } else if (is_prefix(*argv, "ctx_out")) { 1025ba95c745SQuentin Monnet NEXT_ARG(); 1026ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1027ba95c745SQuentin Monnet return -1; 1028ba95c745SQuentin Monnet 1029ba95c745SQuentin Monnet ctx_fname_out = GET_ARG(); 1030ba95c745SQuentin Monnet } else if (is_prefix(*argv, "ctx_size_out")) { 1031ba95c745SQuentin Monnet char *endptr; 1032ba95c745SQuentin Monnet 1033ba95c745SQuentin Monnet NEXT_ARG(); 1034ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1035ba95c745SQuentin Monnet return -1; 1036ba95c745SQuentin Monnet 1037ba95c745SQuentin Monnet test_attr.ctx_size_out = strtoul(*argv, &endptr, 0); 1038ba95c745SQuentin Monnet if (*endptr) { 1039ba95c745SQuentin Monnet p_err("can't parse %s as output context size", 1040ba95c745SQuentin Monnet *argv); 1041ba95c745SQuentin Monnet return -1; 1042ba95c745SQuentin Monnet } 1043ba95c745SQuentin Monnet NEXT_ARG(); 1044ba95c745SQuentin Monnet } else if (is_prefix(*argv, "repeat")) { 1045ba95c745SQuentin Monnet char *endptr; 1046ba95c745SQuentin Monnet 1047ba95c745SQuentin Monnet NEXT_ARG(); 1048ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1049ba95c745SQuentin Monnet return -1; 1050ba95c745SQuentin Monnet 1051ba95c745SQuentin Monnet repeat = strtoul(*argv, &endptr, 0); 1052ba95c745SQuentin Monnet if (*endptr) { 1053ba95c745SQuentin Monnet p_err("can't parse %s as repeat number", 1054ba95c745SQuentin Monnet *argv); 1055ba95c745SQuentin Monnet return -1; 1056ba95c745SQuentin Monnet } 1057ba95c745SQuentin Monnet NEXT_ARG(); 1058ba95c745SQuentin Monnet } else { 1059ba95c745SQuentin Monnet p_err("expected no more arguments, 'data_in', 'data_out', 'data_size_out', 'ctx_in', 'ctx_out', 'ctx_size_out' or 'repeat', got: '%s'?", 1060ba95c745SQuentin Monnet *argv); 1061ba95c745SQuentin Monnet return -1; 1062ba95c745SQuentin Monnet } 1063ba95c745SQuentin Monnet } 1064ba95c745SQuentin Monnet 1065ba95c745SQuentin Monnet err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in); 1066ba95c745SQuentin Monnet if (err) 1067ba95c745SQuentin Monnet return -1; 1068ba95c745SQuentin Monnet 1069ba95c745SQuentin Monnet if (data_in) { 1070ba95c745SQuentin Monnet if (!test_attr.data_size_out) 1071ba95c745SQuentin Monnet test_attr.data_size_out = default_size; 1072ba95c745SQuentin Monnet err = alloc_run_data(&data_out, test_attr.data_size_out); 1073ba95c745SQuentin Monnet if (err) 1074ba95c745SQuentin Monnet goto free_data_in; 1075ba95c745SQuentin Monnet } 1076ba95c745SQuentin Monnet 1077ba95c745SQuentin Monnet err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in); 1078ba95c745SQuentin Monnet if (err) 1079ba95c745SQuentin Monnet goto free_data_out; 1080ba95c745SQuentin Monnet 1081ba95c745SQuentin Monnet if (ctx_in) { 1082ba95c745SQuentin Monnet if (!test_attr.ctx_size_out) 1083ba95c745SQuentin Monnet test_attr.ctx_size_out = default_size; 1084ba95c745SQuentin Monnet err = alloc_run_data(&ctx_out, test_attr.ctx_size_out); 1085ba95c745SQuentin Monnet if (err) 1086ba95c745SQuentin Monnet goto free_ctx_in; 1087ba95c745SQuentin Monnet } 1088ba95c745SQuentin Monnet 1089ba95c745SQuentin Monnet test_attr.prog_fd = fd; 1090ba95c745SQuentin Monnet test_attr.repeat = repeat; 1091ba95c745SQuentin Monnet test_attr.data_in = data_in; 1092ba95c745SQuentin Monnet test_attr.data_out = data_out; 1093ba95c745SQuentin Monnet test_attr.ctx_in = ctx_in; 1094ba95c745SQuentin Monnet test_attr.ctx_out = ctx_out; 1095ba95c745SQuentin Monnet 1096ba95c745SQuentin Monnet err = bpf_prog_test_run_xattr(&test_attr); 1097ba95c745SQuentin Monnet if (err) { 1098ba95c745SQuentin Monnet p_err("failed to run program: %s", strerror(errno)); 1099ba95c745SQuentin Monnet goto free_ctx_out; 1100ba95c745SQuentin Monnet } 1101ba95c745SQuentin Monnet 1102ba95c745SQuentin Monnet err = 0; 1103ba95c745SQuentin Monnet 1104ba95c745SQuentin Monnet if (json_output) 1105ba95c745SQuentin Monnet jsonw_start_object(json_wtr); /* root */ 1106ba95c745SQuentin Monnet 1107ba95c745SQuentin Monnet /* Do not exit on errors occurring when printing output data/context, 1108ba95c745SQuentin Monnet * we still want to print return value and duration for program run. 1109ba95c745SQuentin Monnet */ 1110ba95c745SQuentin Monnet if (test_attr.data_size_out) 1111ba95c745SQuentin Monnet err += print_run_output(test_attr.data_out, 1112ba95c745SQuentin Monnet test_attr.data_size_out, 1113ba95c745SQuentin Monnet data_fname_out, "data_out"); 1114ba95c745SQuentin Monnet if (test_attr.ctx_size_out) 1115ba95c745SQuentin Monnet err += print_run_output(test_attr.ctx_out, 1116ba95c745SQuentin Monnet test_attr.ctx_size_out, 1117ba95c745SQuentin Monnet ctx_fname_out, "ctx_out"); 1118ba95c745SQuentin Monnet 1119ba95c745SQuentin Monnet if (json_output) { 1120ba95c745SQuentin Monnet jsonw_uint_field(json_wtr, "retval", test_attr.retval); 1121ba95c745SQuentin Monnet jsonw_uint_field(json_wtr, "duration", test_attr.duration); 1122ba95c745SQuentin Monnet jsonw_end_object(json_wtr); /* root */ 1123ba95c745SQuentin Monnet } else { 1124ba95c745SQuentin Monnet fprintf(stdout, "Return value: %u, duration%s: %uns\n", 1125ba95c745SQuentin Monnet test_attr.retval, 1126ba95c745SQuentin Monnet repeat > 1 ? " (average)" : "", test_attr.duration); 1127ba95c745SQuentin Monnet } 1128ba95c745SQuentin Monnet 1129ba95c745SQuentin Monnet free_ctx_out: 1130ba95c745SQuentin Monnet free(ctx_out); 1131ba95c745SQuentin Monnet free_ctx_in: 1132ba95c745SQuentin Monnet free(ctx_in); 1133ba95c745SQuentin Monnet free_data_out: 1134ba95c745SQuentin Monnet free(data_out); 1135ba95c745SQuentin Monnet free_data_in: 1136ba95c745SQuentin Monnet free(data_in); 1137ba95c745SQuentin Monnet 1138ba95c745SQuentin Monnet return err; 1139ba95c745SQuentin Monnet } 1140ba95c745SQuentin Monnet 11416ae32b29SQuentin Monnet static int 11426ae32b29SQuentin Monnet get_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type, 11436ae32b29SQuentin Monnet enum bpf_attach_type *expected_attach_type) 11446ae32b29SQuentin Monnet { 11456ae32b29SQuentin Monnet libbpf_print_fn_t print_backup; 11466ae32b29SQuentin Monnet int ret; 11476ae32b29SQuentin Monnet 11486ae32b29SQuentin Monnet ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type); 11496ae32b29SQuentin Monnet if (!ret) 11506ae32b29SQuentin Monnet return ret; 11516ae32b29SQuentin Monnet 11526ae32b29SQuentin Monnet /* libbpf_prog_type_by_name() failed, let's re-run with debug level */ 11536ae32b29SQuentin Monnet print_backup = libbpf_set_print(print_all_levels); 11546ae32b29SQuentin Monnet ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type); 11556ae32b29SQuentin Monnet libbpf_set_print(print_backup); 11566ae32b29SQuentin Monnet 11576ae32b29SQuentin Monnet return ret; 11586ae32b29SQuentin Monnet } 11596ae32b29SQuentin Monnet 116077380998SStanislav Fomichev static int load_with_options(int argc, char **argv, bool first_prog_only) 116149a086c2SRoman Gushchin { 116232e3e58eSAndrii Nakryiko enum bpf_prog_type common_prog_type = BPF_PROG_TYPE_UNSPEC; 1163e00aca65SAndrii Nakryiko DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts, 1164e00aca65SAndrii Nakryiko .relaxed_maps = relaxed_maps, 1165e00aca65SAndrii Nakryiko ); 1166e00aca65SAndrii Nakryiko struct bpf_object_load_attr load_attr = { 0 }; 116755d77807SQuentin Monnet enum bpf_attach_type expected_attach_type; 11683ff5a4dcSJakub Kicinski struct map_replace *map_replace = NULL; 116977380998SStanislav Fomichev struct bpf_program *prog = NULL, *pos; 11703ff5a4dcSJakub Kicinski unsigned int old_map_fds = 0; 11713767a94bSStanislav Fomichev const char *pinmaps = NULL; 117249a086c2SRoman Gushchin struct bpf_object *obj; 1173c8406848SJakub Kicinski struct bpf_map *map; 1174c8406848SJakub Kicinski const char *pinfile; 11753ff5a4dcSJakub Kicinski unsigned int i, j; 1176c8406848SJakub Kicinski __u32 ifindex = 0; 117732e3e58eSAndrii Nakryiko const char *file; 11783ff5a4dcSJakub Kicinski int idx, err; 117949a086c2SRoman Gushchin 118032e3e58eSAndrii Nakryiko 11818d1fc3deSJakub Kicinski if (!REQ_ARGS(2)) 11828d1fc3deSJakub Kicinski return -1; 118332e3e58eSAndrii Nakryiko file = GET_ARG(); 11848d1fc3deSJakub Kicinski pinfile = GET_ARG(); 118549a086c2SRoman Gushchin 1186ba6dd679SJakub Kicinski while (argc) { 118749f2cba3SJakub Kicinski if (is_prefix(*argv, "type")) { 118849f2cba3SJakub Kicinski char *type; 118949f2cba3SJakub Kicinski 119049f2cba3SJakub Kicinski NEXT_ARG(); 119149f2cba3SJakub Kicinski 119232e3e58eSAndrii Nakryiko if (common_prog_type != BPF_PROG_TYPE_UNSPEC) { 119349f2cba3SJakub Kicinski p_err("program type already specified"); 11943ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 119549f2cba3SJakub Kicinski } 119649f2cba3SJakub Kicinski if (!REQ_ARGS(1)) 11973ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 119849f2cba3SJakub Kicinski 119949f2cba3SJakub Kicinski /* Put a '/' at the end of type to appease libbpf */ 120049f2cba3SJakub Kicinski type = malloc(strlen(*argv) + 2); 120149f2cba3SJakub Kicinski if (!type) { 120249f2cba3SJakub Kicinski p_err("mem alloc failed"); 12033ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 120449f2cba3SJakub Kicinski } 120549f2cba3SJakub Kicinski *type = 0; 120649f2cba3SJakub Kicinski strcat(type, *argv); 120749f2cba3SJakub Kicinski strcat(type, "/"); 120849f2cba3SJakub Kicinski 12096ae32b29SQuentin Monnet err = get_prog_type_by_name(type, &common_prog_type, 1210c8406848SJakub Kicinski &expected_attach_type); 121149f2cba3SJakub Kicinski free(type); 1212c76e4c22STaeung Song if (err < 0) 12133ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1214c76e4c22STaeung Song 121549f2cba3SJakub Kicinski NEXT_ARG(); 12163ff5a4dcSJakub Kicinski } else if (is_prefix(*argv, "map")) { 1217dde7011aSJakub Kicinski void *new_map_replace; 12183ff5a4dcSJakub Kicinski char *endptr, *name; 12193ff5a4dcSJakub Kicinski int fd; 12203ff5a4dcSJakub Kicinski 12213ff5a4dcSJakub Kicinski NEXT_ARG(); 12223ff5a4dcSJakub Kicinski 12233ff5a4dcSJakub Kicinski if (!REQ_ARGS(4)) 12243ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 12253ff5a4dcSJakub Kicinski 12263ff5a4dcSJakub Kicinski if (is_prefix(*argv, "idx")) { 12273ff5a4dcSJakub Kicinski NEXT_ARG(); 12283ff5a4dcSJakub Kicinski 12293ff5a4dcSJakub Kicinski idx = strtoul(*argv, &endptr, 0); 12303ff5a4dcSJakub Kicinski if (*endptr) { 12313ff5a4dcSJakub Kicinski p_err("can't parse %s as IDX", *argv); 12323ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 12333ff5a4dcSJakub Kicinski } 12343ff5a4dcSJakub Kicinski name = NULL; 12353ff5a4dcSJakub Kicinski } else if (is_prefix(*argv, "name")) { 12363ff5a4dcSJakub Kicinski NEXT_ARG(); 12373ff5a4dcSJakub Kicinski 12383ff5a4dcSJakub Kicinski name = *argv; 12393ff5a4dcSJakub Kicinski idx = -1; 12403ff5a4dcSJakub Kicinski } else { 12413ff5a4dcSJakub Kicinski p_err("expected 'idx' or 'name', got: '%s'?", 12423ff5a4dcSJakub Kicinski *argv); 12433ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 12443ff5a4dcSJakub Kicinski } 12453ff5a4dcSJakub Kicinski NEXT_ARG(); 12463ff5a4dcSJakub Kicinski 12473ff5a4dcSJakub Kicinski fd = map_parse_fd(&argc, &argv); 12483ff5a4dcSJakub Kicinski if (fd < 0) 12493ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 12503ff5a4dcSJakub Kicinski 1251dde7011aSJakub Kicinski new_map_replace = reallocarray(map_replace, 1252dde7011aSJakub Kicinski old_map_fds + 1, 12533ff5a4dcSJakub Kicinski sizeof(*map_replace)); 1254dde7011aSJakub Kicinski if (!new_map_replace) { 12553ff5a4dcSJakub Kicinski p_err("mem alloc failed"); 12563ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 12573ff5a4dcSJakub Kicinski } 1258dde7011aSJakub Kicinski map_replace = new_map_replace; 1259dde7011aSJakub Kicinski 12603ff5a4dcSJakub Kicinski map_replace[old_map_fds].idx = idx; 12613ff5a4dcSJakub Kicinski map_replace[old_map_fds].name = name; 12623ff5a4dcSJakub Kicinski map_replace[old_map_fds].fd = fd; 12633ff5a4dcSJakub Kicinski old_map_fds++; 126449f2cba3SJakub Kicinski } else if (is_prefix(*argv, "dev")) { 1265ba6dd679SJakub Kicinski NEXT_ARG(); 1266ba6dd679SJakub Kicinski 1267c8406848SJakub Kicinski if (ifindex) { 1268ba6dd679SJakub Kicinski p_err("offload device already specified"); 12693ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1270ba6dd679SJakub Kicinski } 1271ba6dd679SJakub Kicinski if (!REQ_ARGS(1)) 12723ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1273ba6dd679SJakub Kicinski 1274c8406848SJakub Kicinski ifindex = if_nametoindex(*argv); 1275c8406848SJakub Kicinski if (!ifindex) { 1276ba6dd679SJakub Kicinski p_err("unrecognized netdevice '%s': %s", 1277ba6dd679SJakub Kicinski *argv, strerror(errno)); 12783ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1279ba6dd679SJakub Kicinski } 1280ba6dd679SJakub Kicinski NEXT_ARG(); 12813767a94bSStanislav Fomichev } else if (is_prefix(*argv, "pinmaps")) { 12823767a94bSStanislav Fomichev NEXT_ARG(); 12833767a94bSStanislav Fomichev 12843767a94bSStanislav Fomichev if (!REQ_ARGS(1)) 12853767a94bSStanislav Fomichev goto err_free_reuse_maps; 12863767a94bSStanislav Fomichev 12873767a94bSStanislav Fomichev pinmaps = GET_ARG(); 1288ba6dd679SJakub Kicinski } else { 12893ff5a4dcSJakub Kicinski p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?", 1290ba6dd679SJakub Kicinski *argv); 12913ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1292ba6dd679SJakub Kicinski } 1293ba6dd679SJakub Kicinski } 1294ba6dd679SJakub Kicinski 1295ac4e0e05SYonghong Song set_max_rlimit(); 1296ac4e0e05SYonghong Song 129732e3e58eSAndrii Nakryiko obj = bpf_object__open_file(file, &open_opts); 1298c8406848SJakub Kicinski if (IS_ERR_OR_NULL(obj)) { 1299c8406848SJakub Kicinski p_err("failed to open object file"); 13003ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 130149a086c2SRoman Gushchin } 130249a086c2SRoman Gushchin 130377380998SStanislav Fomichev bpf_object__for_each_program(pos, obj) { 130432e3e58eSAndrii Nakryiko enum bpf_prog_type prog_type = common_prog_type; 1305c8406848SJakub Kicinski 130632e3e58eSAndrii Nakryiko if (prog_type == BPF_PROG_TYPE_UNSPEC) { 1307*fd17e272SAndrii Nakryiko const char *sec_name = bpf_program__section_name(pos); 1308c8406848SJakub Kicinski 13096ae32b29SQuentin Monnet err = get_prog_type_by_name(sec_name, &prog_type, 1310c8406848SJakub Kicinski &expected_attach_type); 1311c76e4c22STaeung Song if (err < 0) 1312c8406848SJakub Kicinski goto err_close_obj; 1313c8406848SJakub Kicinski } 131477380998SStanislav Fomichev 131577380998SStanislav Fomichev bpf_program__set_ifindex(pos, ifindex); 131677380998SStanislav Fomichev bpf_program__set_type(pos, prog_type); 131777380998SStanislav Fomichev bpf_program__set_expected_attach_type(pos, expected_attach_type); 131877380998SStanislav Fomichev } 1319c8406848SJakub Kicinski 13203ff5a4dcSJakub Kicinski qsort(map_replace, old_map_fds, sizeof(*map_replace), 13213ff5a4dcSJakub Kicinski map_replace_compar); 13223ff5a4dcSJakub Kicinski 13233ff5a4dcSJakub Kicinski /* After the sort maps by name will be first on the list, because they 13243ff5a4dcSJakub Kicinski * have idx == -1. Resolve them. 13253ff5a4dcSJakub Kicinski */ 13263ff5a4dcSJakub Kicinski j = 0; 13273ff5a4dcSJakub Kicinski while (j < old_map_fds && map_replace[j].name) { 13283ff5a4dcSJakub Kicinski i = 0; 1329f74a53d9SJakub Kicinski bpf_object__for_each_map(map, obj) { 13303ff5a4dcSJakub Kicinski if (!strcmp(bpf_map__name(map), map_replace[j].name)) { 13313ff5a4dcSJakub Kicinski map_replace[j].idx = i; 13323ff5a4dcSJakub Kicinski break; 13333ff5a4dcSJakub Kicinski } 13343ff5a4dcSJakub Kicinski i++; 13353ff5a4dcSJakub Kicinski } 13363ff5a4dcSJakub Kicinski if (map_replace[j].idx == -1) { 13373ff5a4dcSJakub Kicinski p_err("unable to find map '%s'", map_replace[j].name); 13383ff5a4dcSJakub Kicinski goto err_close_obj; 13393ff5a4dcSJakub Kicinski } 13403ff5a4dcSJakub Kicinski j++; 13413ff5a4dcSJakub Kicinski } 13423ff5a4dcSJakub Kicinski /* Resort if any names were resolved */ 13433ff5a4dcSJakub Kicinski if (j) 13443ff5a4dcSJakub Kicinski qsort(map_replace, old_map_fds, sizeof(*map_replace), 13453ff5a4dcSJakub Kicinski map_replace_compar); 13463ff5a4dcSJakub Kicinski 13473ff5a4dcSJakub Kicinski /* Set ifindex and name reuse */ 13483ff5a4dcSJakub Kicinski j = 0; 13493ff5a4dcSJakub Kicinski idx = 0; 1350f74a53d9SJakub Kicinski bpf_object__for_each_map(map, obj) { 1351c8406848SJakub Kicinski if (!bpf_map__is_offload_neutral(map)) 1352c8406848SJakub Kicinski bpf_map__set_ifindex(map, ifindex); 1353c8406848SJakub Kicinski 13543ff5a4dcSJakub Kicinski if (j < old_map_fds && idx == map_replace[j].idx) { 13553ff5a4dcSJakub Kicinski err = bpf_map__reuse_fd(map, map_replace[j++].fd); 13563ff5a4dcSJakub Kicinski if (err) { 13573ff5a4dcSJakub Kicinski p_err("unable to set up map reuse: %d", err); 13583ff5a4dcSJakub Kicinski goto err_close_obj; 13593ff5a4dcSJakub Kicinski } 13603ff5a4dcSJakub Kicinski 13613ff5a4dcSJakub Kicinski /* Next reuse wants to apply to the same map */ 13623ff5a4dcSJakub Kicinski if (j < old_map_fds && map_replace[j].idx == idx) { 13633ff5a4dcSJakub Kicinski p_err("replacement for map idx %d specified more than once", 13643ff5a4dcSJakub Kicinski idx); 13653ff5a4dcSJakub Kicinski goto err_close_obj; 13663ff5a4dcSJakub Kicinski } 13673ff5a4dcSJakub Kicinski } 13683ff5a4dcSJakub Kicinski 13693ff5a4dcSJakub Kicinski idx++; 13703ff5a4dcSJakub Kicinski } 13713ff5a4dcSJakub Kicinski if (j < old_map_fds) { 13723ff5a4dcSJakub Kicinski p_err("map idx '%d' not used", map_replace[j].idx); 13733ff5a4dcSJakub Kicinski goto err_close_obj; 13743ff5a4dcSJakub Kicinski } 13753ff5a4dcSJakub Kicinski 137655d77807SQuentin Monnet load_attr.obj = obj; 137755d77807SQuentin Monnet if (verifier_logs) 137855d77807SQuentin Monnet /* log_level1 + log_level2 + stats, but not stable UAPI */ 137955d77807SQuentin Monnet load_attr.log_level = 1 + 2 + 4; 138055d77807SQuentin Monnet 138155d77807SQuentin Monnet err = bpf_object__load_xattr(&load_attr); 1382c8406848SJakub Kicinski if (err) { 1383c8406848SJakub Kicinski p_err("failed to load object file"); 1384c8406848SJakub Kicinski goto err_close_obj; 1385c8406848SJakub Kicinski } 1386c8406848SJakub Kicinski 138777380998SStanislav Fomichev err = mount_bpffs_for_pin(pinfile); 138877380998SStanislav Fomichev if (err) 1389bfee71fbSJakub Kicinski goto err_close_obj; 139049a086c2SRoman Gushchin 139177380998SStanislav Fomichev if (first_prog_only) { 139277380998SStanislav Fomichev prog = bpf_program__next(NULL, obj); 139377380998SStanislav Fomichev if (!prog) { 139477380998SStanislav Fomichev p_err("object file doesn't contain any bpf program"); 139577380998SStanislav Fomichev goto err_close_obj; 139677380998SStanislav Fomichev } 139777380998SStanislav Fomichev 139877380998SStanislav Fomichev err = bpf_obj_pin(bpf_program__fd(prog), pinfile); 139977380998SStanislav Fomichev if (err) { 140077380998SStanislav Fomichev p_err("failed to pin program %s", 1401*fd17e272SAndrii Nakryiko bpf_program__section_name(prog)); 140277380998SStanislav Fomichev goto err_close_obj; 140377380998SStanislav Fomichev } 140477380998SStanislav Fomichev } else { 140577380998SStanislav Fomichev err = bpf_object__pin_programs(obj, pinfile); 140677380998SStanislav Fomichev if (err) { 140777380998SStanislav Fomichev p_err("failed to pin all programs"); 140877380998SStanislav Fomichev goto err_close_obj; 140977380998SStanislav Fomichev } 141077380998SStanislav Fomichev } 141177380998SStanislav Fomichev 14123767a94bSStanislav Fomichev if (pinmaps) { 14133767a94bSStanislav Fomichev err = bpf_object__pin_maps(obj, pinmaps); 14143767a94bSStanislav Fomichev if (err) { 14153767a94bSStanislav Fomichev p_err("failed to pin all maps"); 14163767a94bSStanislav Fomichev goto err_unpin; 14173767a94bSStanislav Fomichev } 14183767a94bSStanislav Fomichev } 14193767a94bSStanislav Fomichev 142049a086c2SRoman Gushchin if (json_output) 142149a086c2SRoman Gushchin jsonw_null(json_wtr); 142249a086c2SRoman Gushchin 1423bfee71fbSJakub Kicinski bpf_object__close(obj); 14243ff5a4dcSJakub Kicinski for (i = 0; i < old_map_fds; i++) 14253ff5a4dcSJakub Kicinski close(map_replace[i].fd); 14263ff5a4dcSJakub Kicinski free(map_replace); 1427bfee71fbSJakub Kicinski 142849a086c2SRoman Gushchin return 0; 1429bfee71fbSJakub Kicinski 14303767a94bSStanislav Fomichev err_unpin: 14313767a94bSStanislav Fomichev if (first_prog_only) 14323767a94bSStanislav Fomichev unlink(pinfile); 14333767a94bSStanislav Fomichev else 14343767a94bSStanislav Fomichev bpf_object__unpin_programs(obj, pinfile); 1435bfee71fbSJakub Kicinski err_close_obj: 1436bfee71fbSJakub Kicinski bpf_object__close(obj); 14373ff5a4dcSJakub Kicinski err_free_reuse_maps: 14383ff5a4dcSJakub Kicinski for (i = 0; i < old_map_fds; i++) 14393ff5a4dcSJakub Kicinski close(map_replace[i].fd); 14403ff5a4dcSJakub Kicinski free(map_replace); 1441bfee71fbSJakub Kicinski return -1; 144249a086c2SRoman Gushchin } 144349a086c2SRoman Gushchin 144477380998SStanislav Fomichev static int do_load(int argc, char **argv) 144577380998SStanislav Fomichev { 144677380998SStanislav Fomichev return load_with_options(argc, argv, true); 144777380998SStanislav Fomichev } 144877380998SStanislav Fomichev 144977380998SStanislav Fomichev static int do_loadall(int argc, char **argv) 145077380998SStanislav Fomichev { 145177380998SStanislav Fomichev return load_with_options(argc, argv, false); 145277380998SStanislav Fomichev } 145377380998SStanislav Fomichev 145447c09d6aSSong Liu #ifdef BPFTOOL_WITHOUT_SKELETONS 145547c09d6aSSong Liu 145647c09d6aSSong Liu static int do_profile(int argc, char **argv) 145747c09d6aSSong Liu { 145814e5728fSSong Liu p_err("bpftool prog profile command is not supported. Please build bpftool with clang >= 10.0.0"); 145947c09d6aSSong Liu return 0; 146047c09d6aSSong Liu } 146147c09d6aSSong Liu 146247c09d6aSSong Liu #else /* BPFTOOL_WITHOUT_SKELETONS */ 146347c09d6aSSong Liu 146447c09d6aSSong Liu #include "profiler.skel.h" 146547c09d6aSSong Liu 146647c09d6aSSong Liu struct profile_metric { 146747c09d6aSSong Liu const char *name; 146847c09d6aSSong Liu struct bpf_perf_event_value val; 146947c09d6aSSong Liu struct perf_event_attr attr; 147047c09d6aSSong Liu bool selected; 147147c09d6aSSong Liu 147247c09d6aSSong Liu /* calculate ratios like instructions per cycle */ 147347c09d6aSSong Liu const int ratio_metric; /* 0 for N/A, 1 for index 0 (cycles) */ 147447c09d6aSSong Liu const char *ratio_desc; 147547c09d6aSSong Liu const float ratio_mul; 147647c09d6aSSong Liu } metrics[] = { 147747c09d6aSSong Liu { 147847c09d6aSSong Liu .name = "cycles", 147947c09d6aSSong Liu .attr = { 148047c09d6aSSong Liu .type = PERF_TYPE_HARDWARE, 148147c09d6aSSong Liu .config = PERF_COUNT_HW_CPU_CYCLES, 148247c09d6aSSong Liu .exclude_user = 1, 148347c09d6aSSong Liu }, 148447c09d6aSSong Liu }, 148547c09d6aSSong Liu { 148647c09d6aSSong Liu .name = "instructions", 148747c09d6aSSong Liu .attr = { 148847c09d6aSSong Liu .type = PERF_TYPE_HARDWARE, 148947c09d6aSSong Liu .config = PERF_COUNT_HW_INSTRUCTIONS, 149047c09d6aSSong Liu .exclude_user = 1, 149147c09d6aSSong Liu }, 149247c09d6aSSong Liu .ratio_metric = 1, 149347c09d6aSSong Liu .ratio_desc = "insns per cycle", 149447c09d6aSSong Liu .ratio_mul = 1.0, 149547c09d6aSSong Liu }, 149647c09d6aSSong Liu { 149747c09d6aSSong Liu .name = "l1d_loads", 149847c09d6aSSong Liu .attr = { 149947c09d6aSSong Liu .type = PERF_TYPE_HW_CACHE, 150047c09d6aSSong Liu .config = 150147c09d6aSSong Liu PERF_COUNT_HW_CACHE_L1D | 150247c09d6aSSong Liu (PERF_COUNT_HW_CACHE_OP_READ << 8) | 150347c09d6aSSong Liu (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16), 150447c09d6aSSong Liu .exclude_user = 1, 150547c09d6aSSong Liu }, 150647c09d6aSSong Liu }, 150747c09d6aSSong Liu { 150847c09d6aSSong Liu .name = "llc_misses", 150947c09d6aSSong Liu .attr = { 151047c09d6aSSong Liu .type = PERF_TYPE_HW_CACHE, 151147c09d6aSSong Liu .config = 151247c09d6aSSong Liu PERF_COUNT_HW_CACHE_LL | 151347c09d6aSSong Liu (PERF_COUNT_HW_CACHE_OP_READ << 8) | 151447c09d6aSSong Liu (PERF_COUNT_HW_CACHE_RESULT_MISS << 16), 151547c09d6aSSong Liu .exclude_user = 1 151647c09d6aSSong Liu }, 151747c09d6aSSong Liu .ratio_metric = 2, 151847c09d6aSSong Liu .ratio_desc = "LLC misses per million insns", 151947c09d6aSSong Liu .ratio_mul = 1e6, 152047c09d6aSSong Liu }, 152147c09d6aSSong Liu }; 152247c09d6aSSong Liu 152347c09d6aSSong Liu static __u64 profile_total_count; 152447c09d6aSSong Liu 152547c09d6aSSong Liu #define MAX_NUM_PROFILE_METRICS 4 152647c09d6aSSong Liu 152747c09d6aSSong Liu static int profile_parse_metrics(int argc, char **argv) 152847c09d6aSSong Liu { 152947c09d6aSSong Liu unsigned int metric_cnt; 153047c09d6aSSong Liu int selected_cnt = 0; 153147c09d6aSSong Liu unsigned int i; 153247c09d6aSSong Liu 153347c09d6aSSong Liu metric_cnt = sizeof(metrics) / sizeof(struct profile_metric); 153447c09d6aSSong Liu 153547c09d6aSSong Liu while (argc > 0) { 153647c09d6aSSong Liu for (i = 0; i < metric_cnt; i++) { 153747c09d6aSSong Liu if (is_prefix(argv[0], metrics[i].name)) { 153847c09d6aSSong Liu if (!metrics[i].selected) 153947c09d6aSSong Liu selected_cnt++; 154047c09d6aSSong Liu metrics[i].selected = true; 154147c09d6aSSong Liu break; 154247c09d6aSSong Liu } 154347c09d6aSSong Liu } 154447c09d6aSSong Liu if (i == metric_cnt) { 154547c09d6aSSong Liu p_err("unknown metric %s", argv[0]); 154647c09d6aSSong Liu return -1; 154747c09d6aSSong Liu } 154847c09d6aSSong Liu NEXT_ARG(); 154947c09d6aSSong Liu } 155047c09d6aSSong Liu if (selected_cnt > MAX_NUM_PROFILE_METRICS) { 155147c09d6aSSong Liu p_err("too many (%d) metrics, please specify no more than %d metrics at at time", 155247c09d6aSSong Liu selected_cnt, MAX_NUM_PROFILE_METRICS); 155347c09d6aSSong Liu return -1; 155447c09d6aSSong Liu } 155547c09d6aSSong Liu return selected_cnt; 155647c09d6aSSong Liu } 155747c09d6aSSong Liu 155847c09d6aSSong Liu static void profile_read_values(struct profiler_bpf *obj) 155947c09d6aSSong Liu { 156047c09d6aSSong Liu __u32 m, cpu, num_cpu = obj->rodata->num_cpu; 156147c09d6aSSong Liu int reading_map_fd, count_map_fd; 156247c09d6aSSong Liu __u64 counts[num_cpu]; 156347c09d6aSSong Liu __u32 key = 0; 156447c09d6aSSong Liu int err; 156547c09d6aSSong Liu 156647c09d6aSSong Liu reading_map_fd = bpf_map__fd(obj->maps.accum_readings); 156747c09d6aSSong Liu count_map_fd = bpf_map__fd(obj->maps.counts); 156847c09d6aSSong Liu if (reading_map_fd < 0 || count_map_fd < 0) { 156947c09d6aSSong Liu p_err("failed to get fd for map"); 157047c09d6aSSong Liu return; 157147c09d6aSSong Liu } 157247c09d6aSSong Liu 157347c09d6aSSong Liu err = bpf_map_lookup_elem(count_map_fd, &key, counts); 157447c09d6aSSong Liu if (err) { 157547c09d6aSSong Liu p_err("failed to read count_map: %s", strerror(errno)); 157647c09d6aSSong Liu return; 157747c09d6aSSong Liu } 157847c09d6aSSong Liu 157947c09d6aSSong Liu profile_total_count = 0; 158047c09d6aSSong Liu for (cpu = 0; cpu < num_cpu; cpu++) 158147c09d6aSSong Liu profile_total_count += counts[cpu]; 158247c09d6aSSong Liu 158347c09d6aSSong Liu for (m = 0; m < ARRAY_SIZE(metrics); m++) { 158447c09d6aSSong Liu struct bpf_perf_event_value values[num_cpu]; 158547c09d6aSSong Liu 158647c09d6aSSong Liu if (!metrics[m].selected) 158747c09d6aSSong Liu continue; 158847c09d6aSSong Liu 158947c09d6aSSong Liu err = bpf_map_lookup_elem(reading_map_fd, &key, values); 159047c09d6aSSong Liu if (err) { 159147c09d6aSSong Liu p_err("failed to read reading_map: %s", 159247c09d6aSSong Liu strerror(errno)); 159347c09d6aSSong Liu return; 159447c09d6aSSong Liu } 159547c09d6aSSong Liu for (cpu = 0; cpu < num_cpu; cpu++) { 159647c09d6aSSong Liu metrics[m].val.counter += values[cpu].counter; 159747c09d6aSSong Liu metrics[m].val.enabled += values[cpu].enabled; 159847c09d6aSSong Liu metrics[m].val.running += values[cpu].running; 159947c09d6aSSong Liu } 160047c09d6aSSong Liu key++; 160147c09d6aSSong Liu } 160247c09d6aSSong Liu } 160347c09d6aSSong Liu 160447c09d6aSSong Liu static void profile_print_readings_json(void) 160547c09d6aSSong Liu { 160647c09d6aSSong Liu __u32 m; 160747c09d6aSSong Liu 160847c09d6aSSong Liu jsonw_start_array(json_wtr); 160947c09d6aSSong Liu for (m = 0; m < ARRAY_SIZE(metrics); m++) { 161047c09d6aSSong Liu if (!metrics[m].selected) 161147c09d6aSSong Liu continue; 161247c09d6aSSong Liu jsonw_start_object(json_wtr); 161347c09d6aSSong Liu jsonw_string_field(json_wtr, "metric", metrics[m].name); 161447c09d6aSSong Liu jsonw_lluint_field(json_wtr, "run_cnt", profile_total_count); 161547c09d6aSSong Liu jsonw_lluint_field(json_wtr, "value", metrics[m].val.counter); 161647c09d6aSSong Liu jsonw_lluint_field(json_wtr, "enabled", metrics[m].val.enabled); 161747c09d6aSSong Liu jsonw_lluint_field(json_wtr, "running", metrics[m].val.running); 161847c09d6aSSong Liu 161947c09d6aSSong Liu jsonw_end_object(json_wtr); 162047c09d6aSSong Liu } 162147c09d6aSSong Liu jsonw_end_array(json_wtr); 162247c09d6aSSong Liu } 162347c09d6aSSong Liu 162447c09d6aSSong Liu static void profile_print_readings_plain(void) 162547c09d6aSSong Liu { 162647c09d6aSSong Liu __u32 m; 162747c09d6aSSong Liu 162847c09d6aSSong Liu printf("\n%18llu %-20s\n", profile_total_count, "run_cnt"); 162947c09d6aSSong Liu for (m = 0; m < ARRAY_SIZE(metrics); m++) { 163047c09d6aSSong Liu struct bpf_perf_event_value *val = &metrics[m].val; 163147c09d6aSSong Liu int r; 163247c09d6aSSong Liu 163347c09d6aSSong Liu if (!metrics[m].selected) 163447c09d6aSSong Liu continue; 163547c09d6aSSong Liu printf("%18llu %-20s", val->counter, metrics[m].name); 163647c09d6aSSong Liu 163747c09d6aSSong Liu r = metrics[m].ratio_metric - 1; 163847c09d6aSSong Liu if (r >= 0 && metrics[r].selected && 163947c09d6aSSong Liu metrics[r].val.counter > 0) { 164047c09d6aSSong Liu printf("# %8.2f %-30s", 164147c09d6aSSong Liu val->counter * metrics[m].ratio_mul / 164247c09d6aSSong Liu metrics[r].val.counter, 164347c09d6aSSong Liu metrics[m].ratio_desc); 164447c09d6aSSong Liu } else { 164547c09d6aSSong Liu printf("%-41s", ""); 164647c09d6aSSong Liu } 164747c09d6aSSong Liu 164847c09d6aSSong Liu if (val->enabled > val->running) 164947c09d6aSSong Liu printf("(%4.2f%%)", 165047c09d6aSSong Liu val->running * 100.0 / val->enabled); 165147c09d6aSSong Liu printf("\n"); 165247c09d6aSSong Liu } 165347c09d6aSSong Liu } 165447c09d6aSSong Liu 165547c09d6aSSong Liu static void profile_print_readings(void) 165647c09d6aSSong Liu { 165747c09d6aSSong Liu if (json_output) 165847c09d6aSSong Liu profile_print_readings_json(); 165947c09d6aSSong Liu else 166047c09d6aSSong Liu profile_print_readings_plain(); 166147c09d6aSSong Liu } 166247c09d6aSSong Liu 166347c09d6aSSong Liu static char *profile_target_name(int tgt_fd) 166447c09d6aSSong Liu { 166547c09d6aSSong Liu struct bpf_prog_info_linear *info_linear; 166647c09d6aSSong Liu struct bpf_func_info *func_info; 166747c09d6aSSong Liu const struct btf_type *t; 166847c09d6aSSong Liu char *name = NULL; 166947c09d6aSSong Liu struct btf *btf; 167047c09d6aSSong Liu 167147c09d6aSSong Liu info_linear = bpf_program__get_prog_info_linear( 167247c09d6aSSong Liu tgt_fd, 1UL << BPF_PROG_INFO_FUNC_INFO); 167347c09d6aSSong Liu if (IS_ERR_OR_NULL(info_linear)) { 167447c09d6aSSong Liu p_err("failed to get info_linear for prog FD %d", tgt_fd); 167547c09d6aSSong Liu return NULL; 167647c09d6aSSong Liu } 167747c09d6aSSong Liu 167847c09d6aSSong Liu if (info_linear->info.btf_id == 0 || 167947c09d6aSSong Liu btf__get_from_id(info_linear->info.btf_id, &btf)) { 168047c09d6aSSong Liu p_err("prog FD %d doesn't have valid btf", tgt_fd); 168147c09d6aSSong Liu goto out; 168247c09d6aSSong Liu } 168347c09d6aSSong Liu 168409f44b75SAndrii Nakryiko func_info = u64_to_ptr(info_linear->info.func_info); 168547c09d6aSSong Liu t = btf__type_by_id(btf, func_info[0].type_id); 168647c09d6aSSong Liu if (!t) { 168747c09d6aSSong Liu p_err("btf %d doesn't have type %d", 168847c09d6aSSong Liu info_linear->info.btf_id, func_info[0].type_id); 168947c09d6aSSong Liu goto out; 169047c09d6aSSong Liu } 169147c09d6aSSong Liu name = strdup(btf__name_by_offset(btf, t->name_off)); 169247c09d6aSSong Liu out: 169347c09d6aSSong Liu free(info_linear); 169447c09d6aSSong Liu return name; 169547c09d6aSSong Liu } 169647c09d6aSSong Liu 169747c09d6aSSong Liu static struct profiler_bpf *profile_obj; 169847c09d6aSSong Liu static int profile_tgt_fd = -1; 169947c09d6aSSong Liu static char *profile_tgt_name; 170047c09d6aSSong Liu static int *profile_perf_events; 170147c09d6aSSong Liu static int profile_perf_event_cnt; 170247c09d6aSSong Liu 170347c09d6aSSong Liu static void profile_close_perf_events(struct profiler_bpf *obj) 170447c09d6aSSong Liu { 170547c09d6aSSong Liu int i; 170647c09d6aSSong Liu 170747c09d6aSSong Liu for (i = profile_perf_event_cnt - 1; i >= 0; i--) 170847c09d6aSSong Liu close(profile_perf_events[i]); 170947c09d6aSSong Liu 171047c09d6aSSong Liu free(profile_perf_events); 171147c09d6aSSong Liu profile_perf_event_cnt = 0; 171247c09d6aSSong Liu } 171347c09d6aSSong Liu 171447c09d6aSSong Liu static int profile_open_perf_events(struct profiler_bpf *obj) 171547c09d6aSSong Liu { 171647c09d6aSSong Liu unsigned int cpu, m; 171747c09d6aSSong Liu int map_fd, pmu_fd; 171847c09d6aSSong Liu 171947c09d6aSSong Liu profile_perf_events = calloc( 172047c09d6aSSong Liu sizeof(int), obj->rodata->num_cpu * obj->rodata->num_metric); 172147c09d6aSSong Liu if (!profile_perf_events) { 172247c09d6aSSong Liu p_err("failed to allocate memory for perf_event array: %s", 172347c09d6aSSong Liu strerror(errno)); 172447c09d6aSSong Liu return -1; 172547c09d6aSSong Liu } 172647c09d6aSSong Liu map_fd = bpf_map__fd(obj->maps.events); 172747c09d6aSSong Liu if (map_fd < 0) { 172847c09d6aSSong Liu p_err("failed to get fd for events map"); 172947c09d6aSSong Liu return -1; 173047c09d6aSSong Liu } 173147c09d6aSSong Liu 173247c09d6aSSong Liu for (m = 0; m < ARRAY_SIZE(metrics); m++) { 173347c09d6aSSong Liu if (!metrics[m].selected) 173447c09d6aSSong Liu continue; 173547c09d6aSSong Liu for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) { 173647c09d6aSSong Liu pmu_fd = syscall(__NR_perf_event_open, &metrics[m].attr, 173747c09d6aSSong Liu -1/*pid*/, cpu, -1/*group_fd*/, 0); 173847c09d6aSSong Liu if (pmu_fd < 0 || 173947c09d6aSSong Liu bpf_map_update_elem(map_fd, &profile_perf_event_cnt, 174047c09d6aSSong Liu &pmu_fd, BPF_ANY) || 174147c09d6aSSong Liu ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) { 174247c09d6aSSong Liu p_err("failed to create event %s on cpu %d", 174347c09d6aSSong Liu metrics[m].name, cpu); 174447c09d6aSSong Liu return -1; 174547c09d6aSSong Liu } 174647c09d6aSSong Liu profile_perf_events[profile_perf_event_cnt++] = pmu_fd; 174747c09d6aSSong Liu } 174847c09d6aSSong Liu } 174947c09d6aSSong Liu return 0; 175047c09d6aSSong Liu } 175147c09d6aSSong Liu 175247c09d6aSSong Liu static void profile_print_and_cleanup(void) 175347c09d6aSSong Liu { 175447c09d6aSSong Liu profile_close_perf_events(profile_obj); 175547c09d6aSSong Liu profile_read_values(profile_obj); 175647c09d6aSSong Liu profile_print_readings(); 175747c09d6aSSong Liu profiler_bpf__destroy(profile_obj); 175847c09d6aSSong Liu 175947c09d6aSSong Liu close(profile_tgt_fd); 176047c09d6aSSong Liu free(profile_tgt_name); 176147c09d6aSSong Liu } 176247c09d6aSSong Liu 176347c09d6aSSong Liu static void int_exit(int signo) 176447c09d6aSSong Liu { 176547c09d6aSSong Liu profile_print_and_cleanup(); 176647c09d6aSSong Liu exit(0); 176747c09d6aSSong Liu } 176847c09d6aSSong Liu 176947c09d6aSSong Liu static int do_profile(int argc, char **argv) 177047c09d6aSSong Liu { 177147c09d6aSSong Liu int num_metric, num_cpu, err = -1; 177247c09d6aSSong Liu struct bpf_program *prog; 177347c09d6aSSong Liu unsigned long duration; 177447c09d6aSSong Liu char *endptr; 177547c09d6aSSong Liu 177647c09d6aSSong Liu /* we at least need two args for the prog and one metric */ 177747c09d6aSSong Liu if (!REQ_ARGS(3)) 177847c09d6aSSong Liu return -EINVAL; 177947c09d6aSSong Liu 178047c09d6aSSong Liu /* parse target fd */ 178147c09d6aSSong Liu profile_tgt_fd = prog_parse_fd(&argc, &argv); 178247c09d6aSSong Liu if (profile_tgt_fd < 0) { 178347c09d6aSSong Liu p_err("failed to parse fd"); 178447c09d6aSSong Liu return -1; 178547c09d6aSSong Liu } 178647c09d6aSSong Liu 178747c09d6aSSong Liu /* parse profiling optional duration */ 178847c09d6aSSong Liu if (argc > 2 && is_prefix(argv[0], "duration")) { 178947c09d6aSSong Liu NEXT_ARG(); 179047c09d6aSSong Liu duration = strtoul(*argv, &endptr, 0); 179147c09d6aSSong Liu if (*endptr) 179247c09d6aSSong Liu usage(); 179347c09d6aSSong Liu NEXT_ARG(); 179447c09d6aSSong Liu } else { 179547c09d6aSSong Liu duration = UINT_MAX; 179647c09d6aSSong Liu } 179747c09d6aSSong Liu 179847c09d6aSSong Liu num_metric = profile_parse_metrics(argc, argv); 179947c09d6aSSong Liu if (num_metric <= 0) 180047c09d6aSSong Liu goto out; 180147c09d6aSSong Liu 180247c09d6aSSong Liu num_cpu = libbpf_num_possible_cpus(); 180347c09d6aSSong Liu if (num_cpu <= 0) { 180447c09d6aSSong Liu p_err("failed to identify number of CPUs"); 180547c09d6aSSong Liu goto out; 180647c09d6aSSong Liu } 180747c09d6aSSong Liu 180847c09d6aSSong Liu profile_obj = profiler_bpf__open(); 180947c09d6aSSong Liu if (!profile_obj) { 181047c09d6aSSong Liu p_err("failed to open and/or load BPF object"); 181147c09d6aSSong Liu goto out; 181247c09d6aSSong Liu } 181347c09d6aSSong Liu 181447c09d6aSSong Liu profile_obj->rodata->num_cpu = num_cpu; 181547c09d6aSSong Liu profile_obj->rodata->num_metric = num_metric; 181647c09d6aSSong Liu 181747c09d6aSSong Liu /* adjust map sizes */ 181847c09d6aSSong Liu bpf_map__resize(profile_obj->maps.events, num_metric * num_cpu); 181947c09d6aSSong Liu bpf_map__resize(profile_obj->maps.fentry_readings, num_metric); 182047c09d6aSSong Liu bpf_map__resize(profile_obj->maps.accum_readings, num_metric); 182147c09d6aSSong Liu bpf_map__resize(profile_obj->maps.counts, 1); 182247c09d6aSSong Liu 182347c09d6aSSong Liu /* change target name */ 182447c09d6aSSong Liu profile_tgt_name = profile_target_name(profile_tgt_fd); 182547c09d6aSSong Liu if (!profile_tgt_name) 182647c09d6aSSong Liu goto out; 182747c09d6aSSong Liu 182847c09d6aSSong Liu bpf_object__for_each_program(prog, profile_obj->obj) { 182947c09d6aSSong Liu err = bpf_program__set_attach_target(prog, profile_tgt_fd, 183047c09d6aSSong Liu profile_tgt_name); 183147c09d6aSSong Liu if (err) { 183247c09d6aSSong Liu p_err("failed to set attach target\n"); 183347c09d6aSSong Liu goto out; 183447c09d6aSSong Liu } 183547c09d6aSSong Liu } 183647c09d6aSSong Liu 183747c09d6aSSong Liu set_max_rlimit(); 183847c09d6aSSong Liu err = profiler_bpf__load(profile_obj); 183947c09d6aSSong Liu if (err) { 184047c09d6aSSong Liu p_err("failed to load profile_obj"); 184147c09d6aSSong Liu goto out; 184247c09d6aSSong Liu } 184347c09d6aSSong Liu 184447c09d6aSSong Liu err = profile_open_perf_events(profile_obj); 184547c09d6aSSong Liu if (err) 184647c09d6aSSong Liu goto out; 184747c09d6aSSong Liu 184847c09d6aSSong Liu err = profiler_bpf__attach(profile_obj); 184947c09d6aSSong Liu if (err) { 185047c09d6aSSong Liu p_err("failed to attach profile_obj"); 185147c09d6aSSong Liu goto out; 185247c09d6aSSong Liu } 185347c09d6aSSong Liu signal(SIGINT, int_exit); 185447c09d6aSSong Liu 185547c09d6aSSong Liu sleep(duration); 185647c09d6aSSong Liu profile_print_and_cleanup(); 185747c09d6aSSong Liu return 0; 185847c09d6aSSong Liu 185947c09d6aSSong Liu out: 186047c09d6aSSong Liu profile_close_perf_events(profile_obj); 186147c09d6aSSong Liu if (profile_obj) 186247c09d6aSSong Liu profiler_bpf__destroy(profile_obj); 186347c09d6aSSong Liu close(profile_tgt_fd); 186447c09d6aSSong Liu free(profile_tgt_name); 186547c09d6aSSong Liu return err; 186647c09d6aSSong Liu } 186747c09d6aSSong Liu 186847c09d6aSSong Liu #endif /* BPFTOOL_WITHOUT_SKELETONS */ 186947c09d6aSSong Liu 187071bb428fSJakub Kicinski static int do_help(int argc, char **argv) 187171bb428fSJakub Kicinski { 1872004b45c0SQuentin Monnet if (json_output) { 1873004b45c0SQuentin Monnet jsonw_null(json_wtr); 1874004b45c0SQuentin Monnet return 0; 1875004b45c0SQuentin Monnet } 1876004b45c0SQuentin Monnet 187771bb428fSJakub Kicinski fprintf(stderr, 187890040351SQuentin Monnet "Usage: %1$s %2$s { show | list } [PROG]\n" 187990040351SQuentin Monnet " %1$s %2$s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n" 188090040351SQuentin Monnet " %1$s %2$s dump jited PROG [{ file FILE | opcodes | linum }]\n" 188190040351SQuentin Monnet " %1$s %2$s pin PROG FILE\n" 188290040351SQuentin Monnet " %1$s %2$s { load | loadall } OBJ PATH \\\n" 188377380998SStanislav Fomichev " [type TYPE] [dev NAME] \\\n" 18843767a94bSStanislav Fomichev " [map { idx IDX | name NAME } MAP]\\\n" 18853767a94bSStanislav Fomichev " [pinmaps MAP_DIR]\n" 188690040351SQuentin Monnet " %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n" 188790040351SQuentin Monnet " %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n" 188890040351SQuentin Monnet " %1$s %2$s run PROG \\\n" 1889ba95c745SQuentin Monnet " data_in FILE \\\n" 1890ba95c745SQuentin Monnet " [data_out FILE [data_size_out L]] \\\n" 1891ba95c745SQuentin Monnet " [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n" 1892ba95c745SQuentin Monnet " [repeat N]\n" 189390040351SQuentin Monnet " %1$s %2$s profile PROG [duration DURATION] METRICs\n" 189490040351SQuentin Monnet " %1$s %2$s tracelog\n" 189590040351SQuentin Monnet " %1$s %2$s help\n" 189671bb428fSJakub Kicinski "\n" 18973ff5a4dcSJakub Kicinski " " HELP_SPEC_MAP "\n" 189871bb428fSJakub Kicinski " " HELP_SPEC_PROGRAM "\n" 189949f2cba3SJakub Kicinski " TYPE := { socket | kprobe | kretprobe | classifier | action |\n" 190049f2cba3SJakub Kicinski " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n" 190149f2cba3SJakub Kicinski " cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n" 190249f2cba3SJakub Kicinski " lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n" 1903f25377eeSAndrey Ignatov " sk_reuseport | flow_dissector | cgroup/sysctl |\n" 190449f2cba3SJakub Kicinski " cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n" 190549f2cba3SJakub Kicinski " cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n" 190605ee19c1SDaniel Borkmann " cgroup/getpeername4 | cgroup/getpeername6 |\n" 190705ee19c1SDaniel Borkmann " cgroup/getsockname4 | cgroup/getsockname6 | cgroup/sendmsg4 |\n" 190805ee19c1SDaniel Borkmann " cgroup/sendmsg6 | cgroup/recvmsg4 | cgroup/recvmsg6 |\n" 190905ee19c1SDaniel Borkmann " cgroup/getsockopt | cgroup/setsockopt |\n" 191093a3545dSJakub Sitnicki " struct_ops | fentry | fexit | freplace | sk_lookup }\n" 1911a5d9265eSAlban Crequy " ATTACH_TYPE := { msg_verdict | stream_verdict | stream_parser |\n" 1912092f0892SStanislav Fomichev " flow_dissector }\n" 191347c09d6aSSong Liu " METRIC := { cycles | instructions | l1d_loads | llc_misses }\n" 19140641c3c8SQuentin Monnet " " HELP_SPEC_OPTIONS "\n" 191571bb428fSJakub Kicinski "", 191690040351SQuentin Monnet bin_name, argv[-2]); 191771bb428fSJakub Kicinski 191871bb428fSJakub Kicinski return 0; 191971bb428fSJakub Kicinski } 192071bb428fSJakub Kicinski 192171bb428fSJakub Kicinski static const struct cmd cmds[] = { 192271bb428fSJakub Kicinski { "show", do_show }, 19236ebe6dbdSJakub Kicinski { "list", do_show }, 19249f606179SQuentin Monnet { "help", do_help }, 192571bb428fSJakub Kicinski { "dump", do_dump }, 192671bb428fSJakub Kicinski { "pin", do_pin }, 192749a086c2SRoman Gushchin { "load", do_load }, 192877380998SStanislav Fomichev { "loadall", do_loadall }, 1929b7d3826cSJohn Fastabend { "attach", do_attach }, 1930b7d3826cSJohn Fastabend { "detach", do_detach }, 193130da46b5SQuentin Monnet { "tracelog", do_tracelog }, 1932ba95c745SQuentin Monnet { "run", do_run }, 193347c09d6aSSong Liu { "profile", do_profile }, 193471bb428fSJakub Kicinski { 0 } 193571bb428fSJakub Kicinski }; 193671bb428fSJakub Kicinski 193771bb428fSJakub Kicinski int do_prog(int argc, char **argv) 193871bb428fSJakub Kicinski { 193971bb428fSJakub Kicinski return cmd_select(cmds, argc, argv, do_help); 194071bb428fSJakub Kicinski } 1941