xref: /openbmc/linux/tools/bpf/bpftool/prog.c (revision b700eeb406a6c1f4d955242e06151f11f13d3e29)
102ff58dcSJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
202ff58dcSJakub Kicinski /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
371bb428fSJakub Kicinski 
43ff5a4dcSJakub Kicinski #define _GNU_SOURCE
571bb428fSJakub Kicinski #include <errno.h>
671bb428fSJakub Kicinski #include <fcntl.h>
747c09d6aSSong Liu #include <signal.h>
8c9c35995SJakub Kicinski #include <stdarg.h>
971bb428fSJakub Kicinski #include <stdio.h>
1071bb428fSJakub Kicinski #include <stdlib.h>
1171bb428fSJakub Kicinski #include <string.h>
1271bb428fSJakub Kicinski #include <time.h>
1371bb428fSJakub Kicinski #include <unistd.h>
14ba6dd679SJakub Kicinski #include <net/if.h>
1547c09d6aSSong Liu #include <sys/ioctl.h>
1671bb428fSJakub Kicinski #include <sys/types.h>
1771bb428fSJakub Kicinski #include <sys/stat.h>
1847c09d6aSSong Liu #include <sys/syscall.h>
19d510296dSAlexei Starovoitov #include <dirent.h>
2071bb428fSJakub Kicinski 
21c8406848SJakub Kicinski #include <linux/err.h>
2247c09d6aSSong Liu #include <linux/perf_event.h>
23ba95c745SQuentin Monnet #include <linux/sizes.h>
24c8406848SJakub Kicinski 
25229c3b47SToke Høiland-Jørgensen #include <bpf/bpf.h>
26229c3b47SToke Høiland-Jørgensen #include <bpf/btf.h>
278f184732SQuentin Monnet #include <bpf/hashmap.h>
28229c3b47SToke Høiland-Jørgensen #include <bpf/libbpf.h>
29a19df713SMauricio Vásquez #include <bpf/libbpf_internal.h>
30d510296dSAlexei Starovoitov #include <bpf/skel_internal.h>
3171bb428fSJakub Kicinski 
32b6c1cedbSJiong Wang #include "cfg.h"
3371bb428fSJakub Kicinski #include "main.h"
3473bb5b4fSJiong Wang #include "xlated_dumper.h"
3571bb428fSJakub Kicinski 
36aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX "bpf_metadata_"
37aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX_LEN (sizeof(BPF_METADATA_PREFIX) - 1)
38aff52e68SYiFei Zhu 
39ec202509SPaul Chaignon enum dump_mode {
40ec202509SPaul Chaignon 	DUMP_JITED,
41ec202509SPaul Chaignon 	DUMP_XLATED,
42ec202509SPaul Chaignon };
43ec202509SPaul Chaignon 
44b7d3826cSJohn Fastabend static const char * const attach_type_strings[] = {
45b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_PARSER] = "stream_parser",
46b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict",
47a7ba4558SCong Wang 	[BPF_SK_SKB_VERDICT] = "skb_verdict",
48b7d3826cSJohn Fastabend 	[BPF_SK_MSG_VERDICT] = "msg_verdict",
49092f0892SStanislav Fomichev 	[BPF_FLOW_DISSECTOR] = "flow_dissector",
50b7d3826cSJohn Fastabend 	[__MAX_BPF_ATTACH_TYPE] = NULL,
51b7d3826cSJohn Fastabend };
52b7d3826cSJohn Fastabend 
538f184732SQuentin Monnet static struct hashmap *prog_table;
5446241271SQuentin Monnet 
55c101189bSQuentin Monnet static enum bpf_attach_type parse_attach_type(const char *str)
56b7d3826cSJohn Fastabend {
57b7d3826cSJohn Fastabend 	enum bpf_attach_type type;
58b7d3826cSJohn Fastabend 
59b7d3826cSJohn Fastabend 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
60b7d3826cSJohn Fastabend 		if (attach_type_strings[type] &&
61b7d3826cSJohn Fastabend 		    is_prefix(str, attach_type_strings[type]))
62b7d3826cSJohn Fastabend 			return type;
63b7d3826cSJohn Fastabend 	}
64b7d3826cSJohn Fastabend 
65b7d3826cSJohn Fastabend 	return __MAX_BPF_ATTACH_TYPE;
66b7d3826cSJohn Fastabend }
67b7d3826cSJohn Fastabend 
68c59765cfSDave Marchevsky static int prep_prog_info(struct bpf_prog_info *const info, enum dump_mode mode,
69c59765cfSDave Marchevsky 			  void **info_data, size_t *const info_data_sz)
70c59765cfSDave Marchevsky {
71c59765cfSDave Marchevsky 	struct bpf_prog_info holder = {};
72c59765cfSDave Marchevsky 	size_t needed = 0;
73c59765cfSDave Marchevsky 	void *ptr;
74c59765cfSDave Marchevsky 
75c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
76c59765cfSDave Marchevsky 		holder.jited_prog_len = info->jited_prog_len;
77c59765cfSDave Marchevsky 		needed += info->jited_prog_len;
78c59765cfSDave Marchevsky 	} else {
79c59765cfSDave Marchevsky 		holder.xlated_prog_len = info->xlated_prog_len;
80c59765cfSDave Marchevsky 		needed += info->xlated_prog_len;
81c59765cfSDave Marchevsky 	}
82c59765cfSDave Marchevsky 
83c59765cfSDave Marchevsky 	holder.nr_jited_ksyms = info->nr_jited_ksyms;
84c59765cfSDave Marchevsky 	needed += info->nr_jited_ksyms * sizeof(__u64);
85c59765cfSDave Marchevsky 
86c59765cfSDave Marchevsky 	holder.nr_jited_func_lens = info->nr_jited_func_lens;
87c59765cfSDave Marchevsky 	needed += info->nr_jited_func_lens * sizeof(__u32);
88c59765cfSDave Marchevsky 
89c59765cfSDave Marchevsky 	holder.nr_func_info = info->nr_func_info;
90c59765cfSDave Marchevsky 	holder.func_info_rec_size = info->func_info_rec_size;
91c59765cfSDave Marchevsky 	needed += info->nr_func_info * info->func_info_rec_size;
92c59765cfSDave Marchevsky 
93c59765cfSDave Marchevsky 	holder.nr_line_info = info->nr_line_info;
94c59765cfSDave Marchevsky 	holder.line_info_rec_size = info->line_info_rec_size;
95c59765cfSDave Marchevsky 	needed += info->nr_line_info * info->line_info_rec_size;
96c59765cfSDave Marchevsky 
97c59765cfSDave Marchevsky 	holder.nr_jited_line_info = info->nr_jited_line_info;
98c59765cfSDave Marchevsky 	holder.jited_line_info_rec_size = info->jited_line_info_rec_size;
99c59765cfSDave Marchevsky 	needed += info->nr_jited_line_info * info->jited_line_info_rec_size;
100c59765cfSDave Marchevsky 
101c59765cfSDave Marchevsky 	if (needed > *info_data_sz) {
102c59765cfSDave Marchevsky 		ptr = realloc(*info_data, needed);
103c59765cfSDave Marchevsky 		if (!ptr)
104c59765cfSDave Marchevsky 			return -1;
105c59765cfSDave Marchevsky 
106c59765cfSDave Marchevsky 		*info_data = ptr;
107c59765cfSDave Marchevsky 		*info_data_sz = needed;
108c59765cfSDave Marchevsky 	}
109c59765cfSDave Marchevsky 	ptr = *info_data;
110c59765cfSDave Marchevsky 
111c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
112c59765cfSDave Marchevsky 		holder.jited_prog_insns = ptr_to_u64(ptr);
113c59765cfSDave Marchevsky 		ptr += holder.jited_prog_len;
114c59765cfSDave Marchevsky 	} else {
115c59765cfSDave Marchevsky 		holder.xlated_prog_insns = ptr_to_u64(ptr);
116c59765cfSDave Marchevsky 		ptr += holder.xlated_prog_len;
117c59765cfSDave Marchevsky 	}
118c59765cfSDave Marchevsky 
119c59765cfSDave Marchevsky 	holder.jited_ksyms = ptr_to_u64(ptr);
120c59765cfSDave Marchevsky 	ptr += holder.nr_jited_ksyms * sizeof(__u64);
121c59765cfSDave Marchevsky 
122c59765cfSDave Marchevsky 	holder.jited_func_lens = ptr_to_u64(ptr);
123c59765cfSDave Marchevsky 	ptr += holder.nr_jited_func_lens * sizeof(__u32);
124c59765cfSDave Marchevsky 
125c59765cfSDave Marchevsky 	holder.func_info = ptr_to_u64(ptr);
126c59765cfSDave Marchevsky 	ptr += holder.nr_func_info * holder.func_info_rec_size;
127c59765cfSDave Marchevsky 
128c59765cfSDave Marchevsky 	holder.line_info = ptr_to_u64(ptr);
129c59765cfSDave Marchevsky 	ptr += holder.nr_line_info * holder.line_info_rec_size;
130c59765cfSDave Marchevsky 
131c59765cfSDave Marchevsky 	holder.jited_line_info = ptr_to_u64(ptr);
132c59765cfSDave Marchevsky 	ptr += holder.nr_jited_line_info * holder.jited_line_info_rec_size;
133c59765cfSDave Marchevsky 
134c59765cfSDave Marchevsky 	*info = holder;
135c59765cfSDave Marchevsky 	return 0;
136c59765cfSDave Marchevsky }
137c59765cfSDave Marchevsky 
13871bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
13971bb428fSJakub Kicinski {
14071bb428fSJakub Kicinski 	struct timespec real_time_ts, boot_time_ts;
14171bb428fSJakub Kicinski 	time_t wallclock_secs;
14271bb428fSJakub Kicinski 	struct tm load_tm;
14371bb428fSJakub Kicinski 
14471bb428fSJakub Kicinski 	buf[--size] = '\0';
14571bb428fSJakub Kicinski 
14671bb428fSJakub Kicinski 	if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
14771bb428fSJakub Kicinski 	    clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
14871bb428fSJakub Kicinski 		perror("Can't read clocks");
14971bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
15071bb428fSJakub Kicinski 		return;
15171bb428fSJakub Kicinski 	}
15271bb428fSJakub Kicinski 
15371bb428fSJakub Kicinski 	wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
15407480cbcSJakub Kicinski 		(real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) /
15507480cbcSJakub Kicinski 		1000000000;
15607480cbcSJakub Kicinski 
15771bb428fSJakub Kicinski 
15871bb428fSJakub Kicinski 	if (!localtime_r(&wallclock_secs, &load_tm)) {
15971bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
16071bb428fSJakub Kicinski 		return;
16171bb428fSJakub Kicinski 	}
16271bb428fSJakub Kicinski 
163a3fe1f6fSQuentin Monnet 	if (json_output)
164a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%s", &load_tm);
165a3fe1f6fSQuentin Monnet 	else
166a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%FT%T%z", &load_tm);
16771bb428fSJakub Kicinski }
16871bb428fSJakub Kicinski 
1696e7e034eSQuentin Monnet static void show_prog_maps(int fd, __u32 num_maps)
17071bb428fSJakub Kicinski {
17171bb428fSJakub Kicinski 	struct bpf_prog_info info = {};
17271bb428fSJakub Kicinski 	__u32 len = sizeof(info);
17371bb428fSJakub Kicinski 	__u32 map_ids[num_maps];
17471bb428fSJakub Kicinski 	unsigned int i;
17571bb428fSJakub Kicinski 	int err;
17671bb428fSJakub Kicinski 
17771bb428fSJakub Kicinski 	info.nr_map_ids = num_maps;
17871bb428fSJakub Kicinski 	info.map_ids = ptr_to_u64(map_ids);
17971bb428fSJakub Kicinski 
18071bb428fSJakub Kicinski 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
18171bb428fSJakub Kicinski 	if (err || !info.nr_map_ids)
18271bb428fSJakub Kicinski 		return;
18371bb428fSJakub Kicinski 
184743cc665SQuentin Monnet 	if (json_output) {
185743cc665SQuentin Monnet 		jsonw_name(json_wtr, "map_ids");
186743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
187743cc665SQuentin Monnet 		for (i = 0; i < info.nr_map_ids; i++)
188743cc665SQuentin Monnet 			jsonw_uint(json_wtr, map_ids[i]);
189743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
190743cc665SQuentin Monnet 	} else {
19171bb428fSJakub Kicinski 		printf("  map_ids ");
19271bb428fSJakub Kicinski 		for (i = 0; i < info.nr_map_ids; i++)
19371bb428fSJakub Kicinski 			printf("%u%s", map_ids[i],
19471bb428fSJakub Kicinski 			       i == info.nr_map_ids - 1 ? "" : ",");
19571bb428fSJakub Kicinski 	}
19671bb428fSJakub Kicinski }
19771bb428fSJakub Kicinski 
198aff52e68SYiFei Zhu static void *find_metadata(int prog_fd, struct bpf_map_info *map_info)
199aff52e68SYiFei Zhu {
200aff52e68SYiFei Zhu 	struct bpf_prog_info prog_info;
201aff52e68SYiFei Zhu 	__u32 prog_info_len;
202aff52e68SYiFei Zhu 	__u32 map_info_len;
203aff52e68SYiFei Zhu 	void *value = NULL;
204aff52e68SYiFei Zhu 	__u32 *map_ids;
205aff52e68SYiFei Zhu 	int nr_maps;
206aff52e68SYiFei Zhu 	int key = 0;
207aff52e68SYiFei Zhu 	int map_fd;
208aff52e68SYiFei Zhu 	int ret;
209aff52e68SYiFei Zhu 	__u32 i;
210aff52e68SYiFei Zhu 
211aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
212aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
213aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
214aff52e68SYiFei Zhu 	if (ret)
215aff52e68SYiFei Zhu 		return NULL;
216aff52e68SYiFei Zhu 
217aff52e68SYiFei Zhu 	if (!prog_info.nr_map_ids)
218aff52e68SYiFei Zhu 		return NULL;
219aff52e68SYiFei Zhu 
220aff52e68SYiFei Zhu 	map_ids = calloc(prog_info.nr_map_ids, sizeof(__u32));
221aff52e68SYiFei Zhu 	if (!map_ids)
222aff52e68SYiFei Zhu 		return NULL;
223aff52e68SYiFei Zhu 
224aff52e68SYiFei Zhu 	nr_maps = prog_info.nr_map_ids;
225aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
226aff52e68SYiFei Zhu 	prog_info.nr_map_ids = nr_maps;
227aff52e68SYiFei Zhu 	prog_info.map_ids = ptr_to_u64(map_ids);
228aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
229aff52e68SYiFei Zhu 
230aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
231aff52e68SYiFei Zhu 	if (ret)
232aff52e68SYiFei Zhu 		goto free_map_ids;
233aff52e68SYiFei Zhu 
234aff52e68SYiFei Zhu 	for (i = 0; i < prog_info.nr_map_ids; i++) {
235aff52e68SYiFei Zhu 		map_fd = bpf_map_get_fd_by_id(map_ids[i]);
236aff52e68SYiFei Zhu 		if (map_fd < 0)
237aff52e68SYiFei Zhu 			goto free_map_ids;
238aff52e68SYiFei Zhu 
239aff52e68SYiFei Zhu 		memset(map_info, 0, sizeof(*map_info));
240aff52e68SYiFei Zhu 		map_info_len = sizeof(*map_info);
241aff52e68SYiFei Zhu 		ret = bpf_obj_get_info_by_fd(map_fd, map_info, &map_info_len);
242aff52e68SYiFei Zhu 		if (ret < 0) {
243aff52e68SYiFei Zhu 			close(map_fd);
244aff52e68SYiFei Zhu 			goto free_map_ids;
245aff52e68SYiFei Zhu 		}
246aff52e68SYiFei Zhu 
247aff52e68SYiFei Zhu 		if (map_info->type != BPF_MAP_TYPE_ARRAY ||
248aff52e68SYiFei Zhu 		    map_info->key_size != sizeof(int) ||
249aff52e68SYiFei Zhu 		    map_info->max_entries != 1 ||
250aff52e68SYiFei Zhu 		    !map_info->btf_value_type_id ||
251aff52e68SYiFei Zhu 		    !strstr(map_info->name, ".rodata")) {
252aff52e68SYiFei Zhu 			close(map_fd);
253aff52e68SYiFei Zhu 			continue;
254aff52e68SYiFei Zhu 		}
255aff52e68SYiFei Zhu 
256aff52e68SYiFei Zhu 		value = malloc(map_info->value_size);
257aff52e68SYiFei Zhu 		if (!value) {
258aff52e68SYiFei Zhu 			close(map_fd);
259aff52e68SYiFei Zhu 			goto free_map_ids;
260aff52e68SYiFei Zhu 		}
261aff52e68SYiFei Zhu 
262aff52e68SYiFei Zhu 		if (bpf_map_lookup_elem(map_fd, &key, value)) {
263aff52e68SYiFei Zhu 			close(map_fd);
264aff52e68SYiFei Zhu 			free(value);
265aff52e68SYiFei Zhu 			value = NULL;
266aff52e68SYiFei Zhu 			goto free_map_ids;
267aff52e68SYiFei Zhu 		}
268aff52e68SYiFei Zhu 
269aff52e68SYiFei Zhu 		close(map_fd);
270aff52e68SYiFei Zhu 		break;
271aff52e68SYiFei Zhu 	}
272aff52e68SYiFei Zhu 
273aff52e68SYiFei Zhu free_map_ids:
274aff52e68SYiFei Zhu 	free(map_ids);
275aff52e68SYiFei Zhu 	return value;
276aff52e68SYiFei Zhu }
277aff52e68SYiFei Zhu 
278aff52e68SYiFei Zhu static bool has_metadata_prefix(const char *s)
279aff52e68SYiFei Zhu {
280aff52e68SYiFei Zhu 	return strncmp(s, BPF_METADATA_PREFIX, BPF_METADATA_PREFIX_LEN) == 0;
281aff52e68SYiFei Zhu }
282aff52e68SYiFei Zhu 
283aff52e68SYiFei Zhu static void show_prog_metadata(int fd, __u32 num_maps)
284aff52e68SYiFei Zhu {
285aff52e68SYiFei Zhu 	const struct btf_type *t_datasec, *t_var;
286aff52e68SYiFei Zhu 	struct bpf_map_info map_info;
287aff52e68SYiFei Zhu 	struct btf_var_secinfo *vsi;
288aff52e68SYiFei Zhu 	bool printed_header = false;
289aff52e68SYiFei Zhu 	unsigned int i, vlen;
290aff52e68SYiFei Zhu 	void *value = NULL;
291aff52e68SYiFei Zhu 	const char *name;
29286f4b7f2SQuentin Monnet 	struct btf *btf;
293aff52e68SYiFei Zhu 	int err;
294aff52e68SYiFei Zhu 
295aff52e68SYiFei Zhu 	if (!num_maps)
296aff52e68SYiFei Zhu 		return;
297aff52e68SYiFei Zhu 
298aff52e68SYiFei Zhu 	memset(&map_info, 0, sizeof(map_info));
299aff52e68SYiFei Zhu 	value = find_metadata(fd, &map_info);
300aff52e68SYiFei Zhu 	if (!value)
301aff52e68SYiFei Zhu 		return;
302aff52e68SYiFei Zhu 
30386f4b7f2SQuentin Monnet 	btf = btf__load_from_kernel_by_id(map_info.btf_id);
30486f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf))
305aff52e68SYiFei Zhu 		goto out_free;
306aff52e68SYiFei Zhu 
307aff52e68SYiFei Zhu 	t_datasec = btf__type_by_id(btf, map_info.btf_value_type_id);
308aff52e68SYiFei Zhu 	if (!btf_is_datasec(t_datasec))
309aff52e68SYiFei Zhu 		goto out_free;
310aff52e68SYiFei Zhu 
311aff52e68SYiFei Zhu 	vlen = btf_vlen(t_datasec);
312aff52e68SYiFei Zhu 	vsi = btf_var_secinfos(t_datasec);
313aff52e68SYiFei Zhu 
314aff52e68SYiFei Zhu 	/* We don't proceed to check the kinds of the elements of the DATASEC.
315aff52e68SYiFei Zhu 	 * The verifier enforces them to be BTF_KIND_VAR.
316aff52e68SYiFei Zhu 	 */
317aff52e68SYiFei Zhu 
318aff52e68SYiFei Zhu 	if (json_output) {
319aff52e68SYiFei Zhu 		struct btf_dumper d = {
320aff52e68SYiFei Zhu 			.btf = btf,
321aff52e68SYiFei Zhu 			.jw = json_wtr,
322aff52e68SYiFei Zhu 			.is_plain_text = false,
323aff52e68SYiFei Zhu 		};
324aff52e68SYiFei Zhu 
325aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
326aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
327aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
328aff52e68SYiFei Zhu 
329aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
330aff52e68SYiFei Zhu 				continue;
331aff52e68SYiFei Zhu 
332aff52e68SYiFei Zhu 			if (!printed_header) {
333aff52e68SYiFei Zhu 				jsonw_name(json_wtr, "metadata");
334aff52e68SYiFei Zhu 				jsonw_start_object(json_wtr);
335aff52e68SYiFei Zhu 				printed_header = true;
336aff52e68SYiFei Zhu 			}
337aff52e68SYiFei Zhu 
338aff52e68SYiFei Zhu 			jsonw_name(json_wtr, name + BPF_METADATA_PREFIX_LEN);
339aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
340aff52e68SYiFei Zhu 			if (err) {
341aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
342aff52e68SYiFei Zhu 				break;
343aff52e68SYiFei Zhu 			}
344aff52e68SYiFei Zhu 		}
345aff52e68SYiFei Zhu 		if (printed_header)
346aff52e68SYiFei Zhu 			jsonw_end_object(json_wtr);
347aff52e68SYiFei Zhu 	} else {
348e89ef634SQuentin Monnet 		json_writer_t *btf_wtr;
349aff52e68SYiFei Zhu 		struct btf_dumper d = {
350aff52e68SYiFei Zhu 			.btf = btf,
351aff52e68SYiFei Zhu 			.is_plain_text = true,
352aff52e68SYiFei Zhu 		};
353aff52e68SYiFei Zhu 
354aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
355aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
356aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
357aff52e68SYiFei Zhu 
358aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
359aff52e68SYiFei Zhu 				continue;
360aff52e68SYiFei Zhu 
361aff52e68SYiFei Zhu 			if (!printed_header) {
362aff52e68SYiFei Zhu 				printf("\tmetadata:");
363e89ef634SQuentin Monnet 
364e89ef634SQuentin Monnet 				btf_wtr = jsonw_new(stdout);
365e89ef634SQuentin Monnet 				if (!btf_wtr) {
366e89ef634SQuentin Monnet 					p_err("jsonw alloc failed");
367e89ef634SQuentin Monnet 					goto out_free;
368e89ef634SQuentin Monnet 				}
369e89ef634SQuentin Monnet 				d.jw = btf_wtr,
370e89ef634SQuentin Monnet 
371aff52e68SYiFei Zhu 				printed_header = true;
372aff52e68SYiFei Zhu 			}
373aff52e68SYiFei Zhu 
374aff52e68SYiFei Zhu 			printf("\n\t\t%s = ", name + BPF_METADATA_PREFIX_LEN);
375aff52e68SYiFei Zhu 
376aff52e68SYiFei Zhu 			jsonw_reset(btf_wtr);
377aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
378aff52e68SYiFei Zhu 			if (err) {
379aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
380aff52e68SYiFei Zhu 				break;
381aff52e68SYiFei Zhu 			}
382aff52e68SYiFei Zhu 		}
383aff52e68SYiFei Zhu 		if (printed_header)
384aff52e68SYiFei Zhu 			jsonw_destroy(&btf_wtr);
385aff52e68SYiFei Zhu 	}
386aff52e68SYiFei Zhu 
387aff52e68SYiFei Zhu out_free:
388aff52e68SYiFei Zhu 	btf__free(btf);
389aff52e68SYiFei Zhu 	free(value);
390aff52e68SYiFei Zhu }
391aff52e68SYiFei Zhu 
392b662000aSRaman Shukhau static void print_prog_header_json(struct bpf_prog_info *info, int fd)
393743cc665SQuentin Monnet {
394*b700eeb4SDaniel Müller 	const char *prog_type_str;
395b662000aSRaman Shukhau 	char prog_name[MAX_PROG_FULL_NAME];
396b662000aSRaman Shukhau 
397743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "id", info->id);
398*b700eeb4SDaniel Müller 	prog_type_str = libbpf_bpf_prog_type_str(info->type);
399*b700eeb4SDaniel Müller 
400*b700eeb4SDaniel Müller 	if (prog_type_str)
401*b700eeb4SDaniel Müller 		jsonw_string_field(json_wtr, "type", prog_type_str);
40271bb428fSJakub Kicinski 	else
403743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "type", info->type);
40471bb428fSJakub Kicinski 
405b662000aSRaman Shukhau 	if (*info->name) {
406b662000aSRaman Shukhau 		get_prog_full_name(info, fd, prog_name, sizeof(prog_name));
407b662000aSRaman Shukhau 		jsonw_string_field(json_wtr, "name", prog_name);
408b662000aSRaman Shukhau 	}
40971bb428fSJakub Kicinski 
410743cc665SQuentin Monnet 	jsonw_name(json_wtr, "tag");
411743cc665SQuentin Monnet 	jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
412743cc665SQuentin Monnet 		     info->tag[0], info->tag[1], info->tag[2], info->tag[3],
413743cc665SQuentin Monnet 		     info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
41471bb428fSJakub Kicinski 
4159b984a20SJiri Olsa 	jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible);
41688ad472bSAlexei Starovoitov 	if (info->run_time_ns) {
41788ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_time_ns", info->run_time_ns);
41888ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_cnt", info->run_cnt);
41988ad472bSAlexei Starovoitov 	}
4209ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
4219ed9e9baSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "recursion_misses", info->recursion_misses);
422ec202509SPaul Chaignon }
4239b984a20SJiri Olsa 
424ec202509SPaul Chaignon static void print_prog_json(struct bpf_prog_info *info, int fd)
425ec202509SPaul Chaignon {
426ec202509SPaul Chaignon 	char *memlock;
427ec202509SPaul Chaignon 
428ec202509SPaul Chaignon 	jsonw_start_object(json_wtr);
429b662000aSRaman Shukhau 	print_prog_header_json(info, fd);
43052262210SJakub Kicinski 	print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
43152262210SJakub Kicinski 
432743cc665SQuentin Monnet 	if (info->load_time) {
43371bb428fSJakub Kicinski 		char buf[32];
43471bb428fSJakub Kicinski 
435743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
43671bb428fSJakub Kicinski 
43771bb428fSJakub Kicinski 		/* Piggy back on load_time, since 0 uid is a valid one */
438a3fe1f6fSQuentin Monnet 		jsonw_name(json_wtr, "loaded_at");
439a3fe1f6fSQuentin Monnet 		jsonw_printf(json_wtr, "%s", buf);
440743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
44171bb428fSJakub Kicinski 	}
44271bb428fSJakub Kicinski 
443743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
44471bb428fSJakub Kicinski 
445743cc665SQuentin Monnet 	if (info->jited_prog_len) {
446743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", true);
447743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
448743cc665SQuentin Monnet 	} else {
449743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", false);
450743cc665SQuentin Monnet 	}
451743cc665SQuentin Monnet 
452743cc665SQuentin Monnet 	memlock = get_fdinfo(fd, "memlock");
453743cc665SQuentin Monnet 	if (memlock)
454357b3cc3SChris J Arges 		jsonw_int_field(json_wtr, "bytes_memlock", atoll(memlock));
455743cc665SQuentin Monnet 	free(memlock);
456743cc665SQuentin Monnet 
457743cc665SQuentin Monnet 	if (info->nr_map_ids)
458743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
459743cc665SQuentin Monnet 
460569b0c77SPrashant Bhole 	if (info->btf_id)
461569b0c77SPrashant Bhole 		jsonw_int_field(json_wtr, "btf_id", info->btf_id);
462569b0c77SPrashant Bhole 
4638f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
4648f184732SQuentin Monnet 		struct hashmap_entry *entry;
4654990f1f4SPrashant Bhole 
4664990f1f4SPrashant Bhole 		jsonw_name(json_wtr, "pinned");
4674990f1f4SPrashant Bhole 		jsonw_start_array(json_wtr);
4688f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
4698f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
4708f184732SQuentin Monnet 			jsonw_string(json_wtr, entry->value);
4714990f1f4SPrashant Bhole 		jsonw_end_array(json_wtr);
4724990f1f4SPrashant Bhole 	}
4734990f1f4SPrashant Bhole 
474d6699f8eSQuentin Monnet 	emit_obj_refs_json(refs_table, info->id, json_wtr);
475d53dee3fSAndrii Nakryiko 
476aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
477aff52e68SYiFei Zhu 
478743cc665SQuentin Monnet 	jsonw_end_object(json_wtr);
479743cc665SQuentin Monnet }
480743cc665SQuentin Monnet 
481b662000aSRaman Shukhau static void print_prog_header_plain(struct bpf_prog_info *info, int fd)
482743cc665SQuentin Monnet {
483*b700eeb4SDaniel Müller 	const char *prog_type_str;
484b662000aSRaman Shukhau 	char prog_name[MAX_PROG_FULL_NAME];
485b662000aSRaman Shukhau 
486743cc665SQuentin Monnet 	printf("%u: ", info->id);
487*b700eeb4SDaniel Müller 	prog_type_str = libbpf_bpf_prog_type_str(info->type);
488*b700eeb4SDaniel Müller 	if (prog_type_str)
489*b700eeb4SDaniel Müller 		printf("%s  ", prog_type_str);
490743cc665SQuentin Monnet 	else
491743cc665SQuentin Monnet 		printf("type %u  ", info->type);
492743cc665SQuentin Monnet 
493b662000aSRaman Shukhau 	if (*info->name) {
494b662000aSRaman Shukhau 		get_prog_full_name(info, fd, prog_name, sizeof(prog_name));
495b662000aSRaman Shukhau 		printf("name %s  ", prog_name);
496b662000aSRaman Shukhau 	}
497743cc665SQuentin Monnet 
498743cc665SQuentin Monnet 	printf("tag ");
499743cc665SQuentin Monnet 	fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
50052262210SJakub Kicinski 	print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
5019b984a20SJiri Olsa 	printf("%s", info->gpl_compatible ? "  gpl" : "");
50288ad472bSAlexei Starovoitov 	if (info->run_time_ns)
50388ad472bSAlexei Starovoitov 		printf(" run_time_ns %lld run_cnt %lld",
50488ad472bSAlexei Starovoitov 		       info->run_time_ns, info->run_cnt);
5059ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
5069ed9e9baSAlexei Starovoitov 		printf(" recursion_misses %lld", info->recursion_misses);
507743cc665SQuentin Monnet 	printf("\n");
508ec202509SPaul Chaignon }
509ec202509SPaul Chaignon 
510ec202509SPaul Chaignon static void print_prog_plain(struct bpf_prog_info *info, int fd)
511ec202509SPaul Chaignon {
512ec202509SPaul Chaignon 	char *memlock;
513ec202509SPaul Chaignon 
514b662000aSRaman Shukhau 	print_prog_header_plain(info, fd);
515743cc665SQuentin Monnet 
516743cc665SQuentin Monnet 	if (info->load_time) {
517743cc665SQuentin Monnet 		char buf[32];
518743cc665SQuentin Monnet 
519743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
520743cc665SQuentin Monnet 
521743cc665SQuentin Monnet 		/* Piggy back on load_time, since 0 uid is a valid one */
522743cc665SQuentin Monnet 		printf("\tloaded_at %s  uid %u\n", buf, info->created_by_uid);
523743cc665SQuentin Monnet 	}
524743cc665SQuentin Monnet 
525743cc665SQuentin Monnet 	printf("\txlated %uB", info->xlated_prog_len);
526743cc665SQuentin Monnet 
527743cc665SQuentin Monnet 	if (info->jited_prog_len)
528743cc665SQuentin Monnet 		printf("  jited %uB", info->jited_prog_len);
52971bb428fSJakub Kicinski 	else
53071bb428fSJakub Kicinski 		printf("  not jited");
53171bb428fSJakub Kicinski 
53271bb428fSJakub Kicinski 	memlock = get_fdinfo(fd, "memlock");
53371bb428fSJakub Kicinski 	if (memlock)
53471bb428fSJakub Kicinski 		printf("  memlock %sB", memlock);
53571bb428fSJakub Kicinski 	free(memlock);
53671bb428fSJakub Kicinski 
537743cc665SQuentin Monnet 	if (info->nr_map_ids)
538743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
53971bb428fSJakub Kicinski 
5408f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
5418f184732SQuentin Monnet 		struct hashmap_entry *entry;
5424990f1f4SPrashant Bhole 
5438f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
5448f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
5458f184732SQuentin Monnet 			printf("\n\tpinned %s", (char *)entry->value);
5464990f1f4SPrashant Bhole 	}
5474990f1f4SPrashant Bhole 
548569b0c77SPrashant Bhole 	if (info->btf_id)
549031ebc1aSQuentin Monnet 		printf("\n\tbtf_id %d", info->btf_id);
550569b0c77SPrashant Bhole 
551d6699f8eSQuentin Monnet 	emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
552d53dee3fSAndrii Nakryiko 
55371bb428fSJakub Kicinski 	printf("\n");
554aff52e68SYiFei Zhu 
555aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
556743cc665SQuentin Monnet }
557743cc665SQuentin Monnet 
558743cc665SQuentin Monnet static int show_prog(int fd)
559743cc665SQuentin Monnet {
560743cc665SQuentin Monnet 	struct bpf_prog_info info = {};
561743cc665SQuentin Monnet 	__u32 len = sizeof(info);
562743cc665SQuentin Monnet 	int err;
563743cc665SQuentin Monnet 
564743cc665SQuentin Monnet 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
565743cc665SQuentin Monnet 	if (err) {
5669a5ab8bfSQuentin Monnet 		p_err("can't get prog info: %s", strerror(errno));
567743cc665SQuentin Monnet 		return -1;
568743cc665SQuentin Monnet 	}
569743cc665SQuentin Monnet 
570743cc665SQuentin Monnet 	if (json_output)
571743cc665SQuentin Monnet 		print_prog_json(&info, fd);
572743cc665SQuentin Monnet 	else
573743cc665SQuentin Monnet 		print_prog_plain(&info, fd);
57471bb428fSJakub Kicinski 
57571bb428fSJakub Kicinski 	return 0;
57671bb428fSJakub Kicinski }
57771bb428fSJakub Kicinski 
578ec202509SPaul Chaignon static int do_show_subset(int argc, char **argv)
579ec202509SPaul Chaignon {
580ec202509SPaul Chaignon 	int *fds = NULL;
581ec202509SPaul Chaignon 	int nb_fds, i;
582ec202509SPaul Chaignon 	int err = -1;
583ec202509SPaul Chaignon 
584ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
585ec202509SPaul Chaignon 	if (!fds) {
586ec202509SPaul Chaignon 		p_err("mem alloc failed");
587ec202509SPaul Chaignon 		return -1;
588ec202509SPaul Chaignon 	}
589ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
590ec202509SPaul Chaignon 	if (nb_fds < 1)
591ec202509SPaul Chaignon 		goto exit_free;
592ec202509SPaul Chaignon 
593ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
594ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
595ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
596ec202509SPaul Chaignon 		err = show_prog(fds[i]);
597ec202509SPaul Chaignon 		if (err) {
598ec202509SPaul Chaignon 			for (; i < nb_fds; i++)
599ec202509SPaul Chaignon 				close(fds[i]);
600ec202509SPaul Chaignon 			break;
601ec202509SPaul Chaignon 		}
602ec202509SPaul Chaignon 		close(fds[i]);
603ec202509SPaul Chaignon 	}
604ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
605ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
606ec202509SPaul Chaignon 
607ec202509SPaul Chaignon exit_free:
608ec202509SPaul Chaignon 	free(fds);
609ec202509SPaul Chaignon 	return err;
610ec202509SPaul Chaignon }
611ec202509SPaul Chaignon 
61271bb428fSJakub Kicinski static int do_show(int argc, char **argv)
613743cc665SQuentin Monnet {
614743cc665SQuentin Monnet 	__u32 id = 0;
61571bb428fSJakub Kicinski 	int err;
61671bb428fSJakub Kicinski 	int fd;
61771bb428fSJakub Kicinski 
61846241271SQuentin Monnet 	if (show_pinned) {
6198f184732SQuentin Monnet 		prog_table = hashmap__new(hash_fn_for_key_as_id,
6208f184732SQuentin Monnet 					  equal_fn_for_key_as_id, NULL);
621622a5b58SMauricio Vásquez 		if (IS_ERR(prog_table)) {
6228f184732SQuentin Monnet 			p_err("failed to create hashmap for pinned paths");
6238f184732SQuentin Monnet 			return -1;
6248f184732SQuentin Monnet 		}
6258f184732SQuentin Monnet 		build_pinned_obj_table(prog_table, BPF_OBJ_PROG);
62646241271SQuentin Monnet 	}
627d53dee3fSAndrii Nakryiko 	build_obj_refs_table(&refs_table, BPF_OBJ_PROG);
6284990f1f4SPrashant Bhole 
629ec202509SPaul Chaignon 	if (argc == 2)
630ec202509SPaul Chaignon 		return do_show_subset(argc, argv);
63171bb428fSJakub Kicinski 
63271bb428fSJakub Kicinski 	if (argc)
63371bb428fSJakub Kicinski 		return BAD_ARG();
63471bb428fSJakub Kicinski 
635743cc665SQuentin Monnet 	if (json_output)
636743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
63771bb428fSJakub Kicinski 	while (true) {
63871bb428fSJakub Kicinski 		err = bpf_prog_get_next_id(id, &id);
63971bb428fSJakub Kicinski 		if (err) {
6401739c26dSQuentin Monnet 			if (errno == ENOENT) {
6411739c26dSQuentin Monnet 				err = 0;
64271bb428fSJakub Kicinski 				break;
6431739c26dSQuentin Monnet 			}
6449a5ab8bfSQuentin Monnet 			p_err("can't get next program: %s%s", strerror(errno),
6459a5ab8bfSQuentin Monnet 			      errno == EINVAL ? " -- kernel too old?" : "");
646743cc665SQuentin Monnet 			err = -1;
647743cc665SQuentin Monnet 			break;
64871bb428fSJakub Kicinski 		}
64971bb428fSJakub Kicinski 
65071bb428fSJakub Kicinski 		fd = bpf_prog_get_fd_by_id(id);
65171bb428fSJakub Kicinski 		if (fd < 0) {
6528207c6ddSJakub Kicinski 			if (errno == ENOENT)
6538207c6ddSJakub Kicinski 				continue;
6549a5ab8bfSQuentin Monnet 			p_err("can't get prog by id (%u): %s",
65571bb428fSJakub Kicinski 			      id, strerror(errno));
656743cc665SQuentin Monnet 			err = -1;
657743cc665SQuentin Monnet 			break;
65871bb428fSJakub Kicinski 		}
65971bb428fSJakub Kicinski 
66071bb428fSJakub Kicinski 		err = show_prog(fd);
66171bb428fSJakub Kicinski 		close(fd);
66271bb428fSJakub Kicinski 		if (err)
663743cc665SQuentin Monnet 			break;
66471bb428fSJakub Kicinski 	}
66571bb428fSJakub Kicinski 
666743cc665SQuentin Monnet 	if (json_output)
667743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
668743cc665SQuentin Monnet 
669d6699f8eSQuentin Monnet 	delete_obj_refs_table(refs_table);
670d53dee3fSAndrii Nakryiko 
67146241271SQuentin Monnet 	if (show_pinned)
6728f184732SQuentin Monnet 		delete_pinned_obj_table(prog_table);
67346241271SQuentin Monnet 
674743cc665SQuentin Monnet 	return err;
67571bb428fSJakub Kicinski }
67671bb428fSJakub Kicinski 
677ec202509SPaul Chaignon static int
678ec202509SPaul Chaignon prog_dump(struct bpf_prog_info *info, enum dump_mode mode,
679ec202509SPaul Chaignon 	  char *filepath, bool opcodes, bool visual, bool linum)
68071bb428fSJakub Kicinski {
681b053b439SMartin KaFai Lau 	struct bpf_prog_linfo *prog_linfo = NULL;
6823ddeac67SJakub Kicinski 	const char *disasm_opt = NULL;
6837105e828SDaniel Borkmann 	struct dump_data dd = {};
684cae73f23SSong Liu 	void *func_info = NULL;
685254471e5SYonghong Song 	struct btf *btf = NULL;
686254471e5SYonghong Song 	char func_sig[1024];
68771bb428fSJakub Kicinski 	unsigned char *buf;
688cae73f23SSong Liu 	__u32 member_len;
689ebbd7f64SQuentin Monnet 	int fd, err = -1;
69071bb428fSJakub Kicinski 	ssize_t n;
69171bb428fSJakub Kicinski 
692cae73f23SSong Liu 	if (mode == DUMP_JITED) {
6935b79bcdfSToke Høiland-Jørgensen 		if (info->jited_prog_len == 0 || !info->jited_prog_insns) {
6949a5ab8bfSQuentin Monnet 			p_info("no instructions returned");
695ec202509SPaul Chaignon 			return -1;
696f84192eeSSandipan Das 		}
69709f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->jited_prog_insns);
698cae73f23SSong Liu 		member_len = info->jited_prog_len;
699cae73f23SSong Liu 	} else {	/* DUMP_XLATED */
700d95f1e8bSToke Høiland-Jørgensen 		if (info->xlated_prog_len == 0 || !info->xlated_prog_insns) {
7017105e828SDaniel Borkmann 			p_err("error retrieving insn dump: kernel.kptr_restrict set?");
702ec202509SPaul Chaignon 			return -1;
7037105e828SDaniel Borkmann 		}
70409f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->xlated_prog_insns);
705cae73f23SSong Liu 		member_len = info->xlated_prog_len;
706cae73f23SSong Liu 	}
7077105e828SDaniel Borkmann 
70886f4b7f2SQuentin Monnet 	if (info->btf_id) {
70986f4b7f2SQuentin Monnet 		btf = btf__load_from_kernel_by_id(info->btf_id);
71086f4b7f2SQuentin Monnet 		if (libbpf_get_error(btf)) {
711254471e5SYonghong Song 			p_err("failed to get btf");
712ec202509SPaul Chaignon 			return -1;
713254471e5SYonghong Song 		}
71486f4b7f2SQuentin Monnet 	}
715254471e5SYonghong Song 
71609f44b75SAndrii Nakryiko 	func_info = u64_to_ptr(info->func_info);
717cae73f23SSong Liu 
718cae73f23SSong Liu 	if (info->nr_line_info) {
719cae73f23SSong Liu 		prog_linfo = bpf_prog_linfo__new(info);
720b053b439SMartin KaFai Lau 		if (!prog_linfo)
72110a5ce98SMartin KaFai Lau 			p_info("error in processing bpf_line_info.  continue without it.");
722b053b439SMartin KaFai Lau 	}
723b053b439SMartin KaFai Lau 
72471bb428fSJakub Kicinski 	if (filepath) {
72571bb428fSJakub Kicinski 		fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
72671bb428fSJakub Kicinski 		if (fd < 0) {
7279a5ab8bfSQuentin Monnet 			p_err("can't open file %s: %s", filepath,
72871bb428fSJakub Kicinski 			      strerror(errno));
729ebbd7f64SQuentin Monnet 			goto exit_free;
73071bb428fSJakub Kicinski 		}
73171bb428fSJakub Kicinski 
732cae73f23SSong Liu 		n = write(fd, buf, member_len);
73371bb428fSJakub Kicinski 		close(fd);
73409f44b75SAndrii Nakryiko 		if (n != (ssize_t)member_len) {
7359a5ab8bfSQuentin Monnet 			p_err("error writing output file: %s",
73671bb428fSJakub Kicinski 			      n < 0 ? strerror(errno) : "short write");
737ebbd7f64SQuentin Monnet 			goto exit_free;
73871bb428fSJakub Kicinski 		}
73952c84d36SQuentin Monnet 
74052c84d36SQuentin Monnet 		if (json_output)
74152c84d36SQuentin Monnet 			jsonw_null(json_wtr);
742cae73f23SSong Liu 	} else if (mode == DUMP_JITED) {
743e6593596SJiong Wang 		const char *name = NULL;
744e6593596SJiong Wang 
745cae73f23SSong Liu 		if (info->ifindex) {
746cae73f23SSong Liu 			name = ifindex_to_bfd_params(info->ifindex,
747cae73f23SSong Liu 						     info->netns_dev,
748cae73f23SSong Liu 						     info->netns_ino,
7493ddeac67SJakub Kicinski 						     &disasm_opt);
750e6593596SJiong Wang 			if (!name)
751ebbd7f64SQuentin Monnet 				goto exit_free;
752e6593596SJiong Wang 		}
753e6593596SJiong Wang 
754cae73f23SSong Liu 		if (info->nr_jited_func_lens && info->jited_func_lens) {
755f7f62c71SSandipan Das 			struct kernel_sym *sym = NULL;
756254471e5SYonghong Song 			struct bpf_func_info *record;
757f7f62c71SSandipan Das 			char sym_name[SYM_MAX_NAME];
758f7f62c71SSandipan Das 			unsigned char *img = buf;
759f7f62c71SSandipan Das 			__u64 *ksyms = NULL;
760f7f62c71SSandipan Das 			__u32 *lens;
761f7f62c71SSandipan Das 			__u32 i;
762cae73f23SSong Liu 			if (info->nr_jited_ksyms) {
763f7f62c71SSandipan Das 				kernel_syms_load(&dd);
76409f44b75SAndrii Nakryiko 				ksyms = u64_to_ptr(info->jited_ksyms);
765f7f62c71SSandipan Das 			}
766f7f62c71SSandipan Das 
767f7f62c71SSandipan Das 			if (json_output)
768f7f62c71SSandipan Das 				jsonw_start_array(json_wtr);
769f7f62c71SSandipan Das 
77009f44b75SAndrii Nakryiko 			lens = u64_to_ptr(info->jited_func_lens);
771cae73f23SSong Liu 			for (i = 0; i < info->nr_jited_func_lens; i++) {
772f7f62c71SSandipan Das 				if (ksyms) {
773f7f62c71SSandipan Das 					sym = kernel_syms_search(&dd, ksyms[i]);
774f7f62c71SSandipan Das 					if (sym)
775f7f62c71SSandipan Das 						sprintf(sym_name, "%s", sym->name);
776f7f62c71SSandipan Das 					else
777f7f62c71SSandipan Das 						sprintf(sym_name, "0x%016llx", ksyms[i]);
778f7f62c71SSandipan Das 				} else {
779f7f62c71SSandipan Das 					strcpy(sym_name, "unknown");
780f7f62c71SSandipan Das 				}
781f7f62c71SSandipan Das 
782254471e5SYonghong Song 				if (func_info) {
783cae73f23SSong Liu 					record = func_info + i * info->func_info_rec_size;
784254471e5SYonghong Song 					btf_dumper_type_only(btf, record->type_id,
785254471e5SYonghong Song 							     func_sig,
786254471e5SYonghong Song 							     sizeof(func_sig));
787254471e5SYonghong Song 				}
788254471e5SYonghong Song 
789f7f62c71SSandipan Das 				if (json_output) {
790f7f62c71SSandipan Das 					jsonw_start_object(json_wtr);
791254471e5SYonghong Song 					if (func_info && func_sig[0] != '\0') {
792254471e5SYonghong Song 						jsonw_name(json_wtr, "proto");
793254471e5SYonghong Song 						jsonw_string(json_wtr, func_sig);
794254471e5SYonghong Song 					}
795f7f62c71SSandipan Das 					jsonw_name(json_wtr, "name");
796f7f62c71SSandipan Das 					jsonw_string(json_wtr, sym_name);
797f7f62c71SSandipan Das 					jsonw_name(json_wtr, "insns");
798f7f62c71SSandipan Das 				} else {
799254471e5SYonghong Song 					if (func_info && func_sig[0] != '\0')
800254471e5SYonghong Song 						printf("%s:\n", func_sig);
801f7f62c71SSandipan Das 					printf("%s:\n", sym_name);
802f7f62c71SSandipan Das 				}
803f7f62c71SSandipan Das 
804b053b439SMartin KaFai Lau 				disasm_print_insn(img, lens[i], opcodes,
805b053b439SMartin KaFai Lau 						  name, disasm_opt, btf,
806b053b439SMartin KaFai Lau 						  prog_linfo, ksyms[i], i,
807b053b439SMartin KaFai Lau 						  linum);
808b053b439SMartin KaFai Lau 
809f7f62c71SSandipan Das 				img += lens[i];
810f7f62c71SSandipan Das 
811f7f62c71SSandipan Das 				if (json_output)
812f7f62c71SSandipan Das 					jsonw_end_object(json_wtr);
813f7f62c71SSandipan Das 				else
814f7f62c71SSandipan Das 					printf("\n");
815f7f62c71SSandipan Das 			}
816f7f62c71SSandipan Das 
817f7f62c71SSandipan Das 			if (json_output)
818f7f62c71SSandipan Das 				jsonw_end_array(json_wtr);
819f7f62c71SSandipan Das 		} else {
820cae73f23SSong Liu 			disasm_print_insn(buf, member_len, opcodes, name,
821b053b439SMartin KaFai Lau 					  disasm_opt, btf, NULL, 0, 0, false);
822f7f62c71SSandipan Das 		}
823b6c1cedbSJiong Wang 	} else if (visual) {
824b6c1cedbSJiong Wang 		if (json_output)
825b6c1cedbSJiong Wang 			jsonw_null(json_wtr);
826b6c1cedbSJiong Wang 		else
827cae73f23SSong Liu 			dump_xlated_cfg(buf, member_len);
8287105e828SDaniel Borkmann 	} else {
8297105e828SDaniel Borkmann 		kernel_syms_load(&dd);
830cae73f23SSong Liu 		dd.nr_jited_ksyms = info->nr_jited_ksyms;
83109f44b75SAndrii Nakryiko 		dd.jited_ksyms = u64_to_ptr(info->jited_ksyms);
832254471e5SYonghong Song 		dd.btf = btf;
833254471e5SYonghong Song 		dd.func_info = func_info;
834cae73f23SSong Liu 		dd.finfo_rec_size = info->func_info_rec_size;
835b053b439SMartin KaFai Lau 		dd.prog_linfo = prog_linfo;
836f84192eeSSandipan Das 
837f05e2c32SQuentin Monnet 		if (json_output)
838cae73f23SSong Liu 			dump_xlated_json(&dd, buf, member_len, opcodes,
839b053b439SMartin KaFai Lau 					 linum);
840f05e2c32SQuentin Monnet 		else
841cae73f23SSong Liu 			dump_xlated_plain(&dd, buf, member_len, opcodes,
842b053b439SMartin KaFai Lau 					  linum);
8437105e828SDaniel Borkmann 		kernel_syms_destroy(&dd);
8447105e828SDaniel Borkmann 	}
84571bb428fSJakub Kicinski 
846ebbd7f64SQuentin Monnet 	err = 0;
847369e955bSQuentin Monnet 
848ebbd7f64SQuentin Monnet exit_free:
849ebbd7f64SQuentin Monnet 	btf__free(btf);
850ebbd7f64SQuentin Monnet 	bpf_prog_linfo__free(prog_linfo);
851ebbd7f64SQuentin Monnet 	return err;
852ec202509SPaul Chaignon }
85371bb428fSJakub Kicinski 
854ec202509SPaul Chaignon static int do_dump(int argc, char **argv)
855ec202509SPaul Chaignon {
856c59765cfSDave Marchevsky 	struct bpf_prog_info info;
857c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
858c59765cfSDave Marchevsky 	size_t info_data_sz = 0;
859c59765cfSDave Marchevsky 	void *info_data = NULL;
860ec202509SPaul Chaignon 	char *filepath = NULL;
861ec202509SPaul Chaignon 	bool opcodes = false;
862ec202509SPaul Chaignon 	bool visual = false;
863ec202509SPaul Chaignon 	enum dump_mode mode;
864ec202509SPaul Chaignon 	bool linum = false;
865ec202509SPaul Chaignon 	int nb_fds, i = 0;
866c59765cfSDave Marchevsky 	int *fds = NULL;
867ec202509SPaul Chaignon 	int err = -1;
868ec202509SPaul Chaignon 
869ec202509SPaul Chaignon 	if (is_prefix(*argv, "jited")) {
870ec202509SPaul Chaignon 		if (disasm_init())
87171bb428fSJakub Kicinski 			return -1;
872ec202509SPaul Chaignon 		mode = DUMP_JITED;
873ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "xlated")) {
874ec202509SPaul Chaignon 		mode = DUMP_XLATED;
875ec202509SPaul Chaignon 	} else {
876ec202509SPaul Chaignon 		p_err("expected 'xlated' or 'jited', got: %s", *argv);
877ec202509SPaul Chaignon 		return -1;
878ec202509SPaul Chaignon 	}
879ec202509SPaul Chaignon 	NEXT_ARG();
880ec202509SPaul Chaignon 
881ec202509SPaul Chaignon 	if (argc < 2)
882ec202509SPaul Chaignon 		usage();
883ec202509SPaul Chaignon 
884ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
885ec202509SPaul Chaignon 	if (!fds) {
886ec202509SPaul Chaignon 		p_err("mem alloc failed");
887ec202509SPaul Chaignon 		return -1;
888ec202509SPaul Chaignon 	}
889ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
890ec202509SPaul Chaignon 	if (nb_fds < 1)
891ec202509SPaul Chaignon 		goto exit_free;
892ec202509SPaul Chaignon 
893ec202509SPaul Chaignon 	if (is_prefix(*argv, "file")) {
894ec202509SPaul Chaignon 		NEXT_ARG();
895ec202509SPaul Chaignon 		if (!argc) {
896ec202509SPaul Chaignon 			p_err("expected file path");
897ec202509SPaul Chaignon 			goto exit_close;
898ec202509SPaul Chaignon 		}
899ec202509SPaul Chaignon 		if (nb_fds > 1) {
900ec202509SPaul Chaignon 			p_err("several programs matched");
901ec202509SPaul Chaignon 			goto exit_close;
902ec202509SPaul Chaignon 		}
903ec202509SPaul Chaignon 
904ec202509SPaul Chaignon 		filepath = *argv;
905ec202509SPaul Chaignon 		NEXT_ARG();
906ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "opcodes")) {
907ec202509SPaul Chaignon 		opcodes = true;
908ec202509SPaul Chaignon 		NEXT_ARG();
909ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "visual")) {
910ec202509SPaul Chaignon 		if (nb_fds > 1) {
911ec202509SPaul Chaignon 			p_err("several programs matched");
912ec202509SPaul Chaignon 			goto exit_close;
913ec202509SPaul Chaignon 		}
914ec202509SPaul Chaignon 
915ec202509SPaul Chaignon 		visual = true;
916ec202509SPaul Chaignon 		NEXT_ARG();
917ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "linum")) {
918ec202509SPaul Chaignon 		linum = true;
919ec202509SPaul Chaignon 		NEXT_ARG();
920ec202509SPaul Chaignon 	}
921ec202509SPaul Chaignon 
922ec202509SPaul Chaignon 	if (argc) {
923ec202509SPaul Chaignon 		usage();
924ec202509SPaul Chaignon 		goto exit_close;
925ec202509SPaul Chaignon 	}
926ec202509SPaul Chaignon 
927ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
928ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
929ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
930c59765cfSDave Marchevsky 		memset(&info, 0, sizeof(info));
931c59765cfSDave Marchevsky 
932c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
933c59765cfSDave Marchevsky 		if (err) {
934c59765cfSDave Marchevsky 			p_err("can't get prog info: %s", strerror(errno));
935c59765cfSDave Marchevsky 			break;
936c59765cfSDave Marchevsky 		}
937c59765cfSDave Marchevsky 
938c59765cfSDave Marchevsky 		err = prep_prog_info(&info, mode, &info_data, &info_data_sz);
939c59765cfSDave Marchevsky 		if (err) {
940c59765cfSDave Marchevsky 			p_err("can't grow prog info_data");
941c59765cfSDave Marchevsky 			break;
942c59765cfSDave Marchevsky 		}
943c59765cfSDave Marchevsky 
944c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
945c59765cfSDave Marchevsky 		if (err) {
946ec202509SPaul Chaignon 			p_err("can't get prog info: %s", strerror(errno));
947ec202509SPaul Chaignon 			break;
948ec202509SPaul Chaignon 		}
949ec202509SPaul Chaignon 
950ec202509SPaul Chaignon 		if (json_output && nb_fds > 1) {
951ec202509SPaul Chaignon 			jsonw_start_object(json_wtr);	/* prog object */
952b662000aSRaman Shukhau 			print_prog_header_json(&info, fds[i]);
953ec202509SPaul Chaignon 			jsonw_name(json_wtr, "insns");
954ec202509SPaul Chaignon 		} else if (nb_fds > 1) {
955b662000aSRaman Shukhau 			print_prog_header_plain(&info, fds[i]);
956ec202509SPaul Chaignon 		}
957ec202509SPaul Chaignon 
958c59765cfSDave Marchevsky 		err = prog_dump(&info, mode, filepath, opcodes, visual, linum);
959ec202509SPaul Chaignon 
960ec202509SPaul Chaignon 		if (json_output && nb_fds > 1)
961ec202509SPaul Chaignon 			jsonw_end_object(json_wtr);	/* prog object */
962ec202509SPaul Chaignon 		else if (i != nb_fds - 1 && nb_fds > 1)
963ec202509SPaul Chaignon 			printf("\n");
964ec202509SPaul Chaignon 
965ec202509SPaul Chaignon 		if (err)
966ec202509SPaul Chaignon 			break;
967ec202509SPaul Chaignon 		close(fds[i]);
968ec202509SPaul Chaignon 	}
969ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
970ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
971ec202509SPaul Chaignon 
972ec202509SPaul Chaignon exit_close:
973ec202509SPaul Chaignon 	for (; i < nb_fds; i++)
974ec202509SPaul Chaignon 		close(fds[i]);
975ec202509SPaul Chaignon exit_free:
976c59765cfSDave Marchevsky 	free(info_data);
977ec202509SPaul Chaignon 	free(fds);
978ec202509SPaul Chaignon 	return err;
97971bb428fSJakub Kicinski }
98071bb428fSJakub Kicinski 
98171bb428fSJakub Kicinski static int do_pin(int argc, char **argv)
98271bb428fSJakub Kicinski {
983004b45c0SQuentin Monnet 	int err;
984004b45c0SQuentin Monnet 
98575a1e792SQuentin Monnet 	err = do_pin_any(argc, argv, prog_parse_fd);
986004b45c0SQuentin Monnet 	if (!err && json_output)
987004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
988004b45c0SQuentin Monnet 	return err;
98971bb428fSJakub Kicinski }
99071bb428fSJakub Kicinski 
9913ff5a4dcSJakub Kicinski struct map_replace {
9923ff5a4dcSJakub Kicinski 	int idx;
9933ff5a4dcSJakub Kicinski 	int fd;
9943ff5a4dcSJakub Kicinski 	char *name;
9953ff5a4dcSJakub Kicinski };
9963ff5a4dcSJakub Kicinski 
997c101189bSQuentin Monnet static int map_replace_compar(const void *p1, const void *p2)
9983ff5a4dcSJakub Kicinski {
9993ff5a4dcSJakub Kicinski 	const struct map_replace *a = p1, *b = p2;
10003ff5a4dcSJakub Kicinski 
10013ff5a4dcSJakub Kicinski 	return a->idx - b->idx;
10023ff5a4dcSJakub Kicinski }
10033ff5a4dcSJakub Kicinski 
1004092f0892SStanislav Fomichev static int parse_attach_detach_args(int argc, char **argv, int *progfd,
1005092f0892SStanislav Fomichev 				    enum bpf_attach_type *attach_type,
1006092f0892SStanislav Fomichev 				    int *mapfd)
1007092f0892SStanislav Fomichev {
1008092f0892SStanislav Fomichev 	if (!REQ_ARGS(3))
1009092f0892SStanislav Fomichev 		return -EINVAL;
1010092f0892SStanislav Fomichev 
1011092f0892SStanislav Fomichev 	*progfd = prog_parse_fd(&argc, &argv);
1012092f0892SStanislav Fomichev 	if (*progfd < 0)
1013092f0892SStanislav Fomichev 		return *progfd;
1014092f0892SStanislav Fomichev 
1015092f0892SStanislav Fomichev 	*attach_type = parse_attach_type(*argv);
1016092f0892SStanislav Fomichev 	if (*attach_type == __MAX_BPF_ATTACH_TYPE) {
1017092f0892SStanislav Fomichev 		p_err("invalid attach/detach type");
1018092f0892SStanislav Fomichev 		return -EINVAL;
1019092f0892SStanislav Fomichev 	}
1020092f0892SStanislav Fomichev 
1021092f0892SStanislav Fomichev 	if (*attach_type == BPF_FLOW_DISSECTOR) {
1022f9b7ff0dSLorenz Bauer 		*mapfd = 0;
1023092f0892SStanislav Fomichev 		return 0;
1024092f0892SStanislav Fomichev 	}
1025092f0892SStanislav Fomichev 
1026092f0892SStanislav Fomichev 	NEXT_ARG();
1027092f0892SStanislav Fomichev 	if (!REQ_ARGS(2))
1028092f0892SStanislav Fomichev 		return -EINVAL;
1029092f0892SStanislav Fomichev 
1030092f0892SStanislav Fomichev 	*mapfd = map_parse_fd(&argc, &argv);
1031092f0892SStanislav Fomichev 	if (*mapfd < 0)
1032092f0892SStanislav Fomichev 		return *mapfd;
1033092f0892SStanislav Fomichev 
1034092f0892SStanislav Fomichev 	return 0;
1035092f0892SStanislav Fomichev }
1036092f0892SStanislav Fomichev 
1037b7d3826cSJohn Fastabend static int do_attach(int argc, char **argv)
1038b7d3826cSJohn Fastabend {
1039b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1040092f0892SStanislav Fomichev 	int err, progfd;
1041092f0892SStanislav Fomichev 	int mapfd;
1042b7d3826cSJohn Fastabend 
1043092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1044092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1045092f0892SStanislav Fomichev 	if (err)
1046092f0892SStanislav Fomichev 		return err;
1047b7d3826cSJohn Fastabend 
1048b7d3826cSJohn Fastabend 	err = bpf_prog_attach(progfd, mapfd, attach_type, 0);
1049b7d3826cSJohn Fastabend 	if (err) {
1050b7d3826cSJohn Fastabend 		p_err("failed prog attach to map");
1051b7d3826cSJohn Fastabend 		return -EINVAL;
1052b7d3826cSJohn Fastabend 	}
1053b7d3826cSJohn Fastabend 
1054b7d3826cSJohn Fastabend 	if (json_output)
1055b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1056b7d3826cSJohn Fastabend 	return 0;
1057b7d3826cSJohn Fastabend }
1058b7d3826cSJohn Fastabend 
1059b7d3826cSJohn Fastabend static int do_detach(int argc, char **argv)
1060b7d3826cSJohn Fastabend {
1061b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1062092f0892SStanislav Fomichev 	int err, progfd;
1063092f0892SStanislav Fomichev 	int mapfd;
1064b7d3826cSJohn Fastabend 
1065092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1066092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1067092f0892SStanislav Fomichev 	if (err)
1068092f0892SStanislav Fomichev 		return err;
1069b7d3826cSJohn Fastabend 
1070b7d3826cSJohn Fastabend 	err = bpf_prog_detach2(progfd, mapfd, attach_type);
1071b7d3826cSJohn Fastabend 	if (err) {
1072b7d3826cSJohn Fastabend 		p_err("failed prog detach from map");
1073b7d3826cSJohn Fastabend 		return -EINVAL;
1074b7d3826cSJohn Fastabend 	}
1075b7d3826cSJohn Fastabend 
1076b7d3826cSJohn Fastabend 	if (json_output)
1077b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1078b7d3826cSJohn Fastabend 	return 0;
1079b7d3826cSJohn Fastabend }
108077380998SStanislav Fomichev 
1081ba95c745SQuentin Monnet static int check_single_stdin(char *file_data_in, char *file_ctx_in)
1082ba95c745SQuentin Monnet {
1083ba95c745SQuentin Monnet 	if (file_data_in && file_ctx_in &&
1084ba95c745SQuentin Monnet 	    !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) {
1085ba95c745SQuentin Monnet 		p_err("cannot use standard input for both data_in and ctx_in");
1086ba95c745SQuentin Monnet 		return -1;
1087ba95c745SQuentin Monnet 	}
1088ba95c745SQuentin Monnet 
1089ba95c745SQuentin Monnet 	return 0;
1090ba95c745SQuentin Monnet }
1091ba95c745SQuentin Monnet 
1092ba95c745SQuentin Monnet static int get_run_data(const char *fname, void **data_ptr, unsigned int *size)
1093ba95c745SQuentin Monnet {
1094ba95c745SQuentin Monnet 	size_t block_size = 256;
1095ba95c745SQuentin Monnet 	size_t buf_size = block_size;
1096ba95c745SQuentin Monnet 	size_t nb_read = 0;
1097ba95c745SQuentin Monnet 	void *tmp;
1098ba95c745SQuentin Monnet 	FILE *f;
1099ba95c745SQuentin Monnet 
1100ba95c745SQuentin Monnet 	if (!fname) {
1101ba95c745SQuentin Monnet 		*data_ptr = NULL;
1102ba95c745SQuentin Monnet 		*size = 0;
1103ba95c745SQuentin Monnet 		return 0;
1104ba95c745SQuentin Monnet 	}
1105ba95c745SQuentin Monnet 
1106ba95c745SQuentin Monnet 	if (!strcmp(fname, "-"))
1107ba95c745SQuentin Monnet 		f = stdin;
1108ba95c745SQuentin Monnet 	else
1109ba95c745SQuentin Monnet 		f = fopen(fname, "r");
1110ba95c745SQuentin Monnet 	if (!f) {
1111ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1112ba95c745SQuentin Monnet 		return -1;
1113ba95c745SQuentin Monnet 	}
1114ba95c745SQuentin Monnet 
1115ba95c745SQuentin Monnet 	*data_ptr = malloc(block_size);
1116ba95c745SQuentin Monnet 	if (!*data_ptr) {
1117ba95c745SQuentin Monnet 		p_err("failed to allocate memory for data_in/ctx_in: %s",
1118ba95c745SQuentin Monnet 		      strerror(errno));
1119ba95c745SQuentin Monnet 		goto err_fclose;
1120ba95c745SQuentin Monnet 	}
1121ba95c745SQuentin Monnet 
1122ba95c745SQuentin Monnet 	while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) {
1123ba95c745SQuentin Monnet 		if (feof(f))
1124ba95c745SQuentin Monnet 			break;
1125ba95c745SQuentin Monnet 		if (ferror(f)) {
1126ba95c745SQuentin Monnet 			p_err("failed to read data_in/ctx_in from %s: %s",
1127ba95c745SQuentin Monnet 			      fname, strerror(errno));
1128ba95c745SQuentin Monnet 			goto err_free;
1129ba95c745SQuentin Monnet 		}
1130ba95c745SQuentin Monnet 		if (nb_read > buf_size - block_size) {
1131ba95c745SQuentin Monnet 			if (buf_size == UINT32_MAX) {
1132ba95c745SQuentin Monnet 				p_err("data_in/ctx_in is too long (max: %d)",
1133ba95c745SQuentin Monnet 				      UINT32_MAX);
1134ba95c745SQuentin Monnet 				goto err_free;
1135ba95c745SQuentin Monnet 			}
1136ba95c745SQuentin Monnet 			/* No space for fread()-ing next chunk; realloc() */
1137ba95c745SQuentin Monnet 			buf_size *= 2;
1138ba95c745SQuentin Monnet 			tmp = realloc(*data_ptr, buf_size);
1139ba95c745SQuentin Monnet 			if (!tmp) {
1140ba95c745SQuentin Monnet 				p_err("failed to reallocate data_in/ctx_in: %s",
1141ba95c745SQuentin Monnet 				      strerror(errno));
1142ba95c745SQuentin Monnet 				goto err_free;
1143ba95c745SQuentin Monnet 			}
1144ba95c745SQuentin Monnet 			*data_ptr = tmp;
1145ba95c745SQuentin Monnet 		}
1146ba95c745SQuentin Monnet 	}
1147ba95c745SQuentin Monnet 	if (f != stdin)
1148ba95c745SQuentin Monnet 		fclose(f);
1149ba95c745SQuentin Monnet 
1150ba95c745SQuentin Monnet 	*size = nb_read;
1151ba95c745SQuentin Monnet 	return 0;
1152ba95c745SQuentin Monnet 
1153ba95c745SQuentin Monnet err_free:
1154ba95c745SQuentin Monnet 	free(*data_ptr);
1155ba95c745SQuentin Monnet 	*data_ptr = NULL;
1156ba95c745SQuentin Monnet err_fclose:
1157ba95c745SQuentin Monnet 	if (f != stdin)
1158ba95c745SQuentin Monnet 		fclose(f);
1159ba95c745SQuentin Monnet 	return -1;
1160ba95c745SQuentin Monnet }
1161ba95c745SQuentin Monnet 
1162ba95c745SQuentin Monnet static void hex_print(void *data, unsigned int size, FILE *f)
1163ba95c745SQuentin Monnet {
1164ba95c745SQuentin Monnet 	size_t i, j;
1165ba95c745SQuentin Monnet 	char c;
1166ba95c745SQuentin Monnet 
1167ba95c745SQuentin Monnet 	for (i = 0; i < size; i += 16) {
1168ba95c745SQuentin Monnet 		/* Row offset */
1169ba95c745SQuentin Monnet 		fprintf(f, "%07zx\t", i);
1170ba95c745SQuentin Monnet 
1171ba95c745SQuentin Monnet 		/* Hexadecimal values */
1172ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++)
1173ba95c745SQuentin Monnet 			fprintf(f, "%02x%s", *(uint8_t *)(data + j),
1174ba95c745SQuentin Monnet 				j % 2 ? " " : "");
1175ba95c745SQuentin Monnet 		for (; j < i + 16; j++)
1176ba95c745SQuentin Monnet 			fprintf(f, "  %s", j % 2 ? " " : "");
1177ba95c745SQuentin Monnet 
1178ba95c745SQuentin Monnet 		/* ASCII values (if relevant), '.' otherwise */
1179ba95c745SQuentin Monnet 		fprintf(f, "| ");
1180ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++) {
1181ba95c745SQuentin Monnet 			c = *(char *)(data + j);
1182ba95c745SQuentin Monnet 			if (c < ' ' || c > '~')
1183ba95c745SQuentin Monnet 				c = '.';
1184ba95c745SQuentin Monnet 			fprintf(f, "%c%s", c, j == i + 7 ? " " : "");
1185ba95c745SQuentin Monnet 		}
1186ba95c745SQuentin Monnet 
1187ba95c745SQuentin Monnet 		fprintf(f, "\n");
1188ba95c745SQuentin Monnet 	}
1189ba95c745SQuentin Monnet }
1190ba95c745SQuentin Monnet 
1191ba95c745SQuentin Monnet static int
1192ba95c745SQuentin Monnet print_run_output(void *data, unsigned int size, const char *fname,
1193ba95c745SQuentin Monnet 		 const char *json_key)
1194ba95c745SQuentin Monnet {
1195ba95c745SQuentin Monnet 	size_t nb_written;
1196ba95c745SQuentin Monnet 	FILE *f;
1197ba95c745SQuentin Monnet 
1198ba95c745SQuentin Monnet 	if (!fname)
1199ba95c745SQuentin Monnet 		return 0;
1200ba95c745SQuentin Monnet 
1201ba95c745SQuentin Monnet 	if (!strcmp(fname, "-")) {
1202ba95c745SQuentin Monnet 		f = stdout;
1203ba95c745SQuentin Monnet 		if (json_output) {
1204ba95c745SQuentin Monnet 			jsonw_name(json_wtr, json_key);
1205ba95c745SQuentin Monnet 			print_data_json(data, size);
1206ba95c745SQuentin Monnet 		} else {
1207ba95c745SQuentin Monnet 			hex_print(data, size, f);
1208ba95c745SQuentin Monnet 		}
1209ba95c745SQuentin Monnet 		return 0;
1210ba95c745SQuentin Monnet 	}
1211ba95c745SQuentin Monnet 
1212ba95c745SQuentin Monnet 	f = fopen(fname, "w");
1213ba95c745SQuentin Monnet 	if (!f) {
1214ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1215ba95c745SQuentin Monnet 		return -1;
1216ba95c745SQuentin Monnet 	}
1217ba95c745SQuentin Monnet 
1218ba95c745SQuentin Monnet 	nb_written = fwrite(data, 1, size, f);
1219ba95c745SQuentin Monnet 	fclose(f);
1220ba95c745SQuentin Monnet 	if (nb_written != size) {
1221ba95c745SQuentin Monnet 		p_err("failed to write output data/ctx: %s", strerror(errno));
1222ba95c745SQuentin Monnet 		return -1;
1223ba95c745SQuentin Monnet 	}
1224ba95c745SQuentin Monnet 
1225ba95c745SQuentin Monnet 	return 0;
1226ba95c745SQuentin Monnet }
1227ba95c745SQuentin Monnet 
1228ba95c745SQuentin Monnet static int alloc_run_data(void **data_ptr, unsigned int size_out)
1229ba95c745SQuentin Monnet {
1230ba95c745SQuentin Monnet 	*data_ptr = calloc(size_out, 1);
1231ba95c745SQuentin Monnet 	if (!*data_ptr) {
1232ba95c745SQuentin Monnet 		p_err("failed to allocate memory for output data/ctx: %s",
1233ba95c745SQuentin Monnet 		      strerror(errno));
1234ba95c745SQuentin Monnet 		return -1;
1235ba95c745SQuentin Monnet 	}
1236ba95c745SQuentin Monnet 
1237ba95c745SQuentin Monnet 	return 0;
1238ba95c745SQuentin Monnet }
1239ba95c745SQuentin Monnet 
1240ba95c745SQuentin Monnet static int do_run(int argc, char **argv)
1241ba95c745SQuentin Monnet {
1242ba95c745SQuentin Monnet 	char *data_fname_in = NULL, *data_fname_out = NULL;
1243ba95c745SQuentin Monnet 	char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
1244ba95c745SQuentin Monnet 	const unsigned int default_size = SZ_32K;
1245ba95c745SQuentin Monnet 	void *data_in = NULL, *data_out = NULL;
1246ba95c745SQuentin Monnet 	void *ctx_in = NULL, *ctx_out = NULL;
1247ba95c745SQuentin Monnet 	unsigned int repeat = 1;
1248ba95c745SQuentin Monnet 	int fd, err;
12499cce5313SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, test_attr);
1250ba95c745SQuentin Monnet 
1251ba95c745SQuentin Monnet 	if (!REQ_ARGS(4))
1252ba95c745SQuentin Monnet 		return -1;
1253ba95c745SQuentin Monnet 
1254ba95c745SQuentin Monnet 	fd = prog_parse_fd(&argc, &argv);
1255ba95c745SQuentin Monnet 	if (fd < 0)
1256ba95c745SQuentin Monnet 		return -1;
1257ba95c745SQuentin Monnet 
1258ba95c745SQuentin Monnet 	while (argc) {
1259ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "data_in", "data_out",
1260ba95c745SQuentin Monnet 					 "data_size_out", NULL))
1261ba95c745SQuentin Monnet 			return -1;
1262ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "ctx_in", "ctx_out",
1263ba95c745SQuentin Monnet 					 "ctx_size_out", NULL))
1264ba95c745SQuentin Monnet 			return -1;
1265ba95c745SQuentin Monnet 
1266ba95c745SQuentin Monnet 		if (is_prefix(*argv, "data_in")) {
1267ba95c745SQuentin Monnet 			NEXT_ARG();
1268ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1269ba95c745SQuentin Monnet 				return -1;
1270ba95c745SQuentin Monnet 
1271ba95c745SQuentin Monnet 			data_fname_in = GET_ARG();
1272ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1273ba95c745SQuentin Monnet 				return -1;
1274ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_out")) {
1275ba95c745SQuentin Monnet 			NEXT_ARG();
1276ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1277ba95c745SQuentin Monnet 				return -1;
1278ba95c745SQuentin Monnet 
1279ba95c745SQuentin Monnet 			data_fname_out = GET_ARG();
1280ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_size_out")) {
1281ba95c745SQuentin Monnet 			char *endptr;
1282ba95c745SQuentin Monnet 
1283ba95c745SQuentin Monnet 			NEXT_ARG();
1284ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1285ba95c745SQuentin Monnet 				return -1;
1286ba95c745SQuentin Monnet 
1287ba95c745SQuentin Monnet 			test_attr.data_size_out = strtoul(*argv, &endptr, 0);
1288ba95c745SQuentin Monnet 			if (*endptr) {
1289ba95c745SQuentin Monnet 				p_err("can't parse %s as output data size",
1290ba95c745SQuentin Monnet 				      *argv);
1291ba95c745SQuentin Monnet 				return -1;
1292ba95c745SQuentin Monnet 			}
1293ba95c745SQuentin Monnet 			NEXT_ARG();
1294ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_in")) {
1295ba95c745SQuentin Monnet 			NEXT_ARG();
1296ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1297ba95c745SQuentin Monnet 				return -1;
1298ba95c745SQuentin Monnet 
1299ba95c745SQuentin Monnet 			ctx_fname_in = GET_ARG();
1300ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1301ba95c745SQuentin Monnet 				return -1;
1302ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_out")) {
1303ba95c745SQuentin Monnet 			NEXT_ARG();
1304ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1305ba95c745SQuentin Monnet 				return -1;
1306ba95c745SQuentin Monnet 
1307ba95c745SQuentin Monnet 			ctx_fname_out = GET_ARG();
1308ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_size_out")) {
1309ba95c745SQuentin Monnet 			char *endptr;
1310ba95c745SQuentin Monnet 
1311ba95c745SQuentin Monnet 			NEXT_ARG();
1312ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1313ba95c745SQuentin Monnet 				return -1;
1314ba95c745SQuentin Monnet 
1315ba95c745SQuentin Monnet 			test_attr.ctx_size_out = strtoul(*argv, &endptr, 0);
1316ba95c745SQuentin Monnet 			if (*endptr) {
1317ba95c745SQuentin Monnet 				p_err("can't parse %s as output context size",
1318ba95c745SQuentin Monnet 				      *argv);
1319ba95c745SQuentin Monnet 				return -1;
1320ba95c745SQuentin Monnet 			}
1321ba95c745SQuentin Monnet 			NEXT_ARG();
1322ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "repeat")) {
1323ba95c745SQuentin Monnet 			char *endptr;
1324ba95c745SQuentin Monnet 
1325ba95c745SQuentin Monnet 			NEXT_ARG();
1326ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1327ba95c745SQuentin Monnet 				return -1;
1328ba95c745SQuentin Monnet 
1329ba95c745SQuentin Monnet 			repeat = strtoul(*argv, &endptr, 0);
1330ba95c745SQuentin Monnet 			if (*endptr) {
1331ba95c745SQuentin Monnet 				p_err("can't parse %s as repeat number",
1332ba95c745SQuentin Monnet 				      *argv);
1333ba95c745SQuentin Monnet 				return -1;
1334ba95c745SQuentin Monnet 			}
1335ba95c745SQuentin Monnet 			NEXT_ARG();
1336ba95c745SQuentin Monnet 		} else {
1337ba95c745SQuentin 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'?",
1338ba95c745SQuentin Monnet 			      *argv);
1339ba95c745SQuentin Monnet 			return -1;
1340ba95c745SQuentin Monnet 		}
1341ba95c745SQuentin Monnet 	}
1342ba95c745SQuentin Monnet 
1343ba95c745SQuentin Monnet 	err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in);
1344ba95c745SQuentin Monnet 	if (err)
1345ba95c745SQuentin Monnet 		return -1;
1346ba95c745SQuentin Monnet 
1347ba95c745SQuentin Monnet 	if (data_in) {
1348ba95c745SQuentin Monnet 		if (!test_attr.data_size_out)
1349ba95c745SQuentin Monnet 			test_attr.data_size_out = default_size;
1350ba95c745SQuentin Monnet 		err = alloc_run_data(&data_out, test_attr.data_size_out);
1351ba95c745SQuentin Monnet 		if (err)
1352ba95c745SQuentin Monnet 			goto free_data_in;
1353ba95c745SQuentin Monnet 	}
1354ba95c745SQuentin Monnet 
1355ba95c745SQuentin Monnet 	err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in);
1356ba95c745SQuentin Monnet 	if (err)
1357ba95c745SQuentin Monnet 		goto free_data_out;
1358ba95c745SQuentin Monnet 
1359ba95c745SQuentin Monnet 	if (ctx_in) {
1360ba95c745SQuentin Monnet 		if (!test_attr.ctx_size_out)
1361ba95c745SQuentin Monnet 			test_attr.ctx_size_out = default_size;
1362ba95c745SQuentin Monnet 		err = alloc_run_data(&ctx_out, test_attr.ctx_size_out);
1363ba95c745SQuentin Monnet 		if (err)
1364ba95c745SQuentin Monnet 			goto free_ctx_in;
1365ba95c745SQuentin Monnet 	}
1366ba95c745SQuentin Monnet 
1367ba95c745SQuentin Monnet 	test_attr.repeat	= repeat;
1368ba95c745SQuentin Monnet 	test_attr.data_in	= data_in;
1369ba95c745SQuentin Monnet 	test_attr.data_out	= data_out;
1370ba95c745SQuentin Monnet 	test_attr.ctx_in	= ctx_in;
1371ba95c745SQuentin Monnet 	test_attr.ctx_out	= ctx_out;
1372ba95c745SQuentin Monnet 
13739cce5313SDelyan Kratunov 	err = bpf_prog_test_run_opts(fd, &test_attr);
1374ba95c745SQuentin Monnet 	if (err) {
1375ba95c745SQuentin Monnet 		p_err("failed to run program: %s", strerror(errno));
1376ba95c745SQuentin Monnet 		goto free_ctx_out;
1377ba95c745SQuentin Monnet 	}
1378ba95c745SQuentin Monnet 
1379ba95c745SQuentin Monnet 	err = 0;
1380ba95c745SQuentin Monnet 
1381ba95c745SQuentin Monnet 	if (json_output)
1382ba95c745SQuentin Monnet 		jsonw_start_object(json_wtr);	/* root */
1383ba95c745SQuentin Monnet 
1384ba95c745SQuentin Monnet 	/* Do not exit on errors occurring when printing output data/context,
1385ba95c745SQuentin Monnet 	 * we still want to print return value and duration for program run.
1386ba95c745SQuentin Monnet 	 */
1387ba95c745SQuentin Monnet 	if (test_attr.data_size_out)
1388ba95c745SQuentin Monnet 		err += print_run_output(test_attr.data_out,
1389ba95c745SQuentin Monnet 					test_attr.data_size_out,
1390ba95c745SQuentin Monnet 					data_fname_out, "data_out");
1391ba95c745SQuentin Monnet 	if (test_attr.ctx_size_out)
1392ba95c745SQuentin Monnet 		err += print_run_output(test_attr.ctx_out,
1393ba95c745SQuentin Monnet 					test_attr.ctx_size_out,
1394ba95c745SQuentin Monnet 					ctx_fname_out, "ctx_out");
1395ba95c745SQuentin Monnet 
1396ba95c745SQuentin Monnet 	if (json_output) {
1397ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "retval", test_attr.retval);
1398ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "duration", test_attr.duration);
1399ba95c745SQuentin Monnet 		jsonw_end_object(json_wtr);	/* root */
1400ba95c745SQuentin Monnet 	} else {
1401ba95c745SQuentin Monnet 		fprintf(stdout, "Return value: %u, duration%s: %uns\n",
1402ba95c745SQuentin Monnet 			test_attr.retval,
1403ba95c745SQuentin Monnet 			repeat > 1 ? " (average)" : "", test_attr.duration);
1404ba95c745SQuentin Monnet 	}
1405ba95c745SQuentin Monnet 
1406ba95c745SQuentin Monnet free_ctx_out:
1407ba95c745SQuentin Monnet 	free(ctx_out);
1408ba95c745SQuentin Monnet free_ctx_in:
1409ba95c745SQuentin Monnet 	free(ctx_in);
1410ba95c745SQuentin Monnet free_data_out:
1411ba95c745SQuentin Monnet 	free(data_out);
1412ba95c745SQuentin Monnet free_data_in:
1413ba95c745SQuentin Monnet 	free(data_in);
1414ba95c745SQuentin Monnet 
1415ba95c745SQuentin Monnet 	return err;
1416ba95c745SQuentin Monnet }
1417ba95c745SQuentin Monnet 
14186ae32b29SQuentin Monnet static int
14196ae32b29SQuentin Monnet get_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
14206ae32b29SQuentin Monnet 		      enum bpf_attach_type *expected_attach_type)
14216ae32b29SQuentin Monnet {
14226ae32b29SQuentin Monnet 	libbpf_print_fn_t print_backup;
14236ae32b29SQuentin Monnet 	int ret;
14246ae32b29SQuentin Monnet 
14256ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14266ae32b29SQuentin Monnet 	if (!ret)
14276ae32b29SQuentin Monnet 		return ret;
14286ae32b29SQuentin Monnet 
14296ae32b29SQuentin Monnet 	/* libbpf_prog_type_by_name() failed, let's re-run with debug level */
14306ae32b29SQuentin Monnet 	print_backup = libbpf_set_print(print_all_levels);
14316ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14326ae32b29SQuentin Monnet 	libbpf_set_print(print_backup);
14336ae32b29SQuentin Monnet 
14346ae32b29SQuentin Monnet 	return ret;
14356ae32b29SQuentin Monnet }
14366ae32b29SQuentin Monnet 
143777380998SStanislav Fomichev static int load_with_options(int argc, char **argv, bool first_prog_only)
143849a086c2SRoman Gushchin {
143932e3e58eSAndrii Nakryiko 	enum bpf_prog_type common_prog_type = BPF_PROG_TYPE_UNSPEC;
1440e00aca65SAndrii Nakryiko 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
1441e00aca65SAndrii Nakryiko 		.relaxed_maps = relaxed_maps,
1442e00aca65SAndrii Nakryiko 	);
144355d77807SQuentin Monnet 	enum bpf_attach_type expected_attach_type;
14443ff5a4dcSJakub Kicinski 	struct map_replace *map_replace = NULL;
144577380998SStanislav Fomichev 	struct bpf_program *prog = NULL, *pos;
14463ff5a4dcSJakub Kicinski 	unsigned int old_map_fds = 0;
14473767a94bSStanislav Fomichev 	const char *pinmaps = NULL;
144849a086c2SRoman Gushchin 	struct bpf_object *obj;
1449c8406848SJakub Kicinski 	struct bpf_map *map;
1450c8406848SJakub Kicinski 	const char *pinfile;
14513ff5a4dcSJakub Kicinski 	unsigned int i, j;
1452c8406848SJakub Kicinski 	__u32 ifindex = 0;
145332e3e58eSAndrii Nakryiko 	const char *file;
14543ff5a4dcSJakub Kicinski 	int idx, err;
145549a086c2SRoman Gushchin 
145632e3e58eSAndrii Nakryiko 
14578d1fc3deSJakub Kicinski 	if (!REQ_ARGS(2))
14588d1fc3deSJakub Kicinski 		return -1;
145932e3e58eSAndrii Nakryiko 	file = GET_ARG();
14608d1fc3deSJakub Kicinski 	pinfile = GET_ARG();
146149a086c2SRoman Gushchin 
1462ba6dd679SJakub Kicinski 	while (argc) {
146349f2cba3SJakub Kicinski 		if (is_prefix(*argv, "type")) {
146449f2cba3SJakub Kicinski 			NEXT_ARG();
146549f2cba3SJakub Kicinski 
146632e3e58eSAndrii Nakryiko 			if (common_prog_type != BPF_PROG_TYPE_UNSPEC) {
146749f2cba3SJakub Kicinski 				p_err("program type already specified");
14683ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
146949f2cba3SJakub Kicinski 			}
147049f2cba3SJakub Kicinski 			if (!REQ_ARGS(1))
14713ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
147249f2cba3SJakub Kicinski 
1473314f14abSStanislav Fomichev 			err = libbpf_prog_type_by_name(*argv, &common_prog_type,
1474314f14abSStanislav Fomichev 						       &expected_attach_type);
1475314f14abSStanislav Fomichev 			if (err < 0) {
147649f2cba3SJakub Kicinski 				/* Put a '/' at the end of type to appease libbpf */
1477314f14abSStanislav Fomichev 				char *type = malloc(strlen(*argv) + 2);
1478314f14abSStanislav Fomichev 
147949f2cba3SJakub Kicinski 				if (!type) {
148049f2cba3SJakub Kicinski 					p_err("mem alloc failed");
14813ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
148249f2cba3SJakub Kicinski 				}
148349f2cba3SJakub Kicinski 				*type = 0;
148449f2cba3SJakub Kicinski 				strcat(type, *argv);
148549f2cba3SJakub Kicinski 				strcat(type, "/");
148649f2cba3SJakub Kicinski 
14876ae32b29SQuentin Monnet 				err = get_prog_type_by_name(type, &common_prog_type,
1488c8406848SJakub Kicinski 							    &expected_attach_type);
148949f2cba3SJakub Kicinski 				free(type);
1490c76e4c22STaeung Song 				if (err < 0)
14913ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
1492314f14abSStanislav Fomichev 			}
1493c76e4c22STaeung Song 
149449f2cba3SJakub Kicinski 			NEXT_ARG();
14953ff5a4dcSJakub Kicinski 		} else if (is_prefix(*argv, "map")) {
1496dde7011aSJakub Kicinski 			void *new_map_replace;
14973ff5a4dcSJakub Kicinski 			char *endptr, *name;
14983ff5a4dcSJakub Kicinski 			int fd;
14993ff5a4dcSJakub Kicinski 
15003ff5a4dcSJakub Kicinski 			NEXT_ARG();
15013ff5a4dcSJakub Kicinski 
15023ff5a4dcSJakub Kicinski 			if (!REQ_ARGS(4))
15033ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15043ff5a4dcSJakub Kicinski 
15053ff5a4dcSJakub Kicinski 			if (is_prefix(*argv, "idx")) {
15063ff5a4dcSJakub Kicinski 				NEXT_ARG();
15073ff5a4dcSJakub Kicinski 
15083ff5a4dcSJakub Kicinski 				idx = strtoul(*argv, &endptr, 0);
15093ff5a4dcSJakub Kicinski 				if (*endptr) {
15103ff5a4dcSJakub Kicinski 					p_err("can't parse %s as IDX", *argv);
15113ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
15123ff5a4dcSJakub Kicinski 				}
15133ff5a4dcSJakub Kicinski 				name = NULL;
15143ff5a4dcSJakub Kicinski 			} else if (is_prefix(*argv, "name")) {
15153ff5a4dcSJakub Kicinski 				NEXT_ARG();
15163ff5a4dcSJakub Kicinski 
15173ff5a4dcSJakub Kicinski 				name = *argv;
15183ff5a4dcSJakub Kicinski 				idx = -1;
15193ff5a4dcSJakub Kicinski 			} else {
15203ff5a4dcSJakub Kicinski 				p_err("expected 'idx' or 'name', got: '%s'?",
15213ff5a4dcSJakub Kicinski 				      *argv);
15223ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15233ff5a4dcSJakub Kicinski 			}
15243ff5a4dcSJakub Kicinski 			NEXT_ARG();
15253ff5a4dcSJakub Kicinski 
15263ff5a4dcSJakub Kicinski 			fd = map_parse_fd(&argc, &argv);
15273ff5a4dcSJakub Kicinski 			if (fd < 0)
15283ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15293ff5a4dcSJakub Kicinski 
1530a19df713SMauricio Vásquez 			new_map_replace = libbpf_reallocarray(map_replace,
1531dde7011aSJakub Kicinski 							      old_map_fds + 1,
15323ff5a4dcSJakub Kicinski 							      sizeof(*map_replace));
1533dde7011aSJakub Kicinski 			if (!new_map_replace) {
15343ff5a4dcSJakub Kicinski 				p_err("mem alloc failed");
15353ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15363ff5a4dcSJakub Kicinski 			}
1537dde7011aSJakub Kicinski 			map_replace = new_map_replace;
1538dde7011aSJakub Kicinski 
15393ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].idx = idx;
15403ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].name = name;
15413ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].fd = fd;
15423ff5a4dcSJakub Kicinski 			old_map_fds++;
154349f2cba3SJakub Kicinski 		} else if (is_prefix(*argv, "dev")) {
1544ba6dd679SJakub Kicinski 			NEXT_ARG();
1545ba6dd679SJakub Kicinski 
1546c8406848SJakub Kicinski 			if (ifindex) {
1547ba6dd679SJakub Kicinski 				p_err("offload device already specified");
15483ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1549ba6dd679SJakub Kicinski 			}
1550ba6dd679SJakub Kicinski 			if (!REQ_ARGS(1))
15513ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1552ba6dd679SJakub Kicinski 
1553c8406848SJakub Kicinski 			ifindex = if_nametoindex(*argv);
1554c8406848SJakub Kicinski 			if (!ifindex) {
1555ba6dd679SJakub Kicinski 				p_err("unrecognized netdevice '%s': %s",
1556ba6dd679SJakub Kicinski 				      *argv, strerror(errno));
15573ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1558ba6dd679SJakub Kicinski 			}
1559ba6dd679SJakub Kicinski 			NEXT_ARG();
15603767a94bSStanislav Fomichev 		} else if (is_prefix(*argv, "pinmaps")) {
15613767a94bSStanislav Fomichev 			NEXT_ARG();
15623767a94bSStanislav Fomichev 
15633767a94bSStanislav Fomichev 			if (!REQ_ARGS(1))
15643767a94bSStanislav Fomichev 				goto err_free_reuse_maps;
15653767a94bSStanislav Fomichev 
15663767a94bSStanislav Fomichev 			pinmaps = GET_ARG();
1567ba6dd679SJakub Kicinski 		} else {
15683ff5a4dcSJakub Kicinski 			p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
1569ba6dd679SJakub Kicinski 			      *argv);
15703ff5a4dcSJakub Kicinski 			goto err_free_reuse_maps;
1571ba6dd679SJakub Kicinski 		}
1572ba6dd679SJakub Kicinski 	}
1573ba6dd679SJakub Kicinski 
1574b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1575b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1576b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1577b59e4ce8SAndrii Nakryiko 
157832e3e58eSAndrii Nakryiko 	obj = bpf_object__open_file(file, &open_opts);
1579d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1580c8406848SJakub Kicinski 		p_err("failed to open object file");
15813ff5a4dcSJakub Kicinski 		goto err_free_reuse_maps;
158249a086c2SRoman Gushchin 	}
158349a086c2SRoman Gushchin 
158477380998SStanislav Fomichev 	bpf_object__for_each_program(pos, obj) {
158532e3e58eSAndrii Nakryiko 		enum bpf_prog_type prog_type = common_prog_type;
1586c8406848SJakub Kicinski 
158732e3e58eSAndrii Nakryiko 		if (prog_type == BPF_PROG_TYPE_UNSPEC) {
1588fd17e272SAndrii Nakryiko 			const char *sec_name = bpf_program__section_name(pos);
1589c8406848SJakub Kicinski 
15906ae32b29SQuentin Monnet 			err = get_prog_type_by_name(sec_name, &prog_type,
1591c8406848SJakub Kicinski 						    &expected_attach_type);
1592c76e4c22STaeung Song 			if (err < 0)
1593c8406848SJakub Kicinski 				goto err_close_obj;
1594c8406848SJakub Kicinski 		}
159577380998SStanislav Fomichev 
159677380998SStanislav Fomichev 		bpf_program__set_ifindex(pos, ifindex);
159777380998SStanislav Fomichev 		bpf_program__set_type(pos, prog_type);
159877380998SStanislav Fomichev 		bpf_program__set_expected_attach_type(pos, expected_attach_type);
159977380998SStanislav Fomichev 	}
1600c8406848SJakub Kicinski 
16013ff5a4dcSJakub Kicinski 	qsort(map_replace, old_map_fds, sizeof(*map_replace),
16023ff5a4dcSJakub Kicinski 	      map_replace_compar);
16033ff5a4dcSJakub Kicinski 
16043ff5a4dcSJakub Kicinski 	/* After the sort maps by name will be first on the list, because they
16053ff5a4dcSJakub Kicinski 	 * have idx == -1.  Resolve them.
16063ff5a4dcSJakub Kicinski 	 */
16073ff5a4dcSJakub Kicinski 	j = 0;
16083ff5a4dcSJakub Kicinski 	while (j < old_map_fds && map_replace[j].name) {
16093ff5a4dcSJakub Kicinski 		i = 0;
1610f74a53d9SJakub Kicinski 		bpf_object__for_each_map(map, obj) {
16113ff5a4dcSJakub Kicinski 			if (!strcmp(bpf_map__name(map), map_replace[j].name)) {
16123ff5a4dcSJakub Kicinski 				map_replace[j].idx = i;
16133ff5a4dcSJakub Kicinski 				break;
16143ff5a4dcSJakub Kicinski 			}
16153ff5a4dcSJakub Kicinski 			i++;
16163ff5a4dcSJakub Kicinski 		}
16173ff5a4dcSJakub Kicinski 		if (map_replace[j].idx == -1) {
16183ff5a4dcSJakub Kicinski 			p_err("unable to find map '%s'", map_replace[j].name);
16193ff5a4dcSJakub Kicinski 			goto err_close_obj;
16203ff5a4dcSJakub Kicinski 		}
16213ff5a4dcSJakub Kicinski 		j++;
16223ff5a4dcSJakub Kicinski 	}
16233ff5a4dcSJakub Kicinski 	/* Resort if any names were resolved */
16243ff5a4dcSJakub Kicinski 	if (j)
16253ff5a4dcSJakub Kicinski 		qsort(map_replace, old_map_fds, sizeof(*map_replace),
16263ff5a4dcSJakub Kicinski 		      map_replace_compar);
16273ff5a4dcSJakub Kicinski 
16283ff5a4dcSJakub Kicinski 	/* Set ifindex and name reuse */
16293ff5a4dcSJakub Kicinski 	j = 0;
16303ff5a4dcSJakub Kicinski 	idx = 0;
1631f74a53d9SJakub Kicinski 	bpf_object__for_each_map(map, obj) {
16329855c131SChristy Lee 		if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
1633c8406848SJakub Kicinski 			bpf_map__set_ifindex(map, ifindex);
1634c8406848SJakub Kicinski 
16353ff5a4dcSJakub Kicinski 		if (j < old_map_fds && idx == map_replace[j].idx) {
16363ff5a4dcSJakub Kicinski 			err = bpf_map__reuse_fd(map, map_replace[j++].fd);
16373ff5a4dcSJakub Kicinski 			if (err) {
16383ff5a4dcSJakub Kicinski 				p_err("unable to set up map reuse: %d", err);
16393ff5a4dcSJakub Kicinski 				goto err_close_obj;
16403ff5a4dcSJakub Kicinski 			}
16413ff5a4dcSJakub Kicinski 
16423ff5a4dcSJakub Kicinski 			/* Next reuse wants to apply to the same map */
16433ff5a4dcSJakub Kicinski 			if (j < old_map_fds && map_replace[j].idx == idx) {
16443ff5a4dcSJakub Kicinski 				p_err("replacement for map idx %d specified more than once",
16453ff5a4dcSJakub Kicinski 				      idx);
16463ff5a4dcSJakub Kicinski 				goto err_close_obj;
16473ff5a4dcSJakub Kicinski 			}
16483ff5a4dcSJakub Kicinski 		}
16493ff5a4dcSJakub Kicinski 
16503ff5a4dcSJakub Kicinski 		idx++;
16513ff5a4dcSJakub Kicinski 	}
16523ff5a4dcSJakub Kicinski 	if (j < old_map_fds) {
16533ff5a4dcSJakub Kicinski 		p_err("map idx '%d' not used", map_replace[j].idx);
16543ff5a4dcSJakub Kicinski 		goto err_close_obj;
16553ff5a4dcSJakub Kicinski 	}
16563ff5a4dcSJakub Kicinski 
1657b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1658c8406848SJakub Kicinski 	if (err) {
1659c8406848SJakub Kicinski 		p_err("failed to load object file");
1660c8406848SJakub Kicinski 		goto err_close_obj;
1661c8406848SJakub Kicinski 	}
1662c8406848SJakub Kicinski 
166377380998SStanislav Fomichev 	err = mount_bpffs_for_pin(pinfile);
166477380998SStanislav Fomichev 	if (err)
1665bfee71fbSJakub Kicinski 		goto err_close_obj;
166649a086c2SRoman Gushchin 
166777380998SStanislav Fomichev 	if (first_prog_only) {
16686f2b219bSHengqi Chen 		prog = bpf_object__next_program(obj, NULL);
166977380998SStanislav Fomichev 		if (!prog) {
167077380998SStanislav Fomichev 			p_err("object file doesn't contain any bpf program");
167177380998SStanislav Fomichev 			goto err_close_obj;
167277380998SStanislav Fomichev 		}
167377380998SStanislav Fomichev 
167477380998SStanislav Fomichev 		err = bpf_obj_pin(bpf_program__fd(prog), pinfile);
167577380998SStanislav Fomichev 		if (err) {
167677380998SStanislav Fomichev 			p_err("failed to pin program %s",
1677fd17e272SAndrii Nakryiko 			      bpf_program__section_name(prog));
167877380998SStanislav Fomichev 			goto err_close_obj;
167977380998SStanislav Fomichev 		}
168077380998SStanislav Fomichev 	} else {
168177380998SStanislav Fomichev 		err = bpf_object__pin_programs(obj, pinfile);
168277380998SStanislav Fomichev 		if (err) {
168377380998SStanislav Fomichev 			p_err("failed to pin all programs");
168477380998SStanislav Fomichev 			goto err_close_obj;
168577380998SStanislav Fomichev 		}
168677380998SStanislav Fomichev 	}
168777380998SStanislav Fomichev 
16883767a94bSStanislav Fomichev 	if (pinmaps) {
16893767a94bSStanislav Fomichev 		err = bpf_object__pin_maps(obj, pinmaps);
16903767a94bSStanislav Fomichev 		if (err) {
16913767a94bSStanislav Fomichev 			p_err("failed to pin all maps");
16923767a94bSStanislav Fomichev 			goto err_unpin;
16933767a94bSStanislav Fomichev 		}
16943767a94bSStanislav Fomichev 	}
16953767a94bSStanislav Fomichev 
169649a086c2SRoman Gushchin 	if (json_output)
169749a086c2SRoman Gushchin 		jsonw_null(json_wtr);
169849a086c2SRoman Gushchin 
1699bfee71fbSJakub Kicinski 	bpf_object__close(obj);
17003ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
17013ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
17023ff5a4dcSJakub Kicinski 	free(map_replace);
1703bfee71fbSJakub Kicinski 
170449a086c2SRoman Gushchin 	return 0;
1705bfee71fbSJakub Kicinski 
17063767a94bSStanislav Fomichev err_unpin:
17073767a94bSStanislav Fomichev 	if (first_prog_only)
17083767a94bSStanislav Fomichev 		unlink(pinfile);
17093767a94bSStanislav Fomichev 	else
17103767a94bSStanislav Fomichev 		bpf_object__unpin_programs(obj, pinfile);
1711bfee71fbSJakub Kicinski err_close_obj:
1712314f14abSStanislav Fomichev 	if (!legacy_libbpf) {
1713314f14abSStanislav Fomichev 		p_info("Warning: bpftool is now running in libbpf strict mode and has more stringent requirements about BPF programs.\n"
1714314f14abSStanislav Fomichev 		       "If it used to work for this object file but now doesn't, see --legacy option for more details.\n");
1715314f14abSStanislav Fomichev 	}
1716314f14abSStanislav Fomichev 
1717bfee71fbSJakub Kicinski 	bpf_object__close(obj);
17183ff5a4dcSJakub Kicinski err_free_reuse_maps:
17193ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
17203ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
17213ff5a4dcSJakub Kicinski 	free(map_replace);
1722bfee71fbSJakub Kicinski 	return -1;
172349a086c2SRoman Gushchin }
172449a086c2SRoman Gushchin 
1725d510296dSAlexei Starovoitov static int count_open_fds(void)
1726d510296dSAlexei Starovoitov {
1727d510296dSAlexei Starovoitov 	DIR *dp = opendir("/proc/self/fd");
1728d510296dSAlexei Starovoitov 	struct dirent *de;
1729d510296dSAlexei Starovoitov 	int cnt = -3;
1730d510296dSAlexei Starovoitov 
1731d510296dSAlexei Starovoitov 	if (!dp)
1732d510296dSAlexei Starovoitov 		return -1;
1733d510296dSAlexei Starovoitov 
1734d510296dSAlexei Starovoitov 	while ((de = readdir(dp)))
1735d510296dSAlexei Starovoitov 		cnt++;
1736d510296dSAlexei Starovoitov 
1737d510296dSAlexei Starovoitov 	closedir(dp);
1738d510296dSAlexei Starovoitov 	return cnt;
1739d510296dSAlexei Starovoitov }
1740d510296dSAlexei Starovoitov 
1741d510296dSAlexei Starovoitov static int try_loader(struct gen_loader_opts *gen)
1742d510296dSAlexei Starovoitov {
1743d510296dSAlexei Starovoitov 	struct bpf_load_and_run_opts opts = {};
1744d510296dSAlexei Starovoitov 	struct bpf_loader_ctx *ctx;
1745d510296dSAlexei Starovoitov 	int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
1746d510296dSAlexei Starovoitov 					     sizeof(struct bpf_prog_desc));
1747d510296dSAlexei Starovoitov 	int log_buf_sz = (1u << 24) - 1;
1748d510296dSAlexei Starovoitov 	int err, fds_before, fd_delta;
1749942df4dcSAlexei Starovoitov 	char *log_buf = NULL;
1750d510296dSAlexei Starovoitov 
1751d510296dSAlexei Starovoitov 	ctx = alloca(ctx_sz);
1752d510296dSAlexei Starovoitov 	memset(ctx, 0, ctx_sz);
1753d510296dSAlexei Starovoitov 	ctx->sz = ctx_sz;
1754942df4dcSAlexei Starovoitov 	if (verifier_logs) {
1755942df4dcSAlexei Starovoitov 		ctx->log_level = 1 + 2 + 4;
1756d510296dSAlexei Starovoitov 		ctx->log_size = log_buf_sz;
1757d510296dSAlexei Starovoitov 		log_buf = malloc(log_buf_sz);
1758d510296dSAlexei Starovoitov 		if (!log_buf)
1759d510296dSAlexei Starovoitov 			return -ENOMEM;
1760d510296dSAlexei Starovoitov 		ctx->log_buf = (long) log_buf;
1761942df4dcSAlexei Starovoitov 	}
1762d510296dSAlexei Starovoitov 	opts.ctx = ctx;
1763d510296dSAlexei Starovoitov 	opts.data = gen->data;
1764d510296dSAlexei Starovoitov 	opts.data_sz = gen->data_sz;
1765d510296dSAlexei Starovoitov 	opts.insns = gen->insns;
1766d510296dSAlexei Starovoitov 	opts.insns_sz = gen->insns_sz;
1767d510296dSAlexei Starovoitov 	fds_before = count_open_fds();
1768d510296dSAlexei Starovoitov 	err = bpf_load_and_run(&opts);
1769d510296dSAlexei Starovoitov 	fd_delta = count_open_fds() - fds_before;
1770942df4dcSAlexei Starovoitov 	if (err < 0 || verifier_logs) {
1771d510296dSAlexei Starovoitov 		fprintf(stderr, "err %d\n%s\n%s", err, opts.errstr, log_buf);
1772942df4dcSAlexei Starovoitov 		if (fd_delta && err < 0)
1773d510296dSAlexei Starovoitov 			fprintf(stderr, "loader prog leaked %d FDs\n",
1774d510296dSAlexei Starovoitov 				fd_delta);
1775d510296dSAlexei Starovoitov 	}
1776d510296dSAlexei Starovoitov 	free(log_buf);
1777d510296dSAlexei Starovoitov 	return err;
1778d510296dSAlexei Starovoitov }
1779d510296dSAlexei Starovoitov 
1780d510296dSAlexei Starovoitov static int do_loader(int argc, char **argv)
1781d510296dSAlexei Starovoitov {
1782d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts);
1783d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(gen_loader_opts, gen);
1784d510296dSAlexei Starovoitov 	struct bpf_object *obj;
1785d510296dSAlexei Starovoitov 	const char *file;
1786d510296dSAlexei Starovoitov 	int err = 0;
1787d510296dSAlexei Starovoitov 
1788d510296dSAlexei Starovoitov 	if (!REQ_ARGS(1))
1789d510296dSAlexei Starovoitov 		return -1;
1790d510296dSAlexei Starovoitov 	file = GET_ARG();
1791d510296dSAlexei Starovoitov 
1792b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1793b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1794b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1795b59e4ce8SAndrii Nakryiko 
1796d510296dSAlexei Starovoitov 	obj = bpf_object__open_file(file, &open_opts);
1797d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1798d510296dSAlexei Starovoitov 		p_err("failed to open object file");
1799d510296dSAlexei Starovoitov 		goto err_close_obj;
1800d510296dSAlexei Starovoitov 	}
1801d510296dSAlexei Starovoitov 
1802d510296dSAlexei Starovoitov 	err = bpf_object__gen_loader(obj, &gen);
1803d510296dSAlexei Starovoitov 	if (err)
1804d510296dSAlexei Starovoitov 		goto err_close_obj;
1805d510296dSAlexei Starovoitov 
1806b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1807d510296dSAlexei Starovoitov 	if (err) {
1808d510296dSAlexei Starovoitov 		p_err("failed to load object file");
1809d510296dSAlexei Starovoitov 		goto err_close_obj;
1810d510296dSAlexei Starovoitov 	}
1811d510296dSAlexei Starovoitov 
1812d510296dSAlexei Starovoitov 	if (verifier_logs) {
1813d510296dSAlexei Starovoitov 		struct dump_data dd = {};
1814d510296dSAlexei Starovoitov 
1815d510296dSAlexei Starovoitov 		kernel_syms_load(&dd);
1816d510296dSAlexei Starovoitov 		dump_xlated_plain(&dd, (void *)gen.insns, gen.insns_sz, false, false);
1817d510296dSAlexei Starovoitov 		kernel_syms_destroy(&dd);
1818d510296dSAlexei Starovoitov 	}
1819d510296dSAlexei Starovoitov 	err = try_loader(&gen);
1820d510296dSAlexei Starovoitov err_close_obj:
1821d510296dSAlexei Starovoitov 	bpf_object__close(obj);
1822d510296dSAlexei Starovoitov 	return err;
1823d510296dSAlexei Starovoitov }
1824d510296dSAlexei Starovoitov 
182577380998SStanislav Fomichev static int do_load(int argc, char **argv)
182677380998SStanislav Fomichev {
1827d510296dSAlexei Starovoitov 	if (use_loader)
1828d510296dSAlexei Starovoitov 		return do_loader(argc, argv);
182977380998SStanislav Fomichev 	return load_with_options(argc, argv, true);
183077380998SStanislav Fomichev }
183177380998SStanislav Fomichev 
183277380998SStanislav Fomichev static int do_loadall(int argc, char **argv)
183377380998SStanislav Fomichev {
183477380998SStanislav Fomichev 	return load_with_options(argc, argv, false);
183577380998SStanislav Fomichev }
183677380998SStanislav Fomichev 
183747c09d6aSSong Liu #ifdef BPFTOOL_WITHOUT_SKELETONS
183847c09d6aSSong Liu 
183947c09d6aSSong Liu static int do_profile(int argc, char **argv)
184047c09d6aSSong Liu {
184114e5728fSSong Liu 	p_err("bpftool prog profile command is not supported. Please build bpftool with clang >= 10.0.0");
184247c09d6aSSong Liu 	return 0;
184347c09d6aSSong Liu }
184447c09d6aSSong Liu 
184547c09d6aSSong Liu #else /* BPFTOOL_WITHOUT_SKELETONS */
184647c09d6aSSong Liu 
184747c09d6aSSong Liu #include "profiler.skel.h"
184847c09d6aSSong Liu 
184947c09d6aSSong Liu struct profile_metric {
185047c09d6aSSong Liu 	const char *name;
185147c09d6aSSong Liu 	struct bpf_perf_event_value val;
185247c09d6aSSong Liu 	struct perf_event_attr attr;
185347c09d6aSSong Liu 	bool selected;
185447c09d6aSSong Liu 
185547c09d6aSSong Liu 	/* calculate ratios like instructions per cycle */
185647c09d6aSSong Liu 	const int ratio_metric; /* 0 for N/A, 1 for index 0 (cycles) */
185747c09d6aSSong Liu 	const char *ratio_desc;
185847c09d6aSSong Liu 	const float ratio_mul;
185947c09d6aSSong Liu } metrics[] = {
186047c09d6aSSong Liu 	{
186147c09d6aSSong Liu 		.name = "cycles",
186247c09d6aSSong Liu 		.attr = {
186347c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
186447c09d6aSSong Liu 			.config = PERF_COUNT_HW_CPU_CYCLES,
186547c09d6aSSong Liu 			.exclude_user = 1,
186647c09d6aSSong Liu 		},
186747c09d6aSSong Liu 	},
186847c09d6aSSong Liu 	{
186947c09d6aSSong Liu 		.name = "instructions",
187047c09d6aSSong Liu 		.attr = {
187147c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
187247c09d6aSSong Liu 			.config = PERF_COUNT_HW_INSTRUCTIONS,
187347c09d6aSSong Liu 			.exclude_user = 1,
187447c09d6aSSong Liu 		},
187547c09d6aSSong Liu 		.ratio_metric = 1,
187647c09d6aSSong Liu 		.ratio_desc = "insns per cycle",
187747c09d6aSSong Liu 		.ratio_mul = 1.0,
187847c09d6aSSong Liu 	},
187947c09d6aSSong Liu 	{
188047c09d6aSSong Liu 		.name = "l1d_loads",
188147c09d6aSSong Liu 		.attr = {
188247c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
188347c09d6aSSong Liu 			.config =
188447c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_L1D |
188547c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
188647c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
188747c09d6aSSong Liu 			.exclude_user = 1,
188847c09d6aSSong Liu 		},
188947c09d6aSSong Liu 	},
189047c09d6aSSong Liu 	{
189147c09d6aSSong Liu 		.name = "llc_misses",
189247c09d6aSSong Liu 		.attr = {
189347c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
189447c09d6aSSong Liu 			.config =
189547c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_LL |
189647c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
189747c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
189847c09d6aSSong Liu 			.exclude_user = 1
189947c09d6aSSong Liu 		},
190047c09d6aSSong Liu 		.ratio_metric = 2,
190147c09d6aSSong Liu 		.ratio_desc = "LLC misses per million insns",
190247c09d6aSSong Liu 		.ratio_mul = 1e6,
190347c09d6aSSong Liu 	},
1904450d060eSYonghong Song 	{
1905450d060eSYonghong Song 		.name = "itlb_misses",
1906450d060eSYonghong Song 		.attr = {
1907450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
1908450d060eSYonghong Song 			.config =
1909450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_ITLB |
1910450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
1911450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
1912450d060eSYonghong Song 			.exclude_user = 1
1913450d060eSYonghong Song 		},
1914450d060eSYonghong Song 		.ratio_metric = 2,
1915450d060eSYonghong Song 		.ratio_desc = "itlb misses per million insns",
1916450d060eSYonghong Song 		.ratio_mul = 1e6,
1917450d060eSYonghong Song 	},
1918450d060eSYonghong Song 	{
1919450d060eSYonghong Song 		.name = "dtlb_misses",
1920450d060eSYonghong Song 		.attr = {
1921450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
1922450d060eSYonghong Song 			.config =
1923450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_DTLB |
1924450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
1925450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
1926450d060eSYonghong Song 			.exclude_user = 1
1927450d060eSYonghong Song 		},
1928450d060eSYonghong Song 		.ratio_metric = 2,
1929450d060eSYonghong Song 		.ratio_desc = "dtlb misses per million insns",
1930450d060eSYonghong Song 		.ratio_mul = 1e6,
1931450d060eSYonghong Song 	},
193247c09d6aSSong Liu };
193347c09d6aSSong Liu 
193447c09d6aSSong Liu static __u64 profile_total_count;
193547c09d6aSSong Liu 
193647c09d6aSSong Liu #define MAX_NUM_PROFILE_METRICS 4
193747c09d6aSSong Liu 
193847c09d6aSSong Liu static int profile_parse_metrics(int argc, char **argv)
193947c09d6aSSong Liu {
194047c09d6aSSong Liu 	unsigned int metric_cnt;
194147c09d6aSSong Liu 	int selected_cnt = 0;
194247c09d6aSSong Liu 	unsigned int i;
194347c09d6aSSong Liu 
194447c09d6aSSong Liu 	metric_cnt = sizeof(metrics) / sizeof(struct profile_metric);
194547c09d6aSSong Liu 
194647c09d6aSSong Liu 	while (argc > 0) {
194747c09d6aSSong Liu 		for (i = 0; i < metric_cnt; i++) {
194847c09d6aSSong Liu 			if (is_prefix(argv[0], metrics[i].name)) {
194947c09d6aSSong Liu 				if (!metrics[i].selected)
195047c09d6aSSong Liu 					selected_cnt++;
195147c09d6aSSong Liu 				metrics[i].selected = true;
195247c09d6aSSong Liu 				break;
195347c09d6aSSong Liu 			}
195447c09d6aSSong Liu 		}
195547c09d6aSSong Liu 		if (i == metric_cnt) {
195647c09d6aSSong Liu 			p_err("unknown metric %s", argv[0]);
195747c09d6aSSong Liu 			return -1;
195847c09d6aSSong Liu 		}
195947c09d6aSSong Liu 		NEXT_ARG();
196047c09d6aSSong Liu 	}
196147c09d6aSSong Liu 	if (selected_cnt > MAX_NUM_PROFILE_METRICS) {
196247c09d6aSSong Liu 		p_err("too many (%d) metrics, please specify no more than %d metrics at at time",
196347c09d6aSSong Liu 		      selected_cnt, MAX_NUM_PROFILE_METRICS);
196447c09d6aSSong Liu 		return -1;
196547c09d6aSSong Liu 	}
196647c09d6aSSong Liu 	return selected_cnt;
196747c09d6aSSong Liu }
196847c09d6aSSong Liu 
196947c09d6aSSong Liu static void profile_read_values(struct profiler_bpf *obj)
197047c09d6aSSong Liu {
197147c09d6aSSong Liu 	__u32 m, cpu, num_cpu = obj->rodata->num_cpu;
197247c09d6aSSong Liu 	int reading_map_fd, count_map_fd;
197347c09d6aSSong Liu 	__u64 counts[num_cpu];
197447c09d6aSSong Liu 	__u32 key = 0;
197547c09d6aSSong Liu 	int err;
197647c09d6aSSong Liu 
197747c09d6aSSong Liu 	reading_map_fd = bpf_map__fd(obj->maps.accum_readings);
197847c09d6aSSong Liu 	count_map_fd = bpf_map__fd(obj->maps.counts);
197947c09d6aSSong Liu 	if (reading_map_fd < 0 || count_map_fd < 0) {
198047c09d6aSSong Liu 		p_err("failed to get fd for map");
198147c09d6aSSong Liu 		return;
198247c09d6aSSong Liu 	}
198347c09d6aSSong Liu 
198447c09d6aSSong Liu 	err = bpf_map_lookup_elem(count_map_fd, &key, counts);
198547c09d6aSSong Liu 	if (err) {
198647c09d6aSSong Liu 		p_err("failed to read count_map: %s", strerror(errno));
198747c09d6aSSong Liu 		return;
198847c09d6aSSong Liu 	}
198947c09d6aSSong Liu 
199047c09d6aSSong Liu 	profile_total_count = 0;
199147c09d6aSSong Liu 	for (cpu = 0; cpu < num_cpu; cpu++)
199247c09d6aSSong Liu 		profile_total_count += counts[cpu];
199347c09d6aSSong Liu 
199447c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
199547c09d6aSSong Liu 		struct bpf_perf_event_value values[num_cpu];
199647c09d6aSSong Liu 
199747c09d6aSSong Liu 		if (!metrics[m].selected)
199847c09d6aSSong Liu 			continue;
199947c09d6aSSong Liu 
200047c09d6aSSong Liu 		err = bpf_map_lookup_elem(reading_map_fd, &key, values);
200147c09d6aSSong Liu 		if (err) {
200247c09d6aSSong Liu 			p_err("failed to read reading_map: %s",
200347c09d6aSSong Liu 			      strerror(errno));
200447c09d6aSSong Liu 			return;
200547c09d6aSSong Liu 		}
200647c09d6aSSong Liu 		for (cpu = 0; cpu < num_cpu; cpu++) {
200747c09d6aSSong Liu 			metrics[m].val.counter += values[cpu].counter;
200847c09d6aSSong Liu 			metrics[m].val.enabled += values[cpu].enabled;
200947c09d6aSSong Liu 			metrics[m].val.running += values[cpu].running;
201047c09d6aSSong Liu 		}
201147c09d6aSSong Liu 		key++;
201247c09d6aSSong Liu 	}
201347c09d6aSSong Liu }
201447c09d6aSSong Liu 
201547c09d6aSSong Liu static void profile_print_readings_json(void)
201647c09d6aSSong Liu {
201747c09d6aSSong Liu 	__u32 m;
201847c09d6aSSong Liu 
201947c09d6aSSong Liu 	jsonw_start_array(json_wtr);
202047c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
202147c09d6aSSong Liu 		if (!metrics[m].selected)
202247c09d6aSSong Liu 			continue;
202347c09d6aSSong Liu 		jsonw_start_object(json_wtr);
202447c09d6aSSong Liu 		jsonw_string_field(json_wtr, "metric", metrics[m].name);
202547c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "run_cnt", profile_total_count);
202647c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "value", metrics[m].val.counter);
202747c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "enabled", metrics[m].val.enabled);
202847c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "running", metrics[m].val.running);
202947c09d6aSSong Liu 
203047c09d6aSSong Liu 		jsonw_end_object(json_wtr);
203147c09d6aSSong Liu 	}
203247c09d6aSSong Liu 	jsonw_end_array(json_wtr);
203347c09d6aSSong Liu }
203447c09d6aSSong Liu 
203547c09d6aSSong Liu static void profile_print_readings_plain(void)
203647c09d6aSSong Liu {
203747c09d6aSSong Liu 	__u32 m;
203847c09d6aSSong Liu 
203947c09d6aSSong Liu 	printf("\n%18llu %-20s\n", profile_total_count, "run_cnt");
204047c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
204147c09d6aSSong Liu 		struct bpf_perf_event_value *val = &metrics[m].val;
204247c09d6aSSong Liu 		int r;
204347c09d6aSSong Liu 
204447c09d6aSSong Liu 		if (!metrics[m].selected)
204547c09d6aSSong Liu 			continue;
204647c09d6aSSong Liu 		printf("%18llu %-20s", val->counter, metrics[m].name);
204747c09d6aSSong Liu 
204847c09d6aSSong Liu 		r = metrics[m].ratio_metric - 1;
204947c09d6aSSong Liu 		if (r >= 0 && metrics[r].selected &&
205047c09d6aSSong Liu 		    metrics[r].val.counter > 0) {
205147c09d6aSSong Liu 			printf("# %8.2f %-30s",
205247c09d6aSSong Liu 			       val->counter * metrics[m].ratio_mul /
205347c09d6aSSong Liu 			       metrics[r].val.counter,
205447c09d6aSSong Liu 			       metrics[m].ratio_desc);
205547c09d6aSSong Liu 		} else {
205647c09d6aSSong Liu 			printf("%-41s", "");
205747c09d6aSSong Liu 		}
205847c09d6aSSong Liu 
205947c09d6aSSong Liu 		if (val->enabled > val->running)
206047c09d6aSSong Liu 			printf("(%4.2f%%)",
206147c09d6aSSong Liu 			       val->running * 100.0 / val->enabled);
206247c09d6aSSong Liu 		printf("\n");
206347c09d6aSSong Liu 	}
206447c09d6aSSong Liu }
206547c09d6aSSong Liu 
206647c09d6aSSong Liu static void profile_print_readings(void)
206747c09d6aSSong Liu {
206847c09d6aSSong Liu 	if (json_output)
206947c09d6aSSong Liu 		profile_print_readings_json();
207047c09d6aSSong Liu 	else
207147c09d6aSSong Liu 		profile_print_readings_plain();
207247c09d6aSSong Liu }
207347c09d6aSSong Liu 
207447c09d6aSSong Liu static char *profile_target_name(int tgt_fd)
207547c09d6aSSong Liu {
2076c59765cfSDave Marchevsky 	struct bpf_func_info func_info;
2077c59765cfSDave Marchevsky 	struct bpf_prog_info info = {};
2078c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
207947c09d6aSSong Liu 	const struct btf_type *t;
2080c59765cfSDave Marchevsky 	__u32 func_info_rec_size;
2081369e955bSQuentin Monnet 	struct btf *btf = NULL;
208247c09d6aSSong Liu 	char *name = NULL;
2083c59765cfSDave Marchevsky 	int err;
208447c09d6aSSong Liu 
2085c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2086c59765cfSDave Marchevsky 	if (err) {
2087c59765cfSDave Marchevsky 		p_err("failed to bpf_obj_get_info_by_fd for prog FD %d", tgt_fd);
2088c59765cfSDave Marchevsky 		goto out;
208947c09d6aSSong Liu 	}
209047c09d6aSSong Liu 
2091c59765cfSDave Marchevsky 	if (info.btf_id == 0) {
209247c09d6aSSong Liu 		p_err("prog FD %d doesn't have valid btf", tgt_fd);
209347c09d6aSSong Liu 		goto out;
209447c09d6aSSong Liu 	}
209547c09d6aSSong Liu 
2096c59765cfSDave Marchevsky 	func_info_rec_size = info.func_info_rec_size;
2097c59765cfSDave Marchevsky 	if (info.nr_func_info == 0) {
2098c59765cfSDave Marchevsky 		p_err("bpf_obj_get_info_by_fd for prog FD %d found 0 func_info", tgt_fd);
2099c59765cfSDave Marchevsky 		goto out;
2100c59765cfSDave Marchevsky 	}
2101c59765cfSDave Marchevsky 
2102c59765cfSDave Marchevsky 	memset(&info, 0, sizeof(info));
2103c59765cfSDave Marchevsky 	info.nr_func_info = 1;
2104c59765cfSDave Marchevsky 	info.func_info_rec_size = func_info_rec_size;
2105c59765cfSDave Marchevsky 	info.func_info = ptr_to_u64(&func_info);
2106c59765cfSDave Marchevsky 
2107c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2108c59765cfSDave Marchevsky 	if (err) {
2109c59765cfSDave Marchevsky 		p_err("failed to get func_info for prog FD %d", tgt_fd);
2110c59765cfSDave Marchevsky 		goto out;
2111c59765cfSDave Marchevsky 	}
2112c59765cfSDave Marchevsky 
2113c59765cfSDave Marchevsky 	btf = btf__load_from_kernel_by_id(info.btf_id);
211486f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf)) {
211586f4b7f2SQuentin Monnet 		p_err("failed to load btf for prog FD %d", tgt_fd);
211686f4b7f2SQuentin Monnet 		goto out;
211786f4b7f2SQuentin Monnet 	}
211886f4b7f2SQuentin Monnet 
2119c59765cfSDave Marchevsky 	t = btf__type_by_id(btf, func_info.type_id);
212047c09d6aSSong Liu 	if (!t) {
212147c09d6aSSong Liu 		p_err("btf %d doesn't have type %d",
2122c59765cfSDave Marchevsky 		      info.btf_id, func_info.type_id);
212347c09d6aSSong Liu 		goto out;
212447c09d6aSSong Liu 	}
212547c09d6aSSong Liu 	name = strdup(btf__name_by_offset(btf, t->name_off));
212647c09d6aSSong Liu out:
2127369e955bSQuentin Monnet 	btf__free(btf);
212847c09d6aSSong Liu 	return name;
212947c09d6aSSong Liu }
213047c09d6aSSong Liu 
213147c09d6aSSong Liu static struct profiler_bpf *profile_obj;
213247c09d6aSSong Liu static int profile_tgt_fd = -1;
213347c09d6aSSong Liu static char *profile_tgt_name;
213447c09d6aSSong Liu static int *profile_perf_events;
213547c09d6aSSong Liu static int profile_perf_event_cnt;
213647c09d6aSSong Liu 
213747c09d6aSSong Liu static void profile_close_perf_events(struct profiler_bpf *obj)
213847c09d6aSSong Liu {
213947c09d6aSSong Liu 	int i;
214047c09d6aSSong Liu 
214147c09d6aSSong Liu 	for (i = profile_perf_event_cnt - 1; i >= 0; i--)
214247c09d6aSSong Liu 		close(profile_perf_events[i]);
214347c09d6aSSong Liu 
214447c09d6aSSong Liu 	free(profile_perf_events);
214547c09d6aSSong Liu 	profile_perf_event_cnt = 0;
214647c09d6aSSong Liu }
214747c09d6aSSong Liu 
214847c09d6aSSong Liu static int profile_open_perf_events(struct profiler_bpf *obj)
214947c09d6aSSong Liu {
215047c09d6aSSong Liu 	unsigned int cpu, m;
215147c09d6aSSong Liu 	int map_fd, pmu_fd;
215247c09d6aSSong Liu 
215347c09d6aSSong Liu 	profile_perf_events = calloc(
215447c09d6aSSong Liu 		sizeof(int), obj->rodata->num_cpu * obj->rodata->num_metric);
215547c09d6aSSong Liu 	if (!profile_perf_events) {
215647c09d6aSSong Liu 		p_err("failed to allocate memory for perf_event array: %s",
215747c09d6aSSong Liu 		      strerror(errno));
215847c09d6aSSong Liu 		return -1;
215947c09d6aSSong Liu 	}
216047c09d6aSSong Liu 	map_fd = bpf_map__fd(obj->maps.events);
216147c09d6aSSong Liu 	if (map_fd < 0) {
216247c09d6aSSong Liu 		p_err("failed to get fd for events map");
216347c09d6aSSong Liu 		return -1;
216447c09d6aSSong Liu 	}
216547c09d6aSSong Liu 
216647c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
216747c09d6aSSong Liu 		if (!metrics[m].selected)
216847c09d6aSSong Liu 			continue;
216947c09d6aSSong Liu 		for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) {
217047c09d6aSSong Liu 			pmu_fd = syscall(__NR_perf_event_open, &metrics[m].attr,
217147c09d6aSSong Liu 					 -1/*pid*/, cpu, -1/*group_fd*/, 0);
217247c09d6aSSong Liu 			if (pmu_fd < 0 ||
217347c09d6aSSong Liu 			    bpf_map_update_elem(map_fd, &profile_perf_event_cnt,
217447c09d6aSSong Liu 						&pmu_fd, BPF_ANY) ||
217547c09d6aSSong Liu 			    ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
217647c09d6aSSong Liu 				p_err("failed to create event %s on cpu %d",
217747c09d6aSSong Liu 				      metrics[m].name, cpu);
217847c09d6aSSong Liu 				return -1;
217947c09d6aSSong Liu 			}
218047c09d6aSSong Liu 			profile_perf_events[profile_perf_event_cnt++] = pmu_fd;
218147c09d6aSSong Liu 		}
218247c09d6aSSong Liu 	}
218347c09d6aSSong Liu 	return 0;
218447c09d6aSSong Liu }
218547c09d6aSSong Liu 
218647c09d6aSSong Liu static void profile_print_and_cleanup(void)
218747c09d6aSSong Liu {
218847c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
218947c09d6aSSong Liu 	profile_read_values(profile_obj);
219047c09d6aSSong Liu 	profile_print_readings();
219147c09d6aSSong Liu 	profiler_bpf__destroy(profile_obj);
219247c09d6aSSong Liu 
219347c09d6aSSong Liu 	close(profile_tgt_fd);
219447c09d6aSSong Liu 	free(profile_tgt_name);
219547c09d6aSSong Liu }
219647c09d6aSSong Liu 
219747c09d6aSSong Liu static void int_exit(int signo)
219847c09d6aSSong Liu {
219947c09d6aSSong Liu 	profile_print_and_cleanup();
220047c09d6aSSong Liu 	exit(0);
220147c09d6aSSong Liu }
220247c09d6aSSong Liu 
220347c09d6aSSong Liu static int do_profile(int argc, char **argv)
220447c09d6aSSong Liu {
220547c09d6aSSong Liu 	int num_metric, num_cpu, err = -1;
220647c09d6aSSong Liu 	struct bpf_program *prog;
220747c09d6aSSong Liu 	unsigned long duration;
220847c09d6aSSong Liu 	char *endptr;
220947c09d6aSSong Liu 
221047c09d6aSSong Liu 	/* we at least need two args for the prog and one metric */
221147c09d6aSSong Liu 	if (!REQ_ARGS(3))
221247c09d6aSSong Liu 		return -EINVAL;
221347c09d6aSSong Liu 
221447c09d6aSSong Liu 	/* parse target fd */
221547c09d6aSSong Liu 	profile_tgt_fd = prog_parse_fd(&argc, &argv);
221647c09d6aSSong Liu 	if (profile_tgt_fd < 0) {
221747c09d6aSSong Liu 		p_err("failed to parse fd");
221847c09d6aSSong Liu 		return -1;
221947c09d6aSSong Liu 	}
222047c09d6aSSong Liu 
222147c09d6aSSong Liu 	/* parse profiling optional duration */
222247c09d6aSSong Liu 	if (argc > 2 && is_prefix(argv[0], "duration")) {
222347c09d6aSSong Liu 		NEXT_ARG();
222447c09d6aSSong Liu 		duration = strtoul(*argv, &endptr, 0);
222547c09d6aSSong Liu 		if (*endptr)
222647c09d6aSSong Liu 			usage();
222747c09d6aSSong Liu 		NEXT_ARG();
222847c09d6aSSong Liu 	} else {
222947c09d6aSSong Liu 		duration = UINT_MAX;
223047c09d6aSSong Liu 	}
223147c09d6aSSong Liu 
223247c09d6aSSong Liu 	num_metric = profile_parse_metrics(argc, argv);
223347c09d6aSSong Liu 	if (num_metric <= 0)
223447c09d6aSSong Liu 		goto out;
223547c09d6aSSong Liu 
223647c09d6aSSong Liu 	num_cpu = libbpf_num_possible_cpus();
223747c09d6aSSong Liu 	if (num_cpu <= 0) {
223847c09d6aSSong Liu 		p_err("failed to identify number of CPUs");
223947c09d6aSSong Liu 		goto out;
224047c09d6aSSong Liu 	}
224147c09d6aSSong Liu 
224247c09d6aSSong Liu 	profile_obj = profiler_bpf__open();
224347c09d6aSSong Liu 	if (!profile_obj) {
224447c09d6aSSong Liu 		p_err("failed to open and/or load BPF object");
224547c09d6aSSong Liu 		goto out;
224647c09d6aSSong Liu 	}
224747c09d6aSSong Liu 
224847c09d6aSSong Liu 	profile_obj->rodata->num_cpu = num_cpu;
224947c09d6aSSong Liu 	profile_obj->rodata->num_metric = num_metric;
225047c09d6aSSong Liu 
225147c09d6aSSong Liu 	/* adjust map sizes */
225239748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.events, num_metric * num_cpu);
225339748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.fentry_readings, num_metric);
225439748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.accum_readings, num_metric);
225539748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.counts, 1);
225647c09d6aSSong Liu 
225747c09d6aSSong Liu 	/* change target name */
225847c09d6aSSong Liu 	profile_tgt_name = profile_target_name(profile_tgt_fd);
225947c09d6aSSong Liu 	if (!profile_tgt_name)
226047c09d6aSSong Liu 		goto out;
226147c09d6aSSong Liu 
226247c09d6aSSong Liu 	bpf_object__for_each_program(prog, profile_obj->obj) {
226347c09d6aSSong Liu 		err = bpf_program__set_attach_target(prog, profile_tgt_fd,
226447c09d6aSSong Liu 						     profile_tgt_name);
226547c09d6aSSong Liu 		if (err) {
226647c09d6aSSong Liu 			p_err("failed to set attach target\n");
226747c09d6aSSong Liu 			goto out;
226847c09d6aSSong Liu 		}
226947c09d6aSSong Liu 	}
227047c09d6aSSong Liu 
227147c09d6aSSong Liu 	err = profiler_bpf__load(profile_obj);
227247c09d6aSSong Liu 	if (err) {
227347c09d6aSSong Liu 		p_err("failed to load profile_obj");
227447c09d6aSSong Liu 		goto out;
227547c09d6aSSong Liu 	}
227647c09d6aSSong Liu 
227747c09d6aSSong Liu 	err = profile_open_perf_events(profile_obj);
227847c09d6aSSong Liu 	if (err)
227947c09d6aSSong Liu 		goto out;
228047c09d6aSSong Liu 
228147c09d6aSSong Liu 	err = profiler_bpf__attach(profile_obj);
228247c09d6aSSong Liu 	if (err) {
228347c09d6aSSong Liu 		p_err("failed to attach profile_obj");
228447c09d6aSSong Liu 		goto out;
228547c09d6aSSong Liu 	}
228647c09d6aSSong Liu 	signal(SIGINT, int_exit);
228747c09d6aSSong Liu 
228847c09d6aSSong Liu 	sleep(duration);
228947c09d6aSSong Liu 	profile_print_and_cleanup();
229047c09d6aSSong Liu 	return 0;
229147c09d6aSSong Liu 
229247c09d6aSSong Liu out:
229347c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
229447c09d6aSSong Liu 	if (profile_obj)
229547c09d6aSSong Liu 		profiler_bpf__destroy(profile_obj);
229647c09d6aSSong Liu 	close(profile_tgt_fd);
229747c09d6aSSong Liu 	free(profile_tgt_name);
229847c09d6aSSong Liu 	return err;
229947c09d6aSSong Liu }
230047c09d6aSSong Liu 
230147c09d6aSSong Liu #endif /* BPFTOOL_WITHOUT_SKELETONS */
230247c09d6aSSong Liu 
230371bb428fSJakub Kicinski static int do_help(int argc, char **argv)
230471bb428fSJakub Kicinski {
2305004b45c0SQuentin Monnet 	if (json_output) {
2306004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
2307004b45c0SQuentin Monnet 		return 0;
2308004b45c0SQuentin Monnet 	}
2309004b45c0SQuentin Monnet 
231071bb428fSJakub Kicinski 	fprintf(stderr,
231190040351SQuentin Monnet 		"Usage: %1$s %2$s { show | list } [PROG]\n"
231290040351SQuentin Monnet 		"       %1$s %2$s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n"
231390040351SQuentin Monnet 		"       %1$s %2$s dump jited  PROG [{ file FILE | opcodes | linum }]\n"
231490040351SQuentin Monnet 		"       %1$s %2$s pin   PROG FILE\n"
231590040351SQuentin Monnet 		"       %1$s %2$s { load | loadall } OBJ  PATH \\\n"
231677380998SStanislav Fomichev 		"                         [type TYPE] [dev NAME] \\\n"
23173767a94bSStanislav Fomichev 		"                         [map { idx IDX | name NAME } MAP]\\\n"
23183767a94bSStanislav Fomichev 		"                         [pinmaps MAP_DIR]\n"
231990040351SQuentin Monnet 		"       %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n"
232090040351SQuentin Monnet 		"       %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n"
232190040351SQuentin Monnet 		"       %1$s %2$s run PROG \\\n"
2322ba95c745SQuentin Monnet 		"                         data_in FILE \\\n"
2323ba95c745SQuentin Monnet 		"                         [data_out FILE [data_size_out L]] \\\n"
2324ba95c745SQuentin Monnet 		"                         [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
2325ba95c745SQuentin Monnet 		"                         [repeat N]\n"
232690040351SQuentin Monnet 		"       %1$s %2$s profile PROG [duration DURATION] METRICs\n"
232790040351SQuentin Monnet 		"       %1$s %2$s tracelog\n"
232890040351SQuentin Monnet 		"       %1$s %2$s help\n"
232971bb428fSJakub Kicinski 		"\n"
23303ff5a4dcSJakub Kicinski 		"       " HELP_SPEC_MAP "\n"
233171bb428fSJakub Kicinski 		"       " HELP_SPEC_PROGRAM "\n"
233249f2cba3SJakub Kicinski 		"       TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
233349f2cba3SJakub Kicinski 		"                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
233449f2cba3SJakub Kicinski 		"                 cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
233549f2cba3SJakub Kicinski 		"                 lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
2336f25377eeSAndrey Ignatov 		"                 sk_reuseport | flow_dissector | cgroup/sysctl |\n"
233749f2cba3SJakub Kicinski 		"                 cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
233849f2cba3SJakub Kicinski 		"                 cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
233905ee19c1SDaniel Borkmann 		"                 cgroup/getpeername4 | cgroup/getpeername6 |\n"
234005ee19c1SDaniel Borkmann 		"                 cgroup/getsockname4 | cgroup/getsockname6 | cgroup/sendmsg4 |\n"
234105ee19c1SDaniel Borkmann 		"                 cgroup/sendmsg6 | cgroup/recvmsg4 | cgroup/recvmsg6 |\n"
2342a8deba85SLiu Jian 		"                 cgroup/getsockopt | cgroup/setsockopt | cgroup/sock_release |\n"
234393a3545dSJakub Sitnicki 		"                 struct_ops | fentry | fexit | freplace | sk_lookup }\n"
2344b544342eSQuentin Monnet 		"       ATTACH_TYPE := { msg_verdict | skb_verdict | stream_verdict |\n"
2345b544342eSQuentin Monnet 		"                        stream_parser | flow_dissector }\n"
2346450d060eSYonghong Song 		"       METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
2347c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " |\n"
23488cc8c635SQuentin Monnet 		"                    {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
23498cc8c635SQuentin Monnet 		"                    {-L|--use-loader} }\n"
235071bb428fSJakub Kicinski 		"",
235190040351SQuentin Monnet 		bin_name, argv[-2]);
235271bb428fSJakub Kicinski 
235371bb428fSJakub Kicinski 	return 0;
235471bb428fSJakub Kicinski }
235571bb428fSJakub Kicinski 
235671bb428fSJakub Kicinski static const struct cmd cmds[] = {
235771bb428fSJakub Kicinski 	{ "show",	do_show },
23586ebe6dbdSJakub Kicinski 	{ "list",	do_show },
23599f606179SQuentin Monnet 	{ "help",	do_help },
236071bb428fSJakub Kicinski 	{ "dump",	do_dump },
236171bb428fSJakub Kicinski 	{ "pin",	do_pin },
236249a086c2SRoman Gushchin 	{ "load",	do_load },
236377380998SStanislav Fomichev 	{ "loadall",	do_loadall },
2364b7d3826cSJohn Fastabend 	{ "attach",	do_attach },
2365b7d3826cSJohn Fastabend 	{ "detach",	do_detach },
236630da46b5SQuentin Monnet 	{ "tracelog",	do_tracelog },
2367ba95c745SQuentin Monnet 	{ "run",	do_run },
236847c09d6aSSong Liu 	{ "profile",	do_profile },
236971bb428fSJakub Kicinski 	{ 0 }
237071bb428fSJakub Kicinski };
237171bb428fSJakub Kicinski 
237271bb428fSJakub Kicinski int do_prog(int argc, char **argv)
237371bb428fSJakub Kicinski {
237471bb428fSJakub Kicinski 	return cmd_select(cmds, argc, argv, do_help);
237571bb428fSJakub Kicinski }
2376