xref: /openbmc/linux/tools/bpf/bpftool/prog.c (revision 357b3cc3c0467b2f7cd6c4a87f7a18bfd779ce5b)
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>
19d510296dSAlexei Starovoitov #include <dirent.h>
2071bb428fSJakub Kicinski 
21c8406848SJakub Kicinski #include <linux/err.h>
2247c09d6aSSong Liu #include <linux/perf_event.h>
23ba95c745SQuentin Monnet #include <linux/sizes.h>
24c8406848SJakub Kicinski 
25229c3b47SToke Høiland-Jørgensen #include <bpf/bpf.h>
26229c3b47SToke Høiland-Jørgensen #include <bpf/btf.h>
278f184732SQuentin Monnet #include <bpf/hashmap.h>
28229c3b47SToke Høiland-Jørgensen #include <bpf/libbpf.h>
29a19df713SMauricio Vásquez #include <bpf/libbpf_internal.h>
30d510296dSAlexei Starovoitov #include <bpf/skel_internal.h>
3171bb428fSJakub Kicinski 
32b6c1cedbSJiong Wang #include "cfg.h"
3371bb428fSJakub Kicinski #include "main.h"
3473bb5b4fSJiong Wang #include "xlated_dumper.h"
3571bb428fSJakub Kicinski 
36aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX "bpf_metadata_"
37aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX_LEN (sizeof(BPF_METADATA_PREFIX) - 1)
38aff52e68SYiFei Zhu 
399023497dSTobias Klauser const char * const prog_type_name[] = {
409023497dSTobias Klauser 	[BPF_PROG_TYPE_UNSPEC]			= "unspec",
419023497dSTobias Klauser 	[BPF_PROG_TYPE_SOCKET_FILTER]		= "socket_filter",
429023497dSTobias Klauser 	[BPF_PROG_TYPE_KPROBE]			= "kprobe",
439023497dSTobias Klauser 	[BPF_PROG_TYPE_SCHED_CLS]		= "sched_cls",
449023497dSTobias Klauser 	[BPF_PROG_TYPE_SCHED_ACT]		= "sched_act",
459023497dSTobias Klauser 	[BPF_PROG_TYPE_TRACEPOINT]		= "tracepoint",
469023497dSTobias Klauser 	[BPF_PROG_TYPE_XDP]			= "xdp",
479023497dSTobias Klauser 	[BPF_PROG_TYPE_PERF_EVENT]		= "perf_event",
489023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SKB]		= "cgroup_skb",
499023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SOCK]		= "cgroup_sock",
509023497dSTobias Klauser 	[BPF_PROG_TYPE_LWT_IN]			= "lwt_in",
519023497dSTobias Klauser 	[BPF_PROG_TYPE_LWT_OUT]			= "lwt_out",
529023497dSTobias Klauser 	[BPF_PROG_TYPE_LWT_XMIT]		= "lwt_xmit",
539023497dSTobias Klauser 	[BPF_PROG_TYPE_SOCK_OPS]		= "sock_ops",
549023497dSTobias Klauser 	[BPF_PROG_TYPE_SK_SKB]			= "sk_skb",
559023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_DEVICE]		= "cgroup_device",
569023497dSTobias Klauser 	[BPF_PROG_TYPE_SK_MSG]			= "sk_msg",
579023497dSTobias Klauser 	[BPF_PROG_TYPE_RAW_TRACEPOINT]		= "raw_tracepoint",
589023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SOCK_ADDR]	= "cgroup_sock_addr",
599023497dSTobias Klauser 	[BPF_PROG_TYPE_LWT_SEG6LOCAL]		= "lwt_seg6local",
609023497dSTobias Klauser 	[BPF_PROG_TYPE_LIRC_MODE2]		= "lirc_mode2",
619023497dSTobias Klauser 	[BPF_PROG_TYPE_SK_REUSEPORT]		= "sk_reuseport",
629023497dSTobias Klauser 	[BPF_PROG_TYPE_FLOW_DISSECTOR]		= "flow_dissector",
639023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SYSCTL]		= "cgroup_sysctl",
649023497dSTobias Klauser 	[BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE]	= "raw_tracepoint_writable",
659023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SOCKOPT]		= "cgroup_sockopt",
669023497dSTobias Klauser 	[BPF_PROG_TYPE_TRACING]			= "tracing",
679023497dSTobias Klauser 	[BPF_PROG_TYPE_STRUCT_OPS]		= "struct_ops",
689023497dSTobias Klauser 	[BPF_PROG_TYPE_EXT]			= "ext",
699a97c9d2SQuentin Monnet 	[BPF_PROG_TYPE_LSM]			= "lsm",
7093a3545dSJakub Sitnicki 	[BPF_PROG_TYPE_SK_LOOKUP]		= "sk_lookup",
719023497dSTobias Klauser };
729023497dSTobias Klauser 
739023497dSTobias Klauser const size_t prog_type_name_size = ARRAY_SIZE(prog_type_name);
749023497dSTobias Klauser 
75ec202509SPaul Chaignon enum dump_mode {
76ec202509SPaul Chaignon 	DUMP_JITED,
77ec202509SPaul Chaignon 	DUMP_XLATED,
78ec202509SPaul Chaignon };
79ec202509SPaul Chaignon 
80b7d3826cSJohn Fastabend static const char * const attach_type_strings[] = {
81b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_PARSER] = "stream_parser",
82b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict",
83a7ba4558SCong Wang 	[BPF_SK_SKB_VERDICT] = "skb_verdict",
84b7d3826cSJohn Fastabend 	[BPF_SK_MSG_VERDICT] = "msg_verdict",
85092f0892SStanislav Fomichev 	[BPF_FLOW_DISSECTOR] = "flow_dissector",
86b7d3826cSJohn Fastabend 	[__MAX_BPF_ATTACH_TYPE] = NULL,
87b7d3826cSJohn Fastabend };
88b7d3826cSJohn Fastabend 
898f184732SQuentin Monnet static struct hashmap *prog_table;
9046241271SQuentin Monnet 
91c101189bSQuentin Monnet static enum bpf_attach_type parse_attach_type(const char *str)
92b7d3826cSJohn Fastabend {
93b7d3826cSJohn Fastabend 	enum bpf_attach_type type;
94b7d3826cSJohn Fastabend 
95b7d3826cSJohn Fastabend 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
96b7d3826cSJohn Fastabend 		if (attach_type_strings[type] &&
97b7d3826cSJohn Fastabend 		    is_prefix(str, attach_type_strings[type]))
98b7d3826cSJohn Fastabend 			return type;
99b7d3826cSJohn Fastabend 	}
100b7d3826cSJohn Fastabend 
101b7d3826cSJohn Fastabend 	return __MAX_BPF_ATTACH_TYPE;
102b7d3826cSJohn Fastabend }
103b7d3826cSJohn Fastabend 
104c59765cfSDave Marchevsky static int prep_prog_info(struct bpf_prog_info *const info, enum dump_mode mode,
105c59765cfSDave Marchevsky 			  void **info_data, size_t *const info_data_sz)
106c59765cfSDave Marchevsky {
107c59765cfSDave Marchevsky 	struct bpf_prog_info holder = {};
108c59765cfSDave Marchevsky 	size_t needed = 0;
109c59765cfSDave Marchevsky 	void *ptr;
110c59765cfSDave Marchevsky 
111c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
112c59765cfSDave Marchevsky 		holder.jited_prog_len = info->jited_prog_len;
113c59765cfSDave Marchevsky 		needed += info->jited_prog_len;
114c59765cfSDave Marchevsky 	} else {
115c59765cfSDave Marchevsky 		holder.xlated_prog_len = info->xlated_prog_len;
116c59765cfSDave Marchevsky 		needed += info->xlated_prog_len;
117c59765cfSDave Marchevsky 	}
118c59765cfSDave Marchevsky 
119c59765cfSDave Marchevsky 	holder.nr_jited_ksyms = info->nr_jited_ksyms;
120c59765cfSDave Marchevsky 	needed += info->nr_jited_ksyms * sizeof(__u64);
121c59765cfSDave Marchevsky 
122c59765cfSDave Marchevsky 	holder.nr_jited_func_lens = info->nr_jited_func_lens;
123c59765cfSDave Marchevsky 	needed += info->nr_jited_func_lens * sizeof(__u32);
124c59765cfSDave Marchevsky 
125c59765cfSDave Marchevsky 	holder.nr_func_info = info->nr_func_info;
126c59765cfSDave Marchevsky 	holder.func_info_rec_size = info->func_info_rec_size;
127c59765cfSDave Marchevsky 	needed += info->nr_func_info * info->func_info_rec_size;
128c59765cfSDave Marchevsky 
129c59765cfSDave Marchevsky 	holder.nr_line_info = info->nr_line_info;
130c59765cfSDave Marchevsky 	holder.line_info_rec_size = info->line_info_rec_size;
131c59765cfSDave Marchevsky 	needed += info->nr_line_info * info->line_info_rec_size;
132c59765cfSDave Marchevsky 
133c59765cfSDave Marchevsky 	holder.nr_jited_line_info = info->nr_jited_line_info;
134c59765cfSDave Marchevsky 	holder.jited_line_info_rec_size = info->jited_line_info_rec_size;
135c59765cfSDave Marchevsky 	needed += info->nr_jited_line_info * info->jited_line_info_rec_size;
136c59765cfSDave Marchevsky 
137c59765cfSDave Marchevsky 	if (needed > *info_data_sz) {
138c59765cfSDave Marchevsky 		ptr = realloc(*info_data, needed);
139c59765cfSDave Marchevsky 		if (!ptr)
140c59765cfSDave Marchevsky 			return -1;
141c59765cfSDave Marchevsky 
142c59765cfSDave Marchevsky 		*info_data = ptr;
143c59765cfSDave Marchevsky 		*info_data_sz = needed;
144c59765cfSDave Marchevsky 	}
145c59765cfSDave Marchevsky 	ptr = *info_data;
146c59765cfSDave Marchevsky 
147c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
148c59765cfSDave Marchevsky 		holder.jited_prog_insns = ptr_to_u64(ptr);
149c59765cfSDave Marchevsky 		ptr += holder.jited_prog_len;
150c59765cfSDave Marchevsky 	} else {
151c59765cfSDave Marchevsky 		holder.xlated_prog_insns = ptr_to_u64(ptr);
152c59765cfSDave Marchevsky 		ptr += holder.xlated_prog_len;
153c59765cfSDave Marchevsky 	}
154c59765cfSDave Marchevsky 
155c59765cfSDave Marchevsky 	holder.jited_ksyms = ptr_to_u64(ptr);
156c59765cfSDave Marchevsky 	ptr += holder.nr_jited_ksyms * sizeof(__u64);
157c59765cfSDave Marchevsky 
158c59765cfSDave Marchevsky 	holder.jited_func_lens = ptr_to_u64(ptr);
159c59765cfSDave Marchevsky 	ptr += holder.nr_jited_func_lens * sizeof(__u32);
160c59765cfSDave Marchevsky 
161c59765cfSDave Marchevsky 	holder.func_info = ptr_to_u64(ptr);
162c59765cfSDave Marchevsky 	ptr += holder.nr_func_info * holder.func_info_rec_size;
163c59765cfSDave Marchevsky 
164c59765cfSDave Marchevsky 	holder.line_info = ptr_to_u64(ptr);
165c59765cfSDave Marchevsky 	ptr += holder.nr_line_info * holder.line_info_rec_size;
166c59765cfSDave Marchevsky 
167c59765cfSDave Marchevsky 	holder.jited_line_info = ptr_to_u64(ptr);
168c59765cfSDave Marchevsky 	ptr += holder.nr_jited_line_info * holder.jited_line_info_rec_size;
169c59765cfSDave Marchevsky 
170c59765cfSDave Marchevsky 	*info = holder;
171c59765cfSDave Marchevsky 	return 0;
172c59765cfSDave Marchevsky }
173c59765cfSDave Marchevsky 
17471bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
17571bb428fSJakub Kicinski {
17671bb428fSJakub Kicinski 	struct timespec real_time_ts, boot_time_ts;
17771bb428fSJakub Kicinski 	time_t wallclock_secs;
17871bb428fSJakub Kicinski 	struct tm load_tm;
17971bb428fSJakub Kicinski 
18071bb428fSJakub Kicinski 	buf[--size] = '\0';
18171bb428fSJakub Kicinski 
18271bb428fSJakub Kicinski 	if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
18371bb428fSJakub Kicinski 	    clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
18471bb428fSJakub Kicinski 		perror("Can't read clocks");
18571bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
18671bb428fSJakub Kicinski 		return;
18771bb428fSJakub Kicinski 	}
18871bb428fSJakub Kicinski 
18971bb428fSJakub Kicinski 	wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
19007480cbcSJakub Kicinski 		(real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) /
19107480cbcSJakub Kicinski 		1000000000;
19207480cbcSJakub Kicinski 
19371bb428fSJakub Kicinski 
19471bb428fSJakub Kicinski 	if (!localtime_r(&wallclock_secs, &load_tm)) {
19571bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
19671bb428fSJakub Kicinski 		return;
19771bb428fSJakub Kicinski 	}
19871bb428fSJakub Kicinski 
199a3fe1f6fSQuentin Monnet 	if (json_output)
200a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%s", &load_tm);
201a3fe1f6fSQuentin Monnet 	else
202a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%FT%T%z", &load_tm);
20371bb428fSJakub Kicinski }
20471bb428fSJakub Kicinski 
2056e7e034eSQuentin Monnet static void show_prog_maps(int fd, __u32 num_maps)
20671bb428fSJakub Kicinski {
20771bb428fSJakub Kicinski 	struct bpf_prog_info info = {};
20871bb428fSJakub Kicinski 	__u32 len = sizeof(info);
20971bb428fSJakub Kicinski 	__u32 map_ids[num_maps];
21071bb428fSJakub Kicinski 	unsigned int i;
21171bb428fSJakub Kicinski 	int err;
21271bb428fSJakub Kicinski 
21371bb428fSJakub Kicinski 	info.nr_map_ids = num_maps;
21471bb428fSJakub Kicinski 	info.map_ids = ptr_to_u64(map_ids);
21571bb428fSJakub Kicinski 
21671bb428fSJakub Kicinski 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
21771bb428fSJakub Kicinski 	if (err || !info.nr_map_ids)
21871bb428fSJakub Kicinski 		return;
21971bb428fSJakub Kicinski 
220743cc665SQuentin Monnet 	if (json_output) {
221743cc665SQuentin Monnet 		jsonw_name(json_wtr, "map_ids");
222743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
223743cc665SQuentin Monnet 		for (i = 0; i < info.nr_map_ids; i++)
224743cc665SQuentin Monnet 			jsonw_uint(json_wtr, map_ids[i]);
225743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
226743cc665SQuentin Monnet 	} else {
22771bb428fSJakub Kicinski 		printf("  map_ids ");
22871bb428fSJakub Kicinski 		for (i = 0; i < info.nr_map_ids; i++)
22971bb428fSJakub Kicinski 			printf("%u%s", map_ids[i],
23071bb428fSJakub Kicinski 			       i == info.nr_map_ids - 1 ? "" : ",");
23171bb428fSJakub Kicinski 	}
23271bb428fSJakub Kicinski }
23371bb428fSJakub Kicinski 
234aff52e68SYiFei Zhu static void *find_metadata(int prog_fd, struct bpf_map_info *map_info)
235aff52e68SYiFei Zhu {
236aff52e68SYiFei Zhu 	struct bpf_prog_info prog_info;
237aff52e68SYiFei Zhu 	__u32 prog_info_len;
238aff52e68SYiFei Zhu 	__u32 map_info_len;
239aff52e68SYiFei Zhu 	void *value = NULL;
240aff52e68SYiFei Zhu 	__u32 *map_ids;
241aff52e68SYiFei Zhu 	int nr_maps;
242aff52e68SYiFei Zhu 	int key = 0;
243aff52e68SYiFei Zhu 	int map_fd;
244aff52e68SYiFei Zhu 	int ret;
245aff52e68SYiFei Zhu 	__u32 i;
246aff52e68SYiFei Zhu 
247aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
248aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
249aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
250aff52e68SYiFei Zhu 	if (ret)
251aff52e68SYiFei Zhu 		return NULL;
252aff52e68SYiFei Zhu 
253aff52e68SYiFei Zhu 	if (!prog_info.nr_map_ids)
254aff52e68SYiFei Zhu 		return NULL;
255aff52e68SYiFei Zhu 
256aff52e68SYiFei Zhu 	map_ids = calloc(prog_info.nr_map_ids, sizeof(__u32));
257aff52e68SYiFei Zhu 	if (!map_ids)
258aff52e68SYiFei Zhu 		return NULL;
259aff52e68SYiFei Zhu 
260aff52e68SYiFei Zhu 	nr_maps = prog_info.nr_map_ids;
261aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
262aff52e68SYiFei Zhu 	prog_info.nr_map_ids = nr_maps;
263aff52e68SYiFei Zhu 	prog_info.map_ids = ptr_to_u64(map_ids);
264aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
265aff52e68SYiFei Zhu 
266aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
267aff52e68SYiFei Zhu 	if (ret)
268aff52e68SYiFei Zhu 		goto free_map_ids;
269aff52e68SYiFei Zhu 
270aff52e68SYiFei Zhu 	for (i = 0; i < prog_info.nr_map_ids; i++) {
271aff52e68SYiFei Zhu 		map_fd = bpf_map_get_fd_by_id(map_ids[i]);
272aff52e68SYiFei Zhu 		if (map_fd < 0)
273aff52e68SYiFei Zhu 			goto free_map_ids;
274aff52e68SYiFei Zhu 
275aff52e68SYiFei Zhu 		memset(map_info, 0, sizeof(*map_info));
276aff52e68SYiFei Zhu 		map_info_len = sizeof(*map_info);
277aff52e68SYiFei Zhu 		ret = bpf_obj_get_info_by_fd(map_fd, map_info, &map_info_len);
278aff52e68SYiFei Zhu 		if (ret < 0) {
279aff52e68SYiFei Zhu 			close(map_fd);
280aff52e68SYiFei Zhu 			goto free_map_ids;
281aff52e68SYiFei Zhu 		}
282aff52e68SYiFei Zhu 
283aff52e68SYiFei Zhu 		if (map_info->type != BPF_MAP_TYPE_ARRAY ||
284aff52e68SYiFei Zhu 		    map_info->key_size != sizeof(int) ||
285aff52e68SYiFei Zhu 		    map_info->max_entries != 1 ||
286aff52e68SYiFei Zhu 		    !map_info->btf_value_type_id ||
287aff52e68SYiFei Zhu 		    !strstr(map_info->name, ".rodata")) {
288aff52e68SYiFei Zhu 			close(map_fd);
289aff52e68SYiFei Zhu 			continue;
290aff52e68SYiFei Zhu 		}
291aff52e68SYiFei Zhu 
292aff52e68SYiFei Zhu 		value = malloc(map_info->value_size);
293aff52e68SYiFei Zhu 		if (!value) {
294aff52e68SYiFei Zhu 			close(map_fd);
295aff52e68SYiFei Zhu 			goto free_map_ids;
296aff52e68SYiFei Zhu 		}
297aff52e68SYiFei Zhu 
298aff52e68SYiFei Zhu 		if (bpf_map_lookup_elem(map_fd, &key, value)) {
299aff52e68SYiFei Zhu 			close(map_fd);
300aff52e68SYiFei Zhu 			free(value);
301aff52e68SYiFei Zhu 			value = NULL;
302aff52e68SYiFei Zhu 			goto free_map_ids;
303aff52e68SYiFei Zhu 		}
304aff52e68SYiFei Zhu 
305aff52e68SYiFei Zhu 		close(map_fd);
306aff52e68SYiFei Zhu 		break;
307aff52e68SYiFei Zhu 	}
308aff52e68SYiFei Zhu 
309aff52e68SYiFei Zhu free_map_ids:
310aff52e68SYiFei Zhu 	free(map_ids);
311aff52e68SYiFei Zhu 	return value;
312aff52e68SYiFei Zhu }
313aff52e68SYiFei Zhu 
314aff52e68SYiFei Zhu static bool has_metadata_prefix(const char *s)
315aff52e68SYiFei Zhu {
316aff52e68SYiFei Zhu 	return strncmp(s, BPF_METADATA_PREFIX, BPF_METADATA_PREFIX_LEN) == 0;
317aff52e68SYiFei Zhu }
318aff52e68SYiFei Zhu 
319aff52e68SYiFei Zhu static void show_prog_metadata(int fd, __u32 num_maps)
320aff52e68SYiFei Zhu {
321aff52e68SYiFei Zhu 	const struct btf_type *t_datasec, *t_var;
322aff52e68SYiFei Zhu 	struct bpf_map_info map_info;
323aff52e68SYiFei Zhu 	struct btf_var_secinfo *vsi;
324aff52e68SYiFei Zhu 	bool printed_header = false;
325aff52e68SYiFei Zhu 	unsigned int i, vlen;
326aff52e68SYiFei Zhu 	void *value = NULL;
327aff52e68SYiFei Zhu 	const char *name;
32886f4b7f2SQuentin Monnet 	struct btf *btf;
329aff52e68SYiFei Zhu 	int err;
330aff52e68SYiFei Zhu 
331aff52e68SYiFei Zhu 	if (!num_maps)
332aff52e68SYiFei Zhu 		return;
333aff52e68SYiFei Zhu 
334aff52e68SYiFei Zhu 	memset(&map_info, 0, sizeof(map_info));
335aff52e68SYiFei Zhu 	value = find_metadata(fd, &map_info);
336aff52e68SYiFei Zhu 	if (!value)
337aff52e68SYiFei Zhu 		return;
338aff52e68SYiFei Zhu 
33986f4b7f2SQuentin Monnet 	btf = btf__load_from_kernel_by_id(map_info.btf_id);
34086f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf))
341aff52e68SYiFei Zhu 		goto out_free;
342aff52e68SYiFei Zhu 
343aff52e68SYiFei Zhu 	t_datasec = btf__type_by_id(btf, map_info.btf_value_type_id);
344aff52e68SYiFei Zhu 	if (!btf_is_datasec(t_datasec))
345aff52e68SYiFei Zhu 		goto out_free;
346aff52e68SYiFei Zhu 
347aff52e68SYiFei Zhu 	vlen = btf_vlen(t_datasec);
348aff52e68SYiFei Zhu 	vsi = btf_var_secinfos(t_datasec);
349aff52e68SYiFei Zhu 
350aff52e68SYiFei Zhu 	/* We don't proceed to check the kinds of the elements of the DATASEC.
351aff52e68SYiFei Zhu 	 * The verifier enforces them to be BTF_KIND_VAR.
352aff52e68SYiFei Zhu 	 */
353aff52e68SYiFei Zhu 
354aff52e68SYiFei Zhu 	if (json_output) {
355aff52e68SYiFei Zhu 		struct btf_dumper d = {
356aff52e68SYiFei Zhu 			.btf = btf,
357aff52e68SYiFei Zhu 			.jw = json_wtr,
358aff52e68SYiFei Zhu 			.is_plain_text = false,
359aff52e68SYiFei Zhu 		};
360aff52e68SYiFei Zhu 
361aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
362aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
363aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
364aff52e68SYiFei Zhu 
365aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
366aff52e68SYiFei Zhu 				continue;
367aff52e68SYiFei Zhu 
368aff52e68SYiFei Zhu 			if (!printed_header) {
369aff52e68SYiFei Zhu 				jsonw_name(json_wtr, "metadata");
370aff52e68SYiFei Zhu 				jsonw_start_object(json_wtr);
371aff52e68SYiFei Zhu 				printed_header = true;
372aff52e68SYiFei Zhu 			}
373aff52e68SYiFei Zhu 
374aff52e68SYiFei Zhu 			jsonw_name(json_wtr, name + BPF_METADATA_PREFIX_LEN);
375aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
376aff52e68SYiFei Zhu 			if (err) {
377aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
378aff52e68SYiFei Zhu 				break;
379aff52e68SYiFei Zhu 			}
380aff52e68SYiFei Zhu 		}
381aff52e68SYiFei Zhu 		if (printed_header)
382aff52e68SYiFei Zhu 			jsonw_end_object(json_wtr);
383aff52e68SYiFei Zhu 	} else {
384e89ef634SQuentin Monnet 		json_writer_t *btf_wtr;
385aff52e68SYiFei Zhu 		struct btf_dumper d = {
386aff52e68SYiFei Zhu 			.btf = btf,
387aff52e68SYiFei Zhu 			.is_plain_text = true,
388aff52e68SYiFei Zhu 		};
389aff52e68SYiFei Zhu 
390aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
391aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
392aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
393aff52e68SYiFei Zhu 
394aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
395aff52e68SYiFei Zhu 				continue;
396aff52e68SYiFei Zhu 
397aff52e68SYiFei Zhu 			if (!printed_header) {
398aff52e68SYiFei Zhu 				printf("\tmetadata:");
399e89ef634SQuentin Monnet 
400e89ef634SQuentin Monnet 				btf_wtr = jsonw_new(stdout);
401e89ef634SQuentin Monnet 				if (!btf_wtr) {
402e89ef634SQuentin Monnet 					p_err("jsonw alloc failed");
403e89ef634SQuentin Monnet 					goto out_free;
404e89ef634SQuentin Monnet 				}
405e89ef634SQuentin Monnet 				d.jw = btf_wtr,
406e89ef634SQuentin Monnet 
407aff52e68SYiFei Zhu 				printed_header = true;
408aff52e68SYiFei Zhu 			}
409aff52e68SYiFei Zhu 
410aff52e68SYiFei Zhu 			printf("\n\t\t%s = ", name + BPF_METADATA_PREFIX_LEN);
411aff52e68SYiFei Zhu 
412aff52e68SYiFei Zhu 			jsonw_reset(btf_wtr);
413aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
414aff52e68SYiFei Zhu 			if (err) {
415aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
416aff52e68SYiFei Zhu 				break;
417aff52e68SYiFei Zhu 			}
418aff52e68SYiFei Zhu 		}
419aff52e68SYiFei Zhu 		if (printed_header)
420aff52e68SYiFei Zhu 			jsonw_destroy(&btf_wtr);
421aff52e68SYiFei Zhu 	}
422aff52e68SYiFei Zhu 
423aff52e68SYiFei Zhu out_free:
424aff52e68SYiFei Zhu 	btf__free(btf);
425aff52e68SYiFei Zhu 	free(value);
426aff52e68SYiFei Zhu }
427aff52e68SYiFei Zhu 
428b662000aSRaman Shukhau static void print_prog_header_json(struct bpf_prog_info *info, int fd)
429743cc665SQuentin Monnet {
430b662000aSRaman Shukhau 	char prog_name[MAX_PROG_FULL_NAME];
431b662000aSRaman Shukhau 
432743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "id", info->id);
433743cc665SQuentin Monnet 	if (info->type < ARRAY_SIZE(prog_type_name))
434743cc665SQuentin Monnet 		jsonw_string_field(json_wtr, "type",
435743cc665SQuentin Monnet 				   prog_type_name[info->type]);
43671bb428fSJakub Kicinski 	else
437743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "type", info->type);
43871bb428fSJakub Kicinski 
439b662000aSRaman Shukhau 	if (*info->name) {
440b662000aSRaman Shukhau 		get_prog_full_name(info, fd, prog_name, sizeof(prog_name));
441b662000aSRaman Shukhau 		jsonw_string_field(json_wtr, "name", prog_name);
442b662000aSRaman Shukhau 	}
44371bb428fSJakub Kicinski 
444743cc665SQuentin Monnet 	jsonw_name(json_wtr, "tag");
445743cc665SQuentin Monnet 	jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
446743cc665SQuentin Monnet 		     info->tag[0], info->tag[1], info->tag[2], info->tag[3],
447743cc665SQuentin Monnet 		     info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
44871bb428fSJakub Kicinski 
4499b984a20SJiri Olsa 	jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible);
45088ad472bSAlexei Starovoitov 	if (info->run_time_ns) {
45188ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_time_ns", info->run_time_ns);
45288ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_cnt", info->run_cnt);
45388ad472bSAlexei Starovoitov 	}
4549ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
4559ed9e9baSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "recursion_misses", info->recursion_misses);
456ec202509SPaul Chaignon }
4579b984a20SJiri Olsa 
458ec202509SPaul Chaignon static void print_prog_json(struct bpf_prog_info *info, int fd)
459ec202509SPaul Chaignon {
460ec202509SPaul Chaignon 	char *memlock;
461ec202509SPaul Chaignon 
462ec202509SPaul Chaignon 	jsonw_start_object(json_wtr);
463b662000aSRaman Shukhau 	print_prog_header_json(info, fd);
46452262210SJakub Kicinski 	print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
46552262210SJakub Kicinski 
466743cc665SQuentin Monnet 	if (info->load_time) {
46771bb428fSJakub Kicinski 		char buf[32];
46871bb428fSJakub Kicinski 
469743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
47071bb428fSJakub Kicinski 
47171bb428fSJakub Kicinski 		/* Piggy back on load_time, since 0 uid is a valid one */
472a3fe1f6fSQuentin Monnet 		jsonw_name(json_wtr, "loaded_at");
473a3fe1f6fSQuentin Monnet 		jsonw_printf(json_wtr, "%s", buf);
474743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
47571bb428fSJakub Kicinski 	}
47671bb428fSJakub Kicinski 
477743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
47871bb428fSJakub Kicinski 
479743cc665SQuentin Monnet 	if (info->jited_prog_len) {
480743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", true);
481743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
482743cc665SQuentin Monnet 	} else {
483743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", false);
484743cc665SQuentin Monnet 	}
485743cc665SQuentin Monnet 
486743cc665SQuentin Monnet 	memlock = get_fdinfo(fd, "memlock");
487743cc665SQuentin Monnet 	if (memlock)
488*357b3cc3SChris J Arges 		jsonw_int_field(json_wtr, "bytes_memlock", atoll(memlock));
489743cc665SQuentin Monnet 	free(memlock);
490743cc665SQuentin Monnet 
491743cc665SQuentin Monnet 	if (info->nr_map_ids)
492743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
493743cc665SQuentin Monnet 
494569b0c77SPrashant Bhole 	if (info->btf_id)
495569b0c77SPrashant Bhole 		jsonw_int_field(json_wtr, "btf_id", info->btf_id);
496569b0c77SPrashant Bhole 
4978f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
4988f184732SQuentin Monnet 		struct hashmap_entry *entry;
4994990f1f4SPrashant Bhole 
5004990f1f4SPrashant Bhole 		jsonw_name(json_wtr, "pinned");
5014990f1f4SPrashant Bhole 		jsonw_start_array(json_wtr);
5028f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
5038f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
5048f184732SQuentin Monnet 			jsonw_string(json_wtr, entry->value);
5054990f1f4SPrashant Bhole 		jsonw_end_array(json_wtr);
5064990f1f4SPrashant Bhole 	}
5074990f1f4SPrashant Bhole 
508d6699f8eSQuentin Monnet 	emit_obj_refs_json(refs_table, info->id, json_wtr);
509d53dee3fSAndrii Nakryiko 
510aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
511aff52e68SYiFei Zhu 
512743cc665SQuentin Monnet 	jsonw_end_object(json_wtr);
513743cc665SQuentin Monnet }
514743cc665SQuentin Monnet 
515b662000aSRaman Shukhau static void print_prog_header_plain(struct bpf_prog_info *info, int fd)
516743cc665SQuentin Monnet {
517b662000aSRaman Shukhau 	char prog_name[MAX_PROG_FULL_NAME];
518b662000aSRaman Shukhau 
519743cc665SQuentin Monnet 	printf("%u: ", info->id);
520743cc665SQuentin Monnet 	if (info->type < ARRAY_SIZE(prog_type_name))
521743cc665SQuentin Monnet 		printf("%s  ", prog_type_name[info->type]);
522743cc665SQuentin Monnet 	else
523743cc665SQuentin Monnet 		printf("type %u  ", info->type);
524743cc665SQuentin Monnet 
525b662000aSRaman Shukhau 	if (*info->name) {
526b662000aSRaman Shukhau 		get_prog_full_name(info, fd, prog_name, sizeof(prog_name));
527b662000aSRaman Shukhau 		printf("name %s  ", prog_name);
528b662000aSRaman Shukhau 	}
529743cc665SQuentin Monnet 
530743cc665SQuentin Monnet 	printf("tag ");
531743cc665SQuentin Monnet 	fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
53252262210SJakub Kicinski 	print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
5339b984a20SJiri Olsa 	printf("%s", info->gpl_compatible ? "  gpl" : "");
53488ad472bSAlexei Starovoitov 	if (info->run_time_ns)
53588ad472bSAlexei Starovoitov 		printf(" run_time_ns %lld run_cnt %lld",
53688ad472bSAlexei Starovoitov 		       info->run_time_ns, info->run_cnt);
5379ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
5389ed9e9baSAlexei Starovoitov 		printf(" recursion_misses %lld", info->recursion_misses);
539743cc665SQuentin Monnet 	printf("\n");
540ec202509SPaul Chaignon }
541ec202509SPaul Chaignon 
542ec202509SPaul Chaignon static void print_prog_plain(struct bpf_prog_info *info, int fd)
543ec202509SPaul Chaignon {
544ec202509SPaul Chaignon 	char *memlock;
545ec202509SPaul Chaignon 
546b662000aSRaman Shukhau 	print_prog_header_plain(info, fd);
547743cc665SQuentin Monnet 
548743cc665SQuentin Monnet 	if (info->load_time) {
549743cc665SQuentin Monnet 		char buf[32];
550743cc665SQuentin Monnet 
551743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
552743cc665SQuentin Monnet 
553743cc665SQuentin Monnet 		/* Piggy back on load_time, since 0 uid is a valid one */
554743cc665SQuentin Monnet 		printf("\tloaded_at %s  uid %u\n", buf, info->created_by_uid);
555743cc665SQuentin Monnet 	}
556743cc665SQuentin Monnet 
557743cc665SQuentin Monnet 	printf("\txlated %uB", info->xlated_prog_len);
558743cc665SQuentin Monnet 
559743cc665SQuentin Monnet 	if (info->jited_prog_len)
560743cc665SQuentin Monnet 		printf("  jited %uB", info->jited_prog_len);
56171bb428fSJakub Kicinski 	else
56271bb428fSJakub Kicinski 		printf("  not jited");
56371bb428fSJakub Kicinski 
56471bb428fSJakub Kicinski 	memlock = get_fdinfo(fd, "memlock");
56571bb428fSJakub Kicinski 	if (memlock)
56671bb428fSJakub Kicinski 		printf("  memlock %sB", memlock);
56771bb428fSJakub Kicinski 	free(memlock);
56871bb428fSJakub Kicinski 
569743cc665SQuentin Monnet 	if (info->nr_map_ids)
570743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
57171bb428fSJakub Kicinski 
5728f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
5738f184732SQuentin Monnet 		struct hashmap_entry *entry;
5744990f1f4SPrashant Bhole 
5758f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
5768f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
5778f184732SQuentin Monnet 			printf("\n\tpinned %s", (char *)entry->value);
5784990f1f4SPrashant Bhole 	}
5794990f1f4SPrashant Bhole 
580569b0c77SPrashant Bhole 	if (info->btf_id)
581031ebc1aSQuentin Monnet 		printf("\n\tbtf_id %d", info->btf_id);
582569b0c77SPrashant Bhole 
583d6699f8eSQuentin Monnet 	emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
584d53dee3fSAndrii Nakryiko 
58571bb428fSJakub Kicinski 	printf("\n");
586aff52e68SYiFei Zhu 
587aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
588743cc665SQuentin Monnet }
589743cc665SQuentin Monnet 
590743cc665SQuentin Monnet static int show_prog(int fd)
591743cc665SQuentin Monnet {
592743cc665SQuentin Monnet 	struct bpf_prog_info info = {};
593743cc665SQuentin Monnet 	__u32 len = sizeof(info);
594743cc665SQuentin Monnet 	int err;
595743cc665SQuentin Monnet 
596743cc665SQuentin Monnet 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
597743cc665SQuentin Monnet 	if (err) {
5989a5ab8bfSQuentin Monnet 		p_err("can't get prog info: %s", strerror(errno));
599743cc665SQuentin Monnet 		return -1;
600743cc665SQuentin Monnet 	}
601743cc665SQuentin Monnet 
602743cc665SQuentin Monnet 	if (json_output)
603743cc665SQuentin Monnet 		print_prog_json(&info, fd);
604743cc665SQuentin Monnet 	else
605743cc665SQuentin Monnet 		print_prog_plain(&info, fd);
60671bb428fSJakub Kicinski 
60771bb428fSJakub Kicinski 	return 0;
60871bb428fSJakub Kicinski }
60971bb428fSJakub Kicinski 
610ec202509SPaul Chaignon static int do_show_subset(int argc, char **argv)
611ec202509SPaul Chaignon {
612ec202509SPaul Chaignon 	int *fds = NULL;
613ec202509SPaul Chaignon 	int nb_fds, i;
614ec202509SPaul Chaignon 	int err = -1;
615ec202509SPaul Chaignon 
616ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
617ec202509SPaul Chaignon 	if (!fds) {
618ec202509SPaul Chaignon 		p_err("mem alloc failed");
619ec202509SPaul Chaignon 		return -1;
620ec202509SPaul Chaignon 	}
621ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
622ec202509SPaul Chaignon 	if (nb_fds < 1)
623ec202509SPaul Chaignon 		goto exit_free;
624ec202509SPaul Chaignon 
625ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
626ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
627ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
628ec202509SPaul Chaignon 		err = show_prog(fds[i]);
629ec202509SPaul Chaignon 		if (err) {
630ec202509SPaul Chaignon 			for (; i < nb_fds; i++)
631ec202509SPaul Chaignon 				close(fds[i]);
632ec202509SPaul Chaignon 			break;
633ec202509SPaul Chaignon 		}
634ec202509SPaul Chaignon 		close(fds[i]);
635ec202509SPaul Chaignon 	}
636ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
637ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
638ec202509SPaul Chaignon 
639ec202509SPaul Chaignon exit_free:
640ec202509SPaul Chaignon 	free(fds);
641ec202509SPaul Chaignon 	return err;
642ec202509SPaul Chaignon }
643ec202509SPaul Chaignon 
64471bb428fSJakub Kicinski static int do_show(int argc, char **argv)
645743cc665SQuentin Monnet {
646743cc665SQuentin Monnet 	__u32 id = 0;
64771bb428fSJakub Kicinski 	int err;
64871bb428fSJakub Kicinski 	int fd;
64971bb428fSJakub Kicinski 
65046241271SQuentin Monnet 	if (show_pinned) {
6518f184732SQuentin Monnet 		prog_table = hashmap__new(hash_fn_for_key_as_id,
6528f184732SQuentin Monnet 					  equal_fn_for_key_as_id, NULL);
653622a5b58SMauricio Vásquez 		if (IS_ERR(prog_table)) {
6548f184732SQuentin Monnet 			p_err("failed to create hashmap for pinned paths");
6558f184732SQuentin Monnet 			return -1;
6568f184732SQuentin Monnet 		}
6578f184732SQuentin Monnet 		build_pinned_obj_table(prog_table, BPF_OBJ_PROG);
65846241271SQuentin Monnet 	}
659d53dee3fSAndrii Nakryiko 	build_obj_refs_table(&refs_table, BPF_OBJ_PROG);
6604990f1f4SPrashant Bhole 
661ec202509SPaul Chaignon 	if (argc == 2)
662ec202509SPaul Chaignon 		return do_show_subset(argc, argv);
66371bb428fSJakub Kicinski 
66471bb428fSJakub Kicinski 	if (argc)
66571bb428fSJakub Kicinski 		return BAD_ARG();
66671bb428fSJakub Kicinski 
667743cc665SQuentin Monnet 	if (json_output)
668743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
66971bb428fSJakub Kicinski 	while (true) {
67071bb428fSJakub Kicinski 		err = bpf_prog_get_next_id(id, &id);
67171bb428fSJakub Kicinski 		if (err) {
6721739c26dSQuentin Monnet 			if (errno == ENOENT) {
6731739c26dSQuentin Monnet 				err = 0;
67471bb428fSJakub Kicinski 				break;
6751739c26dSQuentin Monnet 			}
6769a5ab8bfSQuentin Monnet 			p_err("can't get next program: %s%s", strerror(errno),
6779a5ab8bfSQuentin Monnet 			      errno == EINVAL ? " -- kernel too old?" : "");
678743cc665SQuentin Monnet 			err = -1;
679743cc665SQuentin Monnet 			break;
68071bb428fSJakub Kicinski 		}
68171bb428fSJakub Kicinski 
68271bb428fSJakub Kicinski 		fd = bpf_prog_get_fd_by_id(id);
68371bb428fSJakub Kicinski 		if (fd < 0) {
6848207c6ddSJakub Kicinski 			if (errno == ENOENT)
6858207c6ddSJakub Kicinski 				continue;
6869a5ab8bfSQuentin Monnet 			p_err("can't get prog by id (%u): %s",
68771bb428fSJakub Kicinski 			      id, strerror(errno));
688743cc665SQuentin Monnet 			err = -1;
689743cc665SQuentin Monnet 			break;
69071bb428fSJakub Kicinski 		}
69171bb428fSJakub Kicinski 
69271bb428fSJakub Kicinski 		err = show_prog(fd);
69371bb428fSJakub Kicinski 		close(fd);
69471bb428fSJakub Kicinski 		if (err)
695743cc665SQuentin Monnet 			break;
69671bb428fSJakub Kicinski 	}
69771bb428fSJakub Kicinski 
698743cc665SQuentin Monnet 	if (json_output)
699743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
700743cc665SQuentin Monnet 
701d6699f8eSQuentin Monnet 	delete_obj_refs_table(refs_table);
702d53dee3fSAndrii Nakryiko 
70346241271SQuentin Monnet 	if (show_pinned)
7048f184732SQuentin Monnet 		delete_pinned_obj_table(prog_table);
70546241271SQuentin Monnet 
706743cc665SQuentin Monnet 	return err;
70771bb428fSJakub Kicinski }
70871bb428fSJakub Kicinski 
709ec202509SPaul Chaignon static int
710ec202509SPaul Chaignon prog_dump(struct bpf_prog_info *info, enum dump_mode mode,
711ec202509SPaul Chaignon 	  char *filepath, bool opcodes, bool visual, bool linum)
71271bb428fSJakub Kicinski {
713b053b439SMartin KaFai Lau 	struct bpf_prog_linfo *prog_linfo = NULL;
7143ddeac67SJakub Kicinski 	const char *disasm_opt = NULL;
7157105e828SDaniel Borkmann 	struct dump_data dd = {};
716cae73f23SSong Liu 	void *func_info = NULL;
717254471e5SYonghong Song 	struct btf *btf = NULL;
718254471e5SYonghong Song 	char func_sig[1024];
71971bb428fSJakub Kicinski 	unsigned char *buf;
720cae73f23SSong Liu 	__u32 member_len;
721ebbd7f64SQuentin Monnet 	int fd, err = -1;
72271bb428fSJakub Kicinski 	ssize_t n;
72371bb428fSJakub Kicinski 
724cae73f23SSong Liu 	if (mode == DUMP_JITED) {
7255b79bcdfSToke Høiland-Jørgensen 		if (info->jited_prog_len == 0 || !info->jited_prog_insns) {
7269a5ab8bfSQuentin Monnet 			p_info("no instructions returned");
727ec202509SPaul Chaignon 			return -1;
728f84192eeSSandipan Das 		}
72909f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->jited_prog_insns);
730cae73f23SSong Liu 		member_len = info->jited_prog_len;
731cae73f23SSong Liu 	} else {	/* DUMP_XLATED */
732d95f1e8bSToke Høiland-Jørgensen 		if (info->xlated_prog_len == 0 || !info->xlated_prog_insns) {
7337105e828SDaniel Borkmann 			p_err("error retrieving insn dump: kernel.kptr_restrict set?");
734ec202509SPaul Chaignon 			return -1;
7357105e828SDaniel Borkmann 		}
73609f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->xlated_prog_insns);
737cae73f23SSong Liu 		member_len = info->xlated_prog_len;
738cae73f23SSong Liu 	}
7397105e828SDaniel Borkmann 
74086f4b7f2SQuentin Monnet 	if (info->btf_id) {
74186f4b7f2SQuentin Monnet 		btf = btf__load_from_kernel_by_id(info->btf_id);
74286f4b7f2SQuentin Monnet 		if (libbpf_get_error(btf)) {
743254471e5SYonghong Song 			p_err("failed to get btf");
744ec202509SPaul Chaignon 			return -1;
745254471e5SYonghong Song 		}
74686f4b7f2SQuentin Monnet 	}
747254471e5SYonghong Song 
74809f44b75SAndrii Nakryiko 	func_info = u64_to_ptr(info->func_info);
749cae73f23SSong Liu 
750cae73f23SSong Liu 	if (info->nr_line_info) {
751cae73f23SSong Liu 		prog_linfo = bpf_prog_linfo__new(info);
752b053b439SMartin KaFai Lau 		if (!prog_linfo)
75310a5ce98SMartin KaFai Lau 			p_info("error in processing bpf_line_info.  continue without it.");
754b053b439SMartin KaFai Lau 	}
755b053b439SMartin KaFai Lau 
75671bb428fSJakub Kicinski 	if (filepath) {
75771bb428fSJakub Kicinski 		fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
75871bb428fSJakub Kicinski 		if (fd < 0) {
7599a5ab8bfSQuentin Monnet 			p_err("can't open file %s: %s", filepath,
76071bb428fSJakub Kicinski 			      strerror(errno));
761ebbd7f64SQuentin Monnet 			goto exit_free;
76271bb428fSJakub Kicinski 		}
76371bb428fSJakub Kicinski 
764cae73f23SSong Liu 		n = write(fd, buf, member_len);
76571bb428fSJakub Kicinski 		close(fd);
76609f44b75SAndrii Nakryiko 		if (n != (ssize_t)member_len) {
7679a5ab8bfSQuentin Monnet 			p_err("error writing output file: %s",
76871bb428fSJakub Kicinski 			      n < 0 ? strerror(errno) : "short write");
769ebbd7f64SQuentin Monnet 			goto exit_free;
77071bb428fSJakub Kicinski 		}
77152c84d36SQuentin Monnet 
77252c84d36SQuentin Monnet 		if (json_output)
77352c84d36SQuentin Monnet 			jsonw_null(json_wtr);
774cae73f23SSong Liu 	} else if (mode == DUMP_JITED) {
775e6593596SJiong Wang 		const char *name = NULL;
776e6593596SJiong Wang 
777cae73f23SSong Liu 		if (info->ifindex) {
778cae73f23SSong Liu 			name = ifindex_to_bfd_params(info->ifindex,
779cae73f23SSong Liu 						     info->netns_dev,
780cae73f23SSong Liu 						     info->netns_ino,
7813ddeac67SJakub Kicinski 						     &disasm_opt);
782e6593596SJiong Wang 			if (!name)
783ebbd7f64SQuentin Monnet 				goto exit_free;
784e6593596SJiong Wang 		}
785e6593596SJiong Wang 
786cae73f23SSong Liu 		if (info->nr_jited_func_lens && info->jited_func_lens) {
787f7f62c71SSandipan Das 			struct kernel_sym *sym = NULL;
788254471e5SYonghong Song 			struct bpf_func_info *record;
789f7f62c71SSandipan Das 			char sym_name[SYM_MAX_NAME];
790f7f62c71SSandipan Das 			unsigned char *img = buf;
791f7f62c71SSandipan Das 			__u64 *ksyms = NULL;
792f7f62c71SSandipan Das 			__u32 *lens;
793f7f62c71SSandipan Das 			__u32 i;
794cae73f23SSong Liu 			if (info->nr_jited_ksyms) {
795f7f62c71SSandipan Das 				kernel_syms_load(&dd);
79609f44b75SAndrii Nakryiko 				ksyms = u64_to_ptr(info->jited_ksyms);
797f7f62c71SSandipan Das 			}
798f7f62c71SSandipan Das 
799f7f62c71SSandipan Das 			if (json_output)
800f7f62c71SSandipan Das 				jsonw_start_array(json_wtr);
801f7f62c71SSandipan Das 
80209f44b75SAndrii Nakryiko 			lens = u64_to_ptr(info->jited_func_lens);
803cae73f23SSong Liu 			for (i = 0; i < info->nr_jited_func_lens; i++) {
804f7f62c71SSandipan Das 				if (ksyms) {
805f7f62c71SSandipan Das 					sym = kernel_syms_search(&dd, ksyms[i]);
806f7f62c71SSandipan Das 					if (sym)
807f7f62c71SSandipan Das 						sprintf(sym_name, "%s", sym->name);
808f7f62c71SSandipan Das 					else
809f7f62c71SSandipan Das 						sprintf(sym_name, "0x%016llx", ksyms[i]);
810f7f62c71SSandipan Das 				} else {
811f7f62c71SSandipan Das 					strcpy(sym_name, "unknown");
812f7f62c71SSandipan Das 				}
813f7f62c71SSandipan Das 
814254471e5SYonghong Song 				if (func_info) {
815cae73f23SSong Liu 					record = func_info + i * info->func_info_rec_size;
816254471e5SYonghong Song 					btf_dumper_type_only(btf, record->type_id,
817254471e5SYonghong Song 							     func_sig,
818254471e5SYonghong Song 							     sizeof(func_sig));
819254471e5SYonghong Song 				}
820254471e5SYonghong Song 
821f7f62c71SSandipan Das 				if (json_output) {
822f7f62c71SSandipan Das 					jsonw_start_object(json_wtr);
823254471e5SYonghong Song 					if (func_info && func_sig[0] != '\0') {
824254471e5SYonghong Song 						jsonw_name(json_wtr, "proto");
825254471e5SYonghong Song 						jsonw_string(json_wtr, func_sig);
826254471e5SYonghong Song 					}
827f7f62c71SSandipan Das 					jsonw_name(json_wtr, "name");
828f7f62c71SSandipan Das 					jsonw_string(json_wtr, sym_name);
829f7f62c71SSandipan Das 					jsonw_name(json_wtr, "insns");
830f7f62c71SSandipan Das 				} else {
831254471e5SYonghong Song 					if (func_info && func_sig[0] != '\0')
832254471e5SYonghong Song 						printf("%s:\n", func_sig);
833f7f62c71SSandipan Das 					printf("%s:\n", sym_name);
834f7f62c71SSandipan Das 				}
835f7f62c71SSandipan Das 
836b053b439SMartin KaFai Lau 				disasm_print_insn(img, lens[i], opcodes,
837b053b439SMartin KaFai Lau 						  name, disasm_opt, btf,
838b053b439SMartin KaFai Lau 						  prog_linfo, ksyms[i], i,
839b053b439SMartin KaFai Lau 						  linum);
840b053b439SMartin KaFai Lau 
841f7f62c71SSandipan Das 				img += lens[i];
842f7f62c71SSandipan Das 
843f7f62c71SSandipan Das 				if (json_output)
844f7f62c71SSandipan Das 					jsonw_end_object(json_wtr);
845f7f62c71SSandipan Das 				else
846f7f62c71SSandipan Das 					printf("\n");
847f7f62c71SSandipan Das 			}
848f7f62c71SSandipan Das 
849f7f62c71SSandipan Das 			if (json_output)
850f7f62c71SSandipan Das 				jsonw_end_array(json_wtr);
851f7f62c71SSandipan Das 		} else {
852cae73f23SSong Liu 			disasm_print_insn(buf, member_len, opcodes, name,
853b053b439SMartin KaFai Lau 					  disasm_opt, btf, NULL, 0, 0, false);
854f7f62c71SSandipan Das 		}
855b6c1cedbSJiong Wang 	} else if (visual) {
856b6c1cedbSJiong Wang 		if (json_output)
857b6c1cedbSJiong Wang 			jsonw_null(json_wtr);
858b6c1cedbSJiong Wang 		else
859cae73f23SSong Liu 			dump_xlated_cfg(buf, member_len);
8607105e828SDaniel Borkmann 	} else {
8617105e828SDaniel Borkmann 		kernel_syms_load(&dd);
862cae73f23SSong Liu 		dd.nr_jited_ksyms = info->nr_jited_ksyms;
86309f44b75SAndrii Nakryiko 		dd.jited_ksyms = u64_to_ptr(info->jited_ksyms);
864254471e5SYonghong Song 		dd.btf = btf;
865254471e5SYonghong Song 		dd.func_info = func_info;
866cae73f23SSong Liu 		dd.finfo_rec_size = info->func_info_rec_size;
867b053b439SMartin KaFai Lau 		dd.prog_linfo = prog_linfo;
868f84192eeSSandipan Das 
869f05e2c32SQuentin Monnet 		if (json_output)
870cae73f23SSong Liu 			dump_xlated_json(&dd, buf, member_len, opcodes,
871b053b439SMartin KaFai Lau 					 linum);
872f05e2c32SQuentin Monnet 		else
873cae73f23SSong Liu 			dump_xlated_plain(&dd, buf, member_len, opcodes,
874b053b439SMartin KaFai Lau 					  linum);
8757105e828SDaniel Borkmann 		kernel_syms_destroy(&dd);
8767105e828SDaniel Borkmann 	}
87771bb428fSJakub Kicinski 
878ebbd7f64SQuentin Monnet 	err = 0;
879369e955bSQuentin Monnet 
880ebbd7f64SQuentin Monnet exit_free:
881ebbd7f64SQuentin Monnet 	btf__free(btf);
882ebbd7f64SQuentin Monnet 	bpf_prog_linfo__free(prog_linfo);
883ebbd7f64SQuentin Monnet 	return err;
884ec202509SPaul Chaignon }
88571bb428fSJakub Kicinski 
886ec202509SPaul Chaignon static int do_dump(int argc, char **argv)
887ec202509SPaul Chaignon {
888c59765cfSDave Marchevsky 	struct bpf_prog_info info;
889c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
890c59765cfSDave Marchevsky 	size_t info_data_sz = 0;
891c59765cfSDave Marchevsky 	void *info_data = NULL;
892ec202509SPaul Chaignon 	char *filepath = NULL;
893ec202509SPaul Chaignon 	bool opcodes = false;
894ec202509SPaul Chaignon 	bool visual = false;
895ec202509SPaul Chaignon 	enum dump_mode mode;
896ec202509SPaul Chaignon 	bool linum = false;
897ec202509SPaul Chaignon 	int nb_fds, i = 0;
898c59765cfSDave Marchevsky 	int *fds = NULL;
899ec202509SPaul Chaignon 	int err = -1;
900ec202509SPaul Chaignon 
901ec202509SPaul Chaignon 	if (is_prefix(*argv, "jited")) {
902ec202509SPaul Chaignon 		if (disasm_init())
90371bb428fSJakub Kicinski 			return -1;
904ec202509SPaul Chaignon 		mode = DUMP_JITED;
905ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "xlated")) {
906ec202509SPaul Chaignon 		mode = DUMP_XLATED;
907ec202509SPaul Chaignon 	} else {
908ec202509SPaul Chaignon 		p_err("expected 'xlated' or 'jited', got: %s", *argv);
909ec202509SPaul Chaignon 		return -1;
910ec202509SPaul Chaignon 	}
911ec202509SPaul Chaignon 	NEXT_ARG();
912ec202509SPaul Chaignon 
913ec202509SPaul Chaignon 	if (argc < 2)
914ec202509SPaul Chaignon 		usage();
915ec202509SPaul Chaignon 
916ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
917ec202509SPaul Chaignon 	if (!fds) {
918ec202509SPaul Chaignon 		p_err("mem alloc failed");
919ec202509SPaul Chaignon 		return -1;
920ec202509SPaul Chaignon 	}
921ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
922ec202509SPaul Chaignon 	if (nb_fds < 1)
923ec202509SPaul Chaignon 		goto exit_free;
924ec202509SPaul Chaignon 
925ec202509SPaul Chaignon 	if (is_prefix(*argv, "file")) {
926ec202509SPaul Chaignon 		NEXT_ARG();
927ec202509SPaul Chaignon 		if (!argc) {
928ec202509SPaul Chaignon 			p_err("expected file path");
929ec202509SPaul Chaignon 			goto exit_close;
930ec202509SPaul Chaignon 		}
931ec202509SPaul Chaignon 		if (nb_fds > 1) {
932ec202509SPaul Chaignon 			p_err("several programs matched");
933ec202509SPaul Chaignon 			goto exit_close;
934ec202509SPaul Chaignon 		}
935ec202509SPaul Chaignon 
936ec202509SPaul Chaignon 		filepath = *argv;
937ec202509SPaul Chaignon 		NEXT_ARG();
938ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "opcodes")) {
939ec202509SPaul Chaignon 		opcodes = true;
940ec202509SPaul Chaignon 		NEXT_ARG();
941ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "visual")) {
942ec202509SPaul Chaignon 		if (nb_fds > 1) {
943ec202509SPaul Chaignon 			p_err("several programs matched");
944ec202509SPaul Chaignon 			goto exit_close;
945ec202509SPaul Chaignon 		}
946ec202509SPaul Chaignon 
947ec202509SPaul Chaignon 		visual = true;
948ec202509SPaul Chaignon 		NEXT_ARG();
949ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "linum")) {
950ec202509SPaul Chaignon 		linum = true;
951ec202509SPaul Chaignon 		NEXT_ARG();
952ec202509SPaul Chaignon 	}
953ec202509SPaul Chaignon 
954ec202509SPaul Chaignon 	if (argc) {
955ec202509SPaul Chaignon 		usage();
956ec202509SPaul Chaignon 		goto exit_close;
957ec202509SPaul Chaignon 	}
958ec202509SPaul Chaignon 
959ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
960ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
961ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
962c59765cfSDave Marchevsky 		memset(&info, 0, sizeof(info));
963c59765cfSDave Marchevsky 
964c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
965c59765cfSDave Marchevsky 		if (err) {
966c59765cfSDave Marchevsky 			p_err("can't get prog info: %s", strerror(errno));
967c59765cfSDave Marchevsky 			break;
968c59765cfSDave Marchevsky 		}
969c59765cfSDave Marchevsky 
970c59765cfSDave Marchevsky 		err = prep_prog_info(&info, mode, &info_data, &info_data_sz);
971c59765cfSDave Marchevsky 		if (err) {
972c59765cfSDave Marchevsky 			p_err("can't grow prog info_data");
973c59765cfSDave Marchevsky 			break;
974c59765cfSDave Marchevsky 		}
975c59765cfSDave Marchevsky 
976c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
977c59765cfSDave Marchevsky 		if (err) {
978ec202509SPaul Chaignon 			p_err("can't get prog info: %s", strerror(errno));
979ec202509SPaul Chaignon 			break;
980ec202509SPaul Chaignon 		}
981ec202509SPaul Chaignon 
982ec202509SPaul Chaignon 		if (json_output && nb_fds > 1) {
983ec202509SPaul Chaignon 			jsonw_start_object(json_wtr);	/* prog object */
984b662000aSRaman Shukhau 			print_prog_header_json(&info, fds[i]);
985ec202509SPaul Chaignon 			jsonw_name(json_wtr, "insns");
986ec202509SPaul Chaignon 		} else if (nb_fds > 1) {
987b662000aSRaman Shukhau 			print_prog_header_plain(&info, fds[i]);
988ec202509SPaul Chaignon 		}
989ec202509SPaul Chaignon 
990c59765cfSDave Marchevsky 		err = prog_dump(&info, mode, filepath, opcodes, visual, linum);
991ec202509SPaul Chaignon 
992ec202509SPaul Chaignon 		if (json_output && nb_fds > 1)
993ec202509SPaul Chaignon 			jsonw_end_object(json_wtr);	/* prog object */
994ec202509SPaul Chaignon 		else if (i != nb_fds - 1 && nb_fds > 1)
995ec202509SPaul Chaignon 			printf("\n");
996ec202509SPaul Chaignon 
997ec202509SPaul Chaignon 		if (err)
998ec202509SPaul Chaignon 			break;
999ec202509SPaul Chaignon 		close(fds[i]);
1000ec202509SPaul Chaignon 	}
1001ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
1002ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
1003ec202509SPaul Chaignon 
1004ec202509SPaul Chaignon exit_close:
1005ec202509SPaul Chaignon 	for (; i < nb_fds; i++)
1006ec202509SPaul Chaignon 		close(fds[i]);
1007ec202509SPaul Chaignon exit_free:
1008c59765cfSDave Marchevsky 	free(info_data);
1009ec202509SPaul Chaignon 	free(fds);
1010ec202509SPaul Chaignon 	return err;
101171bb428fSJakub Kicinski }
101271bb428fSJakub Kicinski 
101371bb428fSJakub Kicinski static int do_pin(int argc, char **argv)
101471bb428fSJakub Kicinski {
1015004b45c0SQuentin Monnet 	int err;
1016004b45c0SQuentin Monnet 
101775a1e792SQuentin Monnet 	err = do_pin_any(argc, argv, prog_parse_fd);
1018004b45c0SQuentin Monnet 	if (!err && json_output)
1019004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
1020004b45c0SQuentin Monnet 	return err;
102171bb428fSJakub Kicinski }
102271bb428fSJakub Kicinski 
10233ff5a4dcSJakub Kicinski struct map_replace {
10243ff5a4dcSJakub Kicinski 	int idx;
10253ff5a4dcSJakub Kicinski 	int fd;
10263ff5a4dcSJakub Kicinski 	char *name;
10273ff5a4dcSJakub Kicinski };
10283ff5a4dcSJakub Kicinski 
1029c101189bSQuentin Monnet static int map_replace_compar(const void *p1, const void *p2)
10303ff5a4dcSJakub Kicinski {
10313ff5a4dcSJakub Kicinski 	const struct map_replace *a = p1, *b = p2;
10323ff5a4dcSJakub Kicinski 
10333ff5a4dcSJakub Kicinski 	return a->idx - b->idx;
10343ff5a4dcSJakub Kicinski }
10353ff5a4dcSJakub Kicinski 
1036092f0892SStanislav Fomichev static int parse_attach_detach_args(int argc, char **argv, int *progfd,
1037092f0892SStanislav Fomichev 				    enum bpf_attach_type *attach_type,
1038092f0892SStanislav Fomichev 				    int *mapfd)
1039092f0892SStanislav Fomichev {
1040092f0892SStanislav Fomichev 	if (!REQ_ARGS(3))
1041092f0892SStanislav Fomichev 		return -EINVAL;
1042092f0892SStanislav Fomichev 
1043092f0892SStanislav Fomichev 	*progfd = prog_parse_fd(&argc, &argv);
1044092f0892SStanislav Fomichev 	if (*progfd < 0)
1045092f0892SStanislav Fomichev 		return *progfd;
1046092f0892SStanislav Fomichev 
1047092f0892SStanislav Fomichev 	*attach_type = parse_attach_type(*argv);
1048092f0892SStanislav Fomichev 	if (*attach_type == __MAX_BPF_ATTACH_TYPE) {
1049092f0892SStanislav Fomichev 		p_err("invalid attach/detach type");
1050092f0892SStanislav Fomichev 		return -EINVAL;
1051092f0892SStanislav Fomichev 	}
1052092f0892SStanislav Fomichev 
1053092f0892SStanislav Fomichev 	if (*attach_type == BPF_FLOW_DISSECTOR) {
1054f9b7ff0dSLorenz Bauer 		*mapfd = 0;
1055092f0892SStanislav Fomichev 		return 0;
1056092f0892SStanislav Fomichev 	}
1057092f0892SStanislav Fomichev 
1058092f0892SStanislav Fomichev 	NEXT_ARG();
1059092f0892SStanislav Fomichev 	if (!REQ_ARGS(2))
1060092f0892SStanislav Fomichev 		return -EINVAL;
1061092f0892SStanislav Fomichev 
1062092f0892SStanislav Fomichev 	*mapfd = map_parse_fd(&argc, &argv);
1063092f0892SStanislav Fomichev 	if (*mapfd < 0)
1064092f0892SStanislav Fomichev 		return *mapfd;
1065092f0892SStanislav Fomichev 
1066092f0892SStanislav Fomichev 	return 0;
1067092f0892SStanislav Fomichev }
1068092f0892SStanislav Fomichev 
1069b7d3826cSJohn Fastabend static int do_attach(int argc, char **argv)
1070b7d3826cSJohn Fastabend {
1071b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1072092f0892SStanislav Fomichev 	int err, progfd;
1073092f0892SStanislav Fomichev 	int mapfd;
1074b7d3826cSJohn Fastabend 
1075092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1076092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1077092f0892SStanislav Fomichev 	if (err)
1078092f0892SStanislav Fomichev 		return err;
1079b7d3826cSJohn Fastabend 
1080b7d3826cSJohn Fastabend 	err = bpf_prog_attach(progfd, mapfd, attach_type, 0);
1081b7d3826cSJohn Fastabend 	if (err) {
1082b7d3826cSJohn Fastabend 		p_err("failed prog attach to map");
1083b7d3826cSJohn Fastabend 		return -EINVAL;
1084b7d3826cSJohn Fastabend 	}
1085b7d3826cSJohn Fastabend 
1086b7d3826cSJohn Fastabend 	if (json_output)
1087b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1088b7d3826cSJohn Fastabend 	return 0;
1089b7d3826cSJohn Fastabend }
1090b7d3826cSJohn Fastabend 
1091b7d3826cSJohn Fastabend static int do_detach(int argc, char **argv)
1092b7d3826cSJohn Fastabend {
1093b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1094092f0892SStanislav Fomichev 	int err, progfd;
1095092f0892SStanislav Fomichev 	int mapfd;
1096b7d3826cSJohn Fastabend 
1097092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1098092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1099092f0892SStanislav Fomichev 	if (err)
1100092f0892SStanislav Fomichev 		return err;
1101b7d3826cSJohn Fastabend 
1102b7d3826cSJohn Fastabend 	err = bpf_prog_detach2(progfd, mapfd, attach_type);
1103b7d3826cSJohn Fastabend 	if (err) {
1104b7d3826cSJohn Fastabend 		p_err("failed prog detach from map");
1105b7d3826cSJohn Fastabend 		return -EINVAL;
1106b7d3826cSJohn Fastabend 	}
1107b7d3826cSJohn Fastabend 
1108b7d3826cSJohn Fastabend 	if (json_output)
1109b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1110b7d3826cSJohn Fastabend 	return 0;
1111b7d3826cSJohn Fastabend }
111277380998SStanislav Fomichev 
1113ba95c745SQuentin Monnet static int check_single_stdin(char *file_data_in, char *file_ctx_in)
1114ba95c745SQuentin Monnet {
1115ba95c745SQuentin Monnet 	if (file_data_in && file_ctx_in &&
1116ba95c745SQuentin Monnet 	    !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) {
1117ba95c745SQuentin Monnet 		p_err("cannot use standard input for both data_in and ctx_in");
1118ba95c745SQuentin Monnet 		return -1;
1119ba95c745SQuentin Monnet 	}
1120ba95c745SQuentin Monnet 
1121ba95c745SQuentin Monnet 	return 0;
1122ba95c745SQuentin Monnet }
1123ba95c745SQuentin Monnet 
1124ba95c745SQuentin Monnet static int get_run_data(const char *fname, void **data_ptr, unsigned int *size)
1125ba95c745SQuentin Monnet {
1126ba95c745SQuentin Monnet 	size_t block_size = 256;
1127ba95c745SQuentin Monnet 	size_t buf_size = block_size;
1128ba95c745SQuentin Monnet 	size_t nb_read = 0;
1129ba95c745SQuentin Monnet 	void *tmp;
1130ba95c745SQuentin Monnet 	FILE *f;
1131ba95c745SQuentin Monnet 
1132ba95c745SQuentin Monnet 	if (!fname) {
1133ba95c745SQuentin Monnet 		*data_ptr = NULL;
1134ba95c745SQuentin Monnet 		*size = 0;
1135ba95c745SQuentin Monnet 		return 0;
1136ba95c745SQuentin Monnet 	}
1137ba95c745SQuentin Monnet 
1138ba95c745SQuentin Monnet 	if (!strcmp(fname, "-"))
1139ba95c745SQuentin Monnet 		f = stdin;
1140ba95c745SQuentin Monnet 	else
1141ba95c745SQuentin Monnet 		f = fopen(fname, "r");
1142ba95c745SQuentin Monnet 	if (!f) {
1143ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1144ba95c745SQuentin Monnet 		return -1;
1145ba95c745SQuentin Monnet 	}
1146ba95c745SQuentin Monnet 
1147ba95c745SQuentin Monnet 	*data_ptr = malloc(block_size);
1148ba95c745SQuentin Monnet 	if (!*data_ptr) {
1149ba95c745SQuentin Monnet 		p_err("failed to allocate memory for data_in/ctx_in: %s",
1150ba95c745SQuentin Monnet 		      strerror(errno));
1151ba95c745SQuentin Monnet 		goto err_fclose;
1152ba95c745SQuentin Monnet 	}
1153ba95c745SQuentin Monnet 
1154ba95c745SQuentin Monnet 	while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) {
1155ba95c745SQuentin Monnet 		if (feof(f))
1156ba95c745SQuentin Monnet 			break;
1157ba95c745SQuentin Monnet 		if (ferror(f)) {
1158ba95c745SQuentin Monnet 			p_err("failed to read data_in/ctx_in from %s: %s",
1159ba95c745SQuentin Monnet 			      fname, strerror(errno));
1160ba95c745SQuentin Monnet 			goto err_free;
1161ba95c745SQuentin Monnet 		}
1162ba95c745SQuentin Monnet 		if (nb_read > buf_size - block_size) {
1163ba95c745SQuentin Monnet 			if (buf_size == UINT32_MAX) {
1164ba95c745SQuentin Monnet 				p_err("data_in/ctx_in is too long (max: %d)",
1165ba95c745SQuentin Monnet 				      UINT32_MAX);
1166ba95c745SQuentin Monnet 				goto err_free;
1167ba95c745SQuentin Monnet 			}
1168ba95c745SQuentin Monnet 			/* No space for fread()-ing next chunk; realloc() */
1169ba95c745SQuentin Monnet 			buf_size *= 2;
1170ba95c745SQuentin Monnet 			tmp = realloc(*data_ptr, buf_size);
1171ba95c745SQuentin Monnet 			if (!tmp) {
1172ba95c745SQuentin Monnet 				p_err("failed to reallocate data_in/ctx_in: %s",
1173ba95c745SQuentin Monnet 				      strerror(errno));
1174ba95c745SQuentin Monnet 				goto err_free;
1175ba95c745SQuentin Monnet 			}
1176ba95c745SQuentin Monnet 			*data_ptr = tmp;
1177ba95c745SQuentin Monnet 		}
1178ba95c745SQuentin Monnet 	}
1179ba95c745SQuentin Monnet 	if (f != stdin)
1180ba95c745SQuentin Monnet 		fclose(f);
1181ba95c745SQuentin Monnet 
1182ba95c745SQuentin Monnet 	*size = nb_read;
1183ba95c745SQuentin Monnet 	return 0;
1184ba95c745SQuentin Monnet 
1185ba95c745SQuentin Monnet err_free:
1186ba95c745SQuentin Monnet 	free(*data_ptr);
1187ba95c745SQuentin Monnet 	*data_ptr = NULL;
1188ba95c745SQuentin Monnet err_fclose:
1189ba95c745SQuentin Monnet 	if (f != stdin)
1190ba95c745SQuentin Monnet 		fclose(f);
1191ba95c745SQuentin Monnet 	return -1;
1192ba95c745SQuentin Monnet }
1193ba95c745SQuentin Monnet 
1194ba95c745SQuentin Monnet static void hex_print(void *data, unsigned int size, FILE *f)
1195ba95c745SQuentin Monnet {
1196ba95c745SQuentin Monnet 	size_t i, j;
1197ba95c745SQuentin Monnet 	char c;
1198ba95c745SQuentin Monnet 
1199ba95c745SQuentin Monnet 	for (i = 0; i < size; i += 16) {
1200ba95c745SQuentin Monnet 		/* Row offset */
1201ba95c745SQuentin Monnet 		fprintf(f, "%07zx\t", i);
1202ba95c745SQuentin Monnet 
1203ba95c745SQuentin Monnet 		/* Hexadecimal values */
1204ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++)
1205ba95c745SQuentin Monnet 			fprintf(f, "%02x%s", *(uint8_t *)(data + j),
1206ba95c745SQuentin Monnet 				j % 2 ? " " : "");
1207ba95c745SQuentin Monnet 		for (; j < i + 16; j++)
1208ba95c745SQuentin Monnet 			fprintf(f, "  %s", j % 2 ? " " : "");
1209ba95c745SQuentin Monnet 
1210ba95c745SQuentin Monnet 		/* ASCII values (if relevant), '.' otherwise */
1211ba95c745SQuentin Monnet 		fprintf(f, "| ");
1212ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++) {
1213ba95c745SQuentin Monnet 			c = *(char *)(data + j);
1214ba95c745SQuentin Monnet 			if (c < ' ' || c > '~')
1215ba95c745SQuentin Monnet 				c = '.';
1216ba95c745SQuentin Monnet 			fprintf(f, "%c%s", c, j == i + 7 ? " " : "");
1217ba95c745SQuentin Monnet 		}
1218ba95c745SQuentin Monnet 
1219ba95c745SQuentin Monnet 		fprintf(f, "\n");
1220ba95c745SQuentin Monnet 	}
1221ba95c745SQuentin Monnet }
1222ba95c745SQuentin Monnet 
1223ba95c745SQuentin Monnet static int
1224ba95c745SQuentin Monnet print_run_output(void *data, unsigned int size, const char *fname,
1225ba95c745SQuentin Monnet 		 const char *json_key)
1226ba95c745SQuentin Monnet {
1227ba95c745SQuentin Monnet 	size_t nb_written;
1228ba95c745SQuentin Monnet 	FILE *f;
1229ba95c745SQuentin Monnet 
1230ba95c745SQuentin Monnet 	if (!fname)
1231ba95c745SQuentin Monnet 		return 0;
1232ba95c745SQuentin Monnet 
1233ba95c745SQuentin Monnet 	if (!strcmp(fname, "-")) {
1234ba95c745SQuentin Monnet 		f = stdout;
1235ba95c745SQuentin Monnet 		if (json_output) {
1236ba95c745SQuentin Monnet 			jsonw_name(json_wtr, json_key);
1237ba95c745SQuentin Monnet 			print_data_json(data, size);
1238ba95c745SQuentin Monnet 		} else {
1239ba95c745SQuentin Monnet 			hex_print(data, size, f);
1240ba95c745SQuentin Monnet 		}
1241ba95c745SQuentin Monnet 		return 0;
1242ba95c745SQuentin Monnet 	}
1243ba95c745SQuentin Monnet 
1244ba95c745SQuentin Monnet 	f = fopen(fname, "w");
1245ba95c745SQuentin Monnet 	if (!f) {
1246ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1247ba95c745SQuentin Monnet 		return -1;
1248ba95c745SQuentin Monnet 	}
1249ba95c745SQuentin Monnet 
1250ba95c745SQuentin Monnet 	nb_written = fwrite(data, 1, size, f);
1251ba95c745SQuentin Monnet 	fclose(f);
1252ba95c745SQuentin Monnet 	if (nb_written != size) {
1253ba95c745SQuentin Monnet 		p_err("failed to write output data/ctx: %s", strerror(errno));
1254ba95c745SQuentin Monnet 		return -1;
1255ba95c745SQuentin Monnet 	}
1256ba95c745SQuentin Monnet 
1257ba95c745SQuentin Monnet 	return 0;
1258ba95c745SQuentin Monnet }
1259ba95c745SQuentin Monnet 
1260ba95c745SQuentin Monnet static int alloc_run_data(void **data_ptr, unsigned int size_out)
1261ba95c745SQuentin Monnet {
1262ba95c745SQuentin Monnet 	*data_ptr = calloc(size_out, 1);
1263ba95c745SQuentin Monnet 	if (!*data_ptr) {
1264ba95c745SQuentin Monnet 		p_err("failed to allocate memory for output data/ctx: %s",
1265ba95c745SQuentin Monnet 		      strerror(errno));
1266ba95c745SQuentin Monnet 		return -1;
1267ba95c745SQuentin Monnet 	}
1268ba95c745SQuentin Monnet 
1269ba95c745SQuentin Monnet 	return 0;
1270ba95c745SQuentin Monnet }
1271ba95c745SQuentin Monnet 
1272ba95c745SQuentin Monnet static int do_run(int argc, char **argv)
1273ba95c745SQuentin Monnet {
1274ba95c745SQuentin Monnet 	char *data_fname_in = NULL, *data_fname_out = NULL;
1275ba95c745SQuentin Monnet 	char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
1276ba95c745SQuentin Monnet 	const unsigned int default_size = SZ_32K;
1277ba95c745SQuentin Monnet 	void *data_in = NULL, *data_out = NULL;
1278ba95c745SQuentin Monnet 	void *ctx_in = NULL, *ctx_out = NULL;
1279ba95c745SQuentin Monnet 	unsigned int repeat = 1;
1280ba95c745SQuentin Monnet 	int fd, err;
12819cce5313SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, test_attr);
1282ba95c745SQuentin Monnet 
1283ba95c745SQuentin Monnet 	if (!REQ_ARGS(4))
1284ba95c745SQuentin Monnet 		return -1;
1285ba95c745SQuentin Monnet 
1286ba95c745SQuentin Monnet 	fd = prog_parse_fd(&argc, &argv);
1287ba95c745SQuentin Monnet 	if (fd < 0)
1288ba95c745SQuentin Monnet 		return -1;
1289ba95c745SQuentin Monnet 
1290ba95c745SQuentin Monnet 	while (argc) {
1291ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "data_in", "data_out",
1292ba95c745SQuentin Monnet 					 "data_size_out", NULL))
1293ba95c745SQuentin Monnet 			return -1;
1294ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "ctx_in", "ctx_out",
1295ba95c745SQuentin Monnet 					 "ctx_size_out", NULL))
1296ba95c745SQuentin Monnet 			return -1;
1297ba95c745SQuentin Monnet 
1298ba95c745SQuentin Monnet 		if (is_prefix(*argv, "data_in")) {
1299ba95c745SQuentin Monnet 			NEXT_ARG();
1300ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1301ba95c745SQuentin Monnet 				return -1;
1302ba95c745SQuentin Monnet 
1303ba95c745SQuentin Monnet 			data_fname_in = GET_ARG();
1304ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1305ba95c745SQuentin Monnet 				return -1;
1306ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_out")) {
1307ba95c745SQuentin Monnet 			NEXT_ARG();
1308ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1309ba95c745SQuentin Monnet 				return -1;
1310ba95c745SQuentin Monnet 
1311ba95c745SQuentin Monnet 			data_fname_out = GET_ARG();
1312ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_size_out")) {
1313ba95c745SQuentin Monnet 			char *endptr;
1314ba95c745SQuentin Monnet 
1315ba95c745SQuentin Monnet 			NEXT_ARG();
1316ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1317ba95c745SQuentin Monnet 				return -1;
1318ba95c745SQuentin Monnet 
1319ba95c745SQuentin Monnet 			test_attr.data_size_out = strtoul(*argv, &endptr, 0);
1320ba95c745SQuentin Monnet 			if (*endptr) {
1321ba95c745SQuentin Monnet 				p_err("can't parse %s as output data size",
1322ba95c745SQuentin Monnet 				      *argv);
1323ba95c745SQuentin Monnet 				return -1;
1324ba95c745SQuentin Monnet 			}
1325ba95c745SQuentin Monnet 			NEXT_ARG();
1326ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_in")) {
1327ba95c745SQuentin Monnet 			NEXT_ARG();
1328ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1329ba95c745SQuentin Monnet 				return -1;
1330ba95c745SQuentin Monnet 
1331ba95c745SQuentin Monnet 			ctx_fname_in = GET_ARG();
1332ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1333ba95c745SQuentin Monnet 				return -1;
1334ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_out")) {
1335ba95c745SQuentin Monnet 			NEXT_ARG();
1336ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1337ba95c745SQuentin Monnet 				return -1;
1338ba95c745SQuentin Monnet 
1339ba95c745SQuentin Monnet 			ctx_fname_out = GET_ARG();
1340ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_size_out")) {
1341ba95c745SQuentin Monnet 			char *endptr;
1342ba95c745SQuentin Monnet 
1343ba95c745SQuentin Monnet 			NEXT_ARG();
1344ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1345ba95c745SQuentin Monnet 				return -1;
1346ba95c745SQuentin Monnet 
1347ba95c745SQuentin Monnet 			test_attr.ctx_size_out = strtoul(*argv, &endptr, 0);
1348ba95c745SQuentin Monnet 			if (*endptr) {
1349ba95c745SQuentin Monnet 				p_err("can't parse %s as output context size",
1350ba95c745SQuentin Monnet 				      *argv);
1351ba95c745SQuentin Monnet 				return -1;
1352ba95c745SQuentin Monnet 			}
1353ba95c745SQuentin Monnet 			NEXT_ARG();
1354ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "repeat")) {
1355ba95c745SQuentin Monnet 			char *endptr;
1356ba95c745SQuentin Monnet 
1357ba95c745SQuentin Monnet 			NEXT_ARG();
1358ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1359ba95c745SQuentin Monnet 				return -1;
1360ba95c745SQuentin Monnet 
1361ba95c745SQuentin Monnet 			repeat = strtoul(*argv, &endptr, 0);
1362ba95c745SQuentin Monnet 			if (*endptr) {
1363ba95c745SQuentin Monnet 				p_err("can't parse %s as repeat number",
1364ba95c745SQuentin Monnet 				      *argv);
1365ba95c745SQuentin Monnet 				return -1;
1366ba95c745SQuentin Monnet 			}
1367ba95c745SQuentin Monnet 			NEXT_ARG();
1368ba95c745SQuentin Monnet 		} else {
1369ba95c745SQuentin 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'?",
1370ba95c745SQuentin Monnet 			      *argv);
1371ba95c745SQuentin Monnet 			return -1;
1372ba95c745SQuentin Monnet 		}
1373ba95c745SQuentin Monnet 	}
1374ba95c745SQuentin Monnet 
1375ba95c745SQuentin Monnet 	err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in);
1376ba95c745SQuentin Monnet 	if (err)
1377ba95c745SQuentin Monnet 		return -1;
1378ba95c745SQuentin Monnet 
1379ba95c745SQuentin Monnet 	if (data_in) {
1380ba95c745SQuentin Monnet 		if (!test_attr.data_size_out)
1381ba95c745SQuentin Monnet 			test_attr.data_size_out = default_size;
1382ba95c745SQuentin Monnet 		err = alloc_run_data(&data_out, test_attr.data_size_out);
1383ba95c745SQuentin Monnet 		if (err)
1384ba95c745SQuentin Monnet 			goto free_data_in;
1385ba95c745SQuentin Monnet 	}
1386ba95c745SQuentin Monnet 
1387ba95c745SQuentin Monnet 	err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in);
1388ba95c745SQuentin Monnet 	if (err)
1389ba95c745SQuentin Monnet 		goto free_data_out;
1390ba95c745SQuentin Monnet 
1391ba95c745SQuentin Monnet 	if (ctx_in) {
1392ba95c745SQuentin Monnet 		if (!test_attr.ctx_size_out)
1393ba95c745SQuentin Monnet 			test_attr.ctx_size_out = default_size;
1394ba95c745SQuentin Monnet 		err = alloc_run_data(&ctx_out, test_attr.ctx_size_out);
1395ba95c745SQuentin Monnet 		if (err)
1396ba95c745SQuentin Monnet 			goto free_ctx_in;
1397ba95c745SQuentin Monnet 	}
1398ba95c745SQuentin Monnet 
1399ba95c745SQuentin Monnet 	test_attr.repeat	= repeat;
1400ba95c745SQuentin Monnet 	test_attr.data_in	= data_in;
1401ba95c745SQuentin Monnet 	test_attr.data_out	= data_out;
1402ba95c745SQuentin Monnet 	test_attr.ctx_in	= ctx_in;
1403ba95c745SQuentin Monnet 	test_attr.ctx_out	= ctx_out;
1404ba95c745SQuentin Monnet 
14059cce5313SDelyan Kratunov 	err = bpf_prog_test_run_opts(fd, &test_attr);
1406ba95c745SQuentin Monnet 	if (err) {
1407ba95c745SQuentin Monnet 		p_err("failed to run program: %s", strerror(errno));
1408ba95c745SQuentin Monnet 		goto free_ctx_out;
1409ba95c745SQuentin Monnet 	}
1410ba95c745SQuentin Monnet 
1411ba95c745SQuentin Monnet 	err = 0;
1412ba95c745SQuentin Monnet 
1413ba95c745SQuentin Monnet 	if (json_output)
1414ba95c745SQuentin Monnet 		jsonw_start_object(json_wtr);	/* root */
1415ba95c745SQuentin Monnet 
1416ba95c745SQuentin Monnet 	/* Do not exit on errors occurring when printing output data/context,
1417ba95c745SQuentin Monnet 	 * we still want to print return value and duration for program run.
1418ba95c745SQuentin Monnet 	 */
1419ba95c745SQuentin Monnet 	if (test_attr.data_size_out)
1420ba95c745SQuentin Monnet 		err += print_run_output(test_attr.data_out,
1421ba95c745SQuentin Monnet 					test_attr.data_size_out,
1422ba95c745SQuentin Monnet 					data_fname_out, "data_out");
1423ba95c745SQuentin Monnet 	if (test_attr.ctx_size_out)
1424ba95c745SQuentin Monnet 		err += print_run_output(test_attr.ctx_out,
1425ba95c745SQuentin Monnet 					test_attr.ctx_size_out,
1426ba95c745SQuentin Monnet 					ctx_fname_out, "ctx_out");
1427ba95c745SQuentin Monnet 
1428ba95c745SQuentin Monnet 	if (json_output) {
1429ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "retval", test_attr.retval);
1430ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "duration", test_attr.duration);
1431ba95c745SQuentin Monnet 		jsonw_end_object(json_wtr);	/* root */
1432ba95c745SQuentin Monnet 	} else {
1433ba95c745SQuentin Monnet 		fprintf(stdout, "Return value: %u, duration%s: %uns\n",
1434ba95c745SQuentin Monnet 			test_attr.retval,
1435ba95c745SQuentin Monnet 			repeat > 1 ? " (average)" : "", test_attr.duration);
1436ba95c745SQuentin Monnet 	}
1437ba95c745SQuentin Monnet 
1438ba95c745SQuentin Monnet free_ctx_out:
1439ba95c745SQuentin Monnet 	free(ctx_out);
1440ba95c745SQuentin Monnet free_ctx_in:
1441ba95c745SQuentin Monnet 	free(ctx_in);
1442ba95c745SQuentin Monnet free_data_out:
1443ba95c745SQuentin Monnet 	free(data_out);
1444ba95c745SQuentin Monnet free_data_in:
1445ba95c745SQuentin Monnet 	free(data_in);
1446ba95c745SQuentin Monnet 
1447ba95c745SQuentin Monnet 	return err;
1448ba95c745SQuentin Monnet }
1449ba95c745SQuentin Monnet 
14506ae32b29SQuentin Monnet static int
14516ae32b29SQuentin Monnet get_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
14526ae32b29SQuentin Monnet 		      enum bpf_attach_type *expected_attach_type)
14536ae32b29SQuentin Monnet {
14546ae32b29SQuentin Monnet 	libbpf_print_fn_t print_backup;
14556ae32b29SQuentin Monnet 	int ret;
14566ae32b29SQuentin Monnet 
14576ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14586ae32b29SQuentin Monnet 	if (!ret)
14596ae32b29SQuentin Monnet 		return ret;
14606ae32b29SQuentin Monnet 
14616ae32b29SQuentin Monnet 	/* libbpf_prog_type_by_name() failed, let's re-run with debug level */
14626ae32b29SQuentin Monnet 	print_backup = libbpf_set_print(print_all_levels);
14636ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14646ae32b29SQuentin Monnet 	libbpf_set_print(print_backup);
14656ae32b29SQuentin Monnet 
14666ae32b29SQuentin Monnet 	return ret;
14676ae32b29SQuentin Monnet }
14686ae32b29SQuentin Monnet 
146977380998SStanislav Fomichev static int load_with_options(int argc, char **argv, bool first_prog_only)
147049a086c2SRoman Gushchin {
147132e3e58eSAndrii Nakryiko 	enum bpf_prog_type common_prog_type = BPF_PROG_TYPE_UNSPEC;
1472e00aca65SAndrii Nakryiko 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
1473e00aca65SAndrii Nakryiko 		.relaxed_maps = relaxed_maps,
1474e00aca65SAndrii Nakryiko 	);
147555d77807SQuentin Monnet 	enum bpf_attach_type expected_attach_type;
14763ff5a4dcSJakub Kicinski 	struct map_replace *map_replace = NULL;
147777380998SStanislav Fomichev 	struct bpf_program *prog = NULL, *pos;
14783ff5a4dcSJakub Kicinski 	unsigned int old_map_fds = 0;
14793767a94bSStanislav Fomichev 	const char *pinmaps = NULL;
148049a086c2SRoman Gushchin 	struct bpf_object *obj;
1481c8406848SJakub Kicinski 	struct bpf_map *map;
1482c8406848SJakub Kicinski 	const char *pinfile;
14833ff5a4dcSJakub Kicinski 	unsigned int i, j;
1484c8406848SJakub Kicinski 	__u32 ifindex = 0;
148532e3e58eSAndrii Nakryiko 	const char *file;
14863ff5a4dcSJakub Kicinski 	int idx, err;
148749a086c2SRoman Gushchin 
148832e3e58eSAndrii Nakryiko 
14898d1fc3deSJakub Kicinski 	if (!REQ_ARGS(2))
14908d1fc3deSJakub Kicinski 		return -1;
149132e3e58eSAndrii Nakryiko 	file = GET_ARG();
14928d1fc3deSJakub Kicinski 	pinfile = GET_ARG();
149349a086c2SRoman Gushchin 
1494ba6dd679SJakub Kicinski 	while (argc) {
149549f2cba3SJakub Kicinski 		if (is_prefix(*argv, "type")) {
149649f2cba3SJakub Kicinski 			NEXT_ARG();
149749f2cba3SJakub Kicinski 
149832e3e58eSAndrii Nakryiko 			if (common_prog_type != BPF_PROG_TYPE_UNSPEC) {
149949f2cba3SJakub Kicinski 				p_err("program type already specified");
15003ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
150149f2cba3SJakub Kicinski 			}
150249f2cba3SJakub Kicinski 			if (!REQ_ARGS(1))
15033ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
150449f2cba3SJakub Kicinski 
1505314f14abSStanislav Fomichev 			err = libbpf_prog_type_by_name(*argv, &common_prog_type,
1506314f14abSStanislav Fomichev 						       &expected_attach_type);
1507314f14abSStanislav Fomichev 			if (err < 0) {
150849f2cba3SJakub Kicinski 				/* Put a '/' at the end of type to appease libbpf */
1509314f14abSStanislav Fomichev 				char *type = malloc(strlen(*argv) + 2);
1510314f14abSStanislav Fomichev 
151149f2cba3SJakub Kicinski 				if (!type) {
151249f2cba3SJakub Kicinski 					p_err("mem alloc failed");
15133ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
151449f2cba3SJakub Kicinski 				}
151549f2cba3SJakub Kicinski 				*type = 0;
151649f2cba3SJakub Kicinski 				strcat(type, *argv);
151749f2cba3SJakub Kicinski 				strcat(type, "/");
151849f2cba3SJakub Kicinski 
15196ae32b29SQuentin Monnet 				err = get_prog_type_by_name(type, &common_prog_type,
1520c8406848SJakub Kicinski 							    &expected_attach_type);
152149f2cba3SJakub Kicinski 				free(type);
1522c76e4c22STaeung Song 				if (err < 0)
15233ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
1524314f14abSStanislav Fomichev 			}
1525c76e4c22STaeung Song 
152649f2cba3SJakub Kicinski 			NEXT_ARG();
15273ff5a4dcSJakub Kicinski 		} else if (is_prefix(*argv, "map")) {
1528dde7011aSJakub Kicinski 			void *new_map_replace;
15293ff5a4dcSJakub Kicinski 			char *endptr, *name;
15303ff5a4dcSJakub Kicinski 			int fd;
15313ff5a4dcSJakub Kicinski 
15323ff5a4dcSJakub Kicinski 			NEXT_ARG();
15333ff5a4dcSJakub Kicinski 
15343ff5a4dcSJakub Kicinski 			if (!REQ_ARGS(4))
15353ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15363ff5a4dcSJakub Kicinski 
15373ff5a4dcSJakub Kicinski 			if (is_prefix(*argv, "idx")) {
15383ff5a4dcSJakub Kicinski 				NEXT_ARG();
15393ff5a4dcSJakub Kicinski 
15403ff5a4dcSJakub Kicinski 				idx = strtoul(*argv, &endptr, 0);
15413ff5a4dcSJakub Kicinski 				if (*endptr) {
15423ff5a4dcSJakub Kicinski 					p_err("can't parse %s as IDX", *argv);
15433ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
15443ff5a4dcSJakub Kicinski 				}
15453ff5a4dcSJakub Kicinski 				name = NULL;
15463ff5a4dcSJakub Kicinski 			} else if (is_prefix(*argv, "name")) {
15473ff5a4dcSJakub Kicinski 				NEXT_ARG();
15483ff5a4dcSJakub Kicinski 
15493ff5a4dcSJakub Kicinski 				name = *argv;
15503ff5a4dcSJakub Kicinski 				idx = -1;
15513ff5a4dcSJakub Kicinski 			} else {
15523ff5a4dcSJakub Kicinski 				p_err("expected 'idx' or 'name', got: '%s'?",
15533ff5a4dcSJakub Kicinski 				      *argv);
15543ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15553ff5a4dcSJakub Kicinski 			}
15563ff5a4dcSJakub Kicinski 			NEXT_ARG();
15573ff5a4dcSJakub Kicinski 
15583ff5a4dcSJakub Kicinski 			fd = map_parse_fd(&argc, &argv);
15593ff5a4dcSJakub Kicinski 			if (fd < 0)
15603ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15613ff5a4dcSJakub Kicinski 
1562a19df713SMauricio Vásquez 			new_map_replace = libbpf_reallocarray(map_replace,
1563dde7011aSJakub Kicinski 							      old_map_fds + 1,
15643ff5a4dcSJakub Kicinski 							      sizeof(*map_replace));
1565dde7011aSJakub Kicinski 			if (!new_map_replace) {
15663ff5a4dcSJakub Kicinski 				p_err("mem alloc failed");
15673ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15683ff5a4dcSJakub Kicinski 			}
1569dde7011aSJakub Kicinski 			map_replace = new_map_replace;
1570dde7011aSJakub Kicinski 
15713ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].idx = idx;
15723ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].name = name;
15733ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].fd = fd;
15743ff5a4dcSJakub Kicinski 			old_map_fds++;
157549f2cba3SJakub Kicinski 		} else if (is_prefix(*argv, "dev")) {
1576ba6dd679SJakub Kicinski 			NEXT_ARG();
1577ba6dd679SJakub Kicinski 
1578c8406848SJakub Kicinski 			if (ifindex) {
1579ba6dd679SJakub Kicinski 				p_err("offload device already specified");
15803ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1581ba6dd679SJakub Kicinski 			}
1582ba6dd679SJakub Kicinski 			if (!REQ_ARGS(1))
15833ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1584ba6dd679SJakub Kicinski 
1585c8406848SJakub Kicinski 			ifindex = if_nametoindex(*argv);
1586c8406848SJakub Kicinski 			if (!ifindex) {
1587ba6dd679SJakub Kicinski 				p_err("unrecognized netdevice '%s': %s",
1588ba6dd679SJakub Kicinski 				      *argv, strerror(errno));
15893ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1590ba6dd679SJakub Kicinski 			}
1591ba6dd679SJakub Kicinski 			NEXT_ARG();
15923767a94bSStanislav Fomichev 		} else if (is_prefix(*argv, "pinmaps")) {
15933767a94bSStanislav Fomichev 			NEXT_ARG();
15943767a94bSStanislav Fomichev 
15953767a94bSStanislav Fomichev 			if (!REQ_ARGS(1))
15963767a94bSStanislav Fomichev 				goto err_free_reuse_maps;
15973767a94bSStanislav Fomichev 
15983767a94bSStanislav Fomichev 			pinmaps = GET_ARG();
1599ba6dd679SJakub Kicinski 		} else {
16003ff5a4dcSJakub Kicinski 			p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
1601ba6dd679SJakub Kicinski 			      *argv);
16023ff5a4dcSJakub Kicinski 			goto err_free_reuse_maps;
1603ba6dd679SJakub Kicinski 		}
1604ba6dd679SJakub Kicinski 	}
1605ba6dd679SJakub Kicinski 
1606ac4e0e05SYonghong Song 	set_max_rlimit();
1607ac4e0e05SYonghong Song 
1608b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1609b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1610b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1611b59e4ce8SAndrii Nakryiko 
161232e3e58eSAndrii Nakryiko 	obj = bpf_object__open_file(file, &open_opts);
1613d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1614c8406848SJakub Kicinski 		p_err("failed to open object file");
16153ff5a4dcSJakub Kicinski 		goto err_free_reuse_maps;
161649a086c2SRoman Gushchin 	}
161749a086c2SRoman Gushchin 
161877380998SStanislav Fomichev 	bpf_object__for_each_program(pos, obj) {
161932e3e58eSAndrii Nakryiko 		enum bpf_prog_type prog_type = common_prog_type;
1620c8406848SJakub Kicinski 
162132e3e58eSAndrii Nakryiko 		if (prog_type == BPF_PROG_TYPE_UNSPEC) {
1622fd17e272SAndrii Nakryiko 			const char *sec_name = bpf_program__section_name(pos);
1623c8406848SJakub Kicinski 
16246ae32b29SQuentin Monnet 			err = get_prog_type_by_name(sec_name, &prog_type,
1625c8406848SJakub Kicinski 						    &expected_attach_type);
1626c76e4c22STaeung Song 			if (err < 0)
1627c8406848SJakub Kicinski 				goto err_close_obj;
1628c8406848SJakub Kicinski 		}
162977380998SStanislav Fomichev 
163077380998SStanislav Fomichev 		bpf_program__set_ifindex(pos, ifindex);
163177380998SStanislav Fomichev 		bpf_program__set_type(pos, prog_type);
163277380998SStanislav Fomichev 		bpf_program__set_expected_attach_type(pos, expected_attach_type);
163377380998SStanislav Fomichev 	}
1634c8406848SJakub Kicinski 
16353ff5a4dcSJakub Kicinski 	qsort(map_replace, old_map_fds, sizeof(*map_replace),
16363ff5a4dcSJakub Kicinski 	      map_replace_compar);
16373ff5a4dcSJakub Kicinski 
16383ff5a4dcSJakub Kicinski 	/* After the sort maps by name will be first on the list, because they
16393ff5a4dcSJakub Kicinski 	 * have idx == -1.  Resolve them.
16403ff5a4dcSJakub Kicinski 	 */
16413ff5a4dcSJakub Kicinski 	j = 0;
16423ff5a4dcSJakub Kicinski 	while (j < old_map_fds && map_replace[j].name) {
16433ff5a4dcSJakub Kicinski 		i = 0;
1644f74a53d9SJakub Kicinski 		bpf_object__for_each_map(map, obj) {
16453ff5a4dcSJakub Kicinski 			if (!strcmp(bpf_map__name(map), map_replace[j].name)) {
16463ff5a4dcSJakub Kicinski 				map_replace[j].idx = i;
16473ff5a4dcSJakub Kicinski 				break;
16483ff5a4dcSJakub Kicinski 			}
16493ff5a4dcSJakub Kicinski 			i++;
16503ff5a4dcSJakub Kicinski 		}
16513ff5a4dcSJakub Kicinski 		if (map_replace[j].idx == -1) {
16523ff5a4dcSJakub Kicinski 			p_err("unable to find map '%s'", map_replace[j].name);
16533ff5a4dcSJakub Kicinski 			goto err_close_obj;
16543ff5a4dcSJakub Kicinski 		}
16553ff5a4dcSJakub Kicinski 		j++;
16563ff5a4dcSJakub Kicinski 	}
16573ff5a4dcSJakub Kicinski 	/* Resort if any names were resolved */
16583ff5a4dcSJakub Kicinski 	if (j)
16593ff5a4dcSJakub Kicinski 		qsort(map_replace, old_map_fds, sizeof(*map_replace),
16603ff5a4dcSJakub Kicinski 		      map_replace_compar);
16613ff5a4dcSJakub Kicinski 
16623ff5a4dcSJakub Kicinski 	/* Set ifindex and name reuse */
16633ff5a4dcSJakub Kicinski 	j = 0;
16643ff5a4dcSJakub Kicinski 	idx = 0;
1665f74a53d9SJakub Kicinski 	bpf_object__for_each_map(map, obj) {
16669855c131SChristy Lee 		if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
1667c8406848SJakub Kicinski 			bpf_map__set_ifindex(map, ifindex);
1668c8406848SJakub Kicinski 
16693ff5a4dcSJakub Kicinski 		if (j < old_map_fds && idx == map_replace[j].idx) {
16703ff5a4dcSJakub Kicinski 			err = bpf_map__reuse_fd(map, map_replace[j++].fd);
16713ff5a4dcSJakub Kicinski 			if (err) {
16723ff5a4dcSJakub Kicinski 				p_err("unable to set up map reuse: %d", err);
16733ff5a4dcSJakub Kicinski 				goto err_close_obj;
16743ff5a4dcSJakub Kicinski 			}
16753ff5a4dcSJakub Kicinski 
16763ff5a4dcSJakub Kicinski 			/* Next reuse wants to apply to the same map */
16773ff5a4dcSJakub Kicinski 			if (j < old_map_fds && map_replace[j].idx == idx) {
16783ff5a4dcSJakub Kicinski 				p_err("replacement for map idx %d specified more than once",
16793ff5a4dcSJakub Kicinski 				      idx);
16803ff5a4dcSJakub Kicinski 				goto err_close_obj;
16813ff5a4dcSJakub Kicinski 			}
16823ff5a4dcSJakub Kicinski 		}
16833ff5a4dcSJakub Kicinski 
16843ff5a4dcSJakub Kicinski 		idx++;
16853ff5a4dcSJakub Kicinski 	}
16863ff5a4dcSJakub Kicinski 	if (j < old_map_fds) {
16873ff5a4dcSJakub Kicinski 		p_err("map idx '%d' not used", map_replace[j].idx);
16883ff5a4dcSJakub Kicinski 		goto err_close_obj;
16893ff5a4dcSJakub Kicinski 	}
16903ff5a4dcSJakub Kicinski 
1691b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1692c8406848SJakub Kicinski 	if (err) {
1693c8406848SJakub Kicinski 		p_err("failed to load object file");
1694c8406848SJakub Kicinski 		goto err_close_obj;
1695c8406848SJakub Kicinski 	}
1696c8406848SJakub Kicinski 
169777380998SStanislav Fomichev 	err = mount_bpffs_for_pin(pinfile);
169877380998SStanislav Fomichev 	if (err)
1699bfee71fbSJakub Kicinski 		goto err_close_obj;
170049a086c2SRoman Gushchin 
170177380998SStanislav Fomichev 	if (first_prog_only) {
17026f2b219bSHengqi Chen 		prog = bpf_object__next_program(obj, NULL);
170377380998SStanislav Fomichev 		if (!prog) {
170477380998SStanislav Fomichev 			p_err("object file doesn't contain any bpf program");
170577380998SStanislav Fomichev 			goto err_close_obj;
170677380998SStanislav Fomichev 		}
170777380998SStanislav Fomichev 
170877380998SStanislav Fomichev 		err = bpf_obj_pin(bpf_program__fd(prog), pinfile);
170977380998SStanislav Fomichev 		if (err) {
171077380998SStanislav Fomichev 			p_err("failed to pin program %s",
1711fd17e272SAndrii Nakryiko 			      bpf_program__section_name(prog));
171277380998SStanislav Fomichev 			goto err_close_obj;
171377380998SStanislav Fomichev 		}
171477380998SStanislav Fomichev 	} else {
171577380998SStanislav Fomichev 		err = bpf_object__pin_programs(obj, pinfile);
171677380998SStanislav Fomichev 		if (err) {
171777380998SStanislav Fomichev 			p_err("failed to pin all programs");
171877380998SStanislav Fomichev 			goto err_close_obj;
171977380998SStanislav Fomichev 		}
172077380998SStanislav Fomichev 	}
172177380998SStanislav Fomichev 
17223767a94bSStanislav Fomichev 	if (pinmaps) {
17233767a94bSStanislav Fomichev 		err = bpf_object__pin_maps(obj, pinmaps);
17243767a94bSStanislav Fomichev 		if (err) {
17253767a94bSStanislav Fomichev 			p_err("failed to pin all maps");
17263767a94bSStanislav Fomichev 			goto err_unpin;
17273767a94bSStanislav Fomichev 		}
17283767a94bSStanislav Fomichev 	}
17293767a94bSStanislav Fomichev 
173049a086c2SRoman Gushchin 	if (json_output)
173149a086c2SRoman Gushchin 		jsonw_null(json_wtr);
173249a086c2SRoman Gushchin 
1733bfee71fbSJakub Kicinski 	bpf_object__close(obj);
17343ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
17353ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
17363ff5a4dcSJakub Kicinski 	free(map_replace);
1737bfee71fbSJakub Kicinski 
173849a086c2SRoman Gushchin 	return 0;
1739bfee71fbSJakub Kicinski 
17403767a94bSStanislav Fomichev err_unpin:
17413767a94bSStanislav Fomichev 	if (first_prog_only)
17423767a94bSStanislav Fomichev 		unlink(pinfile);
17433767a94bSStanislav Fomichev 	else
17443767a94bSStanislav Fomichev 		bpf_object__unpin_programs(obj, pinfile);
1745bfee71fbSJakub Kicinski err_close_obj:
1746314f14abSStanislav Fomichev 	if (!legacy_libbpf) {
1747314f14abSStanislav Fomichev 		p_info("Warning: bpftool is now running in libbpf strict mode and has more stringent requirements about BPF programs.\n"
1748314f14abSStanislav Fomichev 		       "If it used to work for this object file but now doesn't, see --legacy option for more details.\n");
1749314f14abSStanislav Fomichev 	}
1750314f14abSStanislav Fomichev 
1751bfee71fbSJakub Kicinski 	bpf_object__close(obj);
17523ff5a4dcSJakub Kicinski err_free_reuse_maps:
17533ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
17543ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
17553ff5a4dcSJakub Kicinski 	free(map_replace);
1756bfee71fbSJakub Kicinski 	return -1;
175749a086c2SRoman Gushchin }
175849a086c2SRoman Gushchin 
1759d510296dSAlexei Starovoitov static int count_open_fds(void)
1760d510296dSAlexei Starovoitov {
1761d510296dSAlexei Starovoitov 	DIR *dp = opendir("/proc/self/fd");
1762d510296dSAlexei Starovoitov 	struct dirent *de;
1763d510296dSAlexei Starovoitov 	int cnt = -3;
1764d510296dSAlexei Starovoitov 
1765d510296dSAlexei Starovoitov 	if (!dp)
1766d510296dSAlexei Starovoitov 		return -1;
1767d510296dSAlexei Starovoitov 
1768d510296dSAlexei Starovoitov 	while ((de = readdir(dp)))
1769d510296dSAlexei Starovoitov 		cnt++;
1770d510296dSAlexei Starovoitov 
1771d510296dSAlexei Starovoitov 	closedir(dp);
1772d510296dSAlexei Starovoitov 	return cnt;
1773d510296dSAlexei Starovoitov }
1774d510296dSAlexei Starovoitov 
1775d510296dSAlexei Starovoitov static int try_loader(struct gen_loader_opts *gen)
1776d510296dSAlexei Starovoitov {
1777d510296dSAlexei Starovoitov 	struct bpf_load_and_run_opts opts = {};
1778d510296dSAlexei Starovoitov 	struct bpf_loader_ctx *ctx;
1779d510296dSAlexei Starovoitov 	int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
1780d510296dSAlexei Starovoitov 					     sizeof(struct bpf_prog_desc));
1781d510296dSAlexei Starovoitov 	int log_buf_sz = (1u << 24) - 1;
1782d510296dSAlexei Starovoitov 	int err, fds_before, fd_delta;
1783942df4dcSAlexei Starovoitov 	char *log_buf = NULL;
1784d510296dSAlexei Starovoitov 
1785d510296dSAlexei Starovoitov 	ctx = alloca(ctx_sz);
1786d510296dSAlexei Starovoitov 	memset(ctx, 0, ctx_sz);
1787d510296dSAlexei Starovoitov 	ctx->sz = ctx_sz;
1788942df4dcSAlexei Starovoitov 	if (verifier_logs) {
1789942df4dcSAlexei Starovoitov 		ctx->log_level = 1 + 2 + 4;
1790d510296dSAlexei Starovoitov 		ctx->log_size = log_buf_sz;
1791d510296dSAlexei Starovoitov 		log_buf = malloc(log_buf_sz);
1792d510296dSAlexei Starovoitov 		if (!log_buf)
1793d510296dSAlexei Starovoitov 			return -ENOMEM;
1794d510296dSAlexei Starovoitov 		ctx->log_buf = (long) log_buf;
1795942df4dcSAlexei Starovoitov 	}
1796d510296dSAlexei Starovoitov 	opts.ctx = ctx;
1797d510296dSAlexei Starovoitov 	opts.data = gen->data;
1798d510296dSAlexei Starovoitov 	opts.data_sz = gen->data_sz;
1799d510296dSAlexei Starovoitov 	opts.insns = gen->insns;
1800d510296dSAlexei Starovoitov 	opts.insns_sz = gen->insns_sz;
1801d510296dSAlexei Starovoitov 	fds_before = count_open_fds();
1802d510296dSAlexei Starovoitov 	err = bpf_load_and_run(&opts);
1803d510296dSAlexei Starovoitov 	fd_delta = count_open_fds() - fds_before;
1804942df4dcSAlexei Starovoitov 	if (err < 0 || verifier_logs) {
1805d510296dSAlexei Starovoitov 		fprintf(stderr, "err %d\n%s\n%s", err, opts.errstr, log_buf);
1806942df4dcSAlexei Starovoitov 		if (fd_delta && err < 0)
1807d510296dSAlexei Starovoitov 			fprintf(stderr, "loader prog leaked %d FDs\n",
1808d510296dSAlexei Starovoitov 				fd_delta);
1809d510296dSAlexei Starovoitov 	}
1810d510296dSAlexei Starovoitov 	free(log_buf);
1811d510296dSAlexei Starovoitov 	return err;
1812d510296dSAlexei Starovoitov }
1813d510296dSAlexei Starovoitov 
1814d510296dSAlexei Starovoitov static int do_loader(int argc, char **argv)
1815d510296dSAlexei Starovoitov {
1816d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts);
1817d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(gen_loader_opts, gen);
1818d510296dSAlexei Starovoitov 	struct bpf_object *obj;
1819d510296dSAlexei Starovoitov 	const char *file;
1820d510296dSAlexei Starovoitov 	int err = 0;
1821d510296dSAlexei Starovoitov 
1822d510296dSAlexei Starovoitov 	if (!REQ_ARGS(1))
1823d510296dSAlexei Starovoitov 		return -1;
1824d510296dSAlexei Starovoitov 	file = GET_ARG();
1825d510296dSAlexei Starovoitov 
1826b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1827b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1828b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1829b59e4ce8SAndrii Nakryiko 
1830d510296dSAlexei Starovoitov 	obj = bpf_object__open_file(file, &open_opts);
1831d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1832d510296dSAlexei Starovoitov 		p_err("failed to open object file");
1833d510296dSAlexei Starovoitov 		goto err_close_obj;
1834d510296dSAlexei Starovoitov 	}
1835d510296dSAlexei Starovoitov 
1836d510296dSAlexei Starovoitov 	err = bpf_object__gen_loader(obj, &gen);
1837d510296dSAlexei Starovoitov 	if (err)
1838d510296dSAlexei Starovoitov 		goto err_close_obj;
1839d510296dSAlexei Starovoitov 
1840b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1841d510296dSAlexei Starovoitov 	if (err) {
1842d510296dSAlexei Starovoitov 		p_err("failed to load object file");
1843d510296dSAlexei Starovoitov 		goto err_close_obj;
1844d510296dSAlexei Starovoitov 	}
1845d510296dSAlexei Starovoitov 
1846d510296dSAlexei Starovoitov 	if (verifier_logs) {
1847d510296dSAlexei Starovoitov 		struct dump_data dd = {};
1848d510296dSAlexei Starovoitov 
1849d510296dSAlexei Starovoitov 		kernel_syms_load(&dd);
1850d510296dSAlexei Starovoitov 		dump_xlated_plain(&dd, (void *)gen.insns, gen.insns_sz, false, false);
1851d510296dSAlexei Starovoitov 		kernel_syms_destroy(&dd);
1852d510296dSAlexei Starovoitov 	}
1853d510296dSAlexei Starovoitov 	err = try_loader(&gen);
1854d510296dSAlexei Starovoitov err_close_obj:
1855d510296dSAlexei Starovoitov 	bpf_object__close(obj);
1856d510296dSAlexei Starovoitov 	return err;
1857d510296dSAlexei Starovoitov }
1858d510296dSAlexei Starovoitov 
185977380998SStanislav Fomichev static int do_load(int argc, char **argv)
186077380998SStanislav Fomichev {
1861d510296dSAlexei Starovoitov 	if (use_loader)
1862d510296dSAlexei Starovoitov 		return do_loader(argc, argv);
186377380998SStanislav Fomichev 	return load_with_options(argc, argv, true);
186477380998SStanislav Fomichev }
186577380998SStanislav Fomichev 
186677380998SStanislav Fomichev static int do_loadall(int argc, char **argv)
186777380998SStanislav Fomichev {
186877380998SStanislav Fomichev 	return load_with_options(argc, argv, false);
186977380998SStanislav Fomichev }
187077380998SStanislav Fomichev 
187147c09d6aSSong Liu #ifdef BPFTOOL_WITHOUT_SKELETONS
187247c09d6aSSong Liu 
187347c09d6aSSong Liu static int do_profile(int argc, char **argv)
187447c09d6aSSong Liu {
187514e5728fSSong Liu 	p_err("bpftool prog profile command is not supported. Please build bpftool with clang >= 10.0.0");
187647c09d6aSSong Liu 	return 0;
187747c09d6aSSong Liu }
187847c09d6aSSong Liu 
187947c09d6aSSong Liu #else /* BPFTOOL_WITHOUT_SKELETONS */
188047c09d6aSSong Liu 
188147c09d6aSSong Liu #include "profiler.skel.h"
188247c09d6aSSong Liu 
188347c09d6aSSong Liu struct profile_metric {
188447c09d6aSSong Liu 	const char *name;
188547c09d6aSSong Liu 	struct bpf_perf_event_value val;
188647c09d6aSSong Liu 	struct perf_event_attr attr;
188747c09d6aSSong Liu 	bool selected;
188847c09d6aSSong Liu 
188947c09d6aSSong Liu 	/* calculate ratios like instructions per cycle */
189047c09d6aSSong Liu 	const int ratio_metric; /* 0 for N/A, 1 for index 0 (cycles) */
189147c09d6aSSong Liu 	const char *ratio_desc;
189247c09d6aSSong Liu 	const float ratio_mul;
189347c09d6aSSong Liu } metrics[] = {
189447c09d6aSSong Liu 	{
189547c09d6aSSong Liu 		.name = "cycles",
189647c09d6aSSong Liu 		.attr = {
189747c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
189847c09d6aSSong Liu 			.config = PERF_COUNT_HW_CPU_CYCLES,
189947c09d6aSSong Liu 			.exclude_user = 1,
190047c09d6aSSong Liu 		},
190147c09d6aSSong Liu 	},
190247c09d6aSSong Liu 	{
190347c09d6aSSong Liu 		.name = "instructions",
190447c09d6aSSong Liu 		.attr = {
190547c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
190647c09d6aSSong Liu 			.config = PERF_COUNT_HW_INSTRUCTIONS,
190747c09d6aSSong Liu 			.exclude_user = 1,
190847c09d6aSSong Liu 		},
190947c09d6aSSong Liu 		.ratio_metric = 1,
191047c09d6aSSong Liu 		.ratio_desc = "insns per cycle",
191147c09d6aSSong Liu 		.ratio_mul = 1.0,
191247c09d6aSSong Liu 	},
191347c09d6aSSong Liu 	{
191447c09d6aSSong Liu 		.name = "l1d_loads",
191547c09d6aSSong Liu 		.attr = {
191647c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
191747c09d6aSSong Liu 			.config =
191847c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_L1D |
191947c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
192047c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
192147c09d6aSSong Liu 			.exclude_user = 1,
192247c09d6aSSong Liu 		},
192347c09d6aSSong Liu 	},
192447c09d6aSSong Liu 	{
192547c09d6aSSong Liu 		.name = "llc_misses",
192647c09d6aSSong Liu 		.attr = {
192747c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
192847c09d6aSSong Liu 			.config =
192947c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_LL |
193047c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
193147c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
193247c09d6aSSong Liu 			.exclude_user = 1
193347c09d6aSSong Liu 		},
193447c09d6aSSong Liu 		.ratio_metric = 2,
193547c09d6aSSong Liu 		.ratio_desc = "LLC misses per million insns",
193647c09d6aSSong Liu 		.ratio_mul = 1e6,
193747c09d6aSSong Liu 	},
1938450d060eSYonghong Song 	{
1939450d060eSYonghong Song 		.name = "itlb_misses",
1940450d060eSYonghong Song 		.attr = {
1941450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
1942450d060eSYonghong Song 			.config =
1943450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_ITLB |
1944450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
1945450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
1946450d060eSYonghong Song 			.exclude_user = 1
1947450d060eSYonghong Song 		},
1948450d060eSYonghong Song 		.ratio_metric = 2,
1949450d060eSYonghong Song 		.ratio_desc = "itlb misses per million insns",
1950450d060eSYonghong Song 		.ratio_mul = 1e6,
1951450d060eSYonghong Song 	},
1952450d060eSYonghong Song 	{
1953450d060eSYonghong Song 		.name = "dtlb_misses",
1954450d060eSYonghong Song 		.attr = {
1955450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
1956450d060eSYonghong Song 			.config =
1957450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_DTLB |
1958450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
1959450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
1960450d060eSYonghong Song 			.exclude_user = 1
1961450d060eSYonghong Song 		},
1962450d060eSYonghong Song 		.ratio_metric = 2,
1963450d060eSYonghong Song 		.ratio_desc = "dtlb misses per million insns",
1964450d060eSYonghong Song 		.ratio_mul = 1e6,
1965450d060eSYonghong Song 	},
196647c09d6aSSong Liu };
196747c09d6aSSong Liu 
196847c09d6aSSong Liu static __u64 profile_total_count;
196947c09d6aSSong Liu 
197047c09d6aSSong Liu #define MAX_NUM_PROFILE_METRICS 4
197147c09d6aSSong Liu 
197247c09d6aSSong Liu static int profile_parse_metrics(int argc, char **argv)
197347c09d6aSSong Liu {
197447c09d6aSSong Liu 	unsigned int metric_cnt;
197547c09d6aSSong Liu 	int selected_cnt = 0;
197647c09d6aSSong Liu 	unsigned int i;
197747c09d6aSSong Liu 
197847c09d6aSSong Liu 	metric_cnt = sizeof(metrics) / sizeof(struct profile_metric);
197947c09d6aSSong Liu 
198047c09d6aSSong Liu 	while (argc > 0) {
198147c09d6aSSong Liu 		for (i = 0; i < metric_cnt; i++) {
198247c09d6aSSong Liu 			if (is_prefix(argv[0], metrics[i].name)) {
198347c09d6aSSong Liu 				if (!metrics[i].selected)
198447c09d6aSSong Liu 					selected_cnt++;
198547c09d6aSSong Liu 				metrics[i].selected = true;
198647c09d6aSSong Liu 				break;
198747c09d6aSSong Liu 			}
198847c09d6aSSong Liu 		}
198947c09d6aSSong Liu 		if (i == metric_cnt) {
199047c09d6aSSong Liu 			p_err("unknown metric %s", argv[0]);
199147c09d6aSSong Liu 			return -1;
199247c09d6aSSong Liu 		}
199347c09d6aSSong Liu 		NEXT_ARG();
199447c09d6aSSong Liu 	}
199547c09d6aSSong Liu 	if (selected_cnt > MAX_NUM_PROFILE_METRICS) {
199647c09d6aSSong Liu 		p_err("too many (%d) metrics, please specify no more than %d metrics at at time",
199747c09d6aSSong Liu 		      selected_cnt, MAX_NUM_PROFILE_METRICS);
199847c09d6aSSong Liu 		return -1;
199947c09d6aSSong Liu 	}
200047c09d6aSSong Liu 	return selected_cnt;
200147c09d6aSSong Liu }
200247c09d6aSSong Liu 
200347c09d6aSSong Liu static void profile_read_values(struct profiler_bpf *obj)
200447c09d6aSSong Liu {
200547c09d6aSSong Liu 	__u32 m, cpu, num_cpu = obj->rodata->num_cpu;
200647c09d6aSSong Liu 	int reading_map_fd, count_map_fd;
200747c09d6aSSong Liu 	__u64 counts[num_cpu];
200847c09d6aSSong Liu 	__u32 key = 0;
200947c09d6aSSong Liu 	int err;
201047c09d6aSSong Liu 
201147c09d6aSSong Liu 	reading_map_fd = bpf_map__fd(obj->maps.accum_readings);
201247c09d6aSSong Liu 	count_map_fd = bpf_map__fd(obj->maps.counts);
201347c09d6aSSong Liu 	if (reading_map_fd < 0 || count_map_fd < 0) {
201447c09d6aSSong Liu 		p_err("failed to get fd for map");
201547c09d6aSSong Liu 		return;
201647c09d6aSSong Liu 	}
201747c09d6aSSong Liu 
201847c09d6aSSong Liu 	err = bpf_map_lookup_elem(count_map_fd, &key, counts);
201947c09d6aSSong Liu 	if (err) {
202047c09d6aSSong Liu 		p_err("failed to read count_map: %s", strerror(errno));
202147c09d6aSSong Liu 		return;
202247c09d6aSSong Liu 	}
202347c09d6aSSong Liu 
202447c09d6aSSong Liu 	profile_total_count = 0;
202547c09d6aSSong Liu 	for (cpu = 0; cpu < num_cpu; cpu++)
202647c09d6aSSong Liu 		profile_total_count += counts[cpu];
202747c09d6aSSong Liu 
202847c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
202947c09d6aSSong Liu 		struct bpf_perf_event_value values[num_cpu];
203047c09d6aSSong Liu 
203147c09d6aSSong Liu 		if (!metrics[m].selected)
203247c09d6aSSong Liu 			continue;
203347c09d6aSSong Liu 
203447c09d6aSSong Liu 		err = bpf_map_lookup_elem(reading_map_fd, &key, values);
203547c09d6aSSong Liu 		if (err) {
203647c09d6aSSong Liu 			p_err("failed to read reading_map: %s",
203747c09d6aSSong Liu 			      strerror(errno));
203847c09d6aSSong Liu 			return;
203947c09d6aSSong Liu 		}
204047c09d6aSSong Liu 		for (cpu = 0; cpu < num_cpu; cpu++) {
204147c09d6aSSong Liu 			metrics[m].val.counter += values[cpu].counter;
204247c09d6aSSong Liu 			metrics[m].val.enabled += values[cpu].enabled;
204347c09d6aSSong Liu 			metrics[m].val.running += values[cpu].running;
204447c09d6aSSong Liu 		}
204547c09d6aSSong Liu 		key++;
204647c09d6aSSong Liu 	}
204747c09d6aSSong Liu }
204847c09d6aSSong Liu 
204947c09d6aSSong Liu static void profile_print_readings_json(void)
205047c09d6aSSong Liu {
205147c09d6aSSong Liu 	__u32 m;
205247c09d6aSSong Liu 
205347c09d6aSSong Liu 	jsonw_start_array(json_wtr);
205447c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
205547c09d6aSSong Liu 		if (!metrics[m].selected)
205647c09d6aSSong Liu 			continue;
205747c09d6aSSong Liu 		jsonw_start_object(json_wtr);
205847c09d6aSSong Liu 		jsonw_string_field(json_wtr, "metric", metrics[m].name);
205947c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "run_cnt", profile_total_count);
206047c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "value", metrics[m].val.counter);
206147c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "enabled", metrics[m].val.enabled);
206247c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "running", metrics[m].val.running);
206347c09d6aSSong Liu 
206447c09d6aSSong Liu 		jsonw_end_object(json_wtr);
206547c09d6aSSong Liu 	}
206647c09d6aSSong Liu 	jsonw_end_array(json_wtr);
206747c09d6aSSong Liu }
206847c09d6aSSong Liu 
206947c09d6aSSong Liu static void profile_print_readings_plain(void)
207047c09d6aSSong Liu {
207147c09d6aSSong Liu 	__u32 m;
207247c09d6aSSong Liu 
207347c09d6aSSong Liu 	printf("\n%18llu %-20s\n", profile_total_count, "run_cnt");
207447c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
207547c09d6aSSong Liu 		struct bpf_perf_event_value *val = &metrics[m].val;
207647c09d6aSSong Liu 		int r;
207747c09d6aSSong Liu 
207847c09d6aSSong Liu 		if (!metrics[m].selected)
207947c09d6aSSong Liu 			continue;
208047c09d6aSSong Liu 		printf("%18llu %-20s", val->counter, metrics[m].name);
208147c09d6aSSong Liu 
208247c09d6aSSong Liu 		r = metrics[m].ratio_metric - 1;
208347c09d6aSSong Liu 		if (r >= 0 && metrics[r].selected &&
208447c09d6aSSong Liu 		    metrics[r].val.counter > 0) {
208547c09d6aSSong Liu 			printf("# %8.2f %-30s",
208647c09d6aSSong Liu 			       val->counter * metrics[m].ratio_mul /
208747c09d6aSSong Liu 			       metrics[r].val.counter,
208847c09d6aSSong Liu 			       metrics[m].ratio_desc);
208947c09d6aSSong Liu 		} else {
209047c09d6aSSong Liu 			printf("%-41s", "");
209147c09d6aSSong Liu 		}
209247c09d6aSSong Liu 
209347c09d6aSSong Liu 		if (val->enabled > val->running)
209447c09d6aSSong Liu 			printf("(%4.2f%%)",
209547c09d6aSSong Liu 			       val->running * 100.0 / val->enabled);
209647c09d6aSSong Liu 		printf("\n");
209747c09d6aSSong Liu 	}
209847c09d6aSSong Liu }
209947c09d6aSSong Liu 
210047c09d6aSSong Liu static void profile_print_readings(void)
210147c09d6aSSong Liu {
210247c09d6aSSong Liu 	if (json_output)
210347c09d6aSSong Liu 		profile_print_readings_json();
210447c09d6aSSong Liu 	else
210547c09d6aSSong Liu 		profile_print_readings_plain();
210647c09d6aSSong Liu }
210747c09d6aSSong Liu 
210847c09d6aSSong Liu static char *profile_target_name(int tgt_fd)
210947c09d6aSSong Liu {
2110c59765cfSDave Marchevsky 	struct bpf_func_info func_info;
2111c59765cfSDave Marchevsky 	struct bpf_prog_info info = {};
2112c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
211347c09d6aSSong Liu 	const struct btf_type *t;
2114c59765cfSDave Marchevsky 	__u32 func_info_rec_size;
2115369e955bSQuentin Monnet 	struct btf *btf = NULL;
211647c09d6aSSong Liu 	char *name = NULL;
2117c59765cfSDave Marchevsky 	int err;
211847c09d6aSSong Liu 
2119c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2120c59765cfSDave Marchevsky 	if (err) {
2121c59765cfSDave Marchevsky 		p_err("failed to bpf_obj_get_info_by_fd for prog FD %d", tgt_fd);
2122c59765cfSDave Marchevsky 		goto out;
212347c09d6aSSong Liu 	}
212447c09d6aSSong Liu 
2125c59765cfSDave Marchevsky 	if (info.btf_id == 0) {
212647c09d6aSSong Liu 		p_err("prog FD %d doesn't have valid btf", tgt_fd);
212747c09d6aSSong Liu 		goto out;
212847c09d6aSSong Liu 	}
212947c09d6aSSong Liu 
2130c59765cfSDave Marchevsky 	func_info_rec_size = info.func_info_rec_size;
2131c59765cfSDave Marchevsky 	if (info.nr_func_info == 0) {
2132c59765cfSDave Marchevsky 		p_err("bpf_obj_get_info_by_fd for prog FD %d found 0 func_info", tgt_fd);
2133c59765cfSDave Marchevsky 		goto out;
2134c59765cfSDave Marchevsky 	}
2135c59765cfSDave Marchevsky 
2136c59765cfSDave Marchevsky 	memset(&info, 0, sizeof(info));
2137c59765cfSDave Marchevsky 	info.nr_func_info = 1;
2138c59765cfSDave Marchevsky 	info.func_info_rec_size = func_info_rec_size;
2139c59765cfSDave Marchevsky 	info.func_info = ptr_to_u64(&func_info);
2140c59765cfSDave Marchevsky 
2141c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2142c59765cfSDave Marchevsky 	if (err) {
2143c59765cfSDave Marchevsky 		p_err("failed to get func_info for prog FD %d", tgt_fd);
2144c59765cfSDave Marchevsky 		goto out;
2145c59765cfSDave Marchevsky 	}
2146c59765cfSDave Marchevsky 
2147c59765cfSDave Marchevsky 	btf = btf__load_from_kernel_by_id(info.btf_id);
214886f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf)) {
214986f4b7f2SQuentin Monnet 		p_err("failed to load btf for prog FD %d", tgt_fd);
215086f4b7f2SQuentin Monnet 		goto out;
215186f4b7f2SQuentin Monnet 	}
215286f4b7f2SQuentin Monnet 
2153c59765cfSDave Marchevsky 	t = btf__type_by_id(btf, func_info.type_id);
215447c09d6aSSong Liu 	if (!t) {
215547c09d6aSSong Liu 		p_err("btf %d doesn't have type %d",
2156c59765cfSDave Marchevsky 		      info.btf_id, func_info.type_id);
215747c09d6aSSong Liu 		goto out;
215847c09d6aSSong Liu 	}
215947c09d6aSSong Liu 	name = strdup(btf__name_by_offset(btf, t->name_off));
216047c09d6aSSong Liu out:
2161369e955bSQuentin Monnet 	btf__free(btf);
216247c09d6aSSong Liu 	return name;
216347c09d6aSSong Liu }
216447c09d6aSSong Liu 
216547c09d6aSSong Liu static struct profiler_bpf *profile_obj;
216647c09d6aSSong Liu static int profile_tgt_fd = -1;
216747c09d6aSSong Liu static char *profile_tgt_name;
216847c09d6aSSong Liu static int *profile_perf_events;
216947c09d6aSSong Liu static int profile_perf_event_cnt;
217047c09d6aSSong Liu 
217147c09d6aSSong Liu static void profile_close_perf_events(struct profiler_bpf *obj)
217247c09d6aSSong Liu {
217347c09d6aSSong Liu 	int i;
217447c09d6aSSong Liu 
217547c09d6aSSong Liu 	for (i = profile_perf_event_cnt - 1; i >= 0; i--)
217647c09d6aSSong Liu 		close(profile_perf_events[i]);
217747c09d6aSSong Liu 
217847c09d6aSSong Liu 	free(profile_perf_events);
217947c09d6aSSong Liu 	profile_perf_event_cnt = 0;
218047c09d6aSSong Liu }
218147c09d6aSSong Liu 
218247c09d6aSSong Liu static int profile_open_perf_events(struct profiler_bpf *obj)
218347c09d6aSSong Liu {
218447c09d6aSSong Liu 	unsigned int cpu, m;
218547c09d6aSSong Liu 	int map_fd, pmu_fd;
218647c09d6aSSong Liu 
218747c09d6aSSong Liu 	profile_perf_events = calloc(
218847c09d6aSSong Liu 		sizeof(int), obj->rodata->num_cpu * obj->rodata->num_metric);
218947c09d6aSSong Liu 	if (!profile_perf_events) {
219047c09d6aSSong Liu 		p_err("failed to allocate memory for perf_event array: %s",
219147c09d6aSSong Liu 		      strerror(errno));
219247c09d6aSSong Liu 		return -1;
219347c09d6aSSong Liu 	}
219447c09d6aSSong Liu 	map_fd = bpf_map__fd(obj->maps.events);
219547c09d6aSSong Liu 	if (map_fd < 0) {
219647c09d6aSSong Liu 		p_err("failed to get fd for events map");
219747c09d6aSSong Liu 		return -1;
219847c09d6aSSong Liu 	}
219947c09d6aSSong Liu 
220047c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
220147c09d6aSSong Liu 		if (!metrics[m].selected)
220247c09d6aSSong Liu 			continue;
220347c09d6aSSong Liu 		for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) {
220447c09d6aSSong Liu 			pmu_fd = syscall(__NR_perf_event_open, &metrics[m].attr,
220547c09d6aSSong Liu 					 -1/*pid*/, cpu, -1/*group_fd*/, 0);
220647c09d6aSSong Liu 			if (pmu_fd < 0 ||
220747c09d6aSSong Liu 			    bpf_map_update_elem(map_fd, &profile_perf_event_cnt,
220847c09d6aSSong Liu 						&pmu_fd, BPF_ANY) ||
220947c09d6aSSong Liu 			    ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
221047c09d6aSSong Liu 				p_err("failed to create event %s on cpu %d",
221147c09d6aSSong Liu 				      metrics[m].name, cpu);
221247c09d6aSSong Liu 				return -1;
221347c09d6aSSong Liu 			}
221447c09d6aSSong Liu 			profile_perf_events[profile_perf_event_cnt++] = pmu_fd;
221547c09d6aSSong Liu 		}
221647c09d6aSSong Liu 	}
221747c09d6aSSong Liu 	return 0;
221847c09d6aSSong Liu }
221947c09d6aSSong Liu 
222047c09d6aSSong Liu static void profile_print_and_cleanup(void)
222147c09d6aSSong Liu {
222247c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
222347c09d6aSSong Liu 	profile_read_values(profile_obj);
222447c09d6aSSong Liu 	profile_print_readings();
222547c09d6aSSong Liu 	profiler_bpf__destroy(profile_obj);
222647c09d6aSSong Liu 
222747c09d6aSSong Liu 	close(profile_tgt_fd);
222847c09d6aSSong Liu 	free(profile_tgt_name);
222947c09d6aSSong Liu }
223047c09d6aSSong Liu 
223147c09d6aSSong Liu static void int_exit(int signo)
223247c09d6aSSong Liu {
223347c09d6aSSong Liu 	profile_print_and_cleanup();
223447c09d6aSSong Liu 	exit(0);
223547c09d6aSSong Liu }
223647c09d6aSSong Liu 
223747c09d6aSSong Liu static int do_profile(int argc, char **argv)
223847c09d6aSSong Liu {
223947c09d6aSSong Liu 	int num_metric, num_cpu, err = -1;
224047c09d6aSSong Liu 	struct bpf_program *prog;
224147c09d6aSSong Liu 	unsigned long duration;
224247c09d6aSSong Liu 	char *endptr;
224347c09d6aSSong Liu 
224447c09d6aSSong Liu 	/* we at least need two args for the prog and one metric */
224547c09d6aSSong Liu 	if (!REQ_ARGS(3))
224647c09d6aSSong Liu 		return -EINVAL;
224747c09d6aSSong Liu 
224847c09d6aSSong Liu 	/* parse target fd */
224947c09d6aSSong Liu 	profile_tgt_fd = prog_parse_fd(&argc, &argv);
225047c09d6aSSong Liu 	if (profile_tgt_fd < 0) {
225147c09d6aSSong Liu 		p_err("failed to parse fd");
225247c09d6aSSong Liu 		return -1;
225347c09d6aSSong Liu 	}
225447c09d6aSSong Liu 
225547c09d6aSSong Liu 	/* parse profiling optional duration */
225647c09d6aSSong Liu 	if (argc > 2 && is_prefix(argv[0], "duration")) {
225747c09d6aSSong Liu 		NEXT_ARG();
225847c09d6aSSong Liu 		duration = strtoul(*argv, &endptr, 0);
225947c09d6aSSong Liu 		if (*endptr)
226047c09d6aSSong Liu 			usage();
226147c09d6aSSong Liu 		NEXT_ARG();
226247c09d6aSSong Liu 	} else {
226347c09d6aSSong Liu 		duration = UINT_MAX;
226447c09d6aSSong Liu 	}
226547c09d6aSSong Liu 
226647c09d6aSSong Liu 	num_metric = profile_parse_metrics(argc, argv);
226747c09d6aSSong Liu 	if (num_metric <= 0)
226847c09d6aSSong Liu 		goto out;
226947c09d6aSSong Liu 
227047c09d6aSSong Liu 	num_cpu = libbpf_num_possible_cpus();
227147c09d6aSSong Liu 	if (num_cpu <= 0) {
227247c09d6aSSong Liu 		p_err("failed to identify number of CPUs");
227347c09d6aSSong Liu 		goto out;
227447c09d6aSSong Liu 	}
227547c09d6aSSong Liu 
227647c09d6aSSong Liu 	profile_obj = profiler_bpf__open();
227747c09d6aSSong Liu 	if (!profile_obj) {
227847c09d6aSSong Liu 		p_err("failed to open and/or load BPF object");
227947c09d6aSSong Liu 		goto out;
228047c09d6aSSong Liu 	}
228147c09d6aSSong Liu 
228247c09d6aSSong Liu 	profile_obj->rodata->num_cpu = num_cpu;
228347c09d6aSSong Liu 	profile_obj->rodata->num_metric = num_metric;
228447c09d6aSSong Liu 
228547c09d6aSSong Liu 	/* adjust map sizes */
228639748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.events, num_metric * num_cpu);
228739748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.fentry_readings, num_metric);
228839748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.accum_readings, num_metric);
228939748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.counts, 1);
229047c09d6aSSong Liu 
229147c09d6aSSong Liu 	/* change target name */
229247c09d6aSSong Liu 	profile_tgt_name = profile_target_name(profile_tgt_fd);
229347c09d6aSSong Liu 	if (!profile_tgt_name)
229447c09d6aSSong Liu 		goto out;
229547c09d6aSSong Liu 
229647c09d6aSSong Liu 	bpf_object__for_each_program(prog, profile_obj->obj) {
229747c09d6aSSong Liu 		err = bpf_program__set_attach_target(prog, profile_tgt_fd,
229847c09d6aSSong Liu 						     profile_tgt_name);
229947c09d6aSSong Liu 		if (err) {
230047c09d6aSSong Liu 			p_err("failed to set attach target\n");
230147c09d6aSSong Liu 			goto out;
230247c09d6aSSong Liu 		}
230347c09d6aSSong Liu 	}
230447c09d6aSSong Liu 
230547c09d6aSSong Liu 	set_max_rlimit();
230647c09d6aSSong Liu 	err = profiler_bpf__load(profile_obj);
230747c09d6aSSong Liu 	if (err) {
230847c09d6aSSong Liu 		p_err("failed to load profile_obj");
230947c09d6aSSong Liu 		goto out;
231047c09d6aSSong Liu 	}
231147c09d6aSSong Liu 
231247c09d6aSSong Liu 	err = profile_open_perf_events(profile_obj);
231347c09d6aSSong Liu 	if (err)
231447c09d6aSSong Liu 		goto out;
231547c09d6aSSong Liu 
231647c09d6aSSong Liu 	err = profiler_bpf__attach(profile_obj);
231747c09d6aSSong Liu 	if (err) {
231847c09d6aSSong Liu 		p_err("failed to attach profile_obj");
231947c09d6aSSong Liu 		goto out;
232047c09d6aSSong Liu 	}
232147c09d6aSSong Liu 	signal(SIGINT, int_exit);
232247c09d6aSSong Liu 
232347c09d6aSSong Liu 	sleep(duration);
232447c09d6aSSong Liu 	profile_print_and_cleanup();
232547c09d6aSSong Liu 	return 0;
232647c09d6aSSong Liu 
232747c09d6aSSong Liu out:
232847c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
232947c09d6aSSong Liu 	if (profile_obj)
233047c09d6aSSong Liu 		profiler_bpf__destroy(profile_obj);
233147c09d6aSSong Liu 	close(profile_tgt_fd);
233247c09d6aSSong Liu 	free(profile_tgt_name);
233347c09d6aSSong Liu 	return err;
233447c09d6aSSong Liu }
233547c09d6aSSong Liu 
233647c09d6aSSong Liu #endif /* BPFTOOL_WITHOUT_SKELETONS */
233747c09d6aSSong Liu 
233871bb428fSJakub Kicinski static int do_help(int argc, char **argv)
233971bb428fSJakub Kicinski {
2340004b45c0SQuentin Monnet 	if (json_output) {
2341004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
2342004b45c0SQuentin Monnet 		return 0;
2343004b45c0SQuentin Monnet 	}
2344004b45c0SQuentin Monnet 
234571bb428fSJakub Kicinski 	fprintf(stderr,
234690040351SQuentin Monnet 		"Usage: %1$s %2$s { show | list } [PROG]\n"
234790040351SQuentin Monnet 		"       %1$s %2$s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n"
234890040351SQuentin Monnet 		"       %1$s %2$s dump jited  PROG [{ file FILE | opcodes | linum }]\n"
234990040351SQuentin Monnet 		"       %1$s %2$s pin   PROG FILE\n"
235090040351SQuentin Monnet 		"       %1$s %2$s { load | loadall } OBJ  PATH \\\n"
235177380998SStanislav Fomichev 		"                         [type TYPE] [dev NAME] \\\n"
23523767a94bSStanislav Fomichev 		"                         [map { idx IDX | name NAME } MAP]\\\n"
23533767a94bSStanislav Fomichev 		"                         [pinmaps MAP_DIR]\n"
235490040351SQuentin Monnet 		"       %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n"
235590040351SQuentin Monnet 		"       %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n"
235690040351SQuentin Monnet 		"       %1$s %2$s run PROG \\\n"
2357ba95c745SQuentin Monnet 		"                         data_in FILE \\\n"
2358ba95c745SQuentin Monnet 		"                         [data_out FILE [data_size_out L]] \\\n"
2359ba95c745SQuentin Monnet 		"                         [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
2360ba95c745SQuentin Monnet 		"                         [repeat N]\n"
236190040351SQuentin Monnet 		"       %1$s %2$s profile PROG [duration DURATION] METRICs\n"
236290040351SQuentin Monnet 		"       %1$s %2$s tracelog\n"
236390040351SQuentin Monnet 		"       %1$s %2$s help\n"
236471bb428fSJakub Kicinski 		"\n"
23653ff5a4dcSJakub Kicinski 		"       " HELP_SPEC_MAP "\n"
236671bb428fSJakub Kicinski 		"       " HELP_SPEC_PROGRAM "\n"
236749f2cba3SJakub Kicinski 		"       TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
236849f2cba3SJakub Kicinski 		"                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
236949f2cba3SJakub Kicinski 		"                 cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
237049f2cba3SJakub Kicinski 		"                 lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
2371f25377eeSAndrey Ignatov 		"                 sk_reuseport | flow_dissector | cgroup/sysctl |\n"
237249f2cba3SJakub Kicinski 		"                 cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
237349f2cba3SJakub Kicinski 		"                 cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
237405ee19c1SDaniel Borkmann 		"                 cgroup/getpeername4 | cgroup/getpeername6 |\n"
237505ee19c1SDaniel Borkmann 		"                 cgroup/getsockname4 | cgroup/getsockname6 | cgroup/sendmsg4 |\n"
237605ee19c1SDaniel Borkmann 		"                 cgroup/sendmsg6 | cgroup/recvmsg4 | cgroup/recvmsg6 |\n"
2377a8deba85SLiu Jian 		"                 cgroup/getsockopt | cgroup/setsockopt | cgroup/sock_release |\n"
237893a3545dSJakub Sitnicki 		"                 struct_ops | fentry | fexit | freplace | sk_lookup }\n"
2379b544342eSQuentin Monnet 		"       ATTACH_TYPE := { msg_verdict | skb_verdict | stream_verdict |\n"
2380b544342eSQuentin Monnet 		"                        stream_parser | flow_dissector }\n"
2381450d060eSYonghong Song 		"       METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
2382c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " |\n"
23838cc8c635SQuentin Monnet 		"                    {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
23848cc8c635SQuentin Monnet 		"                    {-L|--use-loader} }\n"
238571bb428fSJakub Kicinski 		"",
238690040351SQuentin Monnet 		bin_name, argv[-2]);
238771bb428fSJakub Kicinski 
238871bb428fSJakub Kicinski 	return 0;
238971bb428fSJakub Kicinski }
239071bb428fSJakub Kicinski 
239171bb428fSJakub Kicinski static const struct cmd cmds[] = {
239271bb428fSJakub Kicinski 	{ "show",	do_show },
23936ebe6dbdSJakub Kicinski 	{ "list",	do_show },
23949f606179SQuentin Monnet 	{ "help",	do_help },
239571bb428fSJakub Kicinski 	{ "dump",	do_dump },
239671bb428fSJakub Kicinski 	{ "pin",	do_pin },
239749a086c2SRoman Gushchin 	{ "load",	do_load },
239877380998SStanislav Fomichev 	{ "loadall",	do_loadall },
2399b7d3826cSJohn Fastabend 	{ "attach",	do_attach },
2400b7d3826cSJohn Fastabend 	{ "detach",	do_detach },
240130da46b5SQuentin Monnet 	{ "tracelog",	do_tracelog },
2402ba95c745SQuentin Monnet 	{ "run",	do_run },
240347c09d6aSSong Liu 	{ "profile",	do_profile },
240471bb428fSJakub Kicinski 	{ 0 }
240571bb428fSJakub Kicinski };
240671bb428fSJakub Kicinski 
240771bb428fSJakub Kicinski int do_prog(int argc, char **argv)
240871bb428fSJakub Kicinski {
240971bb428fSJakub Kicinski 	return cmd_select(cmds, argc, argv, do_help);
241071bb428fSJakub Kicinski }
2411