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 32aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX "bpf_metadata_" 33aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX_LEN (sizeof(BPF_METADATA_PREFIX) - 1) 34aff52e68SYiFei Zhu 359023497dSTobias Klauser const char * const prog_type_name[] = { 369023497dSTobias Klauser [BPF_PROG_TYPE_UNSPEC] = "unspec", 379023497dSTobias Klauser [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", 389023497dSTobias Klauser [BPF_PROG_TYPE_KPROBE] = "kprobe", 399023497dSTobias Klauser [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", 409023497dSTobias Klauser [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", 419023497dSTobias Klauser [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", 429023497dSTobias Klauser [BPF_PROG_TYPE_XDP] = "xdp", 439023497dSTobias Klauser [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", 449023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", 459023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", 469023497dSTobias Klauser [BPF_PROG_TYPE_LWT_IN] = "lwt_in", 479023497dSTobias Klauser [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", 489023497dSTobias Klauser [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", 499023497dSTobias Klauser [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", 509023497dSTobias Klauser [BPF_PROG_TYPE_SK_SKB] = "sk_skb", 519023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device", 529023497dSTobias Klauser [BPF_PROG_TYPE_SK_MSG] = "sk_msg", 539023497dSTobias Klauser [BPF_PROG_TYPE_RAW_TRACEPOINT] = "raw_tracepoint", 549023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr", 559023497dSTobias Klauser [BPF_PROG_TYPE_LWT_SEG6LOCAL] = "lwt_seg6local", 569023497dSTobias Klauser [BPF_PROG_TYPE_LIRC_MODE2] = "lirc_mode2", 579023497dSTobias Klauser [BPF_PROG_TYPE_SK_REUSEPORT] = "sk_reuseport", 589023497dSTobias Klauser [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector", 599023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SYSCTL] = "cgroup_sysctl", 609023497dSTobias Klauser [BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE] = "raw_tracepoint_writable", 619023497dSTobias Klauser [BPF_PROG_TYPE_CGROUP_SOCKOPT] = "cgroup_sockopt", 629023497dSTobias Klauser [BPF_PROG_TYPE_TRACING] = "tracing", 639023497dSTobias Klauser [BPF_PROG_TYPE_STRUCT_OPS] = "struct_ops", 649023497dSTobias Klauser [BPF_PROG_TYPE_EXT] = "ext", 659a97c9d2SQuentin Monnet [BPF_PROG_TYPE_LSM] = "lsm", 6693a3545dSJakub Sitnicki [BPF_PROG_TYPE_SK_LOOKUP] = "sk_lookup", 679023497dSTobias Klauser }; 689023497dSTobias Klauser 699023497dSTobias Klauser const size_t prog_type_name_size = ARRAY_SIZE(prog_type_name); 709023497dSTobias Klauser 71ec202509SPaul Chaignon enum dump_mode { 72ec202509SPaul Chaignon DUMP_JITED, 73ec202509SPaul Chaignon DUMP_XLATED, 74ec202509SPaul Chaignon }; 75ec202509SPaul Chaignon 76b7d3826cSJohn Fastabend static const char * const attach_type_strings[] = { 77b7d3826cSJohn Fastabend [BPF_SK_SKB_STREAM_PARSER] = "stream_parser", 78b7d3826cSJohn Fastabend [BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict", 79*a7ba4558SCong Wang [BPF_SK_SKB_VERDICT] = "skb_verdict", 80b7d3826cSJohn Fastabend [BPF_SK_MSG_VERDICT] = "msg_verdict", 81092f0892SStanislav Fomichev [BPF_FLOW_DISSECTOR] = "flow_dissector", 82b7d3826cSJohn Fastabend [__MAX_BPF_ATTACH_TYPE] = NULL, 83b7d3826cSJohn Fastabend }; 84b7d3826cSJohn Fastabend 85c101189bSQuentin Monnet static enum bpf_attach_type parse_attach_type(const char *str) 86b7d3826cSJohn Fastabend { 87b7d3826cSJohn Fastabend enum bpf_attach_type type; 88b7d3826cSJohn Fastabend 89b7d3826cSJohn Fastabend for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { 90b7d3826cSJohn Fastabend if (attach_type_strings[type] && 91b7d3826cSJohn Fastabend is_prefix(str, attach_type_strings[type])) 92b7d3826cSJohn Fastabend return type; 93b7d3826cSJohn Fastabend } 94b7d3826cSJohn Fastabend 95b7d3826cSJohn Fastabend return __MAX_BPF_ATTACH_TYPE; 96b7d3826cSJohn Fastabend } 97b7d3826cSJohn Fastabend 9871bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) 9971bb428fSJakub Kicinski { 10071bb428fSJakub Kicinski struct timespec real_time_ts, boot_time_ts; 10171bb428fSJakub Kicinski time_t wallclock_secs; 10271bb428fSJakub Kicinski struct tm load_tm; 10371bb428fSJakub Kicinski 10471bb428fSJakub Kicinski buf[--size] = '\0'; 10571bb428fSJakub Kicinski 10671bb428fSJakub Kicinski if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || 10771bb428fSJakub Kicinski clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { 10871bb428fSJakub Kicinski perror("Can't read clocks"); 10971bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 11071bb428fSJakub Kicinski return; 11171bb428fSJakub Kicinski } 11271bb428fSJakub Kicinski 11371bb428fSJakub Kicinski wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + 11407480cbcSJakub Kicinski (real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) / 11507480cbcSJakub Kicinski 1000000000; 11607480cbcSJakub Kicinski 11771bb428fSJakub Kicinski 11871bb428fSJakub Kicinski if (!localtime_r(&wallclock_secs, &load_tm)) { 11971bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 12071bb428fSJakub Kicinski return; 12171bb428fSJakub Kicinski } 12271bb428fSJakub Kicinski 123a3fe1f6fSQuentin Monnet if (json_output) 124a3fe1f6fSQuentin Monnet strftime(buf, size, "%s", &load_tm); 125a3fe1f6fSQuentin Monnet else 126a3fe1f6fSQuentin Monnet strftime(buf, size, "%FT%T%z", &load_tm); 12771bb428fSJakub Kicinski } 12871bb428fSJakub Kicinski 1296e7e034eSQuentin Monnet static void show_prog_maps(int fd, __u32 num_maps) 13071bb428fSJakub Kicinski { 13171bb428fSJakub Kicinski struct bpf_prog_info info = {}; 13271bb428fSJakub Kicinski __u32 len = sizeof(info); 13371bb428fSJakub Kicinski __u32 map_ids[num_maps]; 13471bb428fSJakub Kicinski unsigned int i; 13571bb428fSJakub Kicinski int err; 13671bb428fSJakub Kicinski 13771bb428fSJakub Kicinski info.nr_map_ids = num_maps; 13871bb428fSJakub Kicinski info.map_ids = ptr_to_u64(map_ids); 13971bb428fSJakub Kicinski 14071bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 14171bb428fSJakub Kicinski if (err || !info.nr_map_ids) 14271bb428fSJakub Kicinski return; 14371bb428fSJakub Kicinski 144743cc665SQuentin Monnet if (json_output) { 145743cc665SQuentin Monnet jsonw_name(json_wtr, "map_ids"); 146743cc665SQuentin Monnet jsonw_start_array(json_wtr); 147743cc665SQuentin Monnet for (i = 0; i < info.nr_map_ids; i++) 148743cc665SQuentin Monnet jsonw_uint(json_wtr, map_ids[i]); 149743cc665SQuentin Monnet jsonw_end_array(json_wtr); 150743cc665SQuentin Monnet } else { 15171bb428fSJakub Kicinski printf(" map_ids "); 15271bb428fSJakub Kicinski for (i = 0; i < info.nr_map_ids; i++) 15371bb428fSJakub Kicinski printf("%u%s", map_ids[i], 15471bb428fSJakub Kicinski i == info.nr_map_ids - 1 ? "" : ","); 15571bb428fSJakub Kicinski } 15671bb428fSJakub Kicinski } 15771bb428fSJakub Kicinski 158aff52e68SYiFei Zhu static void *find_metadata(int prog_fd, struct bpf_map_info *map_info) 159aff52e68SYiFei Zhu { 160aff52e68SYiFei Zhu struct bpf_prog_info prog_info; 161aff52e68SYiFei Zhu __u32 prog_info_len; 162aff52e68SYiFei Zhu __u32 map_info_len; 163aff52e68SYiFei Zhu void *value = NULL; 164aff52e68SYiFei Zhu __u32 *map_ids; 165aff52e68SYiFei Zhu int nr_maps; 166aff52e68SYiFei Zhu int key = 0; 167aff52e68SYiFei Zhu int map_fd; 168aff52e68SYiFei Zhu int ret; 169aff52e68SYiFei Zhu __u32 i; 170aff52e68SYiFei Zhu 171aff52e68SYiFei Zhu memset(&prog_info, 0, sizeof(prog_info)); 172aff52e68SYiFei Zhu prog_info_len = sizeof(prog_info); 173aff52e68SYiFei Zhu ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len); 174aff52e68SYiFei Zhu if (ret) 175aff52e68SYiFei Zhu return NULL; 176aff52e68SYiFei Zhu 177aff52e68SYiFei Zhu if (!prog_info.nr_map_ids) 178aff52e68SYiFei Zhu return NULL; 179aff52e68SYiFei Zhu 180aff52e68SYiFei Zhu map_ids = calloc(prog_info.nr_map_ids, sizeof(__u32)); 181aff52e68SYiFei Zhu if (!map_ids) 182aff52e68SYiFei Zhu return NULL; 183aff52e68SYiFei Zhu 184aff52e68SYiFei Zhu nr_maps = prog_info.nr_map_ids; 185aff52e68SYiFei Zhu memset(&prog_info, 0, sizeof(prog_info)); 186aff52e68SYiFei Zhu prog_info.nr_map_ids = nr_maps; 187aff52e68SYiFei Zhu prog_info.map_ids = ptr_to_u64(map_ids); 188aff52e68SYiFei Zhu prog_info_len = sizeof(prog_info); 189aff52e68SYiFei Zhu 190aff52e68SYiFei Zhu ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len); 191aff52e68SYiFei Zhu if (ret) 192aff52e68SYiFei Zhu goto free_map_ids; 193aff52e68SYiFei Zhu 194aff52e68SYiFei Zhu for (i = 0; i < prog_info.nr_map_ids; i++) { 195aff52e68SYiFei Zhu map_fd = bpf_map_get_fd_by_id(map_ids[i]); 196aff52e68SYiFei Zhu if (map_fd < 0) 197aff52e68SYiFei Zhu goto free_map_ids; 198aff52e68SYiFei Zhu 199aff52e68SYiFei Zhu memset(map_info, 0, sizeof(*map_info)); 200aff52e68SYiFei Zhu map_info_len = sizeof(*map_info); 201aff52e68SYiFei Zhu ret = bpf_obj_get_info_by_fd(map_fd, map_info, &map_info_len); 202aff52e68SYiFei Zhu if (ret < 0) { 203aff52e68SYiFei Zhu close(map_fd); 204aff52e68SYiFei Zhu goto free_map_ids; 205aff52e68SYiFei Zhu } 206aff52e68SYiFei Zhu 207aff52e68SYiFei Zhu if (map_info->type != BPF_MAP_TYPE_ARRAY || 208aff52e68SYiFei Zhu map_info->key_size != sizeof(int) || 209aff52e68SYiFei Zhu map_info->max_entries != 1 || 210aff52e68SYiFei Zhu !map_info->btf_value_type_id || 211aff52e68SYiFei Zhu !strstr(map_info->name, ".rodata")) { 212aff52e68SYiFei Zhu close(map_fd); 213aff52e68SYiFei Zhu continue; 214aff52e68SYiFei Zhu } 215aff52e68SYiFei Zhu 216aff52e68SYiFei Zhu value = malloc(map_info->value_size); 217aff52e68SYiFei Zhu if (!value) { 218aff52e68SYiFei Zhu close(map_fd); 219aff52e68SYiFei Zhu goto free_map_ids; 220aff52e68SYiFei Zhu } 221aff52e68SYiFei Zhu 222aff52e68SYiFei Zhu if (bpf_map_lookup_elem(map_fd, &key, value)) { 223aff52e68SYiFei Zhu close(map_fd); 224aff52e68SYiFei Zhu free(value); 225aff52e68SYiFei Zhu value = NULL; 226aff52e68SYiFei Zhu goto free_map_ids; 227aff52e68SYiFei Zhu } 228aff52e68SYiFei Zhu 229aff52e68SYiFei Zhu close(map_fd); 230aff52e68SYiFei Zhu break; 231aff52e68SYiFei Zhu } 232aff52e68SYiFei Zhu 233aff52e68SYiFei Zhu free_map_ids: 234aff52e68SYiFei Zhu free(map_ids); 235aff52e68SYiFei Zhu return value; 236aff52e68SYiFei Zhu } 237aff52e68SYiFei Zhu 238aff52e68SYiFei Zhu static bool has_metadata_prefix(const char *s) 239aff52e68SYiFei Zhu { 240aff52e68SYiFei Zhu return strncmp(s, BPF_METADATA_PREFIX, BPF_METADATA_PREFIX_LEN) == 0; 241aff52e68SYiFei Zhu } 242aff52e68SYiFei Zhu 243aff52e68SYiFei Zhu static void show_prog_metadata(int fd, __u32 num_maps) 244aff52e68SYiFei Zhu { 245aff52e68SYiFei Zhu const struct btf_type *t_datasec, *t_var; 246aff52e68SYiFei Zhu struct bpf_map_info map_info; 247aff52e68SYiFei Zhu struct btf_var_secinfo *vsi; 248aff52e68SYiFei Zhu bool printed_header = false; 249aff52e68SYiFei Zhu struct btf *btf = NULL; 250aff52e68SYiFei Zhu unsigned int i, vlen; 251aff52e68SYiFei Zhu void *value = NULL; 252aff52e68SYiFei Zhu const char *name; 253aff52e68SYiFei Zhu int err; 254aff52e68SYiFei Zhu 255aff52e68SYiFei Zhu if (!num_maps) 256aff52e68SYiFei Zhu return; 257aff52e68SYiFei Zhu 258aff52e68SYiFei Zhu memset(&map_info, 0, sizeof(map_info)); 259aff52e68SYiFei Zhu value = find_metadata(fd, &map_info); 260aff52e68SYiFei Zhu if (!value) 261aff52e68SYiFei Zhu return; 262aff52e68SYiFei Zhu 263aff52e68SYiFei Zhu err = btf__get_from_id(map_info.btf_id, &btf); 264aff52e68SYiFei Zhu if (err || !btf) 265aff52e68SYiFei Zhu goto out_free; 266aff52e68SYiFei Zhu 267aff52e68SYiFei Zhu t_datasec = btf__type_by_id(btf, map_info.btf_value_type_id); 268aff52e68SYiFei Zhu if (!btf_is_datasec(t_datasec)) 269aff52e68SYiFei Zhu goto out_free; 270aff52e68SYiFei Zhu 271aff52e68SYiFei Zhu vlen = btf_vlen(t_datasec); 272aff52e68SYiFei Zhu vsi = btf_var_secinfos(t_datasec); 273aff52e68SYiFei Zhu 274aff52e68SYiFei Zhu /* We don't proceed to check the kinds of the elements of the DATASEC. 275aff52e68SYiFei Zhu * The verifier enforces them to be BTF_KIND_VAR. 276aff52e68SYiFei Zhu */ 277aff52e68SYiFei Zhu 278aff52e68SYiFei Zhu if (json_output) { 279aff52e68SYiFei Zhu struct btf_dumper d = { 280aff52e68SYiFei Zhu .btf = btf, 281aff52e68SYiFei Zhu .jw = json_wtr, 282aff52e68SYiFei Zhu .is_plain_text = false, 283aff52e68SYiFei Zhu }; 284aff52e68SYiFei Zhu 285aff52e68SYiFei Zhu for (i = 0; i < vlen; i++, vsi++) { 286aff52e68SYiFei Zhu t_var = btf__type_by_id(btf, vsi->type); 287aff52e68SYiFei Zhu name = btf__name_by_offset(btf, t_var->name_off); 288aff52e68SYiFei Zhu 289aff52e68SYiFei Zhu if (!has_metadata_prefix(name)) 290aff52e68SYiFei Zhu continue; 291aff52e68SYiFei Zhu 292aff52e68SYiFei Zhu if (!printed_header) { 293aff52e68SYiFei Zhu jsonw_name(json_wtr, "metadata"); 294aff52e68SYiFei Zhu jsonw_start_object(json_wtr); 295aff52e68SYiFei Zhu printed_header = true; 296aff52e68SYiFei Zhu } 297aff52e68SYiFei Zhu 298aff52e68SYiFei Zhu jsonw_name(json_wtr, name + BPF_METADATA_PREFIX_LEN); 299aff52e68SYiFei Zhu err = btf_dumper_type(&d, t_var->type, value + vsi->offset); 300aff52e68SYiFei Zhu if (err) { 301aff52e68SYiFei Zhu p_err("btf dump failed: %d", err); 302aff52e68SYiFei Zhu break; 303aff52e68SYiFei Zhu } 304aff52e68SYiFei Zhu } 305aff52e68SYiFei Zhu if (printed_header) 306aff52e68SYiFei Zhu jsonw_end_object(json_wtr); 307aff52e68SYiFei Zhu } else { 308aff52e68SYiFei Zhu json_writer_t *btf_wtr = jsonw_new(stdout); 309aff52e68SYiFei Zhu struct btf_dumper d = { 310aff52e68SYiFei Zhu .btf = btf, 311aff52e68SYiFei Zhu .jw = btf_wtr, 312aff52e68SYiFei Zhu .is_plain_text = true, 313aff52e68SYiFei Zhu }; 314aff52e68SYiFei Zhu 315aff52e68SYiFei Zhu if (!btf_wtr) { 316aff52e68SYiFei Zhu p_err("jsonw alloc failed"); 317aff52e68SYiFei Zhu goto out_free; 318aff52e68SYiFei Zhu } 319aff52e68SYiFei Zhu 320aff52e68SYiFei Zhu for (i = 0; i < vlen; i++, vsi++) { 321aff52e68SYiFei Zhu t_var = btf__type_by_id(btf, vsi->type); 322aff52e68SYiFei Zhu name = btf__name_by_offset(btf, t_var->name_off); 323aff52e68SYiFei Zhu 324aff52e68SYiFei Zhu if (!has_metadata_prefix(name)) 325aff52e68SYiFei Zhu continue; 326aff52e68SYiFei Zhu 327aff52e68SYiFei Zhu if (!printed_header) { 328aff52e68SYiFei Zhu printf("\tmetadata:"); 329aff52e68SYiFei Zhu printed_header = true; 330aff52e68SYiFei Zhu } 331aff52e68SYiFei Zhu 332aff52e68SYiFei Zhu printf("\n\t\t%s = ", name + BPF_METADATA_PREFIX_LEN); 333aff52e68SYiFei Zhu 334aff52e68SYiFei Zhu jsonw_reset(btf_wtr); 335aff52e68SYiFei Zhu err = btf_dumper_type(&d, t_var->type, value + vsi->offset); 336aff52e68SYiFei Zhu if (err) { 337aff52e68SYiFei Zhu p_err("btf dump failed: %d", err); 338aff52e68SYiFei Zhu break; 339aff52e68SYiFei Zhu } 340aff52e68SYiFei Zhu } 341aff52e68SYiFei Zhu if (printed_header) 342aff52e68SYiFei Zhu jsonw_destroy(&btf_wtr); 343aff52e68SYiFei Zhu } 344aff52e68SYiFei Zhu 345aff52e68SYiFei Zhu out_free: 346aff52e68SYiFei Zhu btf__free(btf); 347aff52e68SYiFei Zhu free(value); 348aff52e68SYiFei Zhu } 349aff52e68SYiFei Zhu 350ec202509SPaul Chaignon static void print_prog_header_json(struct bpf_prog_info *info) 351743cc665SQuentin Monnet { 352743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "id", info->id); 353743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 354743cc665SQuentin Monnet jsonw_string_field(json_wtr, "type", 355743cc665SQuentin Monnet prog_type_name[info->type]); 35671bb428fSJakub Kicinski else 357743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "type", info->type); 35871bb428fSJakub Kicinski 359743cc665SQuentin Monnet if (*info->name) 360743cc665SQuentin Monnet jsonw_string_field(json_wtr, "name", info->name); 36171bb428fSJakub Kicinski 362743cc665SQuentin Monnet jsonw_name(json_wtr, "tag"); 363743cc665SQuentin Monnet jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", 364743cc665SQuentin Monnet info->tag[0], info->tag[1], info->tag[2], info->tag[3], 365743cc665SQuentin Monnet info->tag[4], info->tag[5], info->tag[6], info->tag[7]); 36671bb428fSJakub Kicinski 3679b984a20SJiri Olsa jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible); 36888ad472bSAlexei Starovoitov if (info->run_time_ns) { 36988ad472bSAlexei Starovoitov jsonw_uint_field(json_wtr, "run_time_ns", info->run_time_ns); 37088ad472bSAlexei Starovoitov jsonw_uint_field(json_wtr, "run_cnt", info->run_cnt); 37188ad472bSAlexei Starovoitov } 3729ed9e9baSAlexei Starovoitov if (info->recursion_misses) 3739ed9e9baSAlexei Starovoitov jsonw_uint_field(json_wtr, "recursion_misses", info->recursion_misses); 374ec202509SPaul Chaignon } 3759b984a20SJiri Olsa 376ec202509SPaul Chaignon static void print_prog_json(struct bpf_prog_info *info, int fd) 377ec202509SPaul Chaignon { 378ec202509SPaul Chaignon char *memlock; 379ec202509SPaul Chaignon 380ec202509SPaul Chaignon jsonw_start_object(json_wtr); 381ec202509SPaul Chaignon print_prog_header_json(info); 38252262210SJakub Kicinski print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); 38352262210SJakub Kicinski 384743cc665SQuentin Monnet if (info->load_time) { 38571bb428fSJakub Kicinski char buf[32]; 38671bb428fSJakub Kicinski 387743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 38871bb428fSJakub Kicinski 38971bb428fSJakub Kicinski /* Piggy back on load_time, since 0 uid is a valid one */ 390a3fe1f6fSQuentin Monnet jsonw_name(json_wtr, "loaded_at"); 391a3fe1f6fSQuentin Monnet jsonw_printf(json_wtr, "%s", buf); 392743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "uid", info->created_by_uid); 39371bb428fSJakub Kicinski } 39471bb428fSJakub Kicinski 395743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); 39671bb428fSJakub Kicinski 397743cc665SQuentin Monnet if (info->jited_prog_len) { 398743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", true); 399743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); 400743cc665SQuentin Monnet } else { 401743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", false); 402743cc665SQuentin Monnet } 403743cc665SQuentin Monnet 404743cc665SQuentin Monnet memlock = get_fdinfo(fd, "memlock"); 405743cc665SQuentin Monnet if (memlock) 406743cc665SQuentin Monnet jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 407743cc665SQuentin Monnet free(memlock); 408743cc665SQuentin Monnet 409743cc665SQuentin Monnet if (info->nr_map_ids) 410743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 411743cc665SQuentin Monnet 412569b0c77SPrashant Bhole if (info->btf_id) 413569b0c77SPrashant Bhole jsonw_int_field(json_wtr, "btf_id", info->btf_id); 414569b0c77SPrashant Bhole 4154990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 4164990f1f4SPrashant Bhole struct pinned_obj *obj; 4174990f1f4SPrashant Bhole 4184990f1f4SPrashant Bhole jsonw_name(json_wtr, "pinned"); 4194990f1f4SPrashant Bhole jsonw_start_array(json_wtr); 4204990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 4214990f1f4SPrashant Bhole if (obj->id == info->id) 4224990f1f4SPrashant Bhole jsonw_string(json_wtr, obj->path); 4234990f1f4SPrashant Bhole } 4244990f1f4SPrashant Bhole jsonw_end_array(json_wtr); 4254990f1f4SPrashant Bhole } 4264990f1f4SPrashant Bhole 427d53dee3fSAndrii Nakryiko emit_obj_refs_json(&refs_table, info->id, json_wtr); 428d53dee3fSAndrii Nakryiko 429aff52e68SYiFei Zhu show_prog_metadata(fd, info->nr_map_ids); 430aff52e68SYiFei Zhu 431743cc665SQuentin Monnet jsonw_end_object(json_wtr); 432743cc665SQuentin Monnet } 433743cc665SQuentin Monnet 434ec202509SPaul Chaignon static void print_prog_header_plain(struct bpf_prog_info *info) 435743cc665SQuentin Monnet { 436743cc665SQuentin Monnet printf("%u: ", info->id); 437743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 438743cc665SQuentin Monnet printf("%s ", prog_type_name[info->type]); 439743cc665SQuentin Monnet else 440743cc665SQuentin Monnet printf("type %u ", info->type); 441743cc665SQuentin Monnet 442743cc665SQuentin Monnet if (*info->name) 443743cc665SQuentin Monnet printf("name %s ", info->name); 444743cc665SQuentin Monnet 445743cc665SQuentin Monnet printf("tag "); 446743cc665SQuentin Monnet fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); 44752262210SJakub Kicinski print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); 4489b984a20SJiri Olsa printf("%s", info->gpl_compatible ? " gpl" : ""); 44988ad472bSAlexei Starovoitov if (info->run_time_ns) 45088ad472bSAlexei Starovoitov printf(" run_time_ns %lld run_cnt %lld", 45188ad472bSAlexei Starovoitov info->run_time_ns, info->run_cnt); 4529ed9e9baSAlexei Starovoitov if (info->recursion_misses) 4539ed9e9baSAlexei Starovoitov printf(" recursion_misses %lld", info->recursion_misses); 454743cc665SQuentin Monnet printf("\n"); 455ec202509SPaul Chaignon } 456ec202509SPaul Chaignon 457ec202509SPaul Chaignon static void print_prog_plain(struct bpf_prog_info *info, int fd) 458ec202509SPaul Chaignon { 459ec202509SPaul Chaignon char *memlock; 460ec202509SPaul Chaignon 461ec202509SPaul Chaignon print_prog_header_plain(info); 462743cc665SQuentin Monnet 463743cc665SQuentin Monnet if (info->load_time) { 464743cc665SQuentin Monnet char buf[32]; 465743cc665SQuentin Monnet 466743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 467743cc665SQuentin Monnet 468743cc665SQuentin Monnet /* Piggy back on load_time, since 0 uid is a valid one */ 469743cc665SQuentin Monnet printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); 470743cc665SQuentin Monnet } 471743cc665SQuentin Monnet 472743cc665SQuentin Monnet printf("\txlated %uB", info->xlated_prog_len); 473743cc665SQuentin Monnet 474743cc665SQuentin Monnet if (info->jited_prog_len) 475743cc665SQuentin Monnet printf(" jited %uB", info->jited_prog_len); 47671bb428fSJakub Kicinski else 47771bb428fSJakub Kicinski printf(" not jited"); 47871bb428fSJakub Kicinski 47971bb428fSJakub Kicinski memlock = get_fdinfo(fd, "memlock"); 48071bb428fSJakub Kicinski if (memlock) 48171bb428fSJakub Kicinski printf(" memlock %sB", memlock); 48271bb428fSJakub Kicinski free(memlock); 48371bb428fSJakub Kicinski 484743cc665SQuentin Monnet if (info->nr_map_ids) 485743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 48671bb428fSJakub Kicinski 4874990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 4884990f1f4SPrashant Bhole struct pinned_obj *obj; 4894990f1f4SPrashant Bhole 4904990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 4914990f1f4SPrashant Bhole if (obj->id == info->id) 492a8bfd2bcSQuentin Monnet printf("\n\tpinned %s", obj->path); 4934990f1f4SPrashant Bhole } 4944990f1f4SPrashant Bhole } 4954990f1f4SPrashant Bhole 496569b0c77SPrashant Bhole if (info->btf_id) 497031ebc1aSQuentin Monnet printf("\n\tbtf_id %d", info->btf_id); 498569b0c77SPrashant Bhole 499d53dee3fSAndrii Nakryiko emit_obj_refs_plain(&refs_table, info->id, "\n\tpids "); 500d53dee3fSAndrii Nakryiko 50171bb428fSJakub Kicinski printf("\n"); 502aff52e68SYiFei Zhu 503aff52e68SYiFei Zhu show_prog_metadata(fd, info->nr_map_ids); 504743cc665SQuentin Monnet } 505743cc665SQuentin Monnet 506743cc665SQuentin Monnet static int show_prog(int fd) 507743cc665SQuentin Monnet { 508743cc665SQuentin Monnet struct bpf_prog_info info = {}; 509743cc665SQuentin Monnet __u32 len = sizeof(info); 510743cc665SQuentin Monnet int err; 511743cc665SQuentin Monnet 512743cc665SQuentin Monnet err = bpf_obj_get_info_by_fd(fd, &info, &len); 513743cc665SQuentin Monnet if (err) { 5149a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 515743cc665SQuentin Monnet return -1; 516743cc665SQuentin Monnet } 517743cc665SQuentin Monnet 518743cc665SQuentin Monnet if (json_output) 519743cc665SQuentin Monnet print_prog_json(&info, fd); 520743cc665SQuentin Monnet else 521743cc665SQuentin Monnet print_prog_plain(&info, fd); 52271bb428fSJakub Kicinski 52371bb428fSJakub Kicinski return 0; 52471bb428fSJakub Kicinski } 52571bb428fSJakub Kicinski 526ec202509SPaul Chaignon static int do_show_subset(int argc, char **argv) 527ec202509SPaul Chaignon { 528ec202509SPaul Chaignon int *fds = NULL; 529ec202509SPaul Chaignon int nb_fds, i; 530ec202509SPaul Chaignon int err = -1; 531ec202509SPaul Chaignon 532ec202509SPaul Chaignon fds = malloc(sizeof(int)); 533ec202509SPaul Chaignon if (!fds) { 534ec202509SPaul Chaignon p_err("mem alloc failed"); 535ec202509SPaul Chaignon return -1; 536ec202509SPaul Chaignon } 537ec202509SPaul Chaignon nb_fds = prog_parse_fds(&argc, &argv, &fds); 538ec202509SPaul Chaignon if (nb_fds < 1) 539ec202509SPaul Chaignon goto exit_free; 540ec202509SPaul Chaignon 541ec202509SPaul Chaignon if (json_output && nb_fds > 1) 542ec202509SPaul Chaignon jsonw_start_array(json_wtr); /* root array */ 543ec202509SPaul Chaignon for (i = 0; i < nb_fds; i++) { 544ec202509SPaul Chaignon err = show_prog(fds[i]); 545ec202509SPaul Chaignon if (err) { 546ec202509SPaul Chaignon for (; i < nb_fds; i++) 547ec202509SPaul Chaignon close(fds[i]); 548ec202509SPaul Chaignon break; 549ec202509SPaul Chaignon } 550ec202509SPaul Chaignon close(fds[i]); 551ec202509SPaul Chaignon } 552ec202509SPaul Chaignon if (json_output && nb_fds > 1) 553ec202509SPaul Chaignon jsonw_end_array(json_wtr); /* root array */ 554ec202509SPaul Chaignon 555ec202509SPaul Chaignon exit_free: 556ec202509SPaul Chaignon free(fds); 557ec202509SPaul Chaignon return err; 558ec202509SPaul Chaignon } 559ec202509SPaul Chaignon 56071bb428fSJakub Kicinski static int do_show(int argc, char **argv) 561743cc665SQuentin Monnet { 562743cc665SQuentin Monnet __u32 id = 0; 56371bb428fSJakub Kicinski int err; 56471bb428fSJakub Kicinski int fd; 56571bb428fSJakub Kicinski 566c541b734SPrashant Bhole if (show_pinned) 5674990f1f4SPrashant Bhole build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); 568d53dee3fSAndrii Nakryiko build_obj_refs_table(&refs_table, BPF_OBJ_PROG); 5694990f1f4SPrashant Bhole 570ec202509SPaul Chaignon if (argc == 2) 571ec202509SPaul Chaignon return do_show_subset(argc, argv); 57271bb428fSJakub Kicinski 57371bb428fSJakub Kicinski if (argc) 57471bb428fSJakub Kicinski return BAD_ARG(); 57571bb428fSJakub Kicinski 576743cc665SQuentin Monnet if (json_output) 577743cc665SQuentin Monnet jsonw_start_array(json_wtr); 57871bb428fSJakub Kicinski while (true) { 57971bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 58071bb428fSJakub Kicinski if (err) { 5811739c26dSQuentin Monnet if (errno == ENOENT) { 5821739c26dSQuentin Monnet err = 0; 58371bb428fSJakub Kicinski break; 5841739c26dSQuentin Monnet } 5859a5ab8bfSQuentin Monnet p_err("can't get next program: %s%s", strerror(errno), 5869a5ab8bfSQuentin Monnet errno == EINVAL ? " -- kernel too old?" : ""); 587743cc665SQuentin Monnet err = -1; 588743cc665SQuentin Monnet break; 58971bb428fSJakub Kicinski } 59071bb428fSJakub Kicinski 59171bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 59271bb428fSJakub Kicinski if (fd < 0) { 5938207c6ddSJakub Kicinski if (errno == ENOENT) 5948207c6ddSJakub Kicinski continue; 5959a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 59671bb428fSJakub Kicinski id, strerror(errno)); 597743cc665SQuentin Monnet err = -1; 598743cc665SQuentin Monnet break; 59971bb428fSJakub Kicinski } 60071bb428fSJakub Kicinski 60171bb428fSJakub Kicinski err = show_prog(fd); 60271bb428fSJakub Kicinski close(fd); 60371bb428fSJakub Kicinski if (err) 604743cc665SQuentin Monnet break; 60571bb428fSJakub Kicinski } 60671bb428fSJakub Kicinski 607743cc665SQuentin Monnet if (json_output) 608743cc665SQuentin Monnet jsonw_end_array(json_wtr); 609743cc665SQuentin Monnet 610d53dee3fSAndrii Nakryiko delete_obj_refs_table(&refs_table); 611d53dee3fSAndrii Nakryiko 612743cc665SQuentin Monnet return err; 61371bb428fSJakub Kicinski } 61471bb428fSJakub Kicinski 615ec202509SPaul Chaignon static int 616ec202509SPaul Chaignon prog_dump(struct bpf_prog_info *info, enum dump_mode mode, 617ec202509SPaul Chaignon char *filepath, bool opcodes, bool visual, bool linum) 61871bb428fSJakub Kicinski { 619b053b439SMartin KaFai Lau struct bpf_prog_linfo *prog_linfo = NULL; 6203ddeac67SJakub Kicinski const char *disasm_opt = NULL; 6217105e828SDaniel Borkmann struct dump_data dd = {}; 622cae73f23SSong Liu void *func_info = NULL; 623254471e5SYonghong Song struct btf *btf = NULL; 624254471e5SYonghong Song char func_sig[1024]; 62571bb428fSJakub Kicinski unsigned char *buf; 626cae73f23SSong Liu __u32 member_len; 62771bb428fSJakub Kicinski ssize_t n; 62871bb428fSJakub Kicinski int fd; 62971bb428fSJakub Kicinski 630cae73f23SSong Liu if (mode == DUMP_JITED) { 6315b79bcdfSToke Høiland-Jørgensen if (info->jited_prog_len == 0 || !info->jited_prog_insns) { 6329a5ab8bfSQuentin Monnet p_info("no instructions returned"); 633ec202509SPaul Chaignon return -1; 634f84192eeSSandipan Das } 63509f44b75SAndrii Nakryiko buf = u64_to_ptr(info->jited_prog_insns); 636cae73f23SSong Liu member_len = info->jited_prog_len; 637cae73f23SSong Liu } else { /* DUMP_XLATED */ 638d95f1e8bSToke Høiland-Jørgensen if (info->xlated_prog_len == 0 || !info->xlated_prog_insns) { 6397105e828SDaniel Borkmann p_err("error retrieving insn dump: kernel.kptr_restrict set?"); 640ec202509SPaul Chaignon return -1; 6417105e828SDaniel Borkmann } 64209f44b75SAndrii Nakryiko buf = u64_to_ptr(info->xlated_prog_insns); 643cae73f23SSong Liu member_len = info->xlated_prog_len; 644cae73f23SSong Liu } 6457105e828SDaniel Borkmann 646cae73f23SSong Liu if (info->btf_id && btf__get_from_id(info->btf_id, &btf)) { 647254471e5SYonghong Song p_err("failed to get btf"); 648ec202509SPaul Chaignon return -1; 649254471e5SYonghong Song } 650254471e5SYonghong Song 65109f44b75SAndrii Nakryiko func_info = u64_to_ptr(info->func_info); 652cae73f23SSong Liu 653cae73f23SSong Liu if (info->nr_line_info) { 654cae73f23SSong Liu prog_linfo = bpf_prog_linfo__new(info); 655b053b439SMartin KaFai Lau if (!prog_linfo) 65610a5ce98SMartin KaFai Lau p_info("error in processing bpf_line_info. continue without it."); 657b053b439SMartin KaFai Lau } 658b053b439SMartin KaFai Lau 65971bb428fSJakub Kicinski if (filepath) { 66071bb428fSJakub Kicinski fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 66171bb428fSJakub Kicinski if (fd < 0) { 6629a5ab8bfSQuentin Monnet p_err("can't open file %s: %s", filepath, 66371bb428fSJakub Kicinski strerror(errno)); 664ec202509SPaul Chaignon return -1; 66571bb428fSJakub Kicinski } 66671bb428fSJakub Kicinski 667cae73f23SSong Liu n = write(fd, buf, member_len); 66871bb428fSJakub Kicinski close(fd); 66909f44b75SAndrii Nakryiko if (n != (ssize_t)member_len) { 6709a5ab8bfSQuentin Monnet p_err("error writing output file: %s", 67171bb428fSJakub Kicinski n < 0 ? strerror(errno) : "short write"); 672ec202509SPaul Chaignon return -1; 67371bb428fSJakub Kicinski } 67452c84d36SQuentin Monnet 67552c84d36SQuentin Monnet if (json_output) 67652c84d36SQuentin Monnet jsonw_null(json_wtr); 677cae73f23SSong Liu } else if (mode == DUMP_JITED) { 678e6593596SJiong Wang const char *name = NULL; 679e6593596SJiong Wang 680cae73f23SSong Liu if (info->ifindex) { 681cae73f23SSong Liu name = ifindex_to_bfd_params(info->ifindex, 682cae73f23SSong Liu info->netns_dev, 683cae73f23SSong Liu info->netns_ino, 6843ddeac67SJakub Kicinski &disasm_opt); 685e6593596SJiong Wang if (!name) 686ec202509SPaul Chaignon return -1; 687e6593596SJiong Wang } 688e6593596SJiong Wang 689cae73f23SSong Liu if (info->nr_jited_func_lens && info->jited_func_lens) { 690f7f62c71SSandipan Das struct kernel_sym *sym = NULL; 691254471e5SYonghong Song struct bpf_func_info *record; 692f7f62c71SSandipan Das char sym_name[SYM_MAX_NAME]; 693f7f62c71SSandipan Das unsigned char *img = buf; 694f7f62c71SSandipan Das __u64 *ksyms = NULL; 695f7f62c71SSandipan Das __u32 *lens; 696f7f62c71SSandipan Das __u32 i; 697cae73f23SSong Liu if (info->nr_jited_ksyms) { 698f7f62c71SSandipan Das kernel_syms_load(&dd); 69909f44b75SAndrii Nakryiko ksyms = u64_to_ptr(info->jited_ksyms); 700f7f62c71SSandipan Das } 701f7f62c71SSandipan Das 702f7f62c71SSandipan Das if (json_output) 703f7f62c71SSandipan Das jsonw_start_array(json_wtr); 704f7f62c71SSandipan Das 70509f44b75SAndrii Nakryiko lens = u64_to_ptr(info->jited_func_lens); 706cae73f23SSong Liu for (i = 0; i < info->nr_jited_func_lens; i++) { 707f7f62c71SSandipan Das if (ksyms) { 708f7f62c71SSandipan Das sym = kernel_syms_search(&dd, ksyms[i]); 709f7f62c71SSandipan Das if (sym) 710f7f62c71SSandipan Das sprintf(sym_name, "%s", sym->name); 711f7f62c71SSandipan Das else 712f7f62c71SSandipan Das sprintf(sym_name, "0x%016llx", ksyms[i]); 713f7f62c71SSandipan Das } else { 714f7f62c71SSandipan Das strcpy(sym_name, "unknown"); 715f7f62c71SSandipan Das } 716f7f62c71SSandipan Das 717254471e5SYonghong Song if (func_info) { 718cae73f23SSong Liu record = func_info + i * info->func_info_rec_size; 719254471e5SYonghong Song btf_dumper_type_only(btf, record->type_id, 720254471e5SYonghong Song func_sig, 721254471e5SYonghong Song sizeof(func_sig)); 722254471e5SYonghong Song } 723254471e5SYonghong Song 724f7f62c71SSandipan Das if (json_output) { 725f7f62c71SSandipan Das jsonw_start_object(json_wtr); 726254471e5SYonghong Song if (func_info && func_sig[0] != '\0') { 727254471e5SYonghong Song jsonw_name(json_wtr, "proto"); 728254471e5SYonghong Song jsonw_string(json_wtr, func_sig); 729254471e5SYonghong Song } 730f7f62c71SSandipan Das jsonw_name(json_wtr, "name"); 731f7f62c71SSandipan Das jsonw_string(json_wtr, sym_name); 732f7f62c71SSandipan Das jsonw_name(json_wtr, "insns"); 733f7f62c71SSandipan Das } else { 734254471e5SYonghong Song if (func_info && func_sig[0] != '\0') 735254471e5SYonghong Song printf("%s:\n", func_sig); 736f7f62c71SSandipan Das printf("%s:\n", sym_name); 737f7f62c71SSandipan Das } 738f7f62c71SSandipan Das 739b053b439SMartin KaFai Lau disasm_print_insn(img, lens[i], opcodes, 740b053b439SMartin KaFai Lau name, disasm_opt, btf, 741b053b439SMartin KaFai Lau prog_linfo, ksyms[i], i, 742b053b439SMartin KaFai Lau linum); 743b053b439SMartin KaFai Lau 744f7f62c71SSandipan Das img += lens[i]; 745f7f62c71SSandipan Das 746f7f62c71SSandipan Das if (json_output) 747f7f62c71SSandipan Das jsonw_end_object(json_wtr); 748f7f62c71SSandipan Das else 749f7f62c71SSandipan Das printf("\n"); 750f7f62c71SSandipan Das } 751f7f62c71SSandipan Das 752f7f62c71SSandipan Das if (json_output) 753f7f62c71SSandipan Das jsonw_end_array(json_wtr); 754f7f62c71SSandipan Das } else { 755cae73f23SSong Liu disasm_print_insn(buf, member_len, opcodes, name, 756b053b439SMartin KaFai Lau disasm_opt, btf, NULL, 0, 0, false); 757f7f62c71SSandipan Das } 758b6c1cedbSJiong Wang } else if (visual) { 759b6c1cedbSJiong Wang if (json_output) 760b6c1cedbSJiong Wang jsonw_null(json_wtr); 761b6c1cedbSJiong Wang else 762cae73f23SSong Liu dump_xlated_cfg(buf, member_len); 7637105e828SDaniel Borkmann } else { 7647105e828SDaniel Borkmann kernel_syms_load(&dd); 765cae73f23SSong Liu dd.nr_jited_ksyms = info->nr_jited_ksyms; 76609f44b75SAndrii Nakryiko dd.jited_ksyms = u64_to_ptr(info->jited_ksyms); 767254471e5SYonghong Song dd.btf = btf; 768254471e5SYonghong Song dd.func_info = func_info; 769cae73f23SSong Liu dd.finfo_rec_size = info->func_info_rec_size; 770b053b439SMartin KaFai Lau dd.prog_linfo = prog_linfo; 771f84192eeSSandipan Das 772f05e2c32SQuentin Monnet if (json_output) 773cae73f23SSong Liu dump_xlated_json(&dd, buf, member_len, opcodes, 774b053b439SMartin KaFai Lau linum); 775f05e2c32SQuentin Monnet else 776cae73f23SSong Liu dump_xlated_plain(&dd, buf, member_len, opcodes, 777b053b439SMartin KaFai Lau linum); 7787105e828SDaniel Borkmann kernel_syms_destroy(&dd); 7797105e828SDaniel Borkmann } 78071bb428fSJakub Kicinski 78171bb428fSJakub Kicinski return 0; 782ec202509SPaul Chaignon } 78371bb428fSJakub Kicinski 784ec202509SPaul Chaignon static int do_dump(int argc, char **argv) 785ec202509SPaul Chaignon { 786ec202509SPaul Chaignon struct bpf_prog_info_linear *info_linear; 787ec202509SPaul Chaignon char *filepath = NULL; 788ec202509SPaul Chaignon bool opcodes = false; 789ec202509SPaul Chaignon bool visual = false; 790ec202509SPaul Chaignon enum dump_mode mode; 791ec202509SPaul Chaignon bool linum = false; 792ec202509SPaul Chaignon int *fds = NULL; 793ec202509SPaul Chaignon int nb_fds, i = 0; 794ec202509SPaul Chaignon int err = -1; 795ec202509SPaul Chaignon __u64 arrays; 796ec202509SPaul Chaignon 797ec202509SPaul Chaignon if (is_prefix(*argv, "jited")) { 798ec202509SPaul Chaignon if (disasm_init()) 79971bb428fSJakub Kicinski return -1; 800ec202509SPaul Chaignon mode = DUMP_JITED; 801ec202509SPaul Chaignon } else if (is_prefix(*argv, "xlated")) { 802ec202509SPaul Chaignon mode = DUMP_XLATED; 803ec202509SPaul Chaignon } else { 804ec202509SPaul Chaignon p_err("expected 'xlated' or 'jited', got: %s", *argv); 805ec202509SPaul Chaignon return -1; 806ec202509SPaul Chaignon } 807ec202509SPaul Chaignon NEXT_ARG(); 808ec202509SPaul Chaignon 809ec202509SPaul Chaignon if (argc < 2) 810ec202509SPaul Chaignon usage(); 811ec202509SPaul Chaignon 812ec202509SPaul Chaignon fds = malloc(sizeof(int)); 813ec202509SPaul Chaignon if (!fds) { 814ec202509SPaul Chaignon p_err("mem alloc failed"); 815ec202509SPaul Chaignon return -1; 816ec202509SPaul Chaignon } 817ec202509SPaul Chaignon nb_fds = prog_parse_fds(&argc, &argv, &fds); 818ec202509SPaul Chaignon if (nb_fds < 1) 819ec202509SPaul Chaignon goto exit_free; 820ec202509SPaul Chaignon 821ec202509SPaul Chaignon if (is_prefix(*argv, "file")) { 822ec202509SPaul Chaignon NEXT_ARG(); 823ec202509SPaul Chaignon if (!argc) { 824ec202509SPaul Chaignon p_err("expected file path"); 825ec202509SPaul Chaignon goto exit_close; 826ec202509SPaul Chaignon } 827ec202509SPaul Chaignon if (nb_fds > 1) { 828ec202509SPaul Chaignon p_err("several programs matched"); 829ec202509SPaul Chaignon goto exit_close; 830ec202509SPaul Chaignon } 831ec202509SPaul Chaignon 832ec202509SPaul Chaignon filepath = *argv; 833ec202509SPaul Chaignon NEXT_ARG(); 834ec202509SPaul Chaignon } else if (is_prefix(*argv, "opcodes")) { 835ec202509SPaul Chaignon opcodes = true; 836ec202509SPaul Chaignon NEXT_ARG(); 837ec202509SPaul Chaignon } else if (is_prefix(*argv, "visual")) { 838ec202509SPaul Chaignon if (nb_fds > 1) { 839ec202509SPaul Chaignon p_err("several programs matched"); 840ec202509SPaul Chaignon goto exit_close; 841ec202509SPaul Chaignon } 842ec202509SPaul Chaignon 843ec202509SPaul Chaignon visual = true; 844ec202509SPaul Chaignon NEXT_ARG(); 845ec202509SPaul Chaignon } else if (is_prefix(*argv, "linum")) { 846ec202509SPaul Chaignon linum = true; 847ec202509SPaul Chaignon NEXT_ARG(); 848ec202509SPaul Chaignon } 849ec202509SPaul Chaignon 850ec202509SPaul Chaignon if (argc) { 851ec202509SPaul Chaignon usage(); 852ec202509SPaul Chaignon goto exit_close; 853ec202509SPaul Chaignon } 854ec202509SPaul Chaignon 855ec202509SPaul Chaignon if (mode == DUMP_JITED) 856ec202509SPaul Chaignon arrays = 1UL << BPF_PROG_INFO_JITED_INSNS; 857ec202509SPaul Chaignon else 858ec202509SPaul Chaignon arrays = 1UL << BPF_PROG_INFO_XLATED_INSNS; 859ec202509SPaul Chaignon 860ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_JITED_KSYMS; 861ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; 862ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; 863ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; 864ec202509SPaul Chaignon arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; 865ec202509SPaul Chaignon 866ec202509SPaul Chaignon if (json_output && nb_fds > 1) 867ec202509SPaul Chaignon jsonw_start_array(json_wtr); /* root array */ 868ec202509SPaul Chaignon for (i = 0; i < nb_fds; i++) { 869ec202509SPaul Chaignon info_linear = bpf_program__get_prog_info_linear(fds[i], arrays); 870ec202509SPaul Chaignon if (IS_ERR_OR_NULL(info_linear)) { 871ec202509SPaul Chaignon p_err("can't get prog info: %s", strerror(errno)); 872ec202509SPaul Chaignon break; 873ec202509SPaul Chaignon } 874ec202509SPaul Chaignon 875ec202509SPaul Chaignon if (json_output && nb_fds > 1) { 876ec202509SPaul Chaignon jsonw_start_object(json_wtr); /* prog object */ 877ec202509SPaul Chaignon print_prog_header_json(&info_linear->info); 878ec202509SPaul Chaignon jsonw_name(json_wtr, "insns"); 879ec202509SPaul Chaignon } else if (nb_fds > 1) { 880ec202509SPaul Chaignon print_prog_header_plain(&info_linear->info); 881ec202509SPaul Chaignon } 882ec202509SPaul Chaignon 883ec202509SPaul Chaignon err = prog_dump(&info_linear->info, mode, filepath, opcodes, 884ec202509SPaul Chaignon visual, linum); 885ec202509SPaul Chaignon 886ec202509SPaul Chaignon if (json_output && nb_fds > 1) 887ec202509SPaul Chaignon jsonw_end_object(json_wtr); /* prog object */ 888ec202509SPaul Chaignon else if (i != nb_fds - 1 && nb_fds > 1) 889ec202509SPaul Chaignon printf("\n"); 890ec202509SPaul Chaignon 891ec202509SPaul Chaignon free(info_linear); 892ec202509SPaul Chaignon if (err) 893ec202509SPaul Chaignon break; 894ec202509SPaul Chaignon close(fds[i]); 895ec202509SPaul Chaignon } 896ec202509SPaul Chaignon if (json_output && nb_fds > 1) 897ec202509SPaul Chaignon jsonw_end_array(json_wtr); /* root array */ 898ec202509SPaul Chaignon 899ec202509SPaul Chaignon exit_close: 900ec202509SPaul Chaignon for (; i < nb_fds; i++) 901ec202509SPaul Chaignon close(fds[i]); 902ec202509SPaul Chaignon exit_free: 903ec202509SPaul Chaignon free(fds); 904ec202509SPaul Chaignon return err; 90571bb428fSJakub Kicinski } 90671bb428fSJakub Kicinski 90771bb428fSJakub Kicinski static int do_pin(int argc, char **argv) 90871bb428fSJakub Kicinski { 909004b45c0SQuentin Monnet int err; 910004b45c0SQuentin Monnet 91175a1e792SQuentin Monnet err = do_pin_any(argc, argv, prog_parse_fd); 912004b45c0SQuentin Monnet if (!err && json_output) 913004b45c0SQuentin Monnet jsonw_null(json_wtr); 914004b45c0SQuentin Monnet return err; 91571bb428fSJakub Kicinski } 91671bb428fSJakub Kicinski 9173ff5a4dcSJakub Kicinski struct map_replace { 9183ff5a4dcSJakub Kicinski int idx; 9193ff5a4dcSJakub Kicinski int fd; 9203ff5a4dcSJakub Kicinski char *name; 9213ff5a4dcSJakub Kicinski }; 9223ff5a4dcSJakub Kicinski 923c101189bSQuentin Monnet static int map_replace_compar(const void *p1, const void *p2) 9243ff5a4dcSJakub Kicinski { 9253ff5a4dcSJakub Kicinski const struct map_replace *a = p1, *b = p2; 9263ff5a4dcSJakub Kicinski 9273ff5a4dcSJakub Kicinski return a->idx - b->idx; 9283ff5a4dcSJakub Kicinski } 9293ff5a4dcSJakub Kicinski 930092f0892SStanislav Fomichev static int parse_attach_detach_args(int argc, char **argv, int *progfd, 931092f0892SStanislav Fomichev enum bpf_attach_type *attach_type, 932092f0892SStanislav Fomichev int *mapfd) 933092f0892SStanislav Fomichev { 934092f0892SStanislav Fomichev if (!REQ_ARGS(3)) 935092f0892SStanislav Fomichev return -EINVAL; 936092f0892SStanislav Fomichev 937092f0892SStanislav Fomichev *progfd = prog_parse_fd(&argc, &argv); 938092f0892SStanislav Fomichev if (*progfd < 0) 939092f0892SStanislav Fomichev return *progfd; 940092f0892SStanislav Fomichev 941092f0892SStanislav Fomichev *attach_type = parse_attach_type(*argv); 942092f0892SStanislav Fomichev if (*attach_type == __MAX_BPF_ATTACH_TYPE) { 943092f0892SStanislav Fomichev p_err("invalid attach/detach type"); 944092f0892SStanislav Fomichev return -EINVAL; 945092f0892SStanislav Fomichev } 946092f0892SStanislav Fomichev 947092f0892SStanislav Fomichev if (*attach_type == BPF_FLOW_DISSECTOR) { 948f9b7ff0dSLorenz Bauer *mapfd = 0; 949092f0892SStanislav Fomichev return 0; 950092f0892SStanislav Fomichev } 951092f0892SStanislav Fomichev 952092f0892SStanislav Fomichev NEXT_ARG(); 953092f0892SStanislav Fomichev if (!REQ_ARGS(2)) 954092f0892SStanislav Fomichev return -EINVAL; 955092f0892SStanislav Fomichev 956092f0892SStanislav Fomichev *mapfd = map_parse_fd(&argc, &argv); 957092f0892SStanislav Fomichev if (*mapfd < 0) 958092f0892SStanislav Fomichev return *mapfd; 959092f0892SStanislav Fomichev 960092f0892SStanislav Fomichev return 0; 961092f0892SStanislav Fomichev } 962092f0892SStanislav Fomichev 963b7d3826cSJohn Fastabend static int do_attach(int argc, char **argv) 964b7d3826cSJohn Fastabend { 965b7d3826cSJohn Fastabend enum bpf_attach_type attach_type; 966092f0892SStanislav Fomichev int err, progfd; 967092f0892SStanislav Fomichev int mapfd; 968b7d3826cSJohn Fastabend 969092f0892SStanislav Fomichev err = parse_attach_detach_args(argc, argv, 970092f0892SStanislav Fomichev &progfd, &attach_type, &mapfd); 971092f0892SStanislav Fomichev if (err) 972092f0892SStanislav Fomichev return err; 973b7d3826cSJohn Fastabend 974b7d3826cSJohn Fastabend err = bpf_prog_attach(progfd, mapfd, attach_type, 0); 975b7d3826cSJohn Fastabend if (err) { 976b7d3826cSJohn Fastabend p_err("failed prog attach to map"); 977b7d3826cSJohn Fastabend return -EINVAL; 978b7d3826cSJohn Fastabend } 979b7d3826cSJohn Fastabend 980b7d3826cSJohn Fastabend if (json_output) 981b7d3826cSJohn Fastabend jsonw_null(json_wtr); 982b7d3826cSJohn Fastabend return 0; 983b7d3826cSJohn Fastabend } 984b7d3826cSJohn Fastabend 985b7d3826cSJohn Fastabend static int do_detach(int argc, char **argv) 986b7d3826cSJohn Fastabend { 987b7d3826cSJohn Fastabend enum bpf_attach_type attach_type; 988092f0892SStanislav Fomichev int err, progfd; 989092f0892SStanislav Fomichev int mapfd; 990b7d3826cSJohn Fastabend 991092f0892SStanislav Fomichev err = parse_attach_detach_args(argc, argv, 992092f0892SStanislav Fomichev &progfd, &attach_type, &mapfd); 993092f0892SStanislav Fomichev if (err) 994092f0892SStanislav Fomichev return err; 995b7d3826cSJohn Fastabend 996b7d3826cSJohn Fastabend err = bpf_prog_detach2(progfd, mapfd, attach_type); 997b7d3826cSJohn Fastabend if (err) { 998b7d3826cSJohn Fastabend p_err("failed prog detach from map"); 999b7d3826cSJohn Fastabend return -EINVAL; 1000b7d3826cSJohn Fastabend } 1001b7d3826cSJohn Fastabend 1002b7d3826cSJohn Fastabend if (json_output) 1003b7d3826cSJohn Fastabend jsonw_null(json_wtr); 1004b7d3826cSJohn Fastabend return 0; 1005b7d3826cSJohn Fastabend } 100677380998SStanislav Fomichev 1007ba95c745SQuentin Monnet static int check_single_stdin(char *file_data_in, char *file_ctx_in) 1008ba95c745SQuentin Monnet { 1009ba95c745SQuentin Monnet if (file_data_in && file_ctx_in && 1010ba95c745SQuentin Monnet !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) { 1011ba95c745SQuentin Monnet p_err("cannot use standard input for both data_in and ctx_in"); 1012ba95c745SQuentin Monnet return -1; 1013ba95c745SQuentin Monnet } 1014ba95c745SQuentin Monnet 1015ba95c745SQuentin Monnet return 0; 1016ba95c745SQuentin Monnet } 1017ba95c745SQuentin Monnet 1018ba95c745SQuentin Monnet static int get_run_data(const char *fname, void **data_ptr, unsigned int *size) 1019ba95c745SQuentin Monnet { 1020ba95c745SQuentin Monnet size_t block_size = 256; 1021ba95c745SQuentin Monnet size_t buf_size = block_size; 1022ba95c745SQuentin Monnet size_t nb_read = 0; 1023ba95c745SQuentin Monnet void *tmp; 1024ba95c745SQuentin Monnet FILE *f; 1025ba95c745SQuentin Monnet 1026ba95c745SQuentin Monnet if (!fname) { 1027ba95c745SQuentin Monnet *data_ptr = NULL; 1028ba95c745SQuentin Monnet *size = 0; 1029ba95c745SQuentin Monnet return 0; 1030ba95c745SQuentin Monnet } 1031ba95c745SQuentin Monnet 1032ba95c745SQuentin Monnet if (!strcmp(fname, "-")) 1033ba95c745SQuentin Monnet f = stdin; 1034ba95c745SQuentin Monnet else 1035ba95c745SQuentin Monnet f = fopen(fname, "r"); 1036ba95c745SQuentin Monnet if (!f) { 1037ba95c745SQuentin Monnet p_err("failed to open %s: %s", fname, strerror(errno)); 1038ba95c745SQuentin Monnet return -1; 1039ba95c745SQuentin Monnet } 1040ba95c745SQuentin Monnet 1041ba95c745SQuentin Monnet *data_ptr = malloc(block_size); 1042ba95c745SQuentin Monnet if (!*data_ptr) { 1043ba95c745SQuentin Monnet p_err("failed to allocate memory for data_in/ctx_in: %s", 1044ba95c745SQuentin Monnet strerror(errno)); 1045ba95c745SQuentin Monnet goto err_fclose; 1046ba95c745SQuentin Monnet } 1047ba95c745SQuentin Monnet 1048ba95c745SQuentin Monnet while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) { 1049ba95c745SQuentin Monnet if (feof(f)) 1050ba95c745SQuentin Monnet break; 1051ba95c745SQuentin Monnet if (ferror(f)) { 1052ba95c745SQuentin Monnet p_err("failed to read data_in/ctx_in from %s: %s", 1053ba95c745SQuentin Monnet fname, strerror(errno)); 1054ba95c745SQuentin Monnet goto err_free; 1055ba95c745SQuentin Monnet } 1056ba95c745SQuentin Monnet if (nb_read > buf_size - block_size) { 1057ba95c745SQuentin Monnet if (buf_size == UINT32_MAX) { 1058ba95c745SQuentin Monnet p_err("data_in/ctx_in is too long (max: %d)", 1059ba95c745SQuentin Monnet UINT32_MAX); 1060ba95c745SQuentin Monnet goto err_free; 1061ba95c745SQuentin Monnet } 1062ba95c745SQuentin Monnet /* No space for fread()-ing next chunk; realloc() */ 1063ba95c745SQuentin Monnet buf_size *= 2; 1064ba95c745SQuentin Monnet tmp = realloc(*data_ptr, buf_size); 1065ba95c745SQuentin Monnet if (!tmp) { 1066ba95c745SQuentin Monnet p_err("failed to reallocate data_in/ctx_in: %s", 1067ba95c745SQuentin Monnet strerror(errno)); 1068ba95c745SQuentin Monnet goto err_free; 1069ba95c745SQuentin Monnet } 1070ba95c745SQuentin Monnet *data_ptr = tmp; 1071ba95c745SQuentin Monnet } 1072ba95c745SQuentin Monnet } 1073ba95c745SQuentin Monnet if (f != stdin) 1074ba95c745SQuentin Monnet fclose(f); 1075ba95c745SQuentin Monnet 1076ba95c745SQuentin Monnet *size = nb_read; 1077ba95c745SQuentin Monnet return 0; 1078ba95c745SQuentin Monnet 1079ba95c745SQuentin Monnet err_free: 1080ba95c745SQuentin Monnet free(*data_ptr); 1081ba95c745SQuentin Monnet *data_ptr = NULL; 1082ba95c745SQuentin Monnet err_fclose: 1083ba95c745SQuentin Monnet if (f != stdin) 1084ba95c745SQuentin Monnet fclose(f); 1085ba95c745SQuentin Monnet return -1; 1086ba95c745SQuentin Monnet } 1087ba95c745SQuentin Monnet 1088ba95c745SQuentin Monnet static void hex_print(void *data, unsigned int size, FILE *f) 1089ba95c745SQuentin Monnet { 1090ba95c745SQuentin Monnet size_t i, j; 1091ba95c745SQuentin Monnet char c; 1092ba95c745SQuentin Monnet 1093ba95c745SQuentin Monnet for (i = 0; i < size; i += 16) { 1094ba95c745SQuentin Monnet /* Row offset */ 1095ba95c745SQuentin Monnet fprintf(f, "%07zx\t", i); 1096ba95c745SQuentin Monnet 1097ba95c745SQuentin Monnet /* Hexadecimal values */ 1098ba95c745SQuentin Monnet for (j = i; j < i + 16 && j < size; j++) 1099ba95c745SQuentin Monnet fprintf(f, "%02x%s", *(uint8_t *)(data + j), 1100ba95c745SQuentin Monnet j % 2 ? " " : ""); 1101ba95c745SQuentin Monnet for (; j < i + 16; j++) 1102ba95c745SQuentin Monnet fprintf(f, " %s", j % 2 ? " " : ""); 1103ba95c745SQuentin Monnet 1104ba95c745SQuentin Monnet /* ASCII values (if relevant), '.' otherwise */ 1105ba95c745SQuentin Monnet fprintf(f, "| "); 1106ba95c745SQuentin Monnet for (j = i; j < i + 16 && j < size; j++) { 1107ba95c745SQuentin Monnet c = *(char *)(data + j); 1108ba95c745SQuentin Monnet if (c < ' ' || c > '~') 1109ba95c745SQuentin Monnet c = '.'; 1110ba95c745SQuentin Monnet fprintf(f, "%c%s", c, j == i + 7 ? " " : ""); 1111ba95c745SQuentin Monnet } 1112ba95c745SQuentin Monnet 1113ba95c745SQuentin Monnet fprintf(f, "\n"); 1114ba95c745SQuentin Monnet } 1115ba95c745SQuentin Monnet } 1116ba95c745SQuentin Monnet 1117ba95c745SQuentin Monnet static int 1118ba95c745SQuentin Monnet print_run_output(void *data, unsigned int size, const char *fname, 1119ba95c745SQuentin Monnet const char *json_key) 1120ba95c745SQuentin Monnet { 1121ba95c745SQuentin Monnet size_t nb_written; 1122ba95c745SQuentin Monnet FILE *f; 1123ba95c745SQuentin Monnet 1124ba95c745SQuentin Monnet if (!fname) 1125ba95c745SQuentin Monnet return 0; 1126ba95c745SQuentin Monnet 1127ba95c745SQuentin Monnet if (!strcmp(fname, "-")) { 1128ba95c745SQuentin Monnet f = stdout; 1129ba95c745SQuentin Monnet if (json_output) { 1130ba95c745SQuentin Monnet jsonw_name(json_wtr, json_key); 1131ba95c745SQuentin Monnet print_data_json(data, size); 1132ba95c745SQuentin Monnet } else { 1133ba95c745SQuentin Monnet hex_print(data, size, f); 1134ba95c745SQuentin Monnet } 1135ba95c745SQuentin Monnet return 0; 1136ba95c745SQuentin Monnet } 1137ba95c745SQuentin Monnet 1138ba95c745SQuentin Monnet f = fopen(fname, "w"); 1139ba95c745SQuentin Monnet if (!f) { 1140ba95c745SQuentin Monnet p_err("failed to open %s: %s", fname, strerror(errno)); 1141ba95c745SQuentin Monnet return -1; 1142ba95c745SQuentin Monnet } 1143ba95c745SQuentin Monnet 1144ba95c745SQuentin Monnet nb_written = fwrite(data, 1, size, f); 1145ba95c745SQuentin Monnet fclose(f); 1146ba95c745SQuentin Monnet if (nb_written != size) { 1147ba95c745SQuentin Monnet p_err("failed to write output data/ctx: %s", strerror(errno)); 1148ba95c745SQuentin Monnet return -1; 1149ba95c745SQuentin Monnet } 1150ba95c745SQuentin Monnet 1151ba95c745SQuentin Monnet return 0; 1152ba95c745SQuentin Monnet } 1153ba95c745SQuentin Monnet 1154ba95c745SQuentin Monnet static int alloc_run_data(void **data_ptr, unsigned int size_out) 1155ba95c745SQuentin Monnet { 1156ba95c745SQuentin Monnet *data_ptr = calloc(size_out, 1); 1157ba95c745SQuentin Monnet if (!*data_ptr) { 1158ba95c745SQuentin Monnet p_err("failed to allocate memory for output data/ctx: %s", 1159ba95c745SQuentin Monnet strerror(errno)); 1160ba95c745SQuentin Monnet return -1; 1161ba95c745SQuentin Monnet } 1162ba95c745SQuentin Monnet 1163ba95c745SQuentin Monnet return 0; 1164ba95c745SQuentin Monnet } 1165ba95c745SQuentin Monnet 1166ba95c745SQuentin Monnet static int do_run(int argc, char **argv) 1167ba95c745SQuentin Monnet { 1168ba95c745SQuentin Monnet char *data_fname_in = NULL, *data_fname_out = NULL; 1169ba95c745SQuentin Monnet char *ctx_fname_in = NULL, *ctx_fname_out = NULL; 1170ba95c745SQuentin Monnet struct bpf_prog_test_run_attr test_attr = {0}; 1171ba95c745SQuentin Monnet const unsigned int default_size = SZ_32K; 1172ba95c745SQuentin Monnet void *data_in = NULL, *data_out = NULL; 1173ba95c745SQuentin Monnet void *ctx_in = NULL, *ctx_out = NULL; 1174ba95c745SQuentin Monnet unsigned int repeat = 1; 1175ba95c745SQuentin Monnet int fd, err; 1176ba95c745SQuentin Monnet 1177ba95c745SQuentin Monnet if (!REQ_ARGS(4)) 1178ba95c745SQuentin Monnet return -1; 1179ba95c745SQuentin Monnet 1180ba95c745SQuentin Monnet fd = prog_parse_fd(&argc, &argv); 1181ba95c745SQuentin Monnet if (fd < 0) 1182ba95c745SQuentin Monnet return -1; 1183ba95c745SQuentin Monnet 1184ba95c745SQuentin Monnet while (argc) { 1185ba95c745SQuentin Monnet if (detect_common_prefix(*argv, "data_in", "data_out", 1186ba95c745SQuentin Monnet "data_size_out", NULL)) 1187ba95c745SQuentin Monnet return -1; 1188ba95c745SQuentin Monnet if (detect_common_prefix(*argv, "ctx_in", "ctx_out", 1189ba95c745SQuentin Monnet "ctx_size_out", NULL)) 1190ba95c745SQuentin Monnet return -1; 1191ba95c745SQuentin Monnet 1192ba95c745SQuentin Monnet if (is_prefix(*argv, "data_in")) { 1193ba95c745SQuentin Monnet NEXT_ARG(); 1194ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1195ba95c745SQuentin Monnet return -1; 1196ba95c745SQuentin Monnet 1197ba95c745SQuentin Monnet data_fname_in = GET_ARG(); 1198ba95c745SQuentin Monnet if (check_single_stdin(data_fname_in, ctx_fname_in)) 1199ba95c745SQuentin Monnet return -1; 1200ba95c745SQuentin Monnet } else if (is_prefix(*argv, "data_out")) { 1201ba95c745SQuentin Monnet NEXT_ARG(); 1202ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1203ba95c745SQuentin Monnet return -1; 1204ba95c745SQuentin Monnet 1205ba95c745SQuentin Monnet data_fname_out = GET_ARG(); 1206ba95c745SQuentin Monnet } else if (is_prefix(*argv, "data_size_out")) { 1207ba95c745SQuentin Monnet char *endptr; 1208ba95c745SQuentin Monnet 1209ba95c745SQuentin Monnet NEXT_ARG(); 1210ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1211ba95c745SQuentin Monnet return -1; 1212ba95c745SQuentin Monnet 1213ba95c745SQuentin Monnet test_attr.data_size_out = strtoul(*argv, &endptr, 0); 1214ba95c745SQuentin Monnet if (*endptr) { 1215ba95c745SQuentin Monnet p_err("can't parse %s as output data size", 1216ba95c745SQuentin Monnet *argv); 1217ba95c745SQuentin Monnet return -1; 1218ba95c745SQuentin Monnet } 1219ba95c745SQuentin Monnet NEXT_ARG(); 1220ba95c745SQuentin Monnet } else if (is_prefix(*argv, "ctx_in")) { 1221ba95c745SQuentin Monnet NEXT_ARG(); 1222ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1223ba95c745SQuentin Monnet return -1; 1224ba95c745SQuentin Monnet 1225ba95c745SQuentin Monnet ctx_fname_in = GET_ARG(); 1226ba95c745SQuentin Monnet if (check_single_stdin(data_fname_in, ctx_fname_in)) 1227ba95c745SQuentin Monnet return -1; 1228ba95c745SQuentin Monnet } else if (is_prefix(*argv, "ctx_out")) { 1229ba95c745SQuentin Monnet NEXT_ARG(); 1230ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1231ba95c745SQuentin Monnet return -1; 1232ba95c745SQuentin Monnet 1233ba95c745SQuentin Monnet ctx_fname_out = GET_ARG(); 1234ba95c745SQuentin Monnet } else if (is_prefix(*argv, "ctx_size_out")) { 1235ba95c745SQuentin Monnet char *endptr; 1236ba95c745SQuentin Monnet 1237ba95c745SQuentin Monnet NEXT_ARG(); 1238ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1239ba95c745SQuentin Monnet return -1; 1240ba95c745SQuentin Monnet 1241ba95c745SQuentin Monnet test_attr.ctx_size_out = strtoul(*argv, &endptr, 0); 1242ba95c745SQuentin Monnet if (*endptr) { 1243ba95c745SQuentin Monnet p_err("can't parse %s as output context size", 1244ba95c745SQuentin Monnet *argv); 1245ba95c745SQuentin Monnet return -1; 1246ba95c745SQuentin Monnet } 1247ba95c745SQuentin Monnet NEXT_ARG(); 1248ba95c745SQuentin Monnet } else if (is_prefix(*argv, "repeat")) { 1249ba95c745SQuentin Monnet char *endptr; 1250ba95c745SQuentin Monnet 1251ba95c745SQuentin Monnet NEXT_ARG(); 1252ba95c745SQuentin Monnet if (!REQ_ARGS(1)) 1253ba95c745SQuentin Monnet return -1; 1254ba95c745SQuentin Monnet 1255ba95c745SQuentin Monnet repeat = strtoul(*argv, &endptr, 0); 1256ba95c745SQuentin Monnet if (*endptr) { 1257ba95c745SQuentin Monnet p_err("can't parse %s as repeat number", 1258ba95c745SQuentin Monnet *argv); 1259ba95c745SQuentin Monnet return -1; 1260ba95c745SQuentin Monnet } 1261ba95c745SQuentin Monnet NEXT_ARG(); 1262ba95c745SQuentin Monnet } else { 1263ba95c745SQuentin 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'?", 1264ba95c745SQuentin Monnet *argv); 1265ba95c745SQuentin Monnet return -1; 1266ba95c745SQuentin Monnet } 1267ba95c745SQuentin Monnet } 1268ba95c745SQuentin Monnet 1269ba95c745SQuentin Monnet err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in); 1270ba95c745SQuentin Monnet if (err) 1271ba95c745SQuentin Monnet return -1; 1272ba95c745SQuentin Monnet 1273ba95c745SQuentin Monnet if (data_in) { 1274ba95c745SQuentin Monnet if (!test_attr.data_size_out) 1275ba95c745SQuentin Monnet test_attr.data_size_out = default_size; 1276ba95c745SQuentin Monnet err = alloc_run_data(&data_out, test_attr.data_size_out); 1277ba95c745SQuentin Monnet if (err) 1278ba95c745SQuentin Monnet goto free_data_in; 1279ba95c745SQuentin Monnet } 1280ba95c745SQuentin Monnet 1281ba95c745SQuentin Monnet err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in); 1282ba95c745SQuentin Monnet if (err) 1283ba95c745SQuentin Monnet goto free_data_out; 1284ba95c745SQuentin Monnet 1285ba95c745SQuentin Monnet if (ctx_in) { 1286ba95c745SQuentin Monnet if (!test_attr.ctx_size_out) 1287ba95c745SQuentin Monnet test_attr.ctx_size_out = default_size; 1288ba95c745SQuentin Monnet err = alloc_run_data(&ctx_out, test_attr.ctx_size_out); 1289ba95c745SQuentin Monnet if (err) 1290ba95c745SQuentin Monnet goto free_ctx_in; 1291ba95c745SQuentin Monnet } 1292ba95c745SQuentin Monnet 1293ba95c745SQuentin Monnet test_attr.prog_fd = fd; 1294ba95c745SQuentin Monnet test_attr.repeat = repeat; 1295ba95c745SQuentin Monnet test_attr.data_in = data_in; 1296ba95c745SQuentin Monnet test_attr.data_out = data_out; 1297ba95c745SQuentin Monnet test_attr.ctx_in = ctx_in; 1298ba95c745SQuentin Monnet test_attr.ctx_out = ctx_out; 1299ba95c745SQuentin Monnet 1300ba95c745SQuentin Monnet err = bpf_prog_test_run_xattr(&test_attr); 1301ba95c745SQuentin Monnet if (err) { 1302ba95c745SQuentin Monnet p_err("failed to run program: %s", strerror(errno)); 1303ba95c745SQuentin Monnet goto free_ctx_out; 1304ba95c745SQuentin Monnet } 1305ba95c745SQuentin Monnet 1306ba95c745SQuentin Monnet err = 0; 1307ba95c745SQuentin Monnet 1308ba95c745SQuentin Monnet if (json_output) 1309ba95c745SQuentin Monnet jsonw_start_object(json_wtr); /* root */ 1310ba95c745SQuentin Monnet 1311ba95c745SQuentin Monnet /* Do not exit on errors occurring when printing output data/context, 1312ba95c745SQuentin Monnet * we still want to print return value and duration for program run. 1313ba95c745SQuentin Monnet */ 1314ba95c745SQuentin Monnet if (test_attr.data_size_out) 1315ba95c745SQuentin Monnet err += print_run_output(test_attr.data_out, 1316ba95c745SQuentin Monnet test_attr.data_size_out, 1317ba95c745SQuentin Monnet data_fname_out, "data_out"); 1318ba95c745SQuentin Monnet if (test_attr.ctx_size_out) 1319ba95c745SQuentin Monnet err += print_run_output(test_attr.ctx_out, 1320ba95c745SQuentin Monnet test_attr.ctx_size_out, 1321ba95c745SQuentin Monnet ctx_fname_out, "ctx_out"); 1322ba95c745SQuentin Monnet 1323ba95c745SQuentin Monnet if (json_output) { 1324ba95c745SQuentin Monnet jsonw_uint_field(json_wtr, "retval", test_attr.retval); 1325ba95c745SQuentin Monnet jsonw_uint_field(json_wtr, "duration", test_attr.duration); 1326ba95c745SQuentin Monnet jsonw_end_object(json_wtr); /* root */ 1327ba95c745SQuentin Monnet } else { 1328ba95c745SQuentin Monnet fprintf(stdout, "Return value: %u, duration%s: %uns\n", 1329ba95c745SQuentin Monnet test_attr.retval, 1330ba95c745SQuentin Monnet repeat > 1 ? " (average)" : "", test_attr.duration); 1331ba95c745SQuentin Monnet } 1332ba95c745SQuentin Monnet 1333ba95c745SQuentin Monnet free_ctx_out: 1334ba95c745SQuentin Monnet free(ctx_out); 1335ba95c745SQuentin Monnet free_ctx_in: 1336ba95c745SQuentin Monnet free(ctx_in); 1337ba95c745SQuentin Monnet free_data_out: 1338ba95c745SQuentin Monnet free(data_out); 1339ba95c745SQuentin Monnet free_data_in: 1340ba95c745SQuentin Monnet free(data_in); 1341ba95c745SQuentin Monnet 1342ba95c745SQuentin Monnet return err; 1343ba95c745SQuentin Monnet } 1344ba95c745SQuentin Monnet 13456ae32b29SQuentin Monnet static int 13466ae32b29SQuentin Monnet get_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type, 13476ae32b29SQuentin Monnet enum bpf_attach_type *expected_attach_type) 13486ae32b29SQuentin Monnet { 13496ae32b29SQuentin Monnet libbpf_print_fn_t print_backup; 13506ae32b29SQuentin Monnet int ret; 13516ae32b29SQuentin Monnet 13526ae32b29SQuentin Monnet ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type); 13536ae32b29SQuentin Monnet if (!ret) 13546ae32b29SQuentin Monnet return ret; 13556ae32b29SQuentin Monnet 13566ae32b29SQuentin Monnet /* libbpf_prog_type_by_name() failed, let's re-run with debug level */ 13576ae32b29SQuentin Monnet print_backup = libbpf_set_print(print_all_levels); 13586ae32b29SQuentin Monnet ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type); 13596ae32b29SQuentin Monnet libbpf_set_print(print_backup); 13606ae32b29SQuentin Monnet 13616ae32b29SQuentin Monnet return ret; 13626ae32b29SQuentin Monnet } 13636ae32b29SQuentin Monnet 136477380998SStanislav Fomichev static int load_with_options(int argc, char **argv, bool first_prog_only) 136549a086c2SRoman Gushchin { 136632e3e58eSAndrii Nakryiko enum bpf_prog_type common_prog_type = BPF_PROG_TYPE_UNSPEC; 1367e00aca65SAndrii Nakryiko DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts, 1368e00aca65SAndrii Nakryiko .relaxed_maps = relaxed_maps, 1369e00aca65SAndrii Nakryiko ); 1370e00aca65SAndrii Nakryiko struct bpf_object_load_attr load_attr = { 0 }; 137155d77807SQuentin Monnet enum bpf_attach_type expected_attach_type; 13723ff5a4dcSJakub Kicinski struct map_replace *map_replace = NULL; 137377380998SStanislav Fomichev struct bpf_program *prog = NULL, *pos; 13743ff5a4dcSJakub Kicinski unsigned int old_map_fds = 0; 13753767a94bSStanislav Fomichev const char *pinmaps = NULL; 137649a086c2SRoman Gushchin struct bpf_object *obj; 1377c8406848SJakub Kicinski struct bpf_map *map; 1378c8406848SJakub Kicinski const char *pinfile; 13793ff5a4dcSJakub Kicinski unsigned int i, j; 1380c8406848SJakub Kicinski __u32 ifindex = 0; 138132e3e58eSAndrii Nakryiko const char *file; 13823ff5a4dcSJakub Kicinski int idx, err; 138349a086c2SRoman Gushchin 138432e3e58eSAndrii Nakryiko 13858d1fc3deSJakub Kicinski if (!REQ_ARGS(2)) 13868d1fc3deSJakub Kicinski return -1; 138732e3e58eSAndrii Nakryiko file = GET_ARG(); 13888d1fc3deSJakub Kicinski pinfile = GET_ARG(); 138949a086c2SRoman Gushchin 1390ba6dd679SJakub Kicinski while (argc) { 139149f2cba3SJakub Kicinski if (is_prefix(*argv, "type")) { 139249f2cba3SJakub Kicinski char *type; 139349f2cba3SJakub Kicinski 139449f2cba3SJakub Kicinski NEXT_ARG(); 139549f2cba3SJakub Kicinski 139632e3e58eSAndrii Nakryiko if (common_prog_type != BPF_PROG_TYPE_UNSPEC) { 139749f2cba3SJakub Kicinski p_err("program type already specified"); 13983ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 139949f2cba3SJakub Kicinski } 140049f2cba3SJakub Kicinski if (!REQ_ARGS(1)) 14013ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 140249f2cba3SJakub Kicinski 140349f2cba3SJakub Kicinski /* Put a '/' at the end of type to appease libbpf */ 140449f2cba3SJakub Kicinski type = malloc(strlen(*argv) + 2); 140549f2cba3SJakub Kicinski if (!type) { 140649f2cba3SJakub Kicinski p_err("mem alloc failed"); 14073ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 140849f2cba3SJakub Kicinski } 140949f2cba3SJakub Kicinski *type = 0; 141049f2cba3SJakub Kicinski strcat(type, *argv); 141149f2cba3SJakub Kicinski strcat(type, "/"); 141249f2cba3SJakub Kicinski 14136ae32b29SQuentin Monnet err = get_prog_type_by_name(type, &common_prog_type, 1414c8406848SJakub Kicinski &expected_attach_type); 141549f2cba3SJakub Kicinski free(type); 1416c76e4c22STaeung Song if (err < 0) 14173ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1418c76e4c22STaeung Song 141949f2cba3SJakub Kicinski NEXT_ARG(); 14203ff5a4dcSJakub Kicinski } else if (is_prefix(*argv, "map")) { 1421dde7011aSJakub Kicinski void *new_map_replace; 14223ff5a4dcSJakub Kicinski char *endptr, *name; 14233ff5a4dcSJakub Kicinski int fd; 14243ff5a4dcSJakub Kicinski 14253ff5a4dcSJakub Kicinski NEXT_ARG(); 14263ff5a4dcSJakub Kicinski 14273ff5a4dcSJakub Kicinski if (!REQ_ARGS(4)) 14283ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 14293ff5a4dcSJakub Kicinski 14303ff5a4dcSJakub Kicinski if (is_prefix(*argv, "idx")) { 14313ff5a4dcSJakub Kicinski NEXT_ARG(); 14323ff5a4dcSJakub Kicinski 14333ff5a4dcSJakub Kicinski idx = strtoul(*argv, &endptr, 0); 14343ff5a4dcSJakub Kicinski if (*endptr) { 14353ff5a4dcSJakub Kicinski p_err("can't parse %s as IDX", *argv); 14363ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 14373ff5a4dcSJakub Kicinski } 14383ff5a4dcSJakub Kicinski name = NULL; 14393ff5a4dcSJakub Kicinski } else if (is_prefix(*argv, "name")) { 14403ff5a4dcSJakub Kicinski NEXT_ARG(); 14413ff5a4dcSJakub Kicinski 14423ff5a4dcSJakub Kicinski name = *argv; 14433ff5a4dcSJakub Kicinski idx = -1; 14443ff5a4dcSJakub Kicinski } else { 14453ff5a4dcSJakub Kicinski p_err("expected 'idx' or 'name', got: '%s'?", 14463ff5a4dcSJakub Kicinski *argv); 14473ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 14483ff5a4dcSJakub Kicinski } 14493ff5a4dcSJakub Kicinski NEXT_ARG(); 14503ff5a4dcSJakub Kicinski 14513ff5a4dcSJakub Kicinski fd = map_parse_fd(&argc, &argv); 14523ff5a4dcSJakub Kicinski if (fd < 0) 14533ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 14543ff5a4dcSJakub Kicinski 1455dde7011aSJakub Kicinski new_map_replace = reallocarray(map_replace, 1456dde7011aSJakub Kicinski old_map_fds + 1, 14573ff5a4dcSJakub Kicinski sizeof(*map_replace)); 1458dde7011aSJakub Kicinski if (!new_map_replace) { 14593ff5a4dcSJakub Kicinski p_err("mem alloc failed"); 14603ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 14613ff5a4dcSJakub Kicinski } 1462dde7011aSJakub Kicinski map_replace = new_map_replace; 1463dde7011aSJakub Kicinski 14643ff5a4dcSJakub Kicinski map_replace[old_map_fds].idx = idx; 14653ff5a4dcSJakub Kicinski map_replace[old_map_fds].name = name; 14663ff5a4dcSJakub Kicinski map_replace[old_map_fds].fd = fd; 14673ff5a4dcSJakub Kicinski old_map_fds++; 146849f2cba3SJakub Kicinski } else if (is_prefix(*argv, "dev")) { 1469ba6dd679SJakub Kicinski NEXT_ARG(); 1470ba6dd679SJakub Kicinski 1471c8406848SJakub Kicinski if (ifindex) { 1472ba6dd679SJakub Kicinski p_err("offload device already specified"); 14733ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1474ba6dd679SJakub Kicinski } 1475ba6dd679SJakub Kicinski if (!REQ_ARGS(1)) 14763ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1477ba6dd679SJakub Kicinski 1478c8406848SJakub Kicinski ifindex = if_nametoindex(*argv); 1479c8406848SJakub Kicinski if (!ifindex) { 1480ba6dd679SJakub Kicinski p_err("unrecognized netdevice '%s': %s", 1481ba6dd679SJakub Kicinski *argv, strerror(errno)); 14823ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1483ba6dd679SJakub Kicinski } 1484ba6dd679SJakub Kicinski NEXT_ARG(); 14853767a94bSStanislav Fomichev } else if (is_prefix(*argv, "pinmaps")) { 14863767a94bSStanislav Fomichev NEXT_ARG(); 14873767a94bSStanislav Fomichev 14883767a94bSStanislav Fomichev if (!REQ_ARGS(1)) 14893767a94bSStanislav Fomichev goto err_free_reuse_maps; 14903767a94bSStanislav Fomichev 14913767a94bSStanislav Fomichev pinmaps = GET_ARG(); 1492ba6dd679SJakub Kicinski } else { 14933ff5a4dcSJakub Kicinski p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?", 1494ba6dd679SJakub Kicinski *argv); 14953ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 1496ba6dd679SJakub Kicinski } 1497ba6dd679SJakub Kicinski } 1498ba6dd679SJakub Kicinski 1499ac4e0e05SYonghong Song set_max_rlimit(); 1500ac4e0e05SYonghong Song 150132e3e58eSAndrii Nakryiko obj = bpf_object__open_file(file, &open_opts); 1502c8406848SJakub Kicinski if (IS_ERR_OR_NULL(obj)) { 1503c8406848SJakub Kicinski p_err("failed to open object file"); 15043ff5a4dcSJakub Kicinski goto err_free_reuse_maps; 150549a086c2SRoman Gushchin } 150649a086c2SRoman Gushchin 150777380998SStanislav Fomichev bpf_object__for_each_program(pos, obj) { 150832e3e58eSAndrii Nakryiko enum bpf_prog_type prog_type = common_prog_type; 1509c8406848SJakub Kicinski 151032e3e58eSAndrii Nakryiko if (prog_type == BPF_PROG_TYPE_UNSPEC) { 1511fd17e272SAndrii Nakryiko const char *sec_name = bpf_program__section_name(pos); 1512c8406848SJakub Kicinski 15136ae32b29SQuentin Monnet err = get_prog_type_by_name(sec_name, &prog_type, 1514c8406848SJakub Kicinski &expected_attach_type); 1515c76e4c22STaeung Song if (err < 0) 1516c8406848SJakub Kicinski goto err_close_obj; 1517c8406848SJakub Kicinski } 151877380998SStanislav Fomichev 151977380998SStanislav Fomichev bpf_program__set_ifindex(pos, ifindex); 152077380998SStanislav Fomichev bpf_program__set_type(pos, prog_type); 152177380998SStanislav Fomichev bpf_program__set_expected_attach_type(pos, expected_attach_type); 152277380998SStanislav Fomichev } 1523c8406848SJakub Kicinski 15243ff5a4dcSJakub Kicinski qsort(map_replace, old_map_fds, sizeof(*map_replace), 15253ff5a4dcSJakub Kicinski map_replace_compar); 15263ff5a4dcSJakub Kicinski 15273ff5a4dcSJakub Kicinski /* After the sort maps by name will be first on the list, because they 15283ff5a4dcSJakub Kicinski * have idx == -1. Resolve them. 15293ff5a4dcSJakub Kicinski */ 15303ff5a4dcSJakub Kicinski j = 0; 15313ff5a4dcSJakub Kicinski while (j < old_map_fds && map_replace[j].name) { 15323ff5a4dcSJakub Kicinski i = 0; 1533f74a53d9SJakub Kicinski bpf_object__for_each_map(map, obj) { 15343ff5a4dcSJakub Kicinski if (!strcmp(bpf_map__name(map), map_replace[j].name)) { 15353ff5a4dcSJakub Kicinski map_replace[j].idx = i; 15363ff5a4dcSJakub Kicinski break; 15373ff5a4dcSJakub Kicinski } 15383ff5a4dcSJakub Kicinski i++; 15393ff5a4dcSJakub Kicinski } 15403ff5a4dcSJakub Kicinski if (map_replace[j].idx == -1) { 15413ff5a4dcSJakub Kicinski p_err("unable to find map '%s'", map_replace[j].name); 15423ff5a4dcSJakub Kicinski goto err_close_obj; 15433ff5a4dcSJakub Kicinski } 15443ff5a4dcSJakub Kicinski j++; 15453ff5a4dcSJakub Kicinski } 15463ff5a4dcSJakub Kicinski /* Resort if any names were resolved */ 15473ff5a4dcSJakub Kicinski if (j) 15483ff5a4dcSJakub Kicinski qsort(map_replace, old_map_fds, sizeof(*map_replace), 15493ff5a4dcSJakub Kicinski map_replace_compar); 15503ff5a4dcSJakub Kicinski 15513ff5a4dcSJakub Kicinski /* Set ifindex and name reuse */ 15523ff5a4dcSJakub Kicinski j = 0; 15533ff5a4dcSJakub Kicinski idx = 0; 1554f74a53d9SJakub Kicinski bpf_object__for_each_map(map, obj) { 1555c8406848SJakub Kicinski if (!bpf_map__is_offload_neutral(map)) 1556c8406848SJakub Kicinski bpf_map__set_ifindex(map, ifindex); 1557c8406848SJakub Kicinski 15583ff5a4dcSJakub Kicinski if (j < old_map_fds && idx == map_replace[j].idx) { 15593ff5a4dcSJakub Kicinski err = bpf_map__reuse_fd(map, map_replace[j++].fd); 15603ff5a4dcSJakub Kicinski if (err) { 15613ff5a4dcSJakub Kicinski p_err("unable to set up map reuse: %d", err); 15623ff5a4dcSJakub Kicinski goto err_close_obj; 15633ff5a4dcSJakub Kicinski } 15643ff5a4dcSJakub Kicinski 15653ff5a4dcSJakub Kicinski /* Next reuse wants to apply to the same map */ 15663ff5a4dcSJakub Kicinski if (j < old_map_fds && map_replace[j].idx == idx) { 15673ff5a4dcSJakub Kicinski p_err("replacement for map idx %d specified more than once", 15683ff5a4dcSJakub Kicinski idx); 15693ff5a4dcSJakub Kicinski goto err_close_obj; 15703ff5a4dcSJakub Kicinski } 15713ff5a4dcSJakub Kicinski } 15723ff5a4dcSJakub Kicinski 15733ff5a4dcSJakub Kicinski idx++; 15743ff5a4dcSJakub Kicinski } 15753ff5a4dcSJakub Kicinski if (j < old_map_fds) { 15763ff5a4dcSJakub Kicinski p_err("map idx '%d' not used", map_replace[j].idx); 15773ff5a4dcSJakub Kicinski goto err_close_obj; 15783ff5a4dcSJakub Kicinski } 15793ff5a4dcSJakub Kicinski 158055d77807SQuentin Monnet load_attr.obj = obj; 158155d77807SQuentin Monnet if (verifier_logs) 158255d77807SQuentin Monnet /* log_level1 + log_level2 + stats, but not stable UAPI */ 158355d77807SQuentin Monnet load_attr.log_level = 1 + 2 + 4; 158455d77807SQuentin Monnet 158555d77807SQuentin Monnet err = bpf_object__load_xattr(&load_attr); 1586c8406848SJakub Kicinski if (err) { 1587c8406848SJakub Kicinski p_err("failed to load object file"); 1588c8406848SJakub Kicinski goto err_close_obj; 1589c8406848SJakub Kicinski } 1590c8406848SJakub Kicinski 159177380998SStanislav Fomichev err = mount_bpffs_for_pin(pinfile); 159277380998SStanislav Fomichev if (err) 1593bfee71fbSJakub Kicinski goto err_close_obj; 159449a086c2SRoman Gushchin 159577380998SStanislav Fomichev if (first_prog_only) { 159677380998SStanislav Fomichev prog = bpf_program__next(NULL, obj); 159777380998SStanislav Fomichev if (!prog) { 159877380998SStanislav Fomichev p_err("object file doesn't contain any bpf program"); 159977380998SStanislav Fomichev goto err_close_obj; 160077380998SStanislav Fomichev } 160177380998SStanislav Fomichev 160277380998SStanislav Fomichev err = bpf_obj_pin(bpf_program__fd(prog), pinfile); 160377380998SStanislav Fomichev if (err) { 160477380998SStanislav Fomichev p_err("failed to pin program %s", 1605fd17e272SAndrii Nakryiko bpf_program__section_name(prog)); 160677380998SStanislav Fomichev goto err_close_obj; 160777380998SStanislav Fomichev } 160877380998SStanislav Fomichev } else { 160977380998SStanislav Fomichev err = bpf_object__pin_programs(obj, pinfile); 161077380998SStanislav Fomichev if (err) { 161177380998SStanislav Fomichev p_err("failed to pin all programs"); 161277380998SStanislav Fomichev goto err_close_obj; 161377380998SStanislav Fomichev } 161477380998SStanislav Fomichev } 161577380998SStanislav Fomichev 16163767a94bSStanislav Fomichev if (pinmaps) { 16173767a94bSStanislav Fomichev err = bpf_object__pin_maps(obj, pinmaps); 16183767a94bSStanislav Fomichev if (err) { 16193767a94bSStanislav Fomichev p_err("failed to pin all maps"); 16203767a94bSStanislav Fomichev goto err_unpin; 16213767a94bSStanislav Fomichev } 16223767a94bSStanislav Fomichev } 16233767a94bSStanislav Fomichev 162449a086c2SRoman Gushchin if (json_output) 162549a086c2SRoman Gushchin jsonw_null(json_wtr); 162649a086c2SRoman Gushchin 1627bfee71fbSJakub Kicinski bpf_object__close(obj); 16283ff5a4dcSJakub Kicinski for (i = 0; i < old_map_fds; i++) 16293ff5a4dcSJakub Kicinski close(map_replace[i].fd); 16303ff5a4dcSJakub Kicinski free(map_replace); 1631bfee71fbSJakub Kicinski 163249a086c2SRoman Gushchin return 0; 1633bfee71fbSJakub Kicinski 16343767a94bSStanislav Fomichev err_unpin: 16353767a94bSStanislav Fomichev if (first_prog_only) 16363767a94bSStanislav Fomichev unlink(pinfile); 16373767a94bSStanislav Fomichev else 16383767a94bSStanislav Fomichev bpf_object__unpin_programs(obj, pinfile); 1639bfee71fbSJakub Kicinski err_close_obj: 1640bfee71fbSJakub Kicinski bpf_object__close(obj); 16413ff5a4dcSJakub Kicinski err_free_reuse_maps: 16423ff5a4dcSJakub Kicinski for (i = 0; i < old_map_fds; i++) 16433ff5a4dcSJakub Kicinski close(map_replace[i].fd); 16443ff5a4dcSJakub Kicinski free(map_replace); 1645bfee71fbSJakub Kicinski return -1; 164649a086c2SRoman Gushchin } 164749a086c2SRoman Gushchin 164877380998SStanislav Fomichev static int do_load(int argc, char **argv) 164977380998SStanislav Fomichev { 165077380998SStanislav Fomichev return load_with_options(argc, argv, true); 165177380998SStanislav Fomichev } 165277380998SStanislav Fomichev 165377380998SStanislav Fomichev static int do_loadall(int argc, char **argv) 165477380998SStanislav Fomichev { 165577380998SStanislav Fomichev return load_with_options(argc, argv, false); 165677380998SStanislav Fomichev } 165777380998SStanislav Fomichev 165847c09d6aSSong Liu #ifdef BPFTOOL_WITHOUT_SKELETONS 165947c09d6aSSong Liu 166047c09d6aSSong Liu static int do_profile(int argc, char **argv) 166147c09d6aSSong Liu { 166214e5728fSSong Liu p_err("bpftool prog profile command is not supported. Please build bpftool with clang >= 10.0.0"); 166347c09d6aSSong Liu return 0; 166447c09d6aSSong Liu } 166547c09d6aSSong Liu 166647c09d6aSSong Liu #else /* BPFTOOL_WITHOUT_SKELETONS */ 166747c09d6aSSong Liu 166847c09d6aSSong Liu #include "profiler.skel.h" 166947c09d6aSSong Liu 167047c09d6aSSong Liu struct profile_metric { 167147c09d6aSSong Liu const char *name; 167247c09d6aSSong Liu struct bpf_perf_event_value val; 167347c09d6aSSong Liu struct perf_event_attr attr; 167447c09d6aSSong Liu bool selected; 167547c09d6aSSong Liu 167647c09d6aSSong Liu /* calculate ratios like instructions per cycle */ 167747c09d6aSSong Liu const int ratio_metric; /* 0 for N/A, 1 for index 0 (cycles) */ 167847c09d6aSSong Liu const char *ratio_desc; 167947c09d6aSSong Liu const float ratio_mul; 168047c09d6aSSong Liu } metrics[] = { 168147c09d6aSSong Liu { 168247c09d6aSSong Liu .name = "cycles", 168347c09d6aSSong Liu .attr = { 168447c09d6aSSong Liu .type = PERF_TYPE_HARDWARE, 168547c09d6aSSong Liu .config = PERF_COUNT_HW_CPU_CYCLES, 168647c09d6aSSong Liu .exclude_user = 1, 168747c09d6aSSong Liu }, 168847c09d6aSSong Liu }, 168947c09d6aSSong Liu { 169047c09d6aSSong Liu .name = "instructions", 169147c09d6aSSong Liu .attr = { 169247c09d6aSSong Liu .type = PERF_TYPE_HARDWARE, 169347c09d6aSSong Liu .config = PERF_COUNT_HW_INSTRUCTIONS, 169447c09d6aSSong Liu .exclude_user = 1, 169547c09d6aSSong Liu }, 169647c09d6aSSong Liu .ratio_metric = 1, 169747c09d6aSSong Liu .ratio_desc = "insns per cycle", 169847c09d6aSSong Liu .ratio_mul = 1.0, 169947c09d6aSSong Liu }, 170047c09d6aSSong Liu { 170147c09d6aSSong Liu .name = "l1d_loads", 170247c09d6aSSong Liu .attr = { 170347c09d6aSSong Liu .type = PERF_TYPE_HW_CACHE, 170447c09d6aSSong Liu .config = 170547c09d6aSSong Liu PERF_COUNT_HW_CACHE_L1D | 170647c09d6aSSong Liu (PERF_COUNT_HW_CACHE_OP_READ << 8) | 170747c09d6aSSong Liu (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16), 170847c09d6aSSong Liu .exclude_user = 1, 170947c09d6aSSong Liu }, 171047c09d6aSSong Liu }, 171147c09d6aSSong Liu { 171247c09d6aSSong Liu .name = "llc_misses", 171347c09d6aSSong Liu .attr = { 171447c09d6aSSong Liu .type = PERF_TYPE_HW_CACHE, 171547c09d6aSSong Liu .config = 171647c09d6aSSong Liu PERF_COUNT_HW_CACHE_LL | 171747c09d6aSSong Liu (PERF_COUNT_HW_CACHE_OP_READ << 8) | 171847c09d6aSSong Liu (PERF_COUNT_HW_CACHE_RESULT_MISS << 16), 171947c09d6aSSong Liu .exclude_user = 1 172047c09d6aSSong Liu }, 172147c09d6aSSong Liu .ratio_metric = 2, 172247c09d6aSSong Liu .ratio_desc = "LLC misses per million insns", 172347c09d6aSSong Liu .ratio_mul = 1e6, 172447c09d6aSSong Liu }, 1725450d060eSYonghong Song { 1726450d060eSYonghong Song .name = "itlb_misses", 1727450d060eSYonghong Song .attr = { 1728450d060eSYonghong Song .type = PERF_TYPE_HW_CACHE, 1729450d060eSYonghong Song .config = 1730450d060eSYonghong Song PERF_COUNT_HW_CACHE_ITLB | 1731450d060eSYonghong Song (PERF_COUNT_HW_CACHE_OP_READ << 8) | 1732450d060eSYonghong Song (PERF_COUNT_HW_CACHE_RESULT_MISS << 16), 1733450d060eSYonghong Song .exclude_user = 1 1734450d060eSYonghong Song }, 1735450d060eSYonghong Song .ratio_metric = 2, 1736450d060eSYonghong Song .ratio_desc = "itlb misses per million insns", 1737450d060eSYonghong Song .ratio_mul = 1e6, 1738450d060eSYonghong Song }, 1739450d060eSYonghong Song { 1740450d060eSYonghong Song .name = "dtlb_misses", 1741450d060eSYonghong Song .attr = { 1742450d060eSYonghong Song .type = PERF_TYPE_HW_CACHE, 1743450d060eSYonghong Song .config = 1744450d060eSYonghong Song PERF_COUNT_HW_CACHE_DTLB | 1745450d060eSYonghong Song (PERF_COUNT_HW_CACHE_OP_READ << 8) | 1746450d060eSYonghong Song (PERF_COUNT_HW_CACHE_RESULT_MISS << 16), 1747450d060eSYonghong Song .exclude_user = 1 1748450d060eSYonghong Song }, 1749450d060eSYonghong Song .ratio_metric = 2, 1750450d060eSYonghong Song .ratio_desc = "dtlb misses per million insns", 1751450d060eSYonghong Song .ratio_mul = 1e6, 1752450d060eSYonghong Song }, 175347c09d6aSSong Liu }; 175447c09d6aSSong Liu 175547c09d6aSSong Liu static __u64 profile_total_count; 175647c09d6aSSong Liu 175747c09d6aSSong Liu #define MAX_NUM_PROFILE_METRICS 4 175847c09d6aSSong Liu 175947c09d6aSSong Liu static int profile_parse_metrics(int argc, char **argv) 176047c09d6aSSong Liu { 176147c09d6aSSong Liu unsigned int metric_cnt; 176247c09d6aSSong Liu int selected_cnt = 0; 176347c09d6aSSong Liu unsigned int i; 176447c09d6aSSong Liu 176547c09d6aSSong Liu metric_cnt = sizeof(metrics) / sizeof(struct profile_metric); 176647c09d6aSSong Liu 176747c09d6aSSong Liu while (argc > 0) { 176847c09d6aSSong Liu for (i = 0; i < metric_cnt; i++) { 176947c09d6aSSong Liu if (is_prefix(argv[0], metrics[i].name)) { 177047c09d6aSSong Liu if (!metrics[i].selected) 177147c09d6aSSong Liu selected_cnt++; 177247c09d6aSSong Liu metrics[i].selected = true; 177347c09d6aSSong Liu break; 177447c09d6aSSong Liu } 177547c09d6aSSong Liu } 177647c09d6aSSong Liu if (i == metric_cnt) { 177747c09d6aSSong Liu p_err("unknown metric %s", argv[0]); 177847c09d6aSSong Liu return -1; 177947c09d6aSSong Liu } 178047c09d6aSSong Liu NEXT_ARG(); 178147c09d6aSSong Liu } 178247c09d6aSSong Liu if (selected_cnt > MAX_NUM_PROFILE_METRICS) { 178347c09d6aSSong Liu p_err("too many (%d) metrics, please specify no more than %d metrics at at time", 178447c09d6aSSong Liu selected_cnt, MAX_NUM_PROFILE_METRICS); 178547c09d6aSSong Liu return -1; 178647c09d6aSSong Liu } 178747c09d6aSSong Liu return selected_cnt; 178847c09d6aSSong Liu } 178947c09d6aSSong Liu 179047c09d6aSSong Liu static void profile_read_values(struct profiler_bpf *obj) 179147c09d6aSSong Liu { 179247c09d6aSSong Liu __u32 m, cpu, num_cpu = obj->rodata->num_cpu; 179347c09d6aSSong Liu int reading_map_fd, count_map_fd; 179447c09d6aSSong Liu __u64 counts[num_cpu]; 179547c09d6aSSong Liu __u32 key = 0; 179647c09d6aSSong Liu int err; 179747c09d6aSSong Liu 179847c09d6aSSong Liu reading_map_fd = bpf_map__fd(obj->maps.accum_readings); 179947c09d6aSSong Liu count_map_fd = bpf_map__fd(obj->maps.counts); 180047c09d6aSSong Liu if (reading_map_fd < 0 || count_map_fd < 0) { 180147c09d6aSSong Liu p_err("failed to get fd for map"); 180247c09d6aSSong Liu return; 180347c09d6aSSong Liu } 180447c09d6aSSong Liu 180547c09d6aSSong Liu err = bpf_map_lookup_elem(count_map_fd, &key, counts); 180647c09d6aSSong Liu if (err) { 180747c09d6aSSong Liu p_err("failed to read count_map: %s", strerror(errno)); 180847c09d6aSSong Liu return; 180947c09d6aSSong Liu } 181047c09d6aSSong Liu 181147c09d6aSSong Liu profile_total_count = 0; 181247c09d6aSSong Liu for (cpu = 0; cpu < num_cpu; cpu++) 181347c09d6aSSong Liu profile_total_count += counts[cpu]; 181447c09d6aSSong Liu 181547c09d6aSSong Liu for (m = 0; m < ARRAY_SIZE(metrics); m++) { 181647c09d6aSSong Liu struct bpf_perf_event_value values[num_cpu]; 181747c09d6aSSong Liu 181847c09d6aSSong Liu if (!metrics[m].selected) 181947c09d6aSSong Liu continue; 182047c09d6aSSong Liu 182147c09d6aSSong Liu err = bpf_map_lookup_elem(reading_map_fd, &key, values); 182247c09d6aSSong Liu if (err) { 182347c09d6aSSong Liu p_err("failed to read reading_map: %s", 182447c09d6aSSong Liu strerror(errno)); 182547c09d6aSSong Liu return; 182647c09d6aSSong Liu } 182747c09d6aSSong Liu for (cpu = 0; cpu < num_cpu; cpu++) { 182847c09d6aSSong Liu metrics[m].val.counter += values[cpu].counter; 182947c09d6aSSong Liu metrics[m].val.enabled += values[cpu].enabled; 183047c09d6aSSong Liu metrics[m].val.running += values[cpu].running; 183147c09d6aSSong Liu } 183247c09d6aSSong Liu key++; 183347c09d6aSSong Liu } 183447c09d6aSSong Liu } 183547c09d6aSSong Liu 183647c09d6aSSong Liu static void profile_print_readings_json(void) 183747c09d6aSSong Liu { 183847c09d6aSSong Liu __u32 m; 183947c09d6aSSong Liu 184047c09d6aSSong Liu jsonw_start_array(json_wtr); 184147c09d6aSSong Liu for (m = 0; m < ARRAY_SIZE(metrics); m++) { 184247c09d6aSSong Liu if (!metrics[m].selected) 184347c09d6aSSong Liu continue; 184447c09d6aSSong Liu jsonw_start_object(json_wtr); 184547c09d6aSSong Liu jsonw_string_field(json_wtr, "metric", metrics[m].name); 184647c09d6aSSong Liu jsonw_lluint_field(json_wtr, "run_cnt", profile_total_count); 184747c09d6aSSong Liu jsonw_lluint_field(json_wtr, "value", metrics[m].val.counter); 184847c09d6aSSong Liu jsonw_lluint_field(json_wtr, "enabled", metrics[m].val.enabled); 184947c09d6aSSong Liu jsonw_lluint_field(json_wtr, "running", metrics[m].val.running); 185047c09d6aSSong Liu 185147c09d6aSSong Liu jsonw_end_object(json_wtr); 185247c09d6aSSong Liu } 185347c09d6aSSong Liu jsonw_end_array(json_wtr); 185447c09d6aSSong Liu } 185547c09d6aSSong Liu 185647c09d6aSSong Liu static void profile_print_readings_plain(void) 185747c09d6aSSong Liu { 185847c09d6aSSong Liu __u32 m; 185947c09d6aSSong Liu 186047c09d6aSSong Liu printf("\n%18llu %-20s\n", profile_total_count, "run_cnt"); 186147c09d6aSSong Liu for (m = 0; m < ARRAY_SIZE(metrics); m++) { 186247c09d6aSSong Liu struct bpf_perf_event_value *val = &metrics[m].val; 186347c09d6aSSong Liu int r; 186447c09d6aSSong Liu 186547c09d6aSSong Liu if (!metrics[m].selected) 186647c09d6aSSong Liu continue; 186747c09d6aSSong Liu printf("%18llu %-20s", val->counter, metrics[m].name); 186847c09d6aSSong Liu 186947c09d6aSSong Liu r = metrics[m].ratio_metric - 1; 187047c09d6aSSong Liu if (r >= 0 && metrics[r].selected && 187147c09d6aSSong Liu metrics[r].val.counter > 0) { 187247c09d6aSSong Liu printf("# %8.2f %-30s", 187347c09d6aSSong Liu val->counter * metrics[m].ratio_mul / 187447c09d6aSSong Liu metrics[r].val.counter, 187547c09d6aSSong Liu metrics[m].ratio_desc); 187647c09d6aSSong Liu } else { 187747c09d6aSSong Liu printf("%-41s", ""); 187847c09d6aSSong Liu } 187947c09d6aSSong Liu 188047c09d6aSSong Liu if (val->enabled > val->running) 188147c09d6aSSong Liu printf("(%4.2f%%)", 188247c09d6aSSong Liu val->running * 100.0 / val->enabled); 188347c09d6aSSong Liu printf("\n"); 188447c09d6aSSong Liu } 188547c09d6aSSong Liu } 188647c09d6aSSong Liu 188747c09d6aSSong Liu static void profile_print_readings(void) 188847c09d6aSSong Liu { 188947c09d6aSSong Liu if (json_output) 189047c09d6aSSong Liu profile_print_readings_json(); 189147c09d6aSSong Liu else 189247c09d6aSSong Liu profile_print_readings_plain(); 189347c09d6aSSong Liu } 189447c09d6aSSong Liu 189547c09d6aSSong Liu static char *profile_target_name(int tgt_fd) 189647c09d6aSSong Liu { 189747c09d6aSSong Liu struct bpf_prog_info_linear *info_linear; 189847c09d6aSSong Liu struct bpf_func_info *func_info; 189947c09d6aSSong Liu const struct btf_type *t; 190047c09d6aSSong Liu char *name = NULL; 190147c09d6aSSong Liu struct btf *btf; 190247c09d6aSSong Liu 190347c09d6aSSong Liu info_linear = bpf_program__get_prog_info_linear( 190447c09d6aSSong Liu tgt_fd, 1UL << BPF_PROG_INFO_FUNC_INFO); 190547c09d6aSSong Liu if (IS_ERR_OR_NULL(info_linear)) { 190647c09d6aSSong Liu p_err("failed to get info_linear for prog FD %d", tgt_fd); 190747c09d6aSSong Liu return NULL; 190847c09d6aSSong Liu } 190947c09d6aSSong Liu 191047c09d6aSSong Liu if (info_linear->info.btf_id == 0 || 191147c09d6aSSong Liu btf__get_from_id(info_linear->info.btf_id, &btf)) { 191247c09d6aSSong Liu p_err("prog FD %d doesn't have valid btf", tgt_fd); 191347c09d6aSSong Liu goto out; 191447c09d6aSSong Liu } 191547c09d6aSSong Liu 191609f44b75SAndrii Nakryiko func_info = u64_to_ptr(info_linear->info.func_info); 191747c09d6aSSong Liu t = btf__type_by_id(btf, func_info[0].type_id); 191847c09d6aSSong Liu if (!t) { 191947c09d6aSSong Liu p_err("btf %d doesn't have type %d", 192047c09d6aSSong Liu info_linear->info.btf_id, func_info[0].type_id); 192147c09d6aSSong Liu goto out; 192247c09d6aSSong Liu } 192347c09d6aSSong Liu name = strdup(btf__name_by_offset(btf, t->name_off)); 192447c09d6aSSong Liu out: 192547c09d6aSSong Liu free(info_linear); 192647c09d6aSSong Liu return name; 192747c09d6aSSong Liu } 192847c09d6aSSong Liu 192947c09d6aSSong Liu static struct profiler_bpf *profile_obj; 193047c09d6aSSong Liu static int profile_tgt_fd = -1; 193147c09d6aSSong Liu static char *profile_tgt_name; 193247c09d6aSSong Liu static int *profile_perf_events; 193347c09d6aSSong Liu static int profile_perf_event_cnt; 193447c09d6aSSong Liu 193547c09d6aSSong Liu static void profile_close_perf_events(struct profiler_bpf *obj) 193647c09d6aSSong Liu { 193747c09d6aSSong Liu int i; 193847c09d6aSSong Liu 193947c09d6aSSong Liu for (i = profile_perf_event_cnt - 1; i >= 0; i--) 194047c09d6aSSong Liu close(profile_perf_events[i]); 194147c09d6aSSong Liu 194247c09d6aSSong Liu free(profile_perf_events); 194347c09d6aSSong Liu profile_perf_event_cnt = 0; 194447c09d6aSSong Liu } 194547c09d6aSSong Liu 194647c09d6aSSong Liu static int profile_open_perf_events(struct profiler_bpf *obj) 194747c09d6aSSong Liu { 194847c09d6aSSong Liu unsigned int cpu, m; 194947c09d6aSSong Liu int map_fd, pmu_fd; 195047c09d6aSSong Liu 195147c09d6aSSong Liu profile_perf_events = calloc( 195247c09d6aSSong Liu sizeof(int), obj->rodata->num_cpu * obj->rodata->num_metric); 195347c09d6aSSong Liu if (!profile_perf_events) { 195447c09d6aSSong Liu p_err("failed to allocate memory for perf_event array: %s", 195547c09d6aSSong Liu strerror(errno)); 195647c09d6aSSong Liu return -1; 195747c09d6aSSong Liu } 195847c09d6aSSong Liu map_fd = bpf_map__fd(obj->maps.events); 195947c09d6aSSong Liu if (map_fd < 0) { 196047c09d6aSSong Liu p_err("failed to get fd for events map"); 196147c09d6aSSong Liu return -1; 196247c09d6aSSong Liu } 196347c09d6aSSong Liu 196447c09d6aSSong Liu for (m = 0; m < ARRAY_SIZE(metrics); m++) { 196547c09d6aSSong Liu if (!metrics[m].selected) 196647c09d6aSSong Liu continue; 196747c09d6aSSong Liu for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) { 196847c09d6aSSong Liu pmu_fd = syscall(__NR_perf_event_open, &metrics[m].attr, 196947c09d6aSSong Liu -1/*pid*/, cpu, -1/*group_fd*/, 0); 197047c09d6aSSong Liu if (pmu_fd < 0 || 197147c09d6aSSong Liu bpf_map_update_elem(map_fd, &profile_perf_event_cnt, 197247c09d6aSSong Liu &pmu_fd, BPF_ANY) || 197347c09d6aSSong Liu ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) { 197447c09d6aSSong Liu p_err("failed to create event %s on cpu %d", 197547c09d6aSSong Liu metrics[m].name, cpu); 197647c09d6aSSong Liu return -1; 197747c09d6aSSong Liu } 197847c09d6aSSong Liu profile_perf_events[profile_perf_event_cnt++] = pmu_fd; 197947c09d6aSSong Liu } 198047c09d6aSSong Liu } 198147c09d6aSSong Liu return 0; 198247c09d6aSSong Liu } 198347c09d6aSSong Liu 198447c09d6aSSong Liu static void profile_print_and_cleanup(void) 198547c09d6aSSong Liu { 198647c09d6aSSong Liu profile_close_perf_events(profile_obj); 198747c09d6aSSong Liu profile_read_values(profile_obj); 198847c09d6aSSong Liu profile_print_readings(); 198947c09d6aSSong Liu profiler_bpf__destroy(profile_obj); 199047c09d6aSSong Liu 199147c09d6aSSong Liu close(profile_tgt_fd); 199247c09d6aSSong Liu free(profile_tgt_name); 199347c09d6aSSong Liu } 199447c09d6aSSong Liu 199547c09d6aSSong Liu static void int_exit(int signo) 199647c09d6aSSong Liu { 199747c09d6aSSong Liu profile_print_and_cleanup(); 199847c09d6aSSong Liu exit(0); 199947c09d6aSSong Liu } 200047c09d6aSSong Liu 200147c09d6aSSong Liu static int do_profile(int argc, char **argv) 200247c09d6aSSong Liu { 200347c09d6aSSong Liu int num_metric, num_cpu, err = -1; 200447c09d6aSSong Liu struct bpf_program *prog; 200547c09d6aSSong Liu unsigned long duration; 200647c09d6aSSong Liu char *endptr; 200747c09d6aSSong Liu 200847c09d6aSSong Liu /* we at least need two args for the prog and one metric */ 200947c09d6aSSong Liu if (!REQ_ARGS(3)) 201047c09d6aSSong Liu return -EINVAL; 201147c09d6aSSong Liu 201247c09d6aSSong Liu /* parse target fd */ 201347c09d6aSSong Liu profile_tgt_fd = prog_parse_fd(&argc, &argv); 201447c09d6aSSong Liu if (profile_tgt_fd < 0) { 201547c09d6aSSong Liu p_err("failed to parse fd"); 201647c09d6aSSong Liu return -1; 201747c09d6aSSong Liu } 201847c09d6aSSong Liu 201947c09d6aSSong Liu /* parse profiling optional duration */ 202047c09d6aSSong Liu if (argc > 2 && is_prefix(argv[0], "duration")) { 202147c09d6aSSong Liu NEXT_ARG(); 202247c09d6aSSong Liu duration = strtoul(*argv, &endptr, 0); 202347c09d6aSSong Liu if (*endptr) 202447c09d6aSSong Liu usage(); 202547c09d6aSSong Liu NEXT_ARG(); 202647c09d6aSSong Liu } else { 202747c09d6aSSong Liu duration = UINT_MAX; 202847c09d6aSSong Liu } 202947c09d6aSSong Liu 203047c09d6aSSong Liu num_metric = profile_parse_metrics(argc, argv); 203147c09d6aSSong Liu if (num_metric <= 0) 203247c09d6aSSong Liu goto out; 203347c09d6aSSong Liu 203447c09d6aSSong Liu num_cpu = libbpf_num_possible_cpus(); 203547c09d6aSSong Liu if (num_cpu <= 0) { 203647c09d6aSSong Liu p_err("failed to identify number of CPUs"); 203747c09d6aSSong Liu goto out; 203847c09d6aSSong Liu } 203947c09d6aSSong Liu 204047c09d6aSSong Liu profile_obj = profiler_bpf__open(); 204147c09d6aSSong Liu if (!profile_obj) { 204247c09d6aSSong Liu p_err("failed to open and/or load BPF object"); 204347c09d6aSSong Liu goto out; 204447c09d6aSSong Liu } 204547c09d6aSSong Liu 204647c09d6aSSong Liu profile_obj->rodata->num_cpu = num_cpu; 204747c09d6aSSong Liu profile_obj->rodata->num_metric = num_metric; 204847c09d6aSSong Liu 204947c09d6aSSong Liu /* adjust map sizes */ 205047c09d6aSSong Liu bpf_map__resize(profile_obj->maps.events, num_metric * num_cpu); 205147c09d6aSSong Liu bpf_map__resize(profile_obj->maps.fentry_readings, num_metric); 205247c09d6aSSong Liu bpf_map__resize(profile_obj->maps.accum_readings, num_metric); 205347c09d6aSSong Liu bpf_map__resize(profile_obj->maps.counts, 1); 205447c09d6aSSong Liu 205547c09d6aSSong Liu /* change target name */ 205647c09d6aSSong Liu profile_tgt_name = profile_target_name(profile_tgt_fd); 205747c09d6aSSong Liu if (!profile_tgt_name) 205847c09d6aSSong Liu goto out; 205947c09d6aSSong Liu 206047c09d6aSSong Liu bpf_object__for_each_program(prog, profile_obj->obj) { 206147c09d6aSSong Liu err = bpf_program__set_attach_target(prog, profile_tgt_fd, 206247c09d6aSSong Liu profile_tgt_name); 206347c09d6aSSong Liu if (err) { 206447c09d6aSSong Liu p_err("failed to set attach target\n"); 206547c09d6aSSong Liu goto out; 206647c09d6aSSong Liu } 206747c09d6aSSong Liu } 206847c09d6aSSong Liu 206947c09d6aSSong Liu set_max_rlimit(); 207047c09d6aSSong Liu err = profiler_bpf__load(profile_obj); 207147c09d6aSSong Liu if (err) { 207247c09d6aSSong Liu p_err("failed to load profile_obj"); 207347c09d6aSSong Liu goto out; 207447c09d6aSSong Liu } 207547c09d6aSSong Liu 207647c09d6aSSong Liu err = profile_open_perf_events(profile_obj); 207747c09d6aSSong Liu if (err) 207847c09d6aSSong Liu goto out; 207947c09d6aSSong Liu 208047c09d6aSSong Liu err = profiler_bpf__attach(profile_obj); 208147c09d6aSSong Liu if (err) { 208247c09d6aSSong Liu p_err("failed to attach profile_obj"); 208347c09d6aSSong Liu goto out; 208447c09d6aSSong Liu } 208547c09d6aSSong Liu signal(SIGINT, int_exit); 208647c09d6aSSong Liu 208747c09d6aSSong Liu sleep(duration); 208847c09d6aSSong Liu profile_print_and_cleanup(); 208947c09d6aSSong Liu return 0; 209047c09d6aSSong Liu 209147c09d6aSSong Liu out: 209247c09d6aSSong Liu profile_close_perf_events(profile_obj); 209347c09d6aSSong Liu if (profile_obj) 209447c09d6aSSong Liu profiler_bpf__destroy(profile_obj); 209547c09d6aSSong Liu close(profile_tgt_fd); 209647c09d6aSSong Liu free(profile_tgt_name); 209747c09d6aSSong Liu return err; 209847c09d6aSSong Liu } 209947c09d6aSSong Liu 210047c09d6aSSong Liu #endif /* BPFTOOL_WITHOUT_SKELETONS */ 210147c09d6aSSong Liu 210271bb428fSJakub Kicinski static int do_help(int argc, char **argv) 210371bb428fSJakub Kicinski { 2104004b45c0SQuentin Monnet if (json_output) { 2105004b45c0SQuentin Monnet jsonw_null(json_wtr); 2106004b45c0SQuentin Monnet return 0; 2107004b45c0SQuentin Monnet } 2108004b45c0SQuentin Monnet 210971bb428fSJakub Kicinski fprintf(stderr, 211090040351SQuentin Monnet "Usage: %1$s %2$s { show | list } [PROG]\n" 211190040351SQuentin Monnet " %1$s %2$s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n" 211290040351SQuentin Monnet " %1$s %2$s dump jited PROG [{ file FILE | opcodes | linum }]\n" 211390040351SQuentin Monnet " %1$s %2$s pin PROG FILE\n" 211490040351SQuentin Monnet " %1$s %2$s { load | loadall } OBJ PATH \\\n" 211577380998SStanislav Fomichev " [type TYPE] [dev NAME] \\\n" 21163767a94bSStanislav Fomichev " [map { idx IDX | name NAME } MAP]\\\n" 21173767a94bSStanislav Fomichev " [pinmaps MAP_DIR]\n" 211890040351SQuentin Monnet " %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n" 211990040351SQuentin Monnet " %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n" 212090040351SQuentin Monnet " %1$s %2$s run PROG \\\n" 2121ba95c745SQuentin Monnet " data_in FILE \\\n" 2122ba95c745SQuentin Monnet " [data_out FILE [data_size_out L]] \\\n" 2123ba95c745SQuentin Monnet " [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n" 2124ba95c745SQuentin Monnet " [repeat N]\n" 212590040351SQuentin Monnet " %1$s %2$s profile PROG [duration DURATION] METRICs\n" 212690040351SQuentin Monnet " %1$s %2$s tracelog\n" 212790040351SQuentin Monnet " %1$s %2$s help\n" 212871bb428fSJakub Kicinski "\n" 21293ff5a4dcSJakub Kicinski " " HELP_SPEC_MAP "\n" 213071bb428fSJakub Kicinski " " HELP_SPEC_PROGRAM "\n" 213149f2cba3SJakub Kicinski " TYPE := { socket | kprobe | kretprobe | classifier | action |\n" 213249f2cba3SJakub Kicinski " tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n" 213349f2cba3SJakub Kicinski " cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n" 213449f2cba3SJakub Kicinski " lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n" 2135f25377eeSAndrey Ignatov " sk_reuseport | flow_dissector | cgroup/sysctl |\n" 213649f2cba3SJakub Kicinski " cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n" 213749f2cba3SJakub Kicinski " cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n" 213805ee19c1SDaniel Borkmann " cgroup/getpeername4 | cgroup/getpeername6 |\n" 213905ee19c1SDaniel Borkmann " cgroup/getsockname4 | cgroup/getsockname6 | cgroup/sendmsg4 |\n" 214005ee19c1SDaniel Borkmann " cgroup/sendmsg6 | cgroup/recvmsg4 | cgroup/recvmsg6 |\n" 214105ee19c1SDaniel Borkmann " cgroup/getsockopt | cgroup/setsockopt |\n" 214293a3545dSJakub Sitnicki " struct_ops | fentry | fexit | freplace | sk_lookup }\n" 2143a5d9265eSAlban Crequy " ATTACH_TYPE := { msg_verdict | stream_verdict | stream_parser |\n" 2144092f0892SStanislav Fomichev " flow_dissector }\n" 2145450d060eSYonghong Song " METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n" 21460641c3c8SQuentin Monnet " " HELP_SPEC_OPTIONS "\n" 214771bb428fSJakub Kicinski "", 214890040351SQuentin Monnet bin_name, argv[-2]); 214971bb428fSJakub Kicinski 215071bb428fSJakub Kicinski return 0; 215171bb428fSJakub Kicinski } 215271bb428fSJakub Kicinski 215371bb428fSJakub Kicinski static const struct cmd cmds[] = { 215471bb428fSJakub Kicinski { "show", do_show }, 21556ebe6dbdSJakub Kicinski { "list", do_show }, 21569f606179SQuentin Monnet { "help", do_help }, 215771bb428fSJakub Kicinski { "dump", do_dump }, 215871bb428fSJakub Kicinski { "pin", do_pin }, 215949a086c2SRoman Gushchin { "load", do_load }, 216077380998SStanislav Fomichev { "loadall", do_loadall }, 2161b7d3826cSJohn Fastabend { "attach", do_attach }, 2162b7d3826cSJohn Fastabend { "detach", do_detach }, 216330da46b5SQuentin Monnet { "tracelog", do_tracelog }, 2164ba95c745SQuentin Monnet { "run", do_run }, 216547c09d6aSSong Liu { "profile", do_profile }, 216671bb428fSJakub Kicinski { 0 } 216771bb428fSJakub Kicinski }; 216871bb428fSJakub Kicinski 216971bb428fSJakub Kicinski int do_prog(int argc, char **argv) 217071bb428fSJakub Kicinski { 217171bb428fSJakub Kicinski return cmd_select(cmds, argc, argv, do_help); 217271bb428fSJakub Kicinski } 2173