xref: /openbmc/linux/tools/bpf/bpftool/prog.c (revision b59e4ce8bcaab6445f4a0d37a96ca8953caaf5cf)
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>
29d510296dSAlexei Starovoitov #include <bpf/skel_internal.h>
3071bb428fSJakub Kicinski 
31b6c1cedbSJiong Wang #include "cfg.h"
3271bb428fSJakub Kicinski #include "main.h"
3373bb5b4fSJiong Wang #include "xlated_dumper.h"
3471bb428fSJakub Kicinski 
35aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX "bpf_metadata_"
36aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX_LEN (sizeof(BPF_METADATA_PREFIX) - 1)
37aff52e68SYiFei Zhu 
389023497dSTobias Klauser const char * const prog_type_name[] = {
399023497dSTobias Klauser 	[BPF_PROG_TYPE_UNSPEC]			= "unspec",
409023497dSTobias Klauser 	[BPF_PROG_TYPE_SOCKET_FILTER]		= "socket_filter",
419023497dSTobias Klauser 	[BPF_PROG_TYPE_KPROBE]			= "kprobe",
429023497dSTobias Klauser 	[BPF_PROG_TYPE_SCHED_CLS]		= "sched_cls",
439023497dSTobias Klauser 	[BPF_PROG_TYPE_SCHED_ACT]		= "sched_act",
449023497dSTobias Klauser 	[BPF_PROG_TYPE_TRACEPOINT]		= "tracepoint",
459023497dSTobias Klauser 	[BPF_PROG_TYPE_XDP]			= "xdp",
469023497dSTobias Klauser 	[BPF_PROG_TYPE_PERF_EVENT]		= "perf_event",
479023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SKB]		= "cgroup_skb",
489023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SOCK]		= "cgroup_sock",
499023497dSTobias Klauser 	[BPF_PROG_TYPE_LWT_IN]			= "lwt_in",
509023497dSTobias Klauser 	[BPF_PROG_TYPE_LWT_OUT]			= "lwt_out",
519023497dSTobias Klauser 	[BPF_PROG_TYPE_LWT_XMIT]		= "lwt_xmit",
529023497dSTobias Klauser 	[BPF_PROG_TYPE_SOCK_OPS]		= "sock_ops",
539023497dSTobias Klauser 	[BPF_PROG_TYPE_SK_SKB]			= "sk_skb",
549023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_DEVICE]		= "cgroup_device",
559023497dSTobias Klauser 	[BPF_PROG_TYPE_SK_MSG]			= "sk_msg",
569023497dSTobias Klauser 	[BPF_PROG_TYPE_RAW_TRACEPOINT]		= "raw_tracepoint",
579023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SOCK_ADDR]	= "cgroup_sock_addr",
589023497dSTobias Klauser 	[BPF_PROG_TYPE_LWT_SEG6LOCAL]		= "lwt_seg6local",
599023497dSTobias Klauser 	[BPF_PROG_TYPE_LIRC_MODE2]		= "lirc_mode2",
609023497dSTobias Klauser 	[BPF_PROG_TYPE_SK_REUSEPORT]		= "sk_reuseport",
619023497dSTobias Klauser 	[BPF_PROG_TYPE_FLOW_DISSECTOR]		= "flow_dissector",
629023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SYSCTL]		= "cgroup_sysctl",
639023497dSTobias Klauser 	[BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE]	= "raw_tracepoint_writable",
649023497dSTobias Klauser 	[BPF_PROG_TYPE_CGROUP_SOCKOPT]		= "cgroup_sockopt",
659023497dSTobias Klauser 	[BPF_PROG_TYPE_TRACING]			= "tracing",
669023497dSTobias Klauser 	[BPF_PROG_TYPE_STRUCT_OPS]		= "struct_ops",
679023497dSTobias Klauser 	[BPF_PROG_TYPE_EXT]			= "ext",
689a97c9d2SQuentin Monnet 	[BPF_PROG_TYPE_LSM]			= "lsm",
6993a3545dSJakub Sitnicki 	[BPF_PROG_TYPE_SK_LOOKUP]		= "sk_lookup",
709023497dSTobias Klauser };
719023497dSTobias Klauser 
729023497dSTobias Klauser const size_t prog_type_name_size = ARRAY_SIZE(prog_type_name);
739023497dSTobias Klauser 
74ec202509SPaul Chaignon enum dump_mode {
75ec202509SPaul Chaignon 	DUMP_JITED,
76ec202509SPaul Chaignon 	DUMP_XLATED,
77ec202509SPaul Chaignon };
78ec202509SPaul Chaignon 
79b7d3826cSJohn Fastabend static const char * const attach_type_strings[] = {
80b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_PARSER] = "stream_parser",
81b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict",
82a7ba4558SCong Wang 	[BPF_SK_SKB_VERDICT] = "skb_verdict",
83b7d3826cSJohn Fastabend 	[BPF_SK_MSG_VERDICT] = "msg_verdict",
84092f0892SStanislav Fomichev 	[BPF_FLOW_DISSECTOR] = "flow_dissector",
85b7d3826cSJohn Fastabend 	[__MAX_BPF_ATTACH_TYPE] = NULL,
86b7d3826cSJohn Fastabend };
87b7d3826cSJohn Fastabend 
888f184732SQuentin Monnet static struct hashmap *prog_table;
8946241271SQuentin Monnet 
90c101189bSQuentin Monnet static enum bpf_attach_type parse_attach_type(const char *str)
91b7d3826cSJohn Fastabend {
92b7d3826cSJohn Fastabend 	enum bpf_attach_type type;
93b7d3826cSJohn Fastabend 
94b7d3826cSJohn Fastabend 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
95b7d3826cSJohn Fastabend 		if (attach_type_strings[type] &&
96b7d3826cSJohn Fastabend 		    is_prefix(str, attach_type_strings[type]))
97b7d3826cSJohn Fastabend 			return type;
98b7d3826cSJohn Fastabend 	}
99b7d3826cSJohn Fastabend 
100b7d3826cSJohn Fastabend 	return __MAX_BPF_ATTACH_TYPE;
101b7d3826cSJohn Fastabend }
102b7d3826cSJohn Fastabend 
103c59765cfSDave Marchevsky static int prep_prog_info(struct bpf_prog_info *const info, enum dump_mode mode,
104c59765cfSDave Marchevsky 			  void **info_data, size_t *const info_data_sz)
105c59765cfSDave Marchevsky {
106c59765cfSDave Marchevsky 	struct bpf_prog_info holder = {};
107c59765cfSDave Marchevsky 	size_t needed = 0;
108c59765cfSDave Marchevsky 	void *ptr;
109c59765cfSDave Marchevsky 
110c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
111c59765cfSDave Marchevsky 		holder.jited_prog_len = info->jited_prog_len;
112c59765cfSDave Marchevsky 		needed += info->jited_prog_len;
113c59765cfSDave Marchevsky 	} else {
114c59765cfSDave Marchevsky 		holder.xlated_prog_len = info->xlated_prog_len;
115c59765cfSDave Marchevsky 		needed += info->xlated_prog_len;
116c59765cfSDave Marchevsky 	}
117c59765cfSDave Marchevsky 
118c59765cfSDave Marchevsky 	holder.nr_jited_ksyms = info->nr_jited_ksyms;
119c59765cfSDave Marchevsky 	needed += info->nr_jited_ksyms * sizeof(__u64);
120c59765cfSDave Marchevsky 
121c59765cfSDave Marchevsky 	holder.nr_jited_func_lens = info->nr_jited_func_lens;
122c59765cfSDave Marchevsky 	needed += info->nr_jited_func_lens * sizeof(__u32);
123c59765cfSDave Marchevsky 
124c59765cfSDave Marchevsky 	holder.nr_func_info = info->nr_func_info;
125c59765cfSDave Marchevsky 	holder.func_info_rec_size = info->func_info_rec_size;
126c59765cfSDave Marchevsky 	needed += info->nr_func_info * info->func_info_rec_size;
127c59765cfSDave Marchevsky 
128c59765cfSDave Marchevsky 	holder.nr_line_info = info->nr_line_info;
129c59765cfSDave Marchevsky 	holder.line_info_rec_size = info->line_info_rec_size;
130c59765cfSDave Marchevsky 	needed += info->nr_line_info * info->line_info_rec_size;
131c59765cfSDave Marchevsky 
132c59765cfSDave Marchevsky 	holder.nr_jited_line_info = info->nr_jited_line_info;
133c59765cfSDave Marchevsky 	holder.jited_line_info_rec_size = info->jited_line_info_rec_size;
134c59765cfSDave Marchevsky 	needed += info->nr_jited_line_info * info->jited_line_info_rec_size;
135c59765cfSDave Marchevsky 
136c59765cfSDave Marchevsky 	if (needed > *info_data_sz) {
137c59765cfSDave Marchevsky 		ptr = realloc(*info_data, needed);
138c59765cfSDave Marchevsky 		if (!ptr)
139c59765cfSDave Marchevsky 			return -1;
140c59765cfSDave Marchevsky 
141c59765cfSDave Marchevsky 		*info_data = ptr;
142c59765cfSDave Marchevsky 		*info_data_sz = needed;
143c59765cfSDave Marchevsky 	}
144c59765cfSDave Marchevsky 	ptr = *info_data;
145c59765cfSDave Marchevsky 
146c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
147c59765cfSDave Marchevsky 		holder.jited_prog_insns = ptr_to_u64(ptr);
148c59765cfSDave Marchevsky 		ptr += holder.jited_prog_len;
149c59765cfSDave Marchevsky 	} else {
150c59765cfSDave Marchevsky 		holder.xlated_prog_insns = ptr_to_u64(ptr);
151c59765cfSDave Marchevsky 		ptr += holder.xlated_prog_len;
152c59765cfSDave Marchevsky 	}
153c59765cfSDave Marchevsky 
154c59765cfSDave Marchevsky 	holder.jited_ksyms = ptr_to_u64(ptr);
155c59765cfSDave Marchevsky 	ptr += holder.nr_jited_ksyms * sizeof(__u64);
156c59765cfSDave Marchevsky 
157c59765cfSDave Marchevsky 	holder.jited_func_lens = ptr_to_u64(ptr);
158c59765cfSDave Marchevsky 	ptr += holder.nr_jited_func_lens * sizeof(__u32);
159c59765cfSDave Marchevsky 
160c59765cfSDave Marchevsky 	holder.func_info = ptr_to_u64(ptr);
161c59765cfSDave Marchevsky 	ptr += holder.nr_func_info * holder.func_info_rec_size;
162c59765cfSDave Marchevsky 
163c59765cfSDave Marchevsky 	holder.line_info = ptr_to_u64(ptr);
164c59765cfSDave Marchevsky 	ptr += holder.nr_line_info * holder.line_info_rec_size;
165c59765cfSDave Marchevsky 
166c59765cfSDave Marchevsky 	holder.jited_line_info = ptr_to_u64(ptr);
167c59765cfSDave Marchevsky 	ptr += holder.nr_jited_line_info * holder.jited_line_info_rec_size;
168c59765cfSDave Marchevsky 
169c59765cfSDave Marchevsky 	*info = holder;
170c59765cfSDave Marchevsky 	return 0;
171c59765cfSDave Marchevsky }
172c59765cfSDave Marchevsky 
17371bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
17471bb428fSJakub Kicinski {
17571bb428fSJakub Kicinski 	struct timespec real_time_ts, boot_time_ts;
17671bb428fSJakub Kicinski 	time_t wallclock_secs;
17771bb428fSJakub Kicinski 	struct tm load_tm;
17871bb428fSJakub Kicinski 
17971bb428fSJakub Kicinski 	buf[--size] = '\0';
18071bb428fSJakub Kicinski 
18171bb428fSJakub Kicinski 	if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
18271bb428fSJakub Kicinski 	    clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
18371bb428fSJakub Kicinski 		perror("Can't read clocks");
18471bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
18571bb428fSJakub Kicinski 		return;
18671bb428fSJakub Kicinski 	}
18771bb428fSJakub Kicinski 
18871bb428fSJakub Kicinski 	wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
18907480cbcSJakub Kicinski 		(real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) /
19007480cbcSJakub Kicinski 		1000000000;
19107480cbcSJakub Kicinski 
19271bb428fSJakub Kicinski 
19371bb428fSJakub Kicinski 	if (!localtime_r(&wallclock_secs, &load_tm)) {
19471bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
19571bb428fSJakub Kicinski 		return;
19671bb428fSJakub Kicinski 	}
19771bb428fSJakub Kicinski 
198a3fe1f6fSQuentin Monnet 	if (json_output)
199a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%s", &load_tm);
200a3fe1f6fSQuentin Monnet 	else
201a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%FT%T%z", &load_tm);
20271bb428fSJakub Kicinski }
20371bb428fSJakub Kicinski 
2046e7e034eSQuentin Monnet static void show_prog_maps(int fd, __u32 num_maps)
20571bb428fSJakub Kicinski {
20671bb428fSJakub Kicinski 	struct bpf_prog_info info = {};
20771bb428fSJakub Kicinski 	__u32 len = sizeof(info);
20871bb428fSJakub Kicinski 	__u32 map_ids[num_maps];
20971bb428fSJakub Kicinski 	unsigned int i;
21071bb428fSJakub Kicinski 	int err;
21171bb428fSJakub Kicinski 
21271bb428fSJakub Kicinski 	info.nr_map_ids = num_maps;
21371bb428fSJakub Kicinski 	info.map_ids = ptr_to_u64(map_ids);
21471bb428fSJakub Kicinski 
21571bb428fSJakub Kicinski 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
21671bb428fSJakub Kicinski 	if (err || !info.nr_map_ids)
21771bb428fSJakub Kicinski 		return;
21871bb428fSJakub Kicinski 
219743cc665SQuentin Monnet 	if (json_output) {
220743cc665SQuentin Monnet 		jsonw_name(json_wtr, "map_ids");
221743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
222743cc665SQuentin Monnet 		for (i = 0; i < info.nr_map_ids; i++)
223743cc665SQuentin Monnet 			jsonw_uint(json_wtr, map_ids[i]);
224743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
225743cc665SQuentin Monnet 	} else {
22671bb428fSJakub Kicinski 		printf("  map_ids ");
22771bb428fSJakub Kicinski 		for (i = 0; i < info.nr_map_ids; i++)
22871bb428fSJakub Kicinski 			printf("%u%s", map_ids[i],
22971bb428fSJakub Kicinski 			       i == info.nr_map_ids - 1 ? "" : ",");
23071bb428fSJakub Kicinski 	}
23171bb428fSJakub Kicinski }
23271bb428fSJakub Kicinski 
233aff52e68SYiFei Zhu static void *find_metadata(int prog_fd, struct bpf_map_info *map_info)
234aff52e68SYiFei Zhu {
235aff52e68SYiFei Zhu 	struct bpf_prog_info prog_info;
236aff52e68SYiFei Zhu 	__u32 prog_info_len;
237aff52e68SYiFei Zhu 	__u32 map_info_len;
238aff52e68SYiFei Zhu 	void *value = NULL;
239aff52e68SYiFei Zhu 	__u32 *map_ids;
240aff52e68SYiFei Zhu 	int nr_maps;
241aff52e68SYiFei Zhu 	int key = 0;
242aff52e68SYiFei Zhu 	int map_fd;
243aff52e68SYiFei Zhu 	int ret;
244aff52e68SYiFei Zhu 	__u32 i;
245aff52e68SYiFei Zhu 
246aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
247aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
248aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
249aff52e68SYiFei Zhu 	if (ret)
250aff52e68SYiFei Zhu 		return NULL;
251aff52e68SYiFei Zhu 
252aff52e68SYiFei Zhu 	if (!prog_info.nr_map_ids)
253aff52e68SYiFei Zhu 		return NULL;
254aff52e68SYiFei Zhu 
255aff52e68SYiFei Zhu 	map_ids = calloc(prog_info.nr_map_ids, sizeof(__u32));
256aff52e68SYiFei Zhu 	if (!map_ids)
257aff52e68SYiFei Zhu 		return NULL;
258aff52e68SYiFei Zhu 
259aff52e68SYiFei Zhu 	nr_maps = prog_info.nr_map_ids;
260aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
261aff52e68SYiFei Zhu 	prog_info.nr_map_ids = nr_maps;
262aff52e68SYiFei Zhu 	prog_info.map_ids = ptr_to_u64(map_ids);
263aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
264aff52e68SYiFei Zhu 
265aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
266aff52e68SYiFei Zhu 	if (ret)
267aff52e68SYiFei Zhu 		goto free_map_ids;
268aff52e68SYiFei Zhu 
269aff52e68SYiFei Zhu 	for (i = 0; i < prog_info.nr_map_ids; i++) {
270aff52e68SYiFei Zhu 		map_fd = bpf_map_get_fd_by_id(map_ids[i]);
271aff52e68SYiFei Zhu 		if (map_fd < 0)
272aff52e68SYiFei Zhu 			goto free_map_ids;
273aff52e68SYiFei Zhu 
274aff52e68SYiFei Zhu 		memset(map_info, 0, sizeof(*map_info));
275aff52e68SYiFei Zhu 		map_info_len = sizeof(*map_info);
276aff52e68SYiFei Zhu 		ret = bpf_obj_get_info_by_fd(map_fd, map_info, &map_info_len);
277aff52e68SYiFei Zhu 		if (ret < 0) {
278aff52e68SYiFei Zhu 			close(map_fd);
279aff52e68SYiFei Zhu 			goto free_map_ids;
280aff52e68SYiFei Zhu 		}
281aff52e68SYiFei Zhu 
282aff52e68SYiFei Zhu 		if (map_info->type != BPF_MAP_TYPE_ARRAY ||
283aff52e68SYiFei Zhu 		    map_info->key_size != sizeof(int) ||
284aff52e68SYiFei Zhu 		    map_info->max_entries != 1 ||
285aff52e68SYiFei Zhu 		    !map_info->btf_value_type_id ||
286aff52e68SYiFei Zhu 		    !strstr(map_info->name, ".rodata")) {
287aff52e68SYiFei Zhu 			close(map_fd);
288aff52e68SYiFei Zhu 			continue;
289aff52e68SYiFei Zhu 		}
290aff52e68SYiFei Zhu 
291aff52e68SYiFei Zhu 		value = malloc(map_info->value_size);
292aff52e68SYiFei Zhu 		if (!value) {
293aff52e68SYiFei Zhu 			close(map_fd);
294aff52e68SYiFei Zhu 			goto free_map_ids;
295aff52e68SYiFei Zhu 		}
296aff52e68SYiFei Zhu 
297aff52e68SYiFei Zhu 		if (bpf_map_lookup_elem(map_fd, &key, value)) {
298aff52e68SYiFei Zhu 			close(map_fd);
299aff52e68SYiFei Zhu 			free(value);
300aff52e68SYiFei Zhu 			value = NULL;
301aff52e68SYiFei Zhu 			goto free_map_ids;
302aff52e68SYiFei Zhu 		}
303aff52e68SYiFei Zhu 
304aff52e68SYiFei Zhu 		close(map_fd);
305aff52e68SYiFei Zhu 		break;
306aff52e68SYiFei Zhu 	}
307aff52e68SYiFei Zhu 
308aff52e68SYiFei Zhu free_map_ids:
309aff52e68SYiFei Zhu 	free(map_ids);
310aff52e68SYiFei Zhu 	return value;
311aff52e68SYiFei Zhu }
312aff52e68SYiFei Zhu 
313aff52e68SYiFei Zhu static bool has_metadata_prefix(const char *s)
314aff52e68SYiFei Zhu {
315aff52e68SYiFei Zhu 	return strncmp(s, BPF_METADATA_PREFIX, BPF_METADATA_PREFIX_LEN) == 0;
316aff52e68SYiFei Zhu }
317aff52e68SYiFei Zhu 
318aff52e68SYiFei Zhu static void show_prog_metadata(int fd, __u32 num_maps)
319aff52e68SYiFei Zhu {
320aff52e68SYiFei Zhu 	const struct btf_type *t_datasec, *t_var;
321aff52e68SYiFei Zhu 	struct bpf_map_info map_info;
322aff52e68SYiFei Zhu 	struct btf_var_secinfo *vsi;
323aff52e68SYiFei Zhu 	bool printed_header = false;
324aff52e68SYiFei Zhu 	unsigned int i, vlen;
325aff52e68SYiFei Zhu 	void *value = NULL;
326aff52e68SYiFei Zhu 	const char *name;
32786f4b7f2SQuentin Monnet 	struct btf *btf;
328aff52e68SYiFei Zhu 	int err;
329aff52e68SYiFei Zhu 
330aff52e68SYiFei Zhu 	if (!num_maps)
331aff52e68SYiFei Zhu 		return;
332aff52e68SYiFei Zhu 
333aff52e68SYiFei Zhu 	memset(&map_info, 0, sizeof(map_info));
334aff52e68SYiFei Zhu 	value = find_metadata(fd, &map_info);
335aff52e68SYiFei Zhu 	if (!value)
336aff52e68SYiFei Zhu 		return;
337aff52e68SYiFei Zhu 
33886f4b7f2SQuentin Monnet 	btf = btf__load_from_kernel_by_id(map_info.btf_id);
33986f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf))
340aff52e68SYiFei Zhu 		goto out_free;
341aff52e68SYiFei Zhu 
342aff52e68SYiFei Zhu 	t_datasec = btf__type_by_id(btf, map_info.btf_value_type_id);
343aff52e68SYiFei Zhu 	if (!btf_is_datasec(t_datasec))
344aff52e68SYiFei Zhu 		goto out_free;
345aff52e68SYiFei Zhu 
346aff52e68SYiFei Zhu 	vlen = btf_vlen(t_datasec);
347aff52e68SYiFei Zhu 	vsi = btf_var_secinfos(t_datasec);
348aff52e68SYiFei Zhu 
349aff52e68SYiFei Zhu 	/* We don't proceed to check the kinds of the elements of the DATASEC.
350aff52e68SYiFei Zhu 	 * The verifier enforces them to be BTF_KIND_VAR.
351aff52e68SYiFei Zhu 	 */
352aff52e68SYiFei Zhu 
353aff52e68SYiFei Zhu 	if (json_output) {
354aff52e68SYiFei Zhu 		struct btf_dumper d = {
355aff52e68SYiFei Zhu 			.btf = btf,
356aff52e68SYiFei Zhu 			.jw = json_wtr,
357aff52e68SYiFei Zhu 			.is_plain_text = false,
358aff52e68SYiFei Zhu 		};
359aff52e68SYiFei Zhu 
360aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
361aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
362aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
363aff52e68SYiFei Zhu 
364aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
365aff52e68SYiFei Zhu 				continue;
366aff52e68SYiFei Zhu 
367aff52e68SYiFei Zhu 			if (!printed_header) {
368aff52e68SYiFei Zhu 				jsonw_name(json_wtr, "metadata");
369aff52e68SYiFei Zhu 				jsonw_start_object(json_wtr);
370aff52e68SYiFei Zhu 				printed_header = true;
371aff52e68SYiFei Zhu 			}
372aff52e68SYiFei Zhu 
373aff52e68SYiFei Zhu 			jsonw_name(json_wtr, name + BPF_METADATA_PREFIX_LEN);
374aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
375aff52e68SYiFei Zhu 			if (err) {
376aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
377aff52e68SYiFei Zhu 				break;
378aff52e68SYiFei Zhu 			}
379aff52e68SYiFei Zhu 		}
380aff52e68SYiFei Zhu 		if (printed_header)
381aff52e68SYiFei Zhu 			jsonw_end_object(json_wtr);
382aff52e68SYiFei Zhu 	} else {
383e89ef634SQuentin Monnet 		json_writer_t *btf_wtr;
384aff52e68SYiFei Zhu 		struct btf_dumper d = {
385aff52e68SYiFei Zhu 			.btf = btf,
386aff52e68SYiFei Zhu 			.is_plain_text = true,
387aff52e68SYiFei Zhu 		};
388aff52e68SYiFei Zhu 
389aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
390aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
391aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
392aff52e68SYiFei Zhu 
393aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
394aff52e68SYiFei Zhu 				continue;
395aff52e68SYiFei Zhu 
396aff52e68SYiFei Zhu 			if (!printed_header) {
397aff52e68SYiFei Zhu 				printf("\tmetadata:");
398e89ef634SQuentin Monnet 
399e89ef634SQuentin Monnet 				btf_wtr = jsonw_new(stdout);
400e89ef634SQuentin Monnet 				if (!btf_wtr) {
401e89ef634SQuentin Monnet 					p_err("jsonw alloc failed");
402e89ef634SQuentin Monnet 					goto out_free;
403e89ef634SQuentin Monnet 				}
404e89ef634SQuentin Monnet 				d.jw = btf_wtr,
405e89ef634SQuentin Monnet 
406aff52e68SYiFei Zhu 				printed_header = true;
407aff52e68SYiFei Zhu 			}
408aff52e68SYiFei Zhu 
409aff52e68SYiFei Zhu 			printf("\n\t\t%s = ", name + BPF_METADATA_PREFIX_LEN);
410aff52e68SYiFei Zhu 
411aff52e68SYiFei Zhu 			jsonw_reset(btf_wtr);
412aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
413aff52e68SYiFei Zhu 			if (err) {
414aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
415aff52e68SYiFei Zhu 				break;
416aff52e68SYiFei Zhu 			}
417aff52e68SYiFei Zhu 		}
418aff52e68SYiFei Zhu 		if (printed_header)
419aff52e68SYiFei Zhu 			jsonw_destroy(&btf_wtr);
420aff52e68SYiFei Zhu 	}
421aff52e68SYiFei Zhu 
422aff52e68SYiFei Zhu out_free:
423aff52e68SYiFei Zhu 	btf__free(btf);
424aff52e68SYiFei Zhu 	free(value);
425aff52e68SYiFei Zhu }
426aff52e68SYiFei Zhu 
427ec202509SPaul Chaignon static void print_prog_header_json(struct bpf_prog_info *info)
428743cc665SQuentin Monnet {
429743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "id", info->id);
430743cc665SQuentin Monnet 	if (info->type < ARRAY_SIZE(prog_type_name))
431743cc665SQuentin Monnet 		jsonw_string_field(json_wtr, "type",
432743cc665SQuentin Monnet 				   prog_type_name[info->type]);
43371bb428fSJakub Kicinski 	else
434743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "type", info->type);
43571bb428fSJakub Kicinski 
436743cc665SQuentin Monnet 	if (*info->name)
437743cc665SQuentin Monnet 		jsonw_string_field(json_wtr, "name", info->name);
43871bb428fSJakub Kicinski 
439743cc665SQuentin Monnet 	jsonw_name(json_wtr, "tag");
440743cc665SQuentin Monnet 	jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
441743cc665SQuentin Monnet 		     info->tag[0], info->tag[1], info->tag[2], info->tag[3],
442743cc665SQuentin Monnet 		     info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
44371bb428fSJakub Kicinski 
4449b984a20SJiri Olsa 	jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible);
44588ad472bSAlexei Starovoitov 	if (info->run_time_ns) {
44688ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_time_ns", info->run_time_ns);
44788ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_cnt", info->run_cnt);
44888ad472bSAlexei Starovoitov 	}
4499ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
4509ed9e9baSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "recursion_misses", info->recursion_misses);
451ec202509SPaul Chaignon }
4529b984a20SJiri Olsa 
453ec202509SPaul Chaignon static void print_prog_json(struct bpf_prog_info *info, int fd)
454ec202509SPaul Chaignon {
455ec202509SPaul Chaignon 	char *memlock;
456ec202509SPaul Chaignon 
457ec202509SPaul Chaignon 	jsonw_start_object(json_wtr);
458ec202509SPaul Chaignon 	print_prog_header_json(info);
45952262210SJakub Kicinski 	print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
46052262210SJakub Kicinski 
461743cc665SQuentin Monnet 	if (info->load_time) {
46271bb428fSJakub Kicinski 		char buf[32];
46371bb428fSJakub Kicinski 
464743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
46571bb428fSJakub Kicinski 
46671bb428fSJakub Kicinski 		/* Piggy back on load_time, since 0 uid is a valid one */
467a3fe1f6fSQuentin Monnet 		jsonw_name(json_wtr, "loaded_at");
468a3fe1f6fSQuentin Monnet 		jsonw_printf(json_wtr, "%s", buf);
469743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
47071bb428fSJakub Kicinski 	}
47171bb428fSJakub Kicinski 
472743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
47371bb428fSJakub Kicinski 
474743cc665SQuentin Monnet 	if (info->jited_prog_len) {
475743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", true);
476743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
477743cc665SQuentin Monnet 	} else {
478743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", false);
479743cc665SQuentin Monnet 	}
480743cc665SQuentin Monnet 
481743cc665SQuentin Monnet 	memlock = get_fdinfo(fd, "memlock");
482743cc665SQuentin Monnet 	if (memlock)
483743cc665SQuentin Monnet 		jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
484743cc665SQuentin Monnet 	free(memlock);
485743cc665SQuentin Monnet 
486743cc665SQuentin Monnet 	if (info->nr_map_ids)
487743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
488743cc665SQuentin Monnet 
489569b0c77SPrashant Bhole 	if (info->btf_id)
490569b0c77SPrashant Bhole 		jsonw_int_field(json_wtr, "btf_id", info->btf_id);
491569b0c77SPrashant Bhole 
4928f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
4938f184732SQuentin Monnet 		struct hashmap_entry *entry;
4944990f1f4SPrashant Bhole 
4954990f1f4SPrashant Bhole 		jsonw_name(json_wtr, "pinned");
4964990f1f4SPrashant Bhole 		jsonw_start_array(json_wtr);
4978f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
4988f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
4998f184732SQuentin Monnet 			jsonw_string(json_wtr, entry->value);
5004990f1f4SPrashant Bhole 		jsonw_end_array(json_wtr);
5014990f1f4SPrashant Bhole 	}
5024990f1f4SPrashant Bhole 
503d6699f8eSQuentin Monnet 	emit_obj_refs_json(refs_table, info->id, json_wtr);
504d53dee3fSAndrii Nakryiko 
505aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
506aff52e68SYiFei Zhu 
507743cc665SQuentin Monnet 	jsonw_end_object(json_wtr);
508743cc665SQuentin Monnet }
509743cc665SQuentin Monnet 
510ec202509SPaul Chaignon static void print_prog_header_plain(struct bpf_prog_info *info)
511743cc665SQuentin Monnet {
512743cc665SQuentin Monnet 	printf("%u: ", info->id);
513743cc665SQuentin Monnet 	if (info->type < ARRAY_SIZE(prog_type_name))
514743cc665SQuentin Monnet 		printf("%s  ", prog_type_name[info->type]);
515743cc665SQuentin Monnet 	else
516743cc665SQuentin Monnet 		printf("type %u  ", info->type);
517743cc665SQuentin Monnet 
518743cc665SQuentin Monnet 	if (*info->name)
519743cc665SQuentin Monnet 		printf("name %s  ", info->name);
520743cc665SQuentin Monnet 
521743cc665SQuentin Monnet 	printf("tag ");
522743cc665SQuentin Monnet 	fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
52352262210SJakub Kicinski 	print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
5249b984a20SJiri Olsa 	printf("%s", info->gpl_compatible ? "  gpl" : "");
52588ad472bSAlexei Starovoitov 	if (info->run_time_ns)
52688ad472bSAlexei Starovoitov 		printf(" run_time_ns %lld run_cnt %lld",
52788ad472bSAlexei Starovoitov 		       info->run_time_ns, info->run_cnt);
5289ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
5299ed9e9baSAlexei Starovoitov 		printf(" recursion_misses %lld", info->recursion_misses);
530743cc665SQuentin Monnet 	printf("\n");
531ec202509SPaul Chaignon }
532ec202509SPaul Chaignon 
533ec202509SPaul Chaignon static void print_prog_plain(struct bpf_prog_info *info, int fd)
534ec202509SPaul Chaignon {
535ec202509SPaul Chaignon 	char *memlock;
536ec202509SPaul Chaignon 
537ec202509SPaul Chaignon 	print_prog_header_plain(info);
538743cc665SQuentin Monnet 
539743cc665SQuentin Monnet 	if (info->load_time) {
540743cc665SQuentin Monnet 		char buf[32];
541743cc665SQuentin Monnet 
542743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
543743cc665SQuentin Monnet 
544743cc665SQuentin Monnet 		/* Piggy back on load_time, since 0 uid is a valid one */
545743cc665SQuentin Monnet 		printf("\tloaded_at %s  uid %u\n", buf, info->created_by_uid);
546743cc665SQuentin Monnet 	}
547743cc665SQuentin Monnet 
548743cc665SQuentin Monnet 	printf("\txlated %uB", info->xlated_prog_len);
549743cc665SQuentin Monnet 
550743cc665SQuentin Monnet 	if (info->jited_prog_len)
551743cc665SQuentin Monnet 		printf("  jited %uB", info->jited_prog_len);
55271bb428fSJakub Kicinski 	else
55371bb428fSJakub Kicinski 		printf("  not jited");
55471bb428fSJakub Kicinski 
55571bb428fSJakub Kicinski 	memlock = get_fdinfo(fd, "memlock");
55671bb428fSJakub Kicinski 	if (memlock)
55771bb428fSJakub Kicinski 		printf("  memlock %sB", memlock);
55871bb428fSJakub Kicinski 	free(memlock);
55971bb428fSJakub Kicinski 
560743cc665SQuentin Monnet 	if (info->nr_map_ids)
561743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
56271bb428fSJakub Kicinski 
5638f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
5648f184732SQuentin Monnet 		struct hashmap_entry *entry;
5654990f1f4SPrashant Bhole 
5668f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
5678f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
5688f184732SQuentin Monnet 			printf("\n\tpinned %s", (char *)entry->value);
5694990f1f4SPrashant Bhole 	}
5704990f1f4SPrashant Bhole 
571569b0c77SPrashant Bhole 	if (info->btf_id)
572031ebc1aSQuentin Monnet 		printf("\n\tbtf_id %d", info->btf_id);
573569b0c77SPrashant Bhole 
574d6699f8eSQuentin Monnet 	emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
575d53dee3fSAndrii Nakryiko 
57671bb428fSJakub Kicinski 	printf("\n");
577aff52e68SYiFei Zhu 
578aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
579743cc665SQuentin Monnet }
580743cc665SQuentin Monnet 
581743cc665SQuentin Monnet static int show_prog(int fd)
582743cc665SQuentin Monnet {
583743cc665SQuentin Monnet 	struct bpf_prog_info info = {};
584743cc665SQuentin Monnet 	__u32 len = sizeof(info);
585743cc665SQuentin Monnet 	int err;
586743cc665SQuentin Monnet 
587743cc665SQuentin Monnet 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
588743cc665SQuentin Monnet 	if (err) {
5899a5ab8bfSQuentin Monnet 		p_err("can't get prog info: %s", strerror(errno));
590743cc665SQuentin Monnet 		return -1;
591743cc665SQuentin Monnet 	}
592743cc665SQuentin Monnet 
593743cc665SQuentin Monnet 	if (json_output)
594743cc665SQuentin Monnet 		print_prog_json(&info, fd);
595743cc665SQuentin Monnet 	else
596743cc665SQuentin Monnet 		print_prog_plain(&info, fd);
59771bb428fSJakub Kicinski 
59871bb428fSJakub Kicinski 	return 0;
59971bb428fSJakub Kicinski }
60071bb428fSJakub Kicinski 
601ec202509SPaul Chaignon static int do_show_subset(int argc, char **argv)
602ec202509SPaul Chaignon {
603ec202509SPaul Chaignon 	int *fds = NULL;
604ec202509SPaul Chaignon 	int nb_fds, i;
605ec202509SPaul Chaignon 	int err = -1;
606ec202509SPaul Chaignon 
607ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
608ec202509SPaul Chaignon 	if (!fds) {
609ec202509SPaul Chaignon 		p_err("mem alloc failed");
610ec202509SPaul Chaignon 		return -1;
611ec202509SPaul Chaignon 	}
612ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
613ec202509SPaul Chaignon 	if (nb_fds < 1)
614ec202509SPaul Chaignon 		goto exit_free;
615ec202509SPaul Chaignon 
616ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
617ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
618ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
619ec202509SPaul Chaignon 		err = show_prog(fds[i]);
620ec202509SPaul Chaignon 		if (err) {
621ec202509SPaul Chaignon 			for (; i < nb_fds; i++)
622ec202509SPaul Chaignon 				close(fds[i]);
623ec202509SPaul Chaignon 			break;
624ec202509SPaul Chaignon 		}
625ec202509SPaul Chaignon 		close(fds[i]);
626ec202509SPaul Chaignon 	}
627ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
628ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
629ec202509SPaul Chaignon 
630ec202509SPaul Chaignon exit_free:
631ec202509SPaul Chaignon 	free(fds);
632ec202509SPaul Chaignon 	return err;
633ec202509SPaul Chaignon }
634ec202509SPaul Chaignon 
63571bb428fSJakub Kicinski static int do_show(int argc, char **argv)
636743cc665SQuentin Monnet {
637743cc665SQuentin Monnet 	__u32 id = 0;
63871bb428fSJakub Kicinski 	int err;
63971bb428fSJakub Kicinski 	int fd;
64071bb428fSJakub Kicinski 
64146241271SQuentin Monnet 	if (show_pinned) {
6428f184732SQuentin Monnet 		prog_table = hashmap__new(hash_fn_for_key_as_id,
6438f184732SQuentin Monnet 					  equal_fn_for_key_as_id, NULL);
6448f184732SQuentin Monnet 		if (!prog_table) {
6458f184732SQuentin Monnet 			p_err("failed to create hashmap for pinned paths");
6468f184732SQuentin Monnet 			return -1;
6478f184732SQuentin Monnet 		}
6488f184732SQuentin Monnet 		build_pinned_obj_table(prog_table, BPF_OBJ_PROG);
64946241271SQuentin Monnet 	}
650d53dee3fSAndrii Nakryiko 	build_obj_refs_table(&refs_table, BPF_OBJ_PROG);
6514990f1f4SPrashant Bhole 
652ec202509SPaul Chaignon 	if (argc == 2)
653ec202509SPaul Chaignon 		return do_show_subset(argc, argv);
65471bb428fSJakub Kicinski 
65571bb428fSJakub Kicinski 	if (argc)
65671bb428fSJakub Kicinski 		return BAD_ARG();
65771bb428fSJakub Kicinski 
658743cc665SQuentin Monnet 	if (json_output)
659743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
66071bb428fSJakub Kicinski 	while (true) {
66171bb428fSJakub Kicinski 		err = bpf_prog_get_next_id(id, &id);
66271bb428fSJakub Kicinski 		if (err) {
6631739c26dSQuentin Monnet 			if (errno == ENOENT) {
6641739c26dSQuentin Monnet 				err = 0;
66571bb428fSJakub Kicinski 				break;
6661739c26dSQuentin Monnet 			}
6679a5ab8bfSQuentin Monnet 			p_err("can't get next program: %s%s", strerror(errno),
6689a5ab8bfSQuentin Monnet 			      errno == EINVAL ? " -- kernel too old?" : "");
669743cc665SQuentin Monnet 			err = -1;
670743cc665SQuentin Monnet 			break;
67171bb428fSJakub Kicinski 		}
67271bb428fSJakub Kicinski 
67371bb428fSJakub Kicinski 		fd = bpf_prog_get_fd_by_id(id);
67471bb428fSJakub Kicinski 		if (fd < 0) {
6758207c6ddSJakub Kicinski 			if (errno == ENOENT)
6768207c6ddSJakub Kicinski 				continue;
6779a5ab8bfSQuentin Monnet 			p_err("can't get prog by id (%u): %s",
67871bb428fSJakub Kicinski 			      id, strerror(errno));
679743cc665SQuentin Monnet 			err = -1;
680743cc665SQuentin Monnet 			break;
68171bb428fSJakub Kicinski 		}
68271bb428fSJakub Kicinski 
68371bb428fSJakub Kicinski 		err = show_prog(fd);
68471bb428fSJakub Kicinski 		close(fd);
68571bb428fSJakub Kicinski 		if (err)
686743cc665SQuentin Monnet 			break;
68771bb428fSJakub Kicinski 	}
68871bb428fSJakub Kicinski 
689743cc665SQuentin Monnet 	if (json_output)
690743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
691743cc665SQuentin Monnet 
692d6699f8eSQuentin Monnet 	delete_obj_refs_table(refs_table);
693d53dee3fSAndrii Nakryiko 
69446241271SQuentin Monnet 	if (show_pinned)
6958f184732SQuentin Monnet 		delete_pinned_obj_table(prog_table);
69646241271SQuentin Monnet 
697743cc665SQuentin Monnet 	return err;
69871bb428fSJakub Kicinski }
69971bb428fSJakub Kicinski 
700ec202509SPaul Chaignon static int
701ec202509SPaul Chaignon prog_dump(struct bpf_prog_info *info, enum dump_mode mode,
702ec202509SPaul Chaignon 	  char *filepath, bool opcodes, bool visual, bool linum)
70371bb428fSJakub Kicinski {
704b053b439SMartin KaFai Lau 	struct bpf_prog_linfo *prog_linfo = NULL;
7053ddeac67SJakub Kicinski 	const char *disasm_opt = NULL;
7067105e828SDaniel Borkmann 	struct dump_data dd = {};
707cae73f23SSong Liu 	void *func_info = NULL;
708254471e5SYonghong Song 	struct btf *btf = NULL;
709254471e5SYonghong Song 	char func_sig[1024];
71071bb428fSJakub Kicinski 	unsigned char *buf;
711cae73f23SSong Liu 	__u32 member_len;
712ebbd7f64SQuentin Monnet 	int fd, err = -1;
71371bb428fSJakub Kicinski 	ssize_t n;
71471bb428fSJakub Kicinski 
715cae73f23SSong Liu 	if (mode == DUMP_JITED) {
7165b79bcdfSToke Høiland-Jørgensen 		if (info->jited_prog_len == 0 || !info->jited_prog_insns) {
7179a5ab8bfSQuentin Monnet 			p_info("no instructions returned");
718ec202509SPaul Chaignon 			return -1;
719f84192eeSSandipan Das 		}
72009f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->jited_prog_insns);
721cae73f23SSong Liu 		member_len = info->jited_prog_len;
722cae73f23SSong Liu 	} else {	/* DUMP_XLATED */
723d95f1e8bSToke Høiland-Jørgensen 		if (info->xlated_prog_len == 0 || !info->xlated_prog_insns) {
7247105e828SDaniel Borkmann 			p_err("error retrieving insn dump: kernel.kptr_restrict set?");
725ec202509SPaul Chaignon 			return -1;
7267105e828SDaniel Borkmann 		}
72709f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->xlated_prog_insns);
728cae73f23SSong Liu 		member_len = info->xlated_prog_len;
729cae73f23SSong Liu 	}
7307105e828SDaniel Borkmann 
73186f4b7f2SQuentin Monnet 	if (info->btf_id) {
73286f4b7f2SQuentin Monnet 		btf = btf__load_from_kernel_by_id(info->btf_id);
73386f4b7f2SQuentin Monnet 		if (libbpf_get_error(btf)) {
734254471e5SYonghong Song 			p_err("failed to get btf");
735ec202509SPaul Chaignon 			return -1;
736254471e5SYonghong Song 		}
73786f4b7f2SQuentin Monnet 	}
738254471e5SYonghong Song 
73909f44b75SAndrii Nakryiko 	func_info = u64_to_ptr(info->func_info);
740cae73f23SSong Liu 
741cae73f23SSong Liu 	if (info->nr_line_info) {
742cae73f23SSong Liu 		prog_linfo = bpf_prog_linfo__new(info);
743b053b439SMartin KaFai Lau 		if (!prog_linfo)
74410a5ce98SMartin KaFai Lau 			p_info("error in processing bpf_line_info.  continue without it.");
745b053b439SMartin KaFai Lau 	}
746b053b439SMartin KaFai Lau 
74771bb428fSJakub Kicinski 	if (filepath) {
74871bb428fSJakub Kicinski 		fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
74971bb428fSJakub Kicinski 		if (fd < 0) {
7509a5ab8bfSQuentin Monnet 			p_err("can't open file %s: %s", filepath,
75171bb428fSJakub Kicinski 			      strerror(errno));
752ebbd7f64SQuentin Monnet 			goto exit_free;
75371bb428fSJakub Kicinski 		}
75471bb428fSJakub Kicinski 
755cae73f23SSong Liu 		n = write(fd, buf, member_len);
75671bb428fSJakub Kicinski 		close(fd);
75709f44b75SAndrii Nakryiko 		if (n != (ssize_t)member_len) {
7589a5ab8bfSQuentin Monnet 			p_err("error writing output file: %s",
75971bb428fSJakub Kicinski 			      n < 0 ? strerror(errno) : "short write");
760ebbd7f64SQuentin Monnet 			goto exit_free;
76171bb428fSJakub Kicinski 		}
76252c84d36SQuentin Monnet 
76352c84d36SQuentin Monnet 		if (json_output)
76452c84d36SQuentin Monnet 			jsonw_null(json_wtr);
765cae73f23SSong Liu 	} else if (mode == DUMP_JITED) {
766e6593596SJiong Wang 		const char *name = NULL;
767e6593596SJiong Wang 
768cae73f23SSong Liu 		if (info->ifindex) {
769cae73f23SSong Liu 			name = ifindex_to_bfd_params(info->ifindex,
770cae73f23SSong Liu 						     info->netns_dev,
771cae73f23SSong Liu 						     info->netns_ino,
7723ddeac67SJakub Kicinski 						     &disasm_opt);
773e6593596SJiong Wang 			if (!name)
774ebbd7f64SQuentin Monnet 				goto exit_free;
775e6593596SJiong Wang 		}
776e6593596SJiong Wang 
777cae73f23SSong Liu 		if (info->nr_jited_func_lens && info->jited_func_lens) {
778f7f62c71SSandipan Das 			struct kernel_sym *sym = NULL;
779254471e5SYonghong Song 			struct bpf_func_info *record;
780f7f62c71SSandipan Das 			char sym_name[SYM_MAX_NAME];
781f7f62c71SSandipan Das 			unsigned char *img = buf;
782f7f62c71SSandipan Das 			__u64 *ksyms = NULL;
783f7f62c71SSandipan Das 			__u32 *lens;
784f7f62c71SSandipan Das 			__u32 i;
785cae73f23SSong Liu 			if (info->nr_jited_ksyms) {
786f7f62c71SSandipan Das 				kernel_syms_load(&dd);
78709f44b75SAndrii Nakryiko 				ksyms = u64_to_ptr(info->jited_ksyms);
788f7f62c71SSandipan Das 			}
789f7f62c71SSandipan Das 
790f7f62c71SSandipan Das 			if (json_output)
791f7f62c71SSandipan Das 				jsonw_start_array(json_wtr);
792f7f62c71SSandipan Das 
79309f44b75SAndrii Nakryiko 			lens = u64_to_ptr(info->jited_func_lens);
794cae73f23SSong Liu 			for (i = 0; i < info->nr_jited_func_lens; i++) {
795f7f62c71SSandipan Das 				if (ksyms) {
796f7f62c71SSandipan Das 					sym = kernel_syms_search(&dd, ksyms[i]);
797f7f62c71SSandipan Das 					if (sym)
798f7f62c71SSandipan Das 						sprintf(sym_name, "%s", sym->name);
799f7f62c71SSandipan Das 					else
800f7f62c71SSandipan Das 						sprintf(sym_name, "0x%016llx", ksyms[i]);
801f7f62c71SSandipan Das 				} else {
802f7f62c71SSandipan Das 					strcpy(sym_name, "unknown");
803f7f62c71SSandipan Das 				}
804f7f62c71SSandipan Das 
805254471e5SYonghong Song 				if (func_info) {
806cae73f23SSong Liu 					record = func_info + i * info->func_info_rec_size;
807254471e5SYonghong Song 					btf_dumper_type_only(btf, record->type_id,
808254471e5SYonghong Song 							     func_sig,
809254471e5SYonghong Song 							     sizeof(func_sig));
810254471e5SYonghong Song 				}
811254471e5SYonghong Song 
812f7f62c71SSandipan Das 				if (json_output) {
813f7f62c71SSandipan Das 					jsonw_start_object(json_wtr);
814254471e5SYonghong Song 					if (func_info && func_sig[0] != '\0') {
815254471e5SYonghong Song 						jsonw_name(json_wtr, "proto");
816254471e5SYonghong Song 						jsonw_string(json_wtr, func_sig);
817254471e5SYonghong Song 					}
818f7f62c71SSandipan Das 					jsonw_name(json_wtr, "name");
819f7f62c71SSandipan Das 					jsonw_string(json_wtr, sym_name);
820f7f62c71SSandipan Das 					jsonw_name(json_wtr, "insns");
821f7f62c71SSandipan Das 				} else {
822254471e5SYonghong Song 					if (func_info && func_sig[0] != '\0')
823254471e5SYonghong Song 						printf("%s:\n", func_sig);
824f7f62c71SSandipan Das 					printf("%s:\n", sym_name);
825f7f62c71SSandipan Das 				}
826f7f62c71SSandipan Das 
827b053b439SMartin KaFai Lau 				disasm_print_insn(img, lens[i], opcodes,
828b053b439SMartin KaFai Lau 						  name, disasm_opt, btf,
829b053b439SMartin KaFai Lau 						  prog_linfo, ksyms[i], i,
830b053b439SMartin KaFai Lau 						  linum);
831b053b439SMartin KaFai Lau 
832f7f62c71SSandipan Das 				img += lens[i];
833f7f62c71SSandipan Das 
834f7f62c71SSandipan Das 				if (json_output)
835f7f62c71SSandipan Das 					jsonw_end_object(json_wtr);
836f7f62c71SSandipan Das 				else
837f7f62c71SSandipan Das 					printf("\n");
838f7f62c71SSandipan Das 			}
839f7f62c71SSandipan Das 
840f7f62c71SSandipan Das 			if (json_output)
841f7f62c71SSandipan Das 				jsonw_end_array(json_wtr);
842f7f62c71SSandipan Das 		} else {
843cae73f23SSong Liu 			disasm_print_insn(buf, member_len, opcodes, name,
844b053b439SMartin KaFai Lau 					  disasm_opt, btf, NULL, 0, 0, false);
845f7f62c71SSandipan Das 		}
846b6c1cedbSJiong Wang 	} else if (visual) {
847b6c1cedbSJiong Wang 		if (json_output)
848b6c1cedbSJiong Wang 			jsonw_null(json_wtr);
849b6c1cedbSJiong Wang 		else
850cae73f23SSong Liu 			dump_xlated_cfg(buf, member_len);
8517105e828SDaniel Borkmann 	} else {
8527105e828SDaniel Borkmann 		kernel_syms_load(&dd);
853cae73f23SSong Liu 		dd.nr_jited_ksyms = info->nr_jited_ksyms;
85409f44b75SAndrii Nakryiko 		dd.jited_ksyms = u64_to_ptr(info->jited_ksyms);
855254471e5SYonghong Song 		dd.btf = btf;
856254471e5SYonghong Song 		dd.func_info = func_info;
857cae73f23SSong Liu 		dd.finfo_rec_size = info->func_info_rec_size;
858b053b439SMartin KaFai Lau 		dd.prog_linfo = prog_linfo;
859f84192eeSSandipan Das 
860f05e2c32SQuentin Monnet 		if (json_output)
861cae73f23SSong Liu 			dump_xlated_json(&dd, buf, member_len, opcodes,
862b053b439SMartin KaFai Lau 					 linum);
863f05e2c32SQuentin Monnet 		else
864cae73f23SSong Liu 			dump_xlated_plain(&dd, buf, member_len, opcodes,
865b053b439SMartin KaFai Lau 					  linum);
8667105e828SDaniel Borkmann 		kernel_syms_destroy(&dd);
8677105e828SDaniel Borkmann 	}
86871bb428fSJakub Kicinski 
869ebbd7f64SQuentin Monnet 	err = 0;
870369e955bSQuentin Monnet 
871ebbd7f64SQuentin Monnet exit_free:
872ebbd7f64SQuentin Monnet 	btf__free(btf);
873ebbd7f64SQuentin Monnet 	bpf_prog_linfo__free(prog_linfo);
874ebbd7f64SQuentin Monnet 	return err;
875ec202509SPaul Chaignon }
87671bb428fSJakub Kicinski 
877ec202509SPaul Chaignon static int do_dump(int argc, char **argv)
878ec202509SPaul Chaignon {
879c59765cfSDave Marchevsky 	struct bpf_prog_info info;
880c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
881c59765cfSDave Marchevsky 	size_t info_data_sz = 0;
882c59765cfSDave Marchevsky 	void *info_data = NULL;
883ec202509SPaul Chaignon 	char *filepath = NULL;
884ec202509SPaul Chaignon 	bool opcodes = false;
885ec202509SPaul Chaignon 	bool visual = false;
886ec202509SPaul Chaignon 	enum dump_mode mode;
887ec202509SPaul Chaignon 	bool linum = false;
888ec202509SPaul Chaignon 	int nb_fds, i = 0;
889c59765cfSDave Marchevsky 	int *fds = NULL;
890ec202509SPaul Chaignon 	int err = -1;
891ec202509SPaul Chaignon 
892ec202509SPaul Chaignon 	if (is_prefix(*argv, "jited")) {
893ec202509SPaul Chaignon 		if (disasm_init())
89471bb428fSJakub Kicinski 			return -1;
895ec202509SPaul Chaignon 		mode = DUMP_JITED;
896ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "xlated")) {
897ec202509SPaul Chaignon 		mode = DUMP_XLATED;
898ec202509SPaul Chaignon 	} else {
899ec202509SPaul Chaignon 		p_err("expected 'xlated' or 'jited', got: %s", *argv);
900ec202509SPaul Chaignon 		return -1;
901ec202509SPaul Chaignon 	}
902ec202509SPaul Chaignon 	NEXT_ARG();
903ec202509SPaul Chaignon 
904ec202509SPaul Chaignon 	if (argc < 2)
905ec202509SPaul Chaignon 		usage();
906ec202509SPaul Chaignon 
907ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
908ec202509SPaul Chaignon 	if (!fds) {
909ec202509SPaul Chaignon 		p_err("mem alloc failed");
910ec202509SPaul Chaignon 		return -1;
911ec202509SPaul Chaignon 	}
912ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
913ec202509SPaul Chaignon 	if (nb_fds < 1)
914ec202509SPaul Chaignon 		goto exit_free;
915ec202509SPaul Chaignon 
916ec202509SPaul Chaignon 	if (is_prefix(*argv, "file")) {
917ec202509SPaul Chaignon 		NEXT_ARG();
918ec202509SPaul Chaignon 		if (!argc) {
919ec202509SPaul Chaignon 			p_err("expected file path");
920ec202509SPaul Chaignon 			goto exit_close;
921ec202509SPaul Chaignon 		}
922ec202509SPaul Chaignon 		if (nb_fds > 1) {
923ec202509SPaul Chaignon 			p_err("several programs matched");
924ec202509SPaul Chaignon 			goto exit_close;
925ec202509SPaul Chaignon 		}
926ec202509SPaul Chaignon 
927ec202509SPaul Chaignon 		filepath = *argv;
928ec202509SPaul Chaignon 		NEXT_ARG();
929ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "opcodes")) {
930ec202509SPaul Chaignon 		opcodes = true;
931ec202509SPaul Chaignon 		NEXT_ARG();
932ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "visual")) {
933ec202509SPaul Chaignon 		if (nb_fds > 1) {
934ec202509SPaul Chaignon 			p_err("several programs matched");
935ec202509SPaul Chaignon 			goto exit_close;
936ec202509SPaul Chaignon 		}
937ec202509SPaul Chaignon 
938ec202509SPaul Chaignon 		visual = true;
939ec202509SPaul Chaignon 		NEXT_ARG();
940ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "linum")) {
941ec202509SPaul Chaignon 		linum = true;
942ec202509SPaul Chaignon 		NEXT_ARG();
943ec202509SPaul Chaignon 	}
944ec202509SPaul Chaignon 
945ec202509SPaul Chaignon 	if (argc) {
946ec202509SPaul Chaignon 		usage();
947ec202509SPaul Chaignon 		goto exit_close;
948ec202509SPaul Chaignon 	}
949ec202509SPaul Chaignon 
950ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
951ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
952ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
953c59765cfSDave Marchevsky 		memset(&info, 0, sizeof(info));
954c59765cfSDave Marchevsky 
955c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
956c59765cfSDave Marchevsky 		if (err) {
957c59765cfSDave Marchevsky 			p_err("can't get prog info: %s", strerror(errno));
958c59765cfSDave Marchevsky 			break;
959c59765cfSDave Marchevsky 		}
960c59765cfSDave Marchevsky 
961c59765cfSDave Marchevsky 		err = prep_prog_info(&info, mode, &info_data, &info_data_sz);
962c59765cfSDave Marchevsky 		if (err) {
963c59765cfSDave Marchevsky 			p_err("can't grow prog info_data");
964c59765cfSDave Marchevsky 			break;
965c59765cfSDave Marchevsky 		}
966c59765cfSDave Marchevsky 
967c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
968c59765cfSDave Marchevsky 		if (err) {
969ec202509SPaul Chaignon 			p_err("can't get prog info: %s", strerror(errno));
970ec202509SPaul Chaignon 			break;
971ec202509SPaul Chaignon 		}
972ec202509SPaul Chaignon 
973ec202509SPaul Chaignon 		if (json_output && nb_fds > 1) {
974ec202509SPaul Chaignon 			jsonw_start_object(json_wtr);	/* prog object */
975c59765cfSDave Marchevsky 			print_prog_header_json(&info);
976ec202509SPaul Chaignon 			jsonw_name(json_wtr, "insns");
977ec202509SPaul Chaignon 		} else if (nb_fds > 1) {
978c59765cfSDave Marchevsky 			print_prog_header_plain(&info);
979ec202509SPaul Chaignon 		}
980ec202509SPaul Chaignon 
981c59765cfSDave Marchevsky 		err = prog_dump(&info, mode, filepath, opcodes, visual, linum);
982ec202509SPaul Chaignon 
983ec202509SPaul Chaignon 		if (json_output && nb_fds > 1)
984ec202509SPaul Chaignon 			jsonw_end_object(json_wtr);	/* prog object */
985ec202509SPaul Chaignon 		else if (i != nb_fds - 1 && nb_fds > 1)
986ec202509SPaul Chaignon 			printf("\n");
987ec202509SPaul Chaignon 
988ec202509SPaul Chaignon 		if (err)
989ec202509SPaul Chaignon 			break;
990ec202509SPaul Chaignon 		close(fds[i]);
991ec202509SPaul Chaignon 	}
992ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
993ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
994ec202509SPaul Chaignon 
995ec202509SPaul Chaignon exit_close:
996ec202509SPaul Chaignon 	for (; i < nb_fds; i++)
997ec202509SPaul Chaignon 		close(fds[i]);
998ec202509SPaul Chaignon exit_free:
999c59765cfSDave Marchevsky 	free(info_data);
1000ec202509SPaul Chaignon 	free(fds);
1001ec202509SPaul Chaignon 	return err;
100271bb428fSJakub Kicinski }
100371bb428fSJakub Kicinski 
100471bb428fSJakub Kicinski static int do_pin(int argc, char **argv)
100571bb428fSJakub Kicinski {
1006004b45c0SQuentin Monnet 	int err;
1007004b45c0SQuentin Monnet 
100875a1e792SQuentin Monnet 	err = do_pin_any(argc, argv, prog_parse_fd);
1009004b45c0SQuentin Monnet 	if (!err && json_output)
1010004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
1011004b45c0SQuentin Monnet 	return err;
101271bb428fSJakub Kicinski }
101371bb428fSJakub Kicinski 
10143ff5a4dcSJakub Kicinski struct map_replace {
10153ff5a4dcSJakub Kicinski 	int idx;
10163ff5a4dcSJakub Kicinski 	int fd;
10173ff5a4dcSJakub Kicinski 	char *name;
10183ff5a4dcSJakub Kicinski };
10193ff5a4dcSJakub Kicinski 
1020c101189bSQuentin Monnet static int map_replace_compar(const void *p1, const void *p2)
10213ff5a4dcSJakub Kicinski {
10223ff5a4dcSJakub Kicinski 	const struct map_replace *a = p1, *b = p2;
10233ff5a4dcSJakub Kicinski 
10243ff5a4dcSJakub Kicinski 	return a->idx - b->idx;
10253ff5a4dcSJakub Kicinski }
10263ff5a4dcSJakub Kicinski 
1027092f0892SStanislav Fomichev static int parse_attach_detach_args(int argc, char **argv, int *progfd,
1028092f0892SStanislav Fomichev 				    enum bpf_attach_type *attach_type,
1029092f0892SStanislav Fomichev 				    int *mapfd)
1030092f0892SStanislav Fomichev {
1031092f0892SStanislav Fomichev 	if (!REQ_ARGS(3))
1032092f0892SStanislav Fomichev 		return -EINVAL;
1033092f0892SStanislav Fomichev 
1034092f0892SStanislav Fomichev 	*progfd = prog_parse_fd(&argc, &argv);
1035092f0892SStanislav Fomichev 	if (*progfd < 0)
1036092f0892SStanislav Fomichev 		return *progfd;
1037092f0892SStanislav Fomichev 
1038092f0892SStanislav Fomichev 	*attach_type = parse_attach_type(*argv);
1039092f0892SStanislav Fomichev 	if (*attach_type == __MAX_BPF_ATTACH_TYPE) {
1040092f0892SStanislav Fomichev 		p_err("invalid attach/detach type");
1041092f0892SStanislav Fomichev 		return -EINVAL;
1042092f0892SStanislav Fomichev 	}
1043092f0892SStanislav Fomichev 
1044092f0892SStanislav Fomichev 	if (*attach_type == BPF_FLOW_DISSECTOR) {
1045f9b7ff0dSLorenz Bauer 		*mapfd = 0;
1046092f0892SStanislav Fomichev 		return 0;
1047092f0892SStanislav Fomichev 	}
1048092f0892SStanislav Fomichev 
1049092f0892SStanislav Fomichev 	NEXT_ARG();
1050092f0892SStanislav Fomichev 	if (!REQ_ARGS(2))
1051092f0892SStanislav Fomichev 		return -EINVAL;
1052092f0892SStanislav Fomichev 
1053092f0892SStanislav Fomichev 	*mapfd = map_parse_fd(&argc, &argv);
1054092f0892SStanislav Fomichev 	if (*mapfd < 0)
1055092f0892SStanislav Fomichev 		return *mapfd;
1056092f0892SStanislav Fomichev 
1057092f0892SStanislav Fomichev 	return 0;
1058092f0892SStanislav Fomichev }
1059092f0892SStanislav Fomichev 
1060b7d3826cSJohn Fastabend static int do_attach(int argc, char **argv)
1061b7d3826cSJohn Fastabend {
1062b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1063092f0892SStanislav Fomichev 	int err, progfd;
1064092f0892SStanislav Fomichev 	int mapfd;
1065b7d3826cSJohn Fastabend 
1066092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1067092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1068092f0892SStanislav Fomichev 	if (err)
1069092f0892SStanislav Fomichev 		return err;
1070b7d3826cSJohn Fastabend 
1071b7d3826cSJohn Fastabend 	err = bpf_prog_attach(progfd, mapfd, attach_type, 0);
1072b7d3826cSJohn Fastabend 	if (err) {
1073b7d3826cSJohn Fastabend 		p_err("failed prog attach to map");
1074b7d3826cSJohn Fastabend 		return -EINVAL;
1075b7d3826cSJohn Fastabend 	}
1076b7d3826cSJohn Fastabend 
1077b7d3826cSJohn Fastabend 	if (json_output)
1078b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1079b7d3826cSJohn Fastabend 	return 0;
1080b7d3826cSJohn Fastabend }
1081b7d3826cSJohn Fastabend 
1082b7d3826cSJohn Fastabend static int do_detach(int argc, char **argv)
1083b7d3826cSJohn Fastabend {
1084b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1085092f0892SStanislav Fomichev 	int err, progfd;
1086092f0892SStanislav Fomichev 	int mapfd;
1087b7d3826cSJohn Fastabend 
1088092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1089092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1090092f0892SStanislav Fomichev 	if (err)
1091092f0892SStanislav Fomichev 		return err;
1092b7d3826cSJohn Fastabend 
1093b7d3826cSJohn Fastabend 	err = bpf_prog_detach2(progfd, mapfd, attach_type);
1094b7d3826cSJohn Fastabend 	if (err) {
1095b7d3826cSJohn Fastabend 		p_err("failed prog detach from map");
1096b7d3826cSJohn Fastabend 		return -EINVAL;
1097b7d3826cSJohn Fastabend 	}
1098b7d3826cSJohn Fastabend 
1099b7d3826cSJohn Fastabend 	if (json_output)
1100b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1101b7d3826cSJohn Fastabend 	return 0;
1102b7d3826cSJohn Fastabend }
110377380998SStanislav Fomichev 
1104ba95c745SQuentin Monnet static int check_single_stdin(char *file_data_in, char *file_ctx_in)
1105ba95c745SQuentin Monnet {
1106ba95c745SQuentin Monnet 	if (file_data_in && file_ctx_in &&
1107ba95c745SQuentin Monnet 	    !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) {
1108ba95c745SQuentin Monnet 		p_err("cannot use standard input for both data_in and ctx_in");
1109ba95c745SQuentin Monnet 		return -1;
1110ba95c745SQuentin Monnet 	}
1111ba95c745SQuentin Monnet 
1112ba95c745SQuentin Monnet 	return 0;
1113ba95c745SQuentin Monnet }
1114ba95c745SQuentin Monnet 
1115ba95c745SQuentin Monnet static int get_run_data(const char *fname, void **data_ptr, unsigned int *size)
1116ba95c745SQuentin Monnet {
1117ba95c745SQuentin Monnet 	size_t block_size = 256;
1118ba95c745SQuentin Monnet 	size_t buf_size = block_size;
1119ba95c745SQuentin Monnet 	size_t nb_read = 0;
1120ba95c745SQuentin Monnet 	void *tmp;
1121ba95c745SQuentin Monnet 	FILE *f;
1122ba95c745SQuentin Monnet 
1123ba95c745SQuentin Monnet 	if (!fname) {
1124ba95c745SQuentin Monnet 		*data_ptr = NULL;
1125ba95c745SQuentin Monnet 		*size = 0;
1126ba95c745SQuentin Monnet 		return 0;
1127ba95c745SQuentin Monnet 	}
1128ba95c745SQuentin Monnet 
1129ba95c745SQuentin Monnet 	if (!strcmp(fname, "-"))
1130ba95c745SQuentin Monnet 		f = stdin;
1131ba95c745SQuentin Monnet 	else
1132ba95c745SQuentin Monnet 		f = fopen(fname, "r");
1133ba95c745SQuentin Monnet 	if (!f) {
1134ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1135ba95c745SQuentin Monnet 		return -1;
1136ba95c745SQuentin Monnet 	}
1137ba95c745SQuentin Monnet 
1138ba95c745SQuentin Monnet 	*data_ptr = malloc(block_size);
1139ba95c745SQuentin Monnet 	if (!*data_ptr) {
1140ba95c745SQuentin Monnet 		p_err("failed to allocate memory for data_in/ctx_in: %s",
1141ba95c745SQuentin Monnet 		      strerror(errno));
1142ba95c745SQuentin Monnet 		goto err_fclose;
1143ba95c745SQuentin Monnet 	}
1144ba95c745SQuentin Monnet 
1145ba95c745SQuentin Monnet 	while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) {
1146ba95c745SQuentin Monnet 		if (feof(f))
1147ba95c745SQuentin Monnet 			break;
1148ba95c745SQuentin Monnet 		if (ferror(f)) {
1149ba95c745SQuentin Monnet 			p_err("failed to read data_in/ctx_in from %s: %s",
1150ba95c745SQuentin Monnet 			      fname, strerror(errno));
1151ba95c745SQuentin Monnet 			goto err_free;
1152ba95c745SQuentin Monnet 		}
1153ba95c745SQuentin Monnet 		if (nb_read > buf_size - block_size) {
1154ba95c745SQuentin Monnet 			if (buf_size == UINT32_MAX) {
1155ba95c745SQuentin Monnet 				p_err("data_in/ctx_in is too long (max: %d)",
1156ba95c745SQuentin Monnet 				      UINT32_MAX);
1157ba95c745SQuentin Monnet 				goto err_free;
1158ba95c745SQuentin Monnet 			}
1159ba95c745SQuentin Monnet 			/* No space for fread()-ing next chunk; realloc() */
1160ba95c745SQuentin Monnet 			buf_size *= 2;
1161ba95c745SQuentin Monnet 			tmp = realloc(*data_ptr, buf_size);
1162ba95c745SQuentin Monnet 			if (!tmp) {
1163ba95c745SQuentin Monnet 				p_err("failed to reallocate data_in/ctx_in: %s",
1164ba95c745SQuentin Monnet 				      strerror(errno));
1165ba95c745SQuentin Monnet 				goto err_free;
1166ba95c745SQuentin Monnet 			}
1167ba95c745SQuentin Monnet 			*data_ptr = tmp;
1168ba95c745SQuentin Monnet 		}
1169ba95c745SQuentin Monnet 	}
1170ba95c745SQuentin Monnet 	if (f != stdin)
1171ba95c745SQuentin Monnet 		fclose(f);
1172ba95c745SQuentin Monnet 
1173ba95c745SQuentin Monnet 	*size = nb_read;
1174ba95c745SQuentin Monnet 	return 0;
1175ba95c745SQuentin Monnet 
1176ba95c745SQuentin Monnet err_free:
1177ba95c745SQuentin Monnet 	free(*data_ptr);
1178ba95c745SQuentin Monnet 	*data_ptr = NULL;
1179ba95c745SQuentin Monnet err_fclose:
1180ba95c745SQuentin Monnet 	if (f != stdin)
1181ba95c745SQuentin Monnet 		fclose(f);
1182ba95c745SQuentin Monnet 	return -1;
1183ba95c745SQuentin Monnet }
1184ba95c745SQuentin Monnet 
1185ba95c745SQuentin Monnet static void hex_print(void *data, unsigned int size, FILE *f)
1186ba95c745SQuentin Monnet {
1187ba95c745SQuentin Monnet 	size_t i, j;
1188ba95c745SQuentin Monnet 	char c;
1189ba95c745SQuentin Monnet 
1190ba95c745SQuentin Monnet 	for (i = 0; i < size; i += 16) {
1191ba95c745SQuentin Monnet 		/* Row offset */
1192ba95c745SQuentin Monnet 		fprintf(f, "%07zx\t", i);
1193ba95c745SQuentin Monnet 
1194ba95c745SQuentin Monnet 		/* Hexadecimal values */
1195ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++)
1196ba95c745SQuentin Monnet 			fprintf(f, "%02x%s", *(uint8_t *)(data + j),
1197ba95c745SQuentin Monnet 				j % 2 ? " " : "");
1198ba95c745SQuentin Monnet 		for (; j < i + 16; j++)
1199ba95c745SQuentin Monnet 			fprintf(f, "  %s", j % 2 ? " " : "");
1200ba95c745SQuentin Monnet 
1201ba95c745SQuentin Monnet 		/* ASCII values (if relevant), '.' otherwise */
1202ba95c745SQuentin Monnet 		fprintf(f, "| ");
1203ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++) {
1204ba95c745SQuentin Monnet 			c = *(char *)(data + j);
1205ba95c745SQuentin Monnet 			if (c < ' ' || c > '~')
1206ba95c745SQuentin Monnet 				c = '.';
1207ba95c745SQuentin Monnet 			fprintf(f, "%c%s", c, j == i + 7 ? " " : "");
1208ba95c745SQuentin Monnet 		}
1209ba95c745SQuentin Monnet 
1210ba95c745SQuentin Monnet 		fprintf(f, "\n");
1211ba95c745SQuentin Monnet 	}
1212ba95c745SQuentin Monnet }
1213ba95c745SQuentin Monnet 
1214ba95c745SQuentin Monnet static int
1215ba95c745SQuentin Monnet print_run_output(void *data, unsigned int size, const char *fname,
1216ba95c745SQuentin Monnet 		 const char *json_key)
1217ba95c745SQuentin Monnet {
1218ba95c745SQuentin Monnet 	size_t nb_written;
1219ba95c745SQuentin Monnet 	FILE *f;
1220ba95c745SQuentin Monnet 
1221ba95c745SQuentin Monnet 	if (!fname)
1222ba95c745SQuentin Monnet 		return 0;
1223ba95c745SQuentin Monnet 
1224ba95c745SQuentin Monnet 	if (!strcmp(fname, "-")) {
1225ba95c745SQuentin Monnet 		f = stdout;
1226ba95c745SQuentin Monnet 		if (json_output) {
1227ba95c745SQuentin Monnet 			jsonw_name(json_wtr, json_key);
1228ba95c745SQuentin Monnet 			print_data_json(data, size);
1229ba95c745SQuentin Monnet 		} else {
1230ba95c745SQuentin Monnet 			hex_print(data, size, f);
1231ba95c745SQuentin Monnet 		}
1232ba95c745SQuentin Monnet 		return 0;
1233ba95c745SQuentin Monnet 	}
1234ba95c745SQuentin Monnet 
1235ba95c745SQuentin Monnet 	f = fopen(fname, "w");
1236ba95c745SQuentin Monnet 	if (!f) {
1237ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1238ba95c745SQuentin Monnet 		return -1;
1239ba95c745SQuentin Monnet 	}
1240ba95c745SQuentin Monnet 
1241ba95c745SQuentin Monnet 	nb_written = fwrite(data, 1, size, f);
1242ba95c745SQuentin Monnet 	fclose(f);
1243ba95c745SQuentin Monnet 	if (nb_written != size) {
1244ba95c745SQuentin Monnet 		p_err("failed to write output data/ctx: %s", strerror(errno));
1245ba95c745SQuentin Monnet 		return -1;
1246ba95c745SQuentin Monnet 	}
1247ba95c745SQuentin Monnet 
1248ba95c745SQuentin Monnet 	return 0;
1249ba95c745SQuentin Monnet }
1250ba95c745SQuentin Monnet 
1251ba95c745SQuentin Monnet static int alloc_run_data(void **data_ptr, unsigned int size_out)
1252ba95c745SQuentin Monnet {
1253ba95c745SQuentin Monnet 	*data_ptr = calloc(size_out, 1);
1254ba95c745SQuentin Monnet 	if (!*data_ptr) {
1255ba95c745SQuentin Monnet 		p_err("failed to allocate memory for output data/ctx: %s",
1256ba95c745SQuentin Monnet 		      strerror(errno));
1257ba95c745SQuentin Monnet 		return -1;
1258ba95c745SQuentin Monnet 	}
1259ba95c745SQuentin Monnet 
1260ba95c745SQuentin Monnet 	return 0;
1261ba95c745SQuentin Monnet }
1262ba95c745SQuentin Monnet 
1263ba95c745SQuentin Monnet static int do_run(int argc, char **argv)
1264ba95c745SQuentin Monnet {
1265ba95c745SQuentin Monnet 	char *data_fname_in = NULL, *data_fname_out = NULL;
1266ba95c745SQuentin Monnet 	char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
1267ba95c745SQuentin Monnet 	struct bpf_prog_test_run_attr test_attr = {0};
1268ba95c745SQuentin Monnet 	const unsigned int default_size = SZ_32K;
1269ba95c745SQuentin Monnet 	void *data_in = NULL, *data_out = NULL;
1270ba95c745SQuentin Monnet 	void *ctx_in = NULL, *ctx_out = NULL;
1271ba95c745SQuentin Monnet 	unsigned int repeat = 1;
1272ba95c745SQuentin Monnet 	int fd, err;
1273ba95c745SQuentin Monnet 
1274ba95c745SQuentin Monnet 	if (!REQ_ARGS(4))
1275ba95c745SQuentin Monnet 		return -1;
1276ba95c745SQuentin Monnet 
1277ba95c745SQuentin Monnet 	fd = prog_parse_fd(&argc, &argv);
1278ba95c745SQuentin Monnet 	if (fd < 0)
1279ba95c745SQuentin Monnet 		return -1;
1280ba95c745SQuentin Monnet 
1281ba95c745SQuentin Monnet 	while (argc) {
1282ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "data_in", "data_out",
1283ba95c745SQuentin Monnet 					 "data_size_out", NULL))
1284ba95c745SQuentin Monnet 			return -1;
1285ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "ctx_in", "ctx_out",
1286ba95c745SQuentin Monnet 					 "ctx_size_out", NULL))
1287ba95c745SQuentin Monnet 			return -1;
1288ba95c745SQuentin Monnet 
1289ba95c745SQuentin Monnet 		if (is_prefix(*argv, "data_in")) {
1290ba95c745SQuentin Monnet 			NEXT_ARG();
1291ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1292ba95c745SQuentin Monnet 				return -1;
1293ba95c745SQuentin Monnet 
1294ba95c745SQuentin Monnet 			data_fname_in = GET_ARG();
1295ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1296ba95c745SQuentin Monnet 				return -1;
1297ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_out")) {
1298ba95c745SQuentin Monnet 			NEXT_ARG();
1299ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1300ba95c745SQuentin Monnet 				return -1;
1301ba95c745SQuentin Monnet 
1302ba95c745SQuentin Monnet 			data_fname_out = GET_ARG();
1303ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_size_out")) {
1304ba95c745SQuentin Monnet 			char *endptr;
1305ba95c745SQuentin Monnet 
1306ba95c745SQuentin Monnet 			NEXT_ARG();
1307ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1308ba95c745SQuentin Monnet 				return -1;
1309ba95c745SQuentin Monnet 
1310ba95c745SQuentin Monnet 			test_attr.data_size_out = strtoul(*argv, &endptr, 0);
1311ba95c745SQuentin Monnet 			if (*endptr) {
1312ba95c745SQuentin Monnet 				p_err("can't parse %s as output data size",
1313ba95c745SQuentin Monnet 				      *argv);
1314ba95c745SQuentin Monnet 				return -1;
1315ba95c745SQuentin Monnet 			}
1316ba95c745SQuentin Monnet 			NEXT_ARG();
1317ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_in")) {
1318ba95c745SQuentin Monnet 			NEXT_ARG();
1319ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1320ba95c745SQuentin Monnet 				return -1;
1321ba95c745SQuentin Monnet 
1322ba95c745SQuentin Monnet 			ctx_fname_in = GET_ARG();
1323ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1324ba95c745SQuentin Monnet 				return -1;
1325ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_out")) {
1326ba95c745SQuentin Monnet 			NEXT_ARG();
1327ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1328ba95c745SQuentin Monnet 				return -1;
1329ba95c745SQuentin Monnet 
1330ba95c745SQuentin Monnet 			ctx_fname_out = GET_ARG();
1331ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_size_out")) {
1332ba95c745SQuentin Monnet 			char *endptr;
1333ba95c745SQuentin Monnet 
1334ba95c745SQuentin Monnet 			NEXT_ARG();
1335ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1336ba95c745SQuentin Monnet 				return -1;
1337ba95c745SQuentin Monnet 
1338ba95c745SQuentin Monnet 			test_attr.ctx_size_out = strtoul(*argv, &endptr, 0);
1339ba95c745SQuentin Monnet 			if (*endptr) {
1340ba95c745SQuentin Monnet 				p_err("can't parse %s as output context size",
1341ba95c745SQuentin Monnet 				      *argv);
1342ba95c745SQuentin Monnet 				return -1;
1343ba95c745SQuentin Monnet 			}
1344ba95c745SQuentin Monnet 			NEXT_ARG();
1345ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "repeat")) {
1346ba95c745SQuentin Monnet 			char *endptr;
1347ba95c745SQuentin Monnet 
1348ba95c745SQuentin Monnet 			NEXT_ARG();
1349ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1350ba95c745SQuentin Monnet 				return -1;
1351ba95c745SQuentin Monnet 
1352ba95c745SQuentin Monnet 			repeat = strtoul(*argv, &endptr, 0);
1353ba95c745SQuentin Monnet 			if (*endptr) {
1354ba95c745SQuentin Monnet 				p_err("can't parse %s as repeat number",
1355ba95c745SQuentin Monnet 				      *argv);
1356ba95c745SQuentin Monnet 				return -1;
1357ba95c745SQuentin Monnet 			}
1358ba95c745SQuentin Monnet 			NEXT_ARG();
1359ba95c745SQuentin Monnet 		} else {
1360ba95c745SQuentin 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'?",
1361ba95c745SQuentin Monnet 			      *argv);
1362ba95c745SQuentin Monnet 			return -1;
1363ba95c745SQuentin Monnet 		}
1364ba95c745SQuentin Monnet 	}
1365ba95c745SQuentin Monnet 
1366ba95c745SQuentin Monnet 	err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in);
1367ba95c745SQuentin Monnet 	if (err)
1368ba95c745SQuentin Monnet 		return -1;
1369ba95c745SQuentin Monnet 
1370ba95c745SQuentin Monnet 	if (data_in) {
1371ba95c745SQuentin Monnet 		if (!test_attr.data_size_out)
1372ba95c745SQuentin Monnet 			test_attr.data_size_out = default_size;
1373ba95c745SQuentin Monnet 		err = alloc_run_data(&data_out, test_attr.data_size_out);
1374ba95c745SQuentin Monnet 		if (err)
1375ba95c745SQuentin Monnet 			goto free_data_in;
1376ba95c745SQuentin Monnet 	}
1377ba95c745SQuentin Monnet 
1378ba95c745SQuentin Monnet 	err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in);
1379ba95c745SQuentin Monnet 	if (err)
1380ba95c745SQuentin Monnet 		goto free_data_out;
1381ba95c745SQuentin Monnet 
1382ba95c745SQuentin Monnet 	if (ctx_in) {
1383ba95c745SQuentin Monnet 		if (!test_attr.ctx_size_out)
1384ba95c745SQuentin Monnet 			test_attr.ctx_size_out = default_size;
1385ba95c745SQuentin Monnet 		err = alloc_run_data(&ctx_out, test_attr.ctx_size_out);
1386ba95c745SQuentin Monnet 		if (err)
1387ba95c745SQuentin Monnet 			goto free_ctx_in;
1388ba95c745SQuentin Monnet 	}
1389ba95c745SQuentin Monnet 
1390ba95c745SQuentin Monnet 	test_attr.prog_fd	= fd;
1391ba95c745SQuentin Monnet 	test_attr.repeat	= repeat;
1392ba95c745SQuentin Monnet 	test_attr.data_in	= data_in;
1393ba95c745SQuentin Monnet 	test_attr.data_out	= data_out;
1394ba95c745SQuentin Monnet 	test_attr.ctx_in	= ctx_in;
1395ba95c745SQuentin Monnet 	test_attr.ctx_out	= ctx_out;
1396ba95c745SQuentin Monnet 
1397ba95c745SQuentin Monnet 	err = bpf_prog_test_run_xattr(&test_attr);
1398ba95c745SQuentin Monnet 	if (err) {
1399ba95c745SQuentin Monnet 		p_err("failed to run program: %s", strerror(errno));
1400ba95c745SQuentin Monnet 		goto free_ctx_out;
1401ba95c745SQuentin Monnet 	}
1402ba95c745SQuentin Monnet 
1403ba95c745SQuentin Monnet 	err = 0;
1404ba95c745SQuentin Monnet 
1405ba95c745SQuentin Monnet 	if (json_output)
1406ba95c745SQuentin Monnet 		jsonw_start_object(json_wtr);	/* root */
1407ba95c745SQuentin Monnet 
1408ba95c745SQuentin Monnet 	/* Do not exit on errors occurring when printing output data/context,
1409ba95c745SQuentin Monnet 	 * we still want to print return value and duration for program run.
1410ba95c745SQuentin Monnet 	 */
1411ba95c745SQuentin Monnet 	if (test_attr.data_size_out)
1412ba95c745SQuentin Monnet 		err += print_run_output(test_attr.data_out,
1413ba95c745SQuentin Monnet 					test_attr.data_size_out,
1414ba95c745SQuentin Monnet 					data_fname_out, "data_out");
1415ba95c745SQuentin Monnet 	if (test_attr.ctx_size_out)
1416ba95c745SQuentin Monnet 		err += print_run_output(test_attr.ctx_out,
1417ba95c745SQuentin Monnet 					test_attr.ctx_size_out,
1418ba95c745SQuentin Monnet 					ctx_fname_out, "ctx_out");
1419ba95c745SQuentin Monnet 
1420ba95c745SQuentin Monnet 	if (json_output) {
1421ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "retval", test_attr.retval);
1422ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "duration", test_attr.duration);
1423ba95c745SQuentin Monnet 		jsonw_end_object(json_wtr);	/* root */
1424ba95c745SQuentin Monnet 	} else {
1425ba95c745SQuentin Monnet 		fprintf(stdout, "Return value: %u, duration%s: %uns\n",
1426ba95c745SQuentin Monnet 			test_attr.retval,
1427ba95c745SQuentin Monnet 			repeat > 1 ? " (average)" : "", test_attr.duration);
1428ba95c745SQuentin Monnet 	}
1429ba95c745SQuentin Monnet 
1430ba95c745SQuentin Monnet free_ctx_out:
1431ba95c745SQuentin Monnet 	free(ctx_out);
1432ba95c745SQuentin Monnet free_ctx_in:
1433ba95c745SQuentin Monnet 	free(ctx_in);
1434ba95c745SQuentin Monnet free_data_out:
1435ba95c745SQuentin Monnet 	free(data_out);
1436ba95c745SQuentin Monnet free_data_in:
1437ba95c745SQuentin Monnet 	free(data_in);
1438ba95c745SQuentin Monnet 
1439ba95c745SQuentin Monnet 	return err;
1440ba95c745SQuentin Monnet }
1441ba95c745SQuentin Monnet 
14426ae32b29SQuentin Monnet static int
14436ae32b29SQuentin Monnet get_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
14446ae32b29SQuentin Monnet 		      enum bpf_attach_type *expected_attach_type)
14456ae32b29SQuentin Monnet {
14466ae32b29SQuentin Monnet 	libbpf_print_fn_t print_backup;
14476ae32b29SQuentin Monnet 	int ret;
14486ae32b29SQuentin Monnet 
14496ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14506ae32b29SQuentin Monnet 	if (!ret)
14516ae32b29SQuentin Monnet 		return ret;
14526ae32b29SQuentin Monnet 
14536ae32b29SQuentin Monnet 	/* libbpf_prog_type_by_name() failed, let's re-run with debug level */
14546ae32b29SQuentin Monnet 	print_backup = libbpf_set_print(print_all_levels);
14556ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14566ae32b29SQuentin Monnet 	libbpf_set_print(print_backup);
14576ae32b29SQuentin Monnet 
14586ae32b29SQuentin Monnet 	return ret;
14596ae32b29SQuentin Monnet }
14606ae32b29SQuentin Monnet 
146177380998SStanislav Fomichev static int load_with_options(int argc, char **argv, bool first_prog_only)
146249a086c2SRoman Gushchin {
146332e3e58eSAndrii Nakryiko 	enum bpf_prog_type common_prog_type = BPF_PROG_TYPE_UNSPEC;
1464e00aca65SAndrii Nakryiko 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
1465e00aca65SAndrii Nakryiko 		.relaxed_maps = relaxed_maps,
1466e00aca65SAndrii Nakryiko 	);
146755d77807SQuentin Monnet 	enum bpf_attach_type expected_attach_type;
14683ff5a4dcSJakub Kicinski 	struct map_replace *map_replace = NULL;
146977380998SStanislav Fomichev 	struct bpf_program *prog = NULL, *pos;
14703ff5a4dcSJakub Kicinski 	unsigned int old_map_fds = 0;
14713767a94bSStanislav Fomichev 	const char *pinmaps = NULL;
147249a086c2SRoman Gushchin 	struct bpf_object *obj;
1473c8406848SJakub Kicinski 	struct bpf_map *map;
1474c8406848SJakub Kicinski 	const char *pinfile;
14753ff5a4dcSJakub Kicinski 	unsigned int i, j;
1476c8406848SJakub Kicinski 	__u32 ifindex = 0;
147732e3e58eSAndrii Nakryiko 	const char *file;
14783ff5a4dcSJakub Kicinski 	int idx, err;
147949a086c2SRoman Gushchin 
148032e3e58eSAndrii Nakryiko 
14818d1fc3deSJakub Kicinski 	if (!REQ_ARGS(2))
14828d1fc3deSJakub Kicinski 		return -1;
148332e3e58eSAndrii Nakryiko 	file = GET_ARG();
14848d1fc3deSJakub Kicinski 	pinfile = GET_ARG();
148549a086c2SRoman Gushchin 
1486ba6dd679SJakub Kicinski 	while (argc) {
148749f2cba3SJakub Kicinski 		if (is_prefix(*argv, "type")) {
148849f2cba3SJakub Kicinski 			NEXT_ARG();
148949f2cba3SJakub Kicinski 
149032e3e58eSAndrii Nakryiko 			if (common_prog_type != BPF_PROG_TYPE_UNSPEC) {
149149f2cba3SJakub Kicinski 				p_err("program type already specified");
14923ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
149349f2cba3SJakub Kicinski 			}
149449f2cba3SJakub Kicinski 			if (!REQ_ARGS(1))
14953ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
149649f2cba3SJakub Kicinski 
1497314f14abSStanislav Fomichev 			err = libbpf_prog_type_by_name(*argv, &common_prog_type,
1498314f14abSStanislav Fomichev 						       &expected_attach_type);
1499314f14abSStanislav Fomichev 			if (err < 0) {
150049f2cba3SJakub Kicinski 				/* Put a '/' at the end of type to appease libbpf */
1501314f14abSStanislav Fomichev 				char *type = malloc(strlen(*argv) + 2);
1502314f14abSStanislav Fomichev 
150349f2cba3SJakub Kicinski 				if (!type) {
150449f2cba3SJakub Kicinski 					p_err("mem alloc failed");
15053ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
150649f2cba3SJakub Kicinski 				}
150749f2cba3SJakub Kicinski 				*type = 0;
150849f2cba3SJakub Kicinski 				strcat(type, *argv);
150949f2cba3SJakub Kicinski 				strcat(type, "/");
151049f2cba3SJakub Kicinski 
15116ae32b29SQuentin Monnet 				err = get_prog_type_by_name(type, &common_prog_type,
1512c8406848SJakub Kicinski 							    &expected_attach_type);
151349f2cba3SJakub Kicinski 				free(type);
1514c76e4c22STaeung Song 				if (err < 0)
15153ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
1516314f14abSStanislav Fomichev 			}
1517c76e4c22STaeung Song 
151849f2cba3SJakub Kicinski 			NEXT_ARG();
15193ff5a4dcSJakub Kicinski 		} else if (is_prefix(*argv, "map")) {
1520dde7011aSJakub Kicinski 			void *new_map_replace;
15213ff5a4dcSJakub Kicinski 			char *endptr, *name;
15223ff5a4dcSJakub Kicinski 			int fd;
15233ff5a4dcSJakub Kicinski 
15243ff5a4dcSJakub Kicinski 			NEXT_ARG();
15253ff5a4dcSJakub Kicinski 
15263ff5a4dcSJakub Kicinski 			if (!REQ_ARGS(4))
15273ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15283ff5a4dcSJakub Kicinski 
15293ff5a4dcSJakub Kicinski 			if (is_prefix(*argv, "idx")) {
15303ff5a4dcSJakub Kicinski 				NEXT_ARG();
15313ff5a4dcSJakub Kicinski 
15323ff5a4dcSJakub Kicinski 				idx = strtoul(*argv, &endptr, 0);
15333ff5a4dcSJakub Kicinski 				if (*endptr) {
15343ff5a4dcSJakub Kicinski 					p_err("can't parse %s as IDX", *argv);
15353ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
15363ff5a4dcSJakub Kicinski 				}
15373ff5a4dcSJakub Kicinski 				name = NULL;
15383ff5a4dcSJakub Kicinski 			} else if (is_prefix(*argv, "name")) {
15393ff5a4dcSJakub Kicinski 				NEXT_ARG();
15403ff5a4dcSJakub Kicinski 
15413ff5a4dcSJakub Kicinski 				name = *argv;
15423ff5a4dcSJakub Kicinski 				idx = -1;
15433ff5a4dcSJakub Kicinski 			} else {
15443ff5a4dcSJakub Kicinski 				p_err("expected 'idx' or 'name', got: '%s'?",
15453ff5a4dcSJakub Kicinski 				      *argv);
15463ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15473ff5a4dcSJakub Kicinski 			}
15483ff5a4dcSJakub Kicinski 			NEXT_ARG();
15493ff5a4dcSJakub Kicinski 
15503ff5a4dcSJakub Kicinski 			fd = map_parse_fd(&argc, &argv);
15513ff5a4dcSJakub Kicinski 			if (fd < 0)
15523ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15533ff5a4dcSJakub Kicinski 
1554dde7011aSJakub Kicinski 			new_map_replace = reallocarray(map_replace,
1555dde7011aSJakub Kicinski 						       old_map_fds + 1,
15563ff5a4dcSJakub Kicinski 						       sizeof(*map_replace));
1557dde7011aSJakub Kicinski 			if (!new_map_replace) {
15583ff5a4dcSJakub Kicinski 				p_err("mem alloc failed");
15593ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15603ff5a4dcSJakub Kicinski 			}
1561dde7011aSJakub Kicinski 			map_replace = new_map_replace;
1562dde7011aSJakub Kicinski 
15633ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].idx = idx;
15643ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].name = name;
15653ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].fd = fd;
15663ff5a4dcSJakub Kicinski 			old_map_fds++;
156749f2cba3SJakub Kicinski 		} else if (is_prefix(*argv, "dev")) {
1568ba6dd679SJakub Kicinski 			NEXT_ARG();
1569ba6dd679SJakub Kicinski 
1570c8406848SJakub Kicinski 			if (ifindex) {
1571ba6dd679SJakub Kicinski 				p_err("offload device already specified");
15723ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1573ba6dd679SJakub Kicinski 			}
1574ba6dd679SJakub Kicinski 			if (!REQ_ARGS(1))
15753ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1576ba6dd679SJakub Kicinski 
1577c8406848SJakub Kicinski 			ifindex = if_nametoindex(*argv);
1578c8406848SJakub Kicinski 			if (!ifindex) {
1579ba6dd679SJakub Kicinski 				p_err("unrecognized netdevice '%s': %s",
1580ba6dd679SJakub Kicinski 				      *argv, strerror(errno));
15813ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1582ba6dd679SJakub Kicinski 			}
1583ba6dd679SJakub Kicinski 			NEXT_ARG();
15843767a94bSStanislav Fomichev 		} else if (is_prefix(*argv, "pinmaps")) {
15853767a94bSStanislav Fomichev 			NEXT_ARG();
15863767a94bSStanislav Fomichev 
15873767a94bSStanislav Fomichev 			if (!REQ_ARGS(1))
15883767a94bSStanislav Fomichev 				goto err_free_reuse_maps;
15893767a94bSStanislav Fomichev 
15903767a94bSStanislav Fomichev 			pinmaps = GET_ARG();
1591ba6dd679SJakub Kicinski 		} else {
15923ff5a4dcSJakub Kicinski 			p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
1593ba6dd679SJakub Kicinski 			      *argv);
15943ff5a4dcSJakub Kicinski 			goto err_free_reuse_maps;
1595ba6dd679SJakub Kicinski 		}
1596ba6dd679SJakub Kicinski 	}
1597ba6dd679SJakub Kicinski 
1598ac4e0e05SYonghong Song 	set_max_rlimit();
1599ac4e0e05SYonghong Song 
1600*b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1601*b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1602*b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1603*b59e4ce8SAndrii Nakryiko 
160432e3e58eSAndrii Nakryiko 	obj = bpf_object__open_file(file, &open_opts);
1605d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1606c8406848SJakub Kicinski 		p_err("failed to open object file");
16073ff5a4dcSJakub Kicinski 		goto err_free_reuse_maps;
160849a086c2SRoman Gushchin 	}
160949a086c2SRoman Gushchin 
161077380998SStanislav Fomichev 	bpf_object__for_each_program(pos, obj) {
161132e3e58eSAndrii Nakryiko 		enum bpf_prog_type prog_type = common_prog_type;
1612c8406848SJakub Kicinski 
161332e3e58eSAndrii Nakryiko 		if (prog_type == BPF_PROG_TYPE_UNSPEC) {
1614fd17e272SAndrii Nakryiko 			const char *sec_name = bpf_program__section_name(pos);
1615c8406848SJakub Kicinski 
16166ae32b29SQuentin Monnet 			err = get_prog_type_by_name(sec_name, &prog_type,
1617c8406848SJakub Kicinski 						    &expected_attach_type);
1618c76e4c22STaeung Song 			if (err < 0)
1619c8406848SJakub Kicinski 				goto err_close_obj;
1620c8406848SJakub Kicinski 		}
162177380998SStanislav Fomichev 
162277380998SStanislav Fomichev 		bpf_program__set_ifindex(pos, ifindex);
162377380998SStanislav Fomichev 		bpf_program__set_type(pos, prog_type);
162477380998SStanislav Fomichev 		bpf_program__set_expected_attach_type(pos, expected_attach_type);
162577380998SStanislav Fomichev 	}
1626c8406848SJakub Kicinski 
16273ff5a4dcSJakub Kicinski 	qsort(map_replace, old_map_fds, sizeof(*map_replace),
16283ff5a4dcSJakub Kicinski 	      map_replace_compar);
16293ff5a4dcSJakub Kicinski 
16303ff5a4dcSJakub Kicinski 	/* After the sort maps by name will be first on the list, because they
16313ff5a4dcSJakub Kicinski 	 * have idx == -1.  Resolve them.
16323ff5a4dcSJakub Kicinski 	 */
16333ff5a4dcSJakub Kicinski 	j = 0;
16343ff5a4dcSJakub Kicinski 	while (j < old_map_fds && map_replace[j].name) {
16353ff5a4dcSJakub Kicinski 		i = 0;
1636f74a53d9SJakub Kicinski 		bpf_object__for_each_map(map, obj) {
16373ff5a4dcSJakub Kicinski 			if (!strcmp(bpf_map__name(map), map_replace[j].name)) {
16383ff5a4dcSJakub Kicinski 				map_replace[j].idx = i;
16393ff5a4dcSJakub Kicinski 				break;
16403ff5a4dcSJakub Kicinski 			}
16413ff5a4dcSJakub Kicinski 			i++;
16423ff5a4dcSJakub Kicinski 		}
16433ff5a4dcSJakub Kicinski 		if (map_replace[j].idx == -1) {
16443ff5a4dcSJakub Kicinski 			p_err("unable to find map '%s'", map_replace[j].name);
16453ff5a4dcSJakub Kicinski 			goto err_close_obj;
16463ff5a4dcSJakub Kicinski 		}
16473ff5a4dcSJakub Kicinski 		j++;
16483ff5a4dcSJakub Kicinski 	}
16493ff5a4dcSJakub Kicinski 	/* Resort if any names were resolved */
16503ff5a4dcSJakub Kicinski 	if (j)
16513ff5a4dcSJakub Kicinski 		qsort(map_replace, old_map_fds, sizeof(*map_replace),
16523ff5a4dcSJakub Kicinski 		      map_replace_compar);
16533ff5a4dcSJakub Kicinski 
16543ff5a4dcSJakub Kicinski 	/* Set ifindex and name reuse */
16553ff5a4dcSJakub Kicinski 	j = 0;
16563ff5a4dcSJakub Kicinski 	idx = 0;
1657f74a53d9SJakub Kicinski 	bpf_object__for_each_map(map, obj) {
1658c8406848SJakub Kicinski 		if (!bpf_map__is_offload_neutral(map))
1659c8406848SJakub Kicinski 			bpf_map__set_ifindex(map, ifindex);
1660c8406848SJakub Kicinski 
16613ff5a4dcSJakub Kicinski 		if (j < old_map_fds && idx == map_replace[j].idx) {
16623ff5a4dcSJakub Kicinski 			err = bpf_map__reuse_fd(map, map_replace[j++].fd);
16633ff5a4dcSJakub Kicinski 			if (err) {
16643ff5a4dcSJakub Kicinski 				p_err("unable to set up map reuse: %d", err);
16653ff5a4dcSJakub Kicinski 				goto err_close_obj;
16663ff5a4dcSJakub Kicinski 			}
16673ff5a4dcSJakub Kicinski 
16683ff5a4dcSJakub Kicinski 			/* Next reuse wants to apply to the same map */
16693ff5a4dcSJakub Kicinski 			if (j < old_map_fds && map_replace[j].idx == idx) {
16703ff5a4dcSJakub Kicinski 				p_err("replacement for map idx %d specified more than once",
16713ff5a4dcSJakub Kicinski 				      idx);
16723ff5a4dcSJakub Kicinski 				goto err_close_obj;
16733ff5a4dcSJakub Kicinski 			}
16743ff5a4dcSJakub Kicinski 		}
16753ff5a4dcSJakub Kicinski 
16763ff5a4dcSJakub Kicinski 		idx++;
16773ff5a4dcSJakub Kicinski 	}
16783ff5a4dcSJakub Kicinski 	if (j < old_map_fds) {
16793ff5a4dcSJakub Kicinski 		p_err("map idx '%d' not used", map_replace[j].idx);
16803ff5a4dcSJakub Kicinski 		goto err_close_obj;
16813ff5a4dcSJakub Kicinski 	}
16823ff5a4dcSJakub Kicinski 
1683*b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1684c8406848SJakub Kicinski 	if (err) {
1685c8406848SJakub Kicinski 		p_err("failed to load object file");
1686c8406848SJakub Kicinski 		goto err_close_obj;
1687c8406848SJakub Kicinski 	}
1688c8406848SJakub Kicinski 
168977380998SStanislav Fomichev 	err = mount_bpffs_for_pin(pinfile);
169077380998SStanislav Fomichev 	if (err)
1691bfee71fbSJakub Kicinski 		goto err_close_obj;
169249a086c2SRoman Gushchin 
169377380998SStanislav Fomichev 	if (first_prog_only) {
16946f2b219bSHengqi Chen 		prog = bpf_object__next_program(obj, NULL);
169577380998SStanislav Fomichev 		if (!prog) {
169677380998SStanislav Fomichev 			p_err("object file doesn't contain any bpf program");
169777380998SStanislav Fomichev 			goto err_close_obj;
169877380998SStanislav Fomichev 		}
169977380998SStanislav Fomichev 
170077380998SStanislav Fomichev 		err = bpf_obj_pin(bpf_program__fd(prog), pinfile);
170177380998SStanislav Fomichev 		if (err) {
170277380998SStanislav Fomichev 			p_err("failed to pin program %s",
1703fd17e272SAndrii Nakryiko 			      bpf_program__section_name(prog));
170477380998SStanislav Fomichev 			goto err_close_obj;
170577380998SStanislav Fomichev 		}
170677380998SStanislav Fomichev 	} else {
170777380998SStanislav Fomichev 		err = bpf_object__pin_programs(obj, pinfile);
170877380998SStanislav Fomichev 		if (err) {
170977380998SStanislav Fomichev 			p_err("failed to pin all programs");
171077380998SStanislav Fomichev 			goto err_close_obj;
171177380998SStanislav Fomichev 		}
171277380998SStanislav Fomichev 	}
171377380998SStanislav Fomichev 
17143767a94bSStanislav Fomichev 	if (pinmaps) {
17153767a94bSStanislav Fomichev 		err = bpf_object__pin_maps(obj, pinmaps);
17163767a94bSStanislav Fomichev 		if (err) {
17173767a94bSStanislav Fomichev 			p_err("failed to pin all maps");
17183767a94bSStanislav Fomichev 			goto err_unpin;
17193767a94bSStanislav Fomichev 		}
17203767a94bSStanislav Fomichev 	}
17213767a94bSStanislav Fomichev 
172249a086c2SRoman Gushchin 	if (json_output)
172349a086c2SRoman Gushchin 		jsonw_null(json_wtr);
172449a086c2SRoman Gushchin 
1725bfee71fbSJakub Kicinski 	bpf_object__close(obj);
17263ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
17273ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
17283ff5a4dcSJakub Kicinski 	free(map_replace);
1729bfee71fbSJakub Kicinski 
173049a086c2SRoman Gushchin 	return 0;
1731bfee71fbSJakub Kicinski 
17323767a94bSStanislav Fomichev err_unpin:
17333767a94bSStanislav Fomichev 	if (first_prog_only)
17343767a94bSStanislav Fomichev 		unlink(pinfile);
17353767a94bSStanislav Fomichev 	else
17363767a94bSStanislav Fomichev 		bpf_object__unpin_programs(obj, pinfile);
1737bfee71fbSJakub Kicinski err_close_obj:
1738314f14abSStanislav Fomichev 	if (!legacy_libbpf) {
1739314f14abSStanislav Fomichev 		p_info("Warning: bpftool is now running in libbpf strict mode and has more stringent requirements about BPF programs.\n"
1740314f14abSStanislav Fomichev 		       "If it used to work for this object file but now doesn't, see --legacy option for more details.\n");
1741314f14abSStanislav Fomichev 	}
1742314f14abSStanislav Fomichev 
1743bfee71fbSJakub Kicinski 	bpf_object__close(obj);
17443ff5a4dcSJakub Kicinski err_free_reuse_maps:
17453ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
17463ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
17473ff5a4dcSJakub Kicinski 	free(map_replace);
1748bfee71fbSJakub Kicinski 	return -1;
174949a086c2SRoman Gushchin }
175049a086c2SRoman Gushchin 
1751d510296dSAlexei Starovoitov static int count_open_fds(void)
1752d510296dSAlexei Starovoitov {
1753d510296dSAlexei Starovoitov 	DIR *dp = opendir("/proc/self/fd");
1754d510296dSAlexei Starovoitov 	struct dirent *de;
1755d510296dSAlexei Starovoitov 	int cnt = -3;
1756d510296dSAlexei Starovoitov 
1757d510296dSAlexei Starovoitov 	if (!dp)
1758d510296dSAlexei Starovoitov 		return -1;
1759d510296dSAlexei Starovoitov 
1760d510296dSAlexei Starovoitov 	while ((de = readdir(dp)))
1761d510296dSAlexei Starovoitov 		cnt++;
1762d510296dSAlexei Starovoitov 
1763d510296dSAlexei Starovoitov 	closedir(dp);
1764d510296dSAlexei Starovoitov 	return cnt;
1765d510296dSAlexei Starovoitov }
1766d510296dSAlexei Starovoitov 
1767d510296dSAlexei Starovoitov static int try_loader(struct gen_loader_opts *gen)
1768d510296dSAlexei Starovoitov {
1769d510296dSAlexei Starovoitov 	struct bpf_load_and_run_opts opts = {};
1770d510296dSAlexei Starovoitov 	struct bpf_loader_ctx *ctx;
1771d510296dSAlexei Starovoitov 	int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
1772d510296dSAlexei Starovoitov 					     sizeof(struct bpf_prog_desc));
1773d510296dSAlexei Starovoitov 	int log_buf_sz = (1u << 24) - 1;
1774d510296dSAlexei Starovoitov 	int err, fds_before, fd_delta;
1775942df4dcSAlexei Starovoitov 	char *log_buf = NULL;
1776d510296dSAlexei Starovoitov 
1777d510296dSAlexei Starovoitov 	ctx = alloca(ctx_sz);
1778d510296dSAlexei Starovoitov 	memset(ctx, 0, ctx_sz);
1779d510296dSAlexei Starovoitov 	ctx->sz = ctx_sz;
1780942df4dcSAlexei Starovoitov 	if (verifier_logs) {
1781942df4dcSAlexei Starovoitov 		ctx->log_level = 1 + 2 + 4;
1782d510296dSAlexei Starovoitov 		ctx->log_size = log_buf_sz;
1783d510296dSAlexei Starovoitov 		log_buf = malloc(log_buf_sz);
1784d510296dSAlexei Starovoitov 		if (!log_buf)
1785d510296dSAlexei Starovoitov 			return -ENOMEM;
1786d510296dSAlexei Starovoitov 		ctx->log_buf = (long) log_buf;
1787942df4dcSAlexei Starovoitov 	}
1788d510296dSAlexei Starovoitov 	opts.ctx = ctx;
1789d510296dSAlexei Starovoitov 	opts.data = gen->data;
1790d510296dSAlexei Starovoitov 	opts.data_sz = gen->data_sz;
1791d510296dSAlexei Starovoitov 	opts.insns = gen->insns;
1792d510296dSAlexei Starovoitov 	opts.insns_sz = gen->insns_sz;
1793d510296dSAlexei Starovoitov 	fds_before = count_open_fds();
1794d510296dSAlexei Starovoitov 	err = bpf_load_and_run(&opts);
1795d510296dSAlexei Starovoitov 	fd_delta = count_open_fds() - fds_before;
1796942df4dcSAlexei Starovoitov 	if (err < 0 || verifier_logs) {
1797d510296dSAlexei Starovoitov 		fprintf(stderr, "err %d\n%s\n%s", err, opts.errstr, log_buf);
1798942df4dcSAlexei Starovoitov 		if (fd_delta && err < 0)
1799d510296dSAlexei Starovoitov 			fprintf(stderr, "loader prog leaked %d FDs\n",
1800d510296dSAlexei Starovoitov 				fd_delta);
1801d510296dSAlexei Starovoitov 	}
1802d510296dSAlexei Starovoitov 	free(log_buf);
1803d510296dSAlexei Starovoitov 	return err;
1804d510296dSAlexei Starovoitov }
1805d510296dSAlexei Starovoitov 
1806d510296dSAlexei Starovoitov static int do_loader(int argc, char **argv)
1807d510296dSAlexei Starovoitov {
1808d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts);
1809d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(gen_loader_opts, gen);
1810d510296dSAlexei Starovoitov 	struct bpf_object *obj;
1811d510296dSAlexei Starovoitov 	const char *file;
1812d510296dSAlexei Starovoitov 	int err = 0;
1813d510296dSAlexei Starovoitov 
1814d510296dSAlexei Starovoitov 	if (!REQ_ARGS(1))
1815d510296dSAlexei Starovoitov 		return -1;
1816d510296dSAlexei Starovoitov 	file = GET_ARG();
1817d510296dSAlexei Starovoitov 
1818*b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1819*b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1820*b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1821*b59e4ce8SAndrii Nakryiko 
1822d510296dSAlexei Starovoitov 	obj = bpf_object__open_file(file, &open_opts);
1823d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1824d510296dSAlexei Starovoitov 		p_err("failed to open object file");
1825d510296dSAlexei Starovoitov 		goto err_close_obj;
1826d510296dSAlexei Starovoitov 	}
1827d510296dSAlexei Starovoitov 
1828d510296dSAlexei Starovoitov 	err = bpf_object__gen_loader(obj, &gen);
1829d510296dSAlexei Starovoitov 	if (err)
1830d510296dSAlexei Starovoitov 		goto err_close_obj;
1831d510296dSAlexei Starovoitov 
1832*b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1833d510296dSAlexei Starovoitov 	if (err) {
1834d510296dSAlexei Starovoitov 		p_err("failed to load object file");
1835d510296dSAlexei Starovoitov 		goto err_close_obj;
1836d510296dSAlexei Starovoitov 	}
1837d510296dSAlexei Starovoitov 
1838d510296dSAlexei Starovoitov 	if (verifier_logs) {
1839d510296dSAlexei Starovoitov 		struct dump_data dd = {};
1840d510296dSAlexei Starovoitov 
1841d510296dSAlexei Starovoitov 		kernel_syms_load(&dd);
1842d510296dSAlexei Starovoitov 		dump_xlated_plain(&dd, (void *)gen.insns, gen.insns_sz, false, false);
1843d510296dSAlexei Starovoitov 		kernel_syms_destroy(&dd);
1844d510296dSAlexei Starovoitov 	}
1845d510296dSAlexei Starovoitov 	err = try_loader(&gen);
1846d510296dSAlexei Starovoitov err_close_obj:
1847d510296dSAlexei Starovoitov 	bpf_object__close(obj);
1848d510296dSAlexei Starovoitov 	return err;
1849d510296dSAlexei Starovoitov }
1850d510296dSAlexei Starovoitov 
185177380998SStanislav Fomichev static int do_load(int argc, char **argv)
185277380998SStanislav Fomichev {
1853d510296dSAlexei Starovoitov 	if (use_loader)
1854d510296dSAlexei Starovoitov 		return do_loader(argc, argv);
185577380998SStanislav Fomichev 	return load_with_options(argc, argv, true);
185677380998SStanislav Fomichev }
185777380998SStanislav Fomichev 
185877380998SStanislav Fomichev static int do_loadall(int argc, char **argv)
185977380998SStanislav Fomichev {
186077380998SStanislav Fomichev 	return load_with_options(argc, argv, false);
186177380998SStanislav Fomichev }
186277380998SStanislav Fomichev 
186347c09d6aSSong Liu #ifdef BPFTOOL_WITHOUT_SKELETONS
186447c09d6aSSong Liu 
186547c09d6aSSong Liu static int do_profile(int argc, char **argv)
186647c09d6aSSong Liu {
186714e5728fSSong Liu 	p_err("bpftool prog profile command is not supported. Please build bpftool with clang >= 10.0.0");
186847c09d6aSSong Liu 	return 0;
186947c09d6aSSong Liu }
187047c09d6aSSong Liu 
187147c09d6aSSong Liu #else /* BPFTOOL_WITHOUT_SKELETONS */
187247c09d6aSSong Liu 
187347c09d6aSSong Liu #include "profiler.skel.h"
187447c09d6aSSong Liu 
187547c09d6aSSong Liu struct profile_metric {
187647c09d6aSSong Liu 	const char *name;
187747c09d6aSSong Liu 	struct bpf_perf_event_value val;
187847c09d6aSSong Liu 	struct perf_event_attr attr;
187947c09d6aSSong Liu 	bool selected;
188047c09d6aSSong Liu 
188147c09d6aSSong Liu 	/* calculate ratios like instructions per cycle */
188247c09d6aSSong Liu 	const int ratio_metric; /* 0 for N/A, 1 for index 0 (cycles) */
188347c09d6aSSong Liu 	const char *ratio_desc;
188447c09d6aSSong Liu 	const float ratio_mul;
188547c09d6aSSong Liu } metrics[] = {
188647c09d6aSSong Liu 	{
188747c09d6aSSong Liu 		.name = "cycles",
188847c09d6aSSong Liu 		.attr = {
188947c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
189047c09d6aSSong Liu 			.config = PERF_COUNT_HW_CPU_CYCLES,
189147c09d6aSSong Liu 			.exclude_user = 1,
189247c09d6aSSong Liu 		},
189347c09d6aSSong Liu 	},
189447c09d6aSSong Liu 	{
189547c09d6aSSong Liu 		.name = "instructions",
189647c09d6aSSong Liu 		.attr = {
189747c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
189847c09d6aSSong Liu 			.config = PERF_COUNT_HW_INSTRUCTIONS,
189947c09d6aSSong Liu 			.exclude_user = 1,
190047c09d6aSSong Liu 		},
190147c09d6aSSong Liu 		.ratio_metric = 1,
190247c09d6aSSong Liu 		.ratio_desc = "insns per cycle",
190347c09d6aSSong Liu 		.ratio_mul = 1.0,
190447c09d6aSSong Liu 	},
190547c09d6aSSong Liu 	{
190647c09d6aSSong Liu 		.name = "l1d_loads",
190747c09d6aSSong Liu 		.attr = {
190847c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
190947c09d6aSSong Liu 			.config =
191047c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_L1D |
191147c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
191247c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
191347c09d6aSSong Liu 			.exclude_user = 1,
191447c09d6aSSong Liu 		},
191547c09d6aSSong Liu 	},
191647c09d6aSSong Liu 	{
191747c09d6aSSong Liu 		.name = "llc_misses",
191847c09d6aSSong Liu 		.attr = {
191947c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
192047c09d6aSSong Liu 			.config =
192147c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_LL |
192247c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
192347c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
192447c09d6aSSong Liu 			.exclude_user = 1
192547c09d6aSSong Liu 		},
192647c09d6aSSong Liu 		.ratio_metric = 2,
192747c09d6aSSong Liu 		.ratio_desc = "LLC misses per million insns",
192847c09d6aSSong Liu 		.ratio_mul = 1e6,
192947c09d6aSSong Liu 	},
1930450d060eSYonghong Song 	{
1931450d060eSYonghong Song 		.name = "itlb_misses",
1932450d060eSYonghong Song 		.attr = {
1933450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
1934450d060eSYonghong Song 			.config =
1935450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_ITLB |
1936450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
1937450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
1938450d060eSYonghong Song 			.exclude_user = 1
1939450d060eSYonghong Song 		},
1940450d060eSYonghong Song 		.ratio_metric = 2,
1941450d060eSYonghong Song 		.ratio_desc = "itlb misses per million insns",
1942450d060eSYonghong Song 		.ratio_mul = 1e6,
1943450d060eSYonghong Song 	},
1944450d060eSYonghong Song 	{
1945450d060eSYonghong Song 		.name = "dtlb_misses",
1946450d060eSYonghong Song 		.attr = {
1947450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
1948450d060eSYonghong Song 			.config =
1949450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_DTLB |
1950450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
1951450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
1952450d060eSYonghong Song 			.exclude_user = 1
1953450d060eSYonghong Song 		},
1954450d060eSYonghong Song 		.ratio_metric = 2,
1955450d060eSYonghong Song 		.ratio_desc = "dtlb misses per million insns",
1956450d060eSYonghong Song 		.ratio_mul = 1e6,
1957450d060eSYonghong Song 	},
195847c09d6aSSong Liu };
195947c09d6aSSong Liu 
196047c09d6aSSong Liu static __u64 profile_total_count;
196147c09d6aSSong Liu 
196247c09d6aSSong Liu #define MAX_NUM_PROFILE_METRICS 4
196347c09d6aSSong Liu 
196447c09d6aSSong Liu static int profile_parse_metrics(int argc, char **argv)
196547c09d6aSSong Liu {
196647c09d6aSSong Liu 	unsigned int metric_cnt;
196747c09d6aSSong Liu 	int selected_cnt = 0;
196847c09d6aSSong Liu 	unsigned int i;
196947c09d6aSSong Liu 
197047c09d6aSSong Liu 	metric_cnt = sizeof(metrics) / sizeof(struct profile_metric);
197147c09d6aSSong Liu 
197247c09d6aSSong Liu 	while (argc > 0) {
197347c09d6aSSong Liu 		for (i = 0; i < metric_cnt; i++) {
197447c09d6aSSong Liu 			if (is_prefix(argv[0], metrics[i].name)) {
197547c09d6aSSong Liu 				if (!metrics[i].selected)
197647c09d6aSSong Liu 					selected_cnt++;
197747c09d6aSSong Liu 				metrics[i].selected = true;
197847c09d6aSSong Liu 				break;
197947c09d6aSSong Liu 			}
198047c09d6aSSong Liu 		}
198147c09d6aSSong Liu 		if (i == metric_cnt) {
198247c09d6aSSong Liu 			p_err("unknown metric %s", argv[0]);
198347c09d6aSSong Liu 			return -1;
198447c09d6aSSong Liu 		}
198547c09d6aSSong Liu 		NEXT_ARG();
198647c09d6aSSong Liu 	}
198747c09d6aSSong Liu 	if (selected_cnt > MAX_NUM_PROFILE_METRICS) {
198847c09d6aSSong Liu 		p_err("too many (%d) metrics, please specify no more than %d metrics at at time",
198947c09d6aSSong Liu 		      selected_cnt, MAX_NUM_PROFILE_METRICS);
199047c09d6aSSong Liu 		return -1;
199147c09d6aSSong Liu 	}
199247c09d6aSSong Liu 	return selected_cnt;
199347c09d6aSSong Liu }
199447c09d6aSSong Liu 
199547c09d6aSSong Liu static void profile_read_values(struct profiler_bpf *obj)
199647c09d6aSSong Liu {
199747c09d6aSSong Liu 	__u32 m, cpu, num_cpu = obj->rodata->num_cpu;
199847c09d6aSSong Liu 	int reading_map_fd, count_map_fd;
199947c09d6aSSong Liu 	__u64 counts[num_cpu];
200047c09d6aSSong Liu 	__u32 key = 0;
200147c09d6aSSong Liu 	int err;
200247c09d6aSSong Liu 
200347c09d6aSSong Liu 	reading_map_fd = bpf_map__fd(obj->maps.accum_readings);
200447c09d6aSSong Liu 	count_map_fd = bpf_map__fd(obj->maps.counts);
200547c09d6aSSong Liu 	if (reading_map_fd < 0 || count_map_fd < 0) {
200647c09d6aSSong Liu 		p_err("failed to get fd for map");
200747c09d6aSSong Liu 		return;
200847c09d6aSSong Liu 	}
200947c09d6aSSong Liu 
201047c09d6aSSong Liu 	err = bpf_map_lookup_elem(count_map_fd, &key, counts);
201147c09d6aSSong Liu 	if (err) {
201247c09d6aSSong Liu 		p_err("failed to read count_map: %s", strerror(errno));
201347c09d6aSSong Liu 		return;
201447c09d6aSSong Liu 	}
201547c09d6aSSong Liu 
201647c09d6aSSong Liu 	profile_total_count = 0;
201747c09d6aSSong Liu 	for (cpu = 0; cpu < num_cpu; cpu++)
201847c09d6aSSong Liu 		profile_total_count += counts[cpu];
201947c09d6aSSong Liu 
202047c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
202147c09d6aSSong Liu 		struct bpf_perf_event_value values[num_cpu];
202247c09d6aSSong Liu 
202347c09d6aSSong Liu 		if (!metrics[m].selected)
202447c09d6aSSong Liu 			continue;
202547c09d6aSSong Liu 
202647c09d6aSSong Liu 		err = bpf_map_lookup_elem(reading_map_fd, &key, values);
202747c09d6aSSong Liu 		if (err) {
202847c09d6aSSong Liu 			p_err("failed to read reading_map: %s",
202947c09d6aSSong Liu 			      strerror(errno));
203047c09d6aSSong Liu 			return;
203147c09d6aSSong Liu 		}
203247c09d6aSSong Liu 		for (cpu = 0; cpu < num_cpu; cpu++) {
203347c09d6aSSong Liu 			metrics[m].val.counter += values[cpu].counter;
203447c09d6aSSong Liu 			metrics[m].val.enabled += values[cpu].enabled;
203547c09d6aSSong Liu 			metrics[m].val.running += values[cpu].running;
203647c09d6aSSong Liu 		}
203747c09d6aSSong Liu 		key++;
203847c09d6aSSong Liu 	}
203947c09d6aSSong Liu }
204047c09d6aSSong Liu 
204147c09d6aSSong Liu static void profile_print_readings_json(void)
204247c09d6aSSong Liu {
204347c09d6aSSong Liu 	__u32 m;
204447c09d6aSSong Liu 
204547c09d6aSSong Liu 	jsonw_start_array(json_wtr);
204647c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
204747c09d6aSSong Liu 		if (!metrics[m].selected)
204847c09d6aSSong Liu 			continue;
204947c09d6aSSong Liu 		jsonw_start_object(json_wtr);
205047c09d6aSSong Liu 		jsonw_string_field(json_wtr, "metric", metrics[m].name);
205147c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "run_cnt", profile_total_count);
205247c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "value", metrics[m].val.counter);
205347c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "enabled", metrics[m].val.enabled);
205447c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "running", metrics[m].val.running);
205547c09d6aSSong Liu 
205647c09d6aSSong Liu 		jsonw_end_object(json_wtr);
205747c09d6aSSong Liu 	}
205847c09d6aSSong Liu 	jsonw_end_array(json_wtr);
205947c09d6aSSong Liu }
206047c09d6aSSong Liu 
206147c09d6aSSong Liu static void profile_print_readings_plain(void)
206247c09d6aSSong Liu {
206347c09d6aSSong Liu 	__u32 m;
206447c09d6aSSong Liu 
206547c09d6aSSong Liu 	printf("\n%18llu %-20s\n", profile_total_count, "run_cnt");
206647c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
206747c09d6aSSong Liu 		struct bpf_perf_event_value *val = &metrics[m].val;
206847c09d6aSSong Liu 		int r;
206947c09d6aSSong Liu 
207047c09d6aSSong Liu 		if (!metrics[m].selected)
207147c09d6aSSong Liu 			continue;
207247c09d6aSSong Liu 		printf("%18llu %-20s", val->counter, metrics[m].name);
207347c09d6aSSong Liu 
207447c09d6aSSong Liu 		r = metrics[m].ratio_metric - 1;
207547c09d6aSSong Liu 		if (r >= 0 && metrics[r].selected &&
207647c09d6aSSong Liu 		    metrics[r].val.counter > 0) {
207747c09d6aSSong Liu 			printf("# %8.2f %-30s",
207847c09d6aSSong Liu 			       val->counter * metrics[m].ratio_mul /
207947c09d6aSSong Liu 			       metrics[r].val.counter,
208047c09d6aSSong Liu 			       metrics[m].ratio_desc);
208147c09d6aSSong Liu 		} else {
208247c09d6aSSong Liu 			printf("%-41s", "");
208347c09d6aSSong Liu 		}
208447c09d6aSSong Liu 
208547c09d6aSSong Liu 		if (val->enabled > val->running)
208647c09d6aSSong Liu 			printf("(%4.2f%%)",
208747c09d6aSSong Liu 			       val->running * 100.0 / val->enabled);
208847c09d6aSSong Liu 		printf("\n");
208947c09d6aSSong Liu 	}
209047c09d6aSSong Liu }
209147c09d6aSSong Liu 
209247c09d6aSSong Liu static void profile_print_readings(void)
209347c09d6aSSong Liu {
209447c09d6aSSong Liu 	if (json_output)
209547c09d6aSSong Liu 		profile_print_readings_json();
209647c09d6aSSong Liu 	else
209747c09d6aSSong Liu 		profile_print_readings_plain();
209847c09d6aSSong Liu }
209947c09d6aSSong Liu 
210047c09d6aSSong Liu static char *profile_target_name(int tgt_fd)
210147c09d6aSSong Liu {
2102c59765cfSDave Marchevsky 	struct bpf_func_info func_info;
2103c59765cfSDave Marchevsky 	struct bpf_prog_info info = {};
2104c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
210547c09d6aSSong Liu 	const struct btf_type *t;
2106c59765cfSDave Marchevsky 	__u32 func_info_rec_size;
2107369e955bSQuentin Monnet 	struct btf *btf = NULL;
210847c09d6aSSong Liu 	char *name = NULL;
2109c59765cfSDave Marchevsky 	int err;
211047c09d6aSSong Liu 
2111c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2112c59765cfSDave Marchevsky 	if (err) {
2113c59765cfSDave Marchevsky 		p_err("failed to bpf_obj_get_info_by_fd for prog FD %d", tgt_fd);
2114c59765cfSDave Marchevsky 		goto out;
211547c09d6aSSong Liu 	}
211647c09d6aSSong Liu 
2117c59765cfSDave Marchevsky 	if (info.btf_id == 0) {
211847c09d6aSSong Liu 		p_err("prog FD %d doesn't have valid btf", tgt_fd);
211947c09d6aSSong Liu 		goto out;
212047c09d6aSSong Liu 	}
212147c09d6aSSong Liu 
2122c59765cfSDave Marchevsky 	func_info_rec_size = info.func_info_rec_size;
2123c59765cfSDave Marchevsky 	if (info.nr_func_info == 0) {
2124c59765cfSDave Marchevsky 		p_err("bpf_obj_get_info_by_fd for prog FD %d found 0 func_info", tgt_fd);
2125c59765cfSDave Marchevsky 		goto out;
2126c59765cfSDave Marchevsky 	}
2127c59765cfSDave Marchevsky 
2128c59765cfSDave Marchevsky 	memset(&info, 0, sizeof(info));
2129c59765cfSDave Marchevsky 	info.nr_func_info = 1;
2130c59765cfSDave Marchevsky 	info.func_info_rec_size = func_info_rec_size;
2131c59765cfSDave Marchevsky 	info.func_info = ptr_to_u64(&func_info);
2132c59765cfSDave Marchevsky 
2133c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2134c59765cfSDave Marchevsky 	if (err) {
2135c59765cfSDave Marchevsky 		p_err("failed to get func_info for prog FD %d", tgt_fd);
2136c59765cfSDave Marchevsky 		goto out;
2137c59765cfSDave Marchevsky 	}
2138c59765cfSDave Marchevsky 
2139c59765cfSDave Marchevsky 	btf = btf__load_from_kernel_by_id(info.btf_id);
214086f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf)) {
214186f4b7f2SQuentin Monnet 		p_err("failed to load btf for prog FD %d", tgt_fd);
214286f4b7f2SQuentin Monnet 		goto out;
214386f4b7f2SQuentin Monnet 	}
214486f4b7f2SQuentin Monnet 
2145c59765cfSDave Marchevsky 	t = btf__type_by_id(btf, func_info.type_id);
214647c09d6aSSong Liu 	if (!t) {
214747c09d6aSSong Liu 		p_err("btf %d doesn't have type %d",
2148c59765cfSDave Marchevsky 		      info.btf_id, func_info.type_id);
214947c09d6aSSong Liu 		goto out;
215047c09d6aSSong Liu 	}
215147c09d6aSSong Liu 	name = strdup(btf__name_by_offset(btf, t->name_off));
215247c09d6aSSong Liu out:
2153369e955bSQuentin Monnet 	btf__free(btf);
215447c09d6aSSong Liu 	return name;
215547c09d6aSSong Liu }
215647c09d6aSSong Liu 
215747c09d6aSSong Liu static struct profiler_bpf *profile_obj;
215847c09d6aSSong Liu static int profile_tgt_fd = -1;
215947c09d6aSSong Liu static char *profile_tgt_name;
216047c09d6aSSong Liu static int *profile_perf_events;
216147c09d6aSSong Liu static int profile_perf_event_cnt;
216247c09d6aSSong Liu 
216347c09d6aSSong Liu static void profile_close_perf_events(struct profiler_bpf *obj)
216447c09d6aSSong Liu {
216547c09d6aSSong Liu 	int i;
216647c09d6aSSong Liu 
216747c09d6aSSong Liu 	for (i = profile_perf_event_cnt - 1; i >= 0; i--)
216847c09d6aSSong Liu 		close(profile_perf_events[i]);
216947c09d6aSSong Liu 
217047c09d6aSSong Liu 	free(profile_perf_events);
217147c09d6aSSong Liu 	profile_perf_event_cnt = 0;
217247c09d6aSSong Liu }
217347c09d6aSSong Liu 
217447c09d6aSSong Liu static int profile_open_perf_events(struct profiler_bpf *obj)
217547c09d6aSSong Liu {
217647c09d6aSSong Liu 	unsigned int cpu, m;
217747c09d6aSSong Liu 	int map_fd, pmu_fd;
217847c09d6aSSong Liu 
217947c09d6aSSong Liu 	profile_perf_events = calloc(
218047c09d6aSSong Liu 		sizeof(int), obj->rodata->num_cpu * obj->rodata->num_metric);
218147c09d6aSSong Liu 	if (!profile_perf_events) {
218247c09d6aSSong Liu 		p_err("failed to allocate memory for perf_event array: %s",
218347c09d6aSSong Liu 		      strerror(errno));
218447c09d6aSSong Liu 		return -1;
218547c09d6aSSong Liu 	}
218647c09d6aSSong Liu 	map_fd = bpf_map__fd(obj->maps.events);
218747c09d6aSSong Liu 	if (map_fd < 0) {
218847c09d6aSSong Liu 		p_err("failed to get fd for events map");
218947c09d6aSSong Liu 		return -1;
219047c09d6aSSong Liu 	}
219147c09d6aSSong Liu 
219247c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
219347c09d6aSSong Liu 		if (!metrics[m].selected)
219447c09d6aSSong Liu 			continue;
219547c09d6aSSong Liu 		for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) {
219647c09d6aSSong Liu 			pmu_fd = syscall(__NR_perf_event_open, &metrics[m].attr,
219747c09d6aSSong Liu 					 -1/*pid*/, cpu, -1/*group_fd*/, 0);
219847c09d6aSSong Liu 			if (pmu_fd < 0 ||
219947c09d6aSSong Liu 			    bpf_map_update_elem(map_fd, &profile_perf_event_cnt,
220047c09d6aSSong Liu 						&pmu_fd, BPF_ANY) ||
220147c09d6aSSong Liu 			    ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
220247c09d6aSSong Liu 				p_err("failed to create event %s on cpu %d",
220347c09d6aSSong Liu 				      metrics[m].name, cpu);
220447c09d6aSSong Liu 				return -1;
220547c09d6aSSong Liu 			}
220647c09d6aSSong Liu 			profile_perf_events[profile_perf_event_cnt++] = pmu_fd;
220747c09d6aSSong Liu 		}
220847c09d6aSSong Liu 	}
220947c09d6aSSong Liu 	return 0;
221047c09d6aSSong Liu }
221147c09d6aSSong Liu 
221247c09d6aSSong Liu static void profile_print_and_cleanup(void)
221347c09d6aSSong Liu {
221447c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
221547c09d6aSSong Liu 	profile_read_values(profile_obj);
221647c09d6aSSong Liu 	profile_print_readings();
221747c09d6aSSong Liu 	profiler_bpf__destroy(profile_obj);
221847c09d6aSSong Liu 
221947c09d6aSSong Liu 	close(profile_tgt_fd);
222047c09d6aSSong Liu 	free(profile_tgt_name);
222147c09d6aSSong Liu }
222247c09d6aSSong Liu 
222347c09d6aSSong Liu static void int_exit(int signo)
222447c09d6aSSong Liu {
222547c09d6aSSong Liu 	profile_print_and_cleanup();
222647c09d6aSSong Liu 	exit(0);
222747c09d6aSSong Liu }
222847c09d6aSSong Liu 
222947c09d6aSSong Liu static int do_profile(int argc, char **argv)
223047c09d6aSSong Liu {
223147c09d6aSSong Liu 	int num_metric, num_cpu, err = -1;
223247c09d6aSSong Liu 	struct bpf_program *prog;
223347c09d6aSSong Liu 	unsigned long duration;
223447c09d6aSSong Liu 	char *endptr;
223547c09d6aSSong Liu 
223647c09d6aSSong Liu 	/* we at least need two args for the prog and one metric */
223747c09d6aSSong Liu 	if (!REQ_ARGS(3))
223847c09d6aSSong Liu 		return -EINVAL;
223947c09d6aSSong Liu 
224047c09d6aSSong Liu 	/* parse target fd */
224147c09d6aSSong Liu 	profile_tgt_fd = prog_parse_fd(&argc, &argv);
224247c09d6aSSong Liu 	if (profile_tgt_fd < 0) {
224347c09d6aSSong Liu 		p_err("failed to parse fd");
224447c09d6aSSong Liu 		return -1;
224547c09d6aSSong Liu 	}
224647c09d6aSSong Liu 
224747c09d6aSSong Liu 	/* parse profiling optional duration */
224847c09d6aSSong Liu 	if (argc > 2 && is_prefix(argv[0], "duration")) {
224947c09d6aSSong Liu 		NEXT_ARG();
225047c09d6aSSong Liu 		duration = strtoul(*argv, &endptr, 0);
225147c09d6aSSong Liu 		if (*endptr)
225247c09d6aSSong Liu 			usage();
225347c09d6aSSong Liu 		NEXT_ARG();
225447c09d6aSSong Liu 	} else {
225547c09d6aSSong Liu 		duration = UINT_MAX;
225647c09d6aSSong Liu 	}
225747c09d6aSSong Liu 
225847c09d6aSSong Liu 	num_metric = profile_parse_metrics(argc, argv);
225947c09d6aSSong Liu 	if (num_metric <= 0)
226047c09d6aSSong Liu 		goto out;
226147c09d6aSSong Liu 
226247c09d6aSSong Liu 	num_cpu = libbpf_num_possible_cpus();
226347c09d6aSSong Liu 	if (num_cpu <= 0) {
226447c09d6aSSong Liu 		p_err("failed to identify number of CPUs");
226547c09d6aSSong Liu 		goto out;
226647c09d6aSSong Liu 	}
226747c09d6aSSong Liu 
226847c09d6aSSong Liu 	profile_obj = profiler_bpf__open();
226947c09d6aSSong Liu 	if (!profile_obj) {
227047c09d6aSSong Liu 		p_err("failed to open and/or load BPF object");
227147c09d6aSSong Liu 		goto out;
227247c09d6aSSong Liu 	}
227347c09d6aSSong Liu 
227447c09d6aSSong Liu 	profile_obj->rodata->num_cpu = num_cpu;
227547c09d6aSSong Liu 	profile_obj->rodata->num_metric = num_metric;
227647c09d6aSSong Liu 
227747c09d6aSSong Liu 	/* adjust map sizes */
227847c09d6aSSong Liu 	bpf_map__resize(profile_obj->maps.events, num_metric * num_cpu);
227947c09d6aSSong Liu 	bpf_map__resize(profile_obj->maps.fentry_readings, num_metric);
228047c09d6aSSong Liu 	bpf_map__resize(profile_obj->maps.accum_readings, num_metric);
228147c09d6aSSong Liu 	bpf_map__resize(profile_obj->maps.counts, 1);
228247c09d6aSSong Liu 
228347c09d6aSSong Liu 	/* change target name */
228447c09d6aSSong Liu 	profile_tgt_name = profile_target_name(profile_tgt_fd);
228547c09d6aSSong Liu 	if (!profile_tgt_name)
228647c09d6aSSong Liu 		goto out;
228747c09d6aSSong Liu 
228847c09d6aSSong Liu 	bpf_object__for_each_program(prog, profile_obj->obj) {
228947c09d6aSSong Liu 		err = bpf_program__set_attach_target(prog, profile_tgt_fd,
229047c09d6aSSong Liu 						     profile_tgt_name);
229147c09d6aSSong Liu 		if (err) {
229247c09d6aSSong Liu 			p_err("failed to set attach target\n");
229347c09d6aSSong Liu 			goto out;
229447c09d6aSSong Liu 		}
229547c09d6aSSong Liu 	}
229647c09d6aSSong Liu 
229747c09d6aSSong Liu 	set_max_rlimit();
229847c09d6aSSong Liu 	err = profiler_bpf__load(profile_obj);
229947c09d6aSSong Liu 	if (err) {
230047c09d6aSSong Liu 		p_err("failed to load profile_obj");
230147c09d6aSSong Liu 		goto out;
230247c09d6aSSong Liu 	}
230347c09d6aSSong Liu 
230447c09d6aSSong Liu 	err = profile_open_perf_events(profile_obj);
230547c09d6aSSong Liu 	if (err)
230647c09d6aSSong Liu 		goto out;
230747c09d6aSSong Liu 
230847c09d6aSSong Liu 	err = profiler_bpf__attach(profile_obj);
230947c09d6aSSong Liu 	if (err) {
231047c09d6aSSong Liu 		p_err("failed to attach profile_obj");
231147c09d6aSSong Liu 		goto out;
231247c09d6aSSong Liu 	}
231347c09d6aSSong Liu 	signal(SIGINT, int_exit);
231447c09d6aSSong Liu 
231547c09d6aSSong Liu 	sleep(duration);
231647c09d6aSSong Liu 	profile_print_and_cleanup();
231747c09d6aSSong Liu 	return 0;
231847c09d6aSSong Liu 
231947c09d6aSSong Liu out:
232047c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
232147c09d6aSSong Liu 	if (profile_obj)
232247c09d6aSSong Liu 		profiler_bpf__destroy(profile_obj);
232347c09d6aSSong Liu 	close(profile_tgt_fd);
232447c09d6aSSong Liu 	free(profile_tgt_name);
232547c09d6aSSong Liu 	return err;
232647c09d6aSSong Liu }
232747c09d6aSSong Liu 
232847c09d6aSSong Liu #endif /* BPFTOOL_WITHOUT_SKELETONS */
232947c09d6aSSong Liu 
233071bb428fSJakub Kicinski static int do_help(int argc, char **argv)
233171bb428fSJakub Kicinski {
2332004b45c0SQuentin Monnet 	if (json_output) {
2333004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
2334004b45c0SQuentin Monnet 		return 0;
2335004b45c0SQuentin Monnet 	}
2336004b45c0SQuentin Monnet 
233771bb428fSJakub Kicinski 	fprintf(stderr,
233890040351SQuentin Monnet 		"Usage: %1$s %2$s { show | list } [PROG]\n"
233990040351SQuentin Monnet 		"       %1$s %2$s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n"
234090040351SQuentin Monnet 		"       %1$s %2$s dump jited  PROG [{ file FILE | opcodes | linum }]\n"
234190040351SQuentin Monnet 		"       %1$s %2$s pin   PROG FILE\n"
234290040351SQuentin Monnet 		"       %1$s %2$s { load | loadall } OBJ  PATH \\\n"
234377380998SStanislav Fomichev 		"                         [type TYPE] [dev NAME] \\\n"
23443767a94bSStanislav Fomichev 		"                         [map { idx IDX | name NAME } MAP]\\\n"
23453767a94bSStanislav Fomichev 		"                         [pinmaps MAP_DIR]\n"
234690040351SQuentin Monnet 		"       %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n"
234790040351SQuentin Monnet 		"       %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n"
234890040351SQuentin Monnet 		"       %1$s %2$s run PROG \\\n"
2349ba95c745SQuentin Monnet 		"                         data_in FILE \\\n"
2350ba95c745SQuentin Monnet 		"                         [data_out FILE [data_size_out L]] \\\n"
2351ba95c745SQuentin Monnet 		"                         [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
2352ba95c745SQuentin Monnet 		"                         [repeat N]\n"
235390040351SQuentin Monnet 		"       %1$s %2$s profile PROG [duration DURATION] METRICs\n"
235490040351SQuentin Monnet 		"       %1$s %2$s tracelog\n"
235590040351SQuentin Monnet 		"       %1$s %2$s help\n"
235671bb428fSJakub Kicinski 		"\n"
23573ff5a4dcSJakub Kicinski 		"       " HELP_SPEC_MAP "\n"
235871bb428fSJakub Kicinski 		"       " HELP_SPEC_PROGRAM "\n"
235949f2cba3SJakub Kicinski 		"       TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
236049f2cba3SJakub Kicinski 		"                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
236149f2cba3SJakub Kicinski 		"                 cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
236249f2cba3SJakub Kicinski 		"                 lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
2363f25377eeSAndrey Ignatov 		"                 sk_reuseport | flow_dissector | cgroup/sysctl |\n"
236449f2cba3SJakub Kicinski 		"                 cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
236549f2cba3SJakub Kicinski 		"                 cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
236605ee19c1SDaniel Borkmann 		"                 cgroup/getpeername4 | cgroup/getpeername6 |\n"
236705ee19c1SDaniel Borkmann 		"                 cgroup/getsockname4 | cgroup/getsockname6 | cgroup/sendmsg4 |\n"
236805ee19c1SDaniel Borkmann 		"                 cgroup/sendmsg6 | cgroup/recvmsg4 | cgroup/recvmsg6 |\n"
2369a8deba85SLiu Jian 		"                 cgroup/getsockopt | cgroup/setsockopt | cgroup/sock_release |\n"
237093a3545dSJakub Sitnicki 		"                 struct_ops | fentry | fexit | freplace | sk_lookup }\n"
2371b544342eSQuentin Monnet 		"       ATTACH_TYPE := { msg_verdict | skb_verdict | stream_verdict |\n"
2372b544342eSQuentin Monnet 		"                        stream_parser | flow_dissector }\n"
2373450d060eSYonghong Song 		"       METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
2374c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " |\n"
23758cc8c635SQuentin Monnet 		"                    {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
23768cc8c635SQuentin Monnet 		"                    {-L|--use-loader} }\n"
237771bb428fSJakub Kicinski 		"",
237890040351SQuentin Monnet 		bin_name, argv[-2]);
237971bb428fSJakub Kicinski 
238071bb428fSJakub Kicinski 	return 0;
238171bb428fSJakub Kicinski }
238271bb428fSJakub Kicinski 
238371bb428fSJakub Kicinski static const struct cmd cmds[] = {
238471bb428fSJakub Kicinski 	{ "show",	do_show },
23856ebe6dbdSJakub Kicinski 	{ "list",	do_show },
23869f606179SQuentin Monnet 	{ "help",	do_help },
238771bb428fSJakub Kicinski 	{ "dump",	do_dump },
238871bb428fSJakub Kicinski 	{ "pin",	do_pin },
238949a086c2SRoman Gushchin 	{ "load",	do_load },
239077380998SStanislav Fomichev 	{ "loadall",	do_loadall },
2391b7d3826cSJohn Fastabend 	{ "attach",	do_attach },
2392b7d3826cSJohn Fastabend 	{ "detach",	do_detach },
239330da46b5SQuentin Monnet 	{ "tracelog",	do_tracelog },
2394ba95c745SQuentin Monnet 	{ "run",	do_run },
239547c09d6aSSong Liu 	{ "profile",	do_profile },
239671bb428fSJakub Kicinski 	{ 0 }
239771bb428fSJakub Kicinski };
239871bb428fSJakub Kicinski 
239971bb428fSJakub Kicinski int do_prog(int argc, char **argv)
240071bb428fSJakub Kicinski {
240171bb428fSJakub Kicinski 	return cmd_select(cmds, argc, argv, do_help);
240271bb428fSJakub Kicinski }
2403