xref: /openbmc/linux/tools/bpf/bpftool/prog.c (revision 19526e701ea096d13ebf881d32511e567dc41557)
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 
441ba5ad36SDaniel Müller static const bool attach_types[] = {
451ba5ad36SDaniel Müller 	[BPF_SK_SKB_STREAM_PARSER] = true,
461ba5ad36SDaniel Müller 	[BPF_SK_SKB_STREAM_VERDICT] = true,
471ba5ad36SDaniel Müller 	[BPF_SK_SKB_VERDICT] = true,
481ba5ad36SDaniel Müller 	[BPF_SK_MSG_VERDICT] = true,
491ba5ad36SDaniel Müller 	[BPF_FLOW_DISSECTOR] = true,
501ba5ad36SDaniel Müller 	[__MAX_BPF_ATTACH_TYPE] = false,
511ba5ad36SDaniel Müller };
521ba5ad36SDaniel Müller 
531ba5ad36SDaniel Müller /* Textual representations traditionally used by the program and kept around
541ba5ad36SDaniel Müller  * for the sake of backwards compatibility.
551ba5ad36SDaniel Müller  */
56b7d3826cSJohn Fastabend static const char * const attach_type_strings[] = {
57b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_PARSER] = "stream_parser",
58b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict",
59a7ba4558SCong Wang 	[BPF_SK_SKB_VERDICT] = "skb_verdict",
60b7d3826cSJohn Fastabend 	[BPF_SK_MSG_VERDICT] = "msg_verdict",
61b7d3826cSJohn Fastabend 	[__MAX_BPF_ATTACH_TYPE] = NULL,
62b7d3826cSJohn Fastabend };
63b7d3826cSJohn Fastabend 
648f184732SQuentin Monnet static struct hashmap *prog_table;
6546241271SQuentin Monnet 
66c101189bSQuentin Monnet static enum bpf_attach_type parse_attach_type(const char *str)
67b7d3826cSJohn Fastabend {
68b7d3826cSJohn Fastabend 	enum bpf_attach_type type;
69b7d3826cSJohn Fastabend 
70b7d3826cSJohn Fastabend 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
711ba5ad36SDaniel Müller 		if (attach_types[type]) {
721ba5ad36SDaniel Müller 			const char *attach_type_str;
731ba5ad36SDaniel Müller 
741ba5ad36SDaniel Müller 			attach_type_str = libbpf_bpf_attach_type_str(type);
751ba5ad36SDaniel Müller 			if (!strcmp(str, attach_type_str))
761ba5ad36SDaniel Müller 				return type;
771ba5ad36SDaniel Müller 		}
781ba5ad36SDaniel Müller 
79b7d3826cSJohn Fastabend 		if (attach_type_strings[type] &&
80b7d3826cSJohn Fastabend 		    is_prefix(str, attach_type_strings[type]))
81b7d3826cSJohn Fastabend 			return type;
82b7d3826cSJohn Fastabend 	}
83b7d3826cSJohn Fastabend 
84b7d3826cSJohn Fastabend 	return __MAX_BPF_ATTACH_TYPE;
85b7d3826cSJohn Fastabend }
86b7d3826cSJohn Fastabend 
87c59765cfSDave Marchevsky static int prep_prog_info(struct bpf_prog_info *const info, enum dump_mode mode,
88c59765cfSDave Marchevsky 			  void **info_data, size_t *const info_data_sz)
89c59765cfSDave Marchevsky {
90c59765cfSDave Marchevsky 	struct bpf_prog_info holder = {};
91c59765cfSDave Marchevsky 	size_t needed = 0;
92c59765cfSDave Marchevsky 	void *ptr;
93c59765cfSDave Marchevsky 
94c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
95c59765cfSDave Marchevsky 		holder.jited_prog_len = info->jited_prog_len;
96c59765cfSDave Marchevsky 		needed += info->jited_prog_len;
97c59765cfSDave Marchevsky 	} else {
98c59765cfSDave Marchevsky 		holder.xlated_prog_len = info->xlated_prog_len;
99c59765cfSDave Marchevsky 		needed += info->xlated_prog_len;
100c59765cfSDave Marchevsky 	}
101c59765cfSDave Marchevsky 
102c59765cfSDave Marchevsky 	holder.nr_jited_ksyms = info->nr_jited_ksyms;
103c59765cfSDave Marchevsky 	needed += info->nr_jited_ksyms * sizeof(__u64);
104c59765cfSDave Marchevsky 
105c59765cfSDave Marchevsky 	holder.nr_jited_func_lens = info->nr_jited_func_lens;
106c59765cfSDave Marchevsky 	needed += info->nr_jited_func_lens * sizeof(__u32);
107c59765cfSDave Marchevsky 
108c59765cfSDave Marchevsky 	holder.nr_func_info = info->nr_func_info;
109c59765cfSDave Marchevsky 	holder.func_info_rec_size = info->func_info_rec_size;
110c59765cfSDave Marchevsky 	needed += info->nr_func_info * info->func_info_rec_size;
111c59765cfSDave Marchevsky 
112c59765cfSDave Marchevsky 	holder.nr_line_info = info->nr_line_info;
113c59765cfSDave Marchevsky 	holder.line_info_rec_size = info->line_info_rec_size;
114c59765cfSDave Marchevsky 	needed += info->nr_line_info * info->line_info_rec_size;
115c59765cfSDave Marchevsky 
116c59765cfSDave Marchevsky 	holder.nr_jited_line_info = info->nr_jited_line_info;
117c59765cfSDave Marchevsky 	holder.jited_line_info_rec_size = info->jited_line_info_rec_size;
118c59765cfSDave Marchevsky 	needed += info->nr_jited_line_info * info->jited_line_info_rec_size;
119c59765cfSDave Marchevsky 
120c59765cfSDave Marchevsky 	if (needed > *info_data_sz) {
121c59765cfSDave Marchevsky 		ptr = realloc(*info_data, needed);
122c59765cfSDave Marchevsky 		if (!ptr)
123c59765cfSDave Marchevsky 			return -1;
124c59765cfSDave Marchevsky 
125c59765cfSDave Marchevsky 		*info_data = ptr;
126c59765cfSDave Marchevsky 		*info_data_sz = needed;
127c59765cfSDave Marchevsky 	}
128c59765cfSDave Marchevsky 	ptr = *info_data;
129c59765cfSDave Marchevsky 
130c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
131c59765cfSDave Marchevsky 		holder.jited_prog_insns = ptr_to_u64(ptr);
132c59765cfSDave Marchevsky 		ptr += holder.jited_prog_len;
133c59765cfSDave Marchevsky 	} else {
134c59765cfSDave Marchevsky 		holder.xlated_prog_insns = ptr_to_u64(ptr);
135c59765cfSDave Marchevsky 		ptr += holder.xlated_prog_len;
136c59765cfSDave Marchevsky 	}
137c59765cfSDave Marchevsky 
138c59765cfSDave Marchevsky 	holder.jited_ksyms = ptr_to_u64(ptr);
139c59765cfSDave Marchevsky 	ptr += holder.nr_jited_ksyms * sizeof(__u64);
140c59765cfSDave Marchevsky 
141c59765cfSDave Marchevsky 	holder.jited_func_lens = ptr_to_u64(ptr);
142c59765cfSDave Marchevsky 	ptr += holder.nr_jited_func_lens * sizeof(__u32);
143c59765cfSDave Marchevsky 
144c59765cfSDave Marchevsky 	holder.func_info = ptr_to_u64(ptr);
145c59765cfSDave Marchevsky 	ptr += holder.nr_func_info * holder.func_info_rec_size;
146c59765cfSDave Marchevsky 
147c59765cfSDave Marchevsky 	holder.line_info = ptr_to_u64(ptr);
148c59765cfSDave Marchevsky 	ptr += holder.nr_line_info * holder.line_info_rec_size;
149c59765cfSDave Marchevsky 
150c59765cfSDave Marchevsky 	holder.jited_line_info = ptr_to_u64(ptr);
151c59765cfSDave Marchevsky 	ptr += holder.nr_jited_line_info * holder.jited_line_info_rec_size;
152c59765cfSDave Marchevsky 
153c59765cfSDave Marchevsky 	*info = holder;
154c59765cfSDave Marchevsky 	return 0;
155c59765cfSDave Marchevsky }
156c59765cfSDave Marchevsky 
15771bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
15871bb428fSJakub Kicinski {
15971bb428fSJakub Kicinski 	struct timespec real_time_ts, boot_time_ts;
16071bb428fSJakub Kicinski 	time_t wallclock_secs;
16171bb428fSJakub Kicinski 	struct tm load_tm;
16271bb428fSJakub Kicinski 
16371bb428fSJakub Kicinski 	buf[--size] = '\0';
16471bb428fSJakub Kicinski 
16571bb428fSJakub Kicinski 	if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
16671bb428fSJakub Kicinski 	    clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
16771bb428fSJakub Kicinski 		perror("Can't read clocks");
16871bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
16971bb428fSJakub Kicinski 		return;
17071bb428fSJakub Kicinski 	}
17171bb428fSJakub Kicinski 
17271bb428fSJakub Kicinski 	wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
17307480cbcSJakub Kicinski 		(real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) /
17407480cbcSJakub Kicinski 		1000000000;
17507480cbcSJakub Kicinski 
17671bb428fSJakub Kicinski 
17771bb428fSJakub Kicinski 	if (!localtime_r(&wallclock_secs, &load_tm)) {
17871bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
17971bb428fSJakub Kicinski 		return;
18071bb428fSJakub Kicinski 	}
18171bb428fSJakub Kicinski 
182a3fe1f6fSQuentin Monnet 	if (json_output)
183a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%s", &load_tm);
184a3fe1f6fSQuentin Monnet 	else
185a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%FT%T%z", &load_tm);
18671bb428fSJakub Kicinski }
18771bb428fSJakub Kicinski 
1886e7e034eSQuentin Monnet static void show_prog_maps(int fd, __u32 num_maps)
18971bb428fSJakub Kicinski {
19071bb428fSJakub Kicinski 	struct bpf_prog_info info = {};
19171bb428fSJakub Kicinski 	__u32 len = sizeof(info);
19271bb428fSJakub Kicinski 	__u32 map_ids[num_maps];
19371bb428fSJakub Kicinski 	unsigned int i;
19471bb428fSJakub Kicinski 	int err;
19571bb428fSJakub Kicinski 
19671bb428fSJakub Kicinski 	info.nr_map_ids = num_maps;
19771bb428fSJakub Kicinski 	info.map_ids = ptr_to_u64(map_ids);
19871bb428fSJakub Kicinski 
19971bb428fSJakub Kicinski 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
20071bb428fSJakub Kicinski 	if (err || !info.nr_map_ids)
20171bb428fSJakub Kicinski 		return;
20271bb428fSJakub Kicinski 
203743cc665SQuentin Monnet 	if (json_output) {
204743cc665SQuentin Monnet 		jsonw_name(json_wtr, "map_ids");
205743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
206743cc665SQuentin Monnet 		for (i = 0; i < info.nr_map_ids; i++)
207743cc665SQuentin Monnet 			jsonw_uint(json_wtr, map_ids[i]);
208743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
209743cc665SQuentin Monnet 	} else {
21071bb428fSJakub Kicinski 		printf("  map_ids ");
21171bb428fSJakub Kicinski 		for (i = 0; i < info.nr_map_ids; i++)
21271bb428fSJakub Kicinski 			printf("%u%s", map_ids[i],
21371bb428fSJakub Kicinski 			       i == info.nr_map_ids - 1 ? "" : ",");
21471bb428fSJakub Kicinski 	}
21571bb428fSJakub Kicinski }
21671bb428fSJakub Kicinski 
217aff52e68SYiFei Zhu static void *find_metadata(int prog_fd, struct bpf_map_info *map_info)
218aff52e68SYiFei Zhu {
219aff52e68SYiFei Zhu 	struct bpf_prog_info prog_info;
220aff52e68SYiFei Zhu 	__u32 prog_info_len;
221aff52e68SYiFei Zhu 	__u32 map_info_len;
222aff52e68SYiFei Zhu 	void *value = NULL;
223aff52e68SYiFei Zhu 	__u32 *map_ids;
224aff52e68SYiFei Zhu 	int nr_maps;
225aff52e68SYiFei Zhu 	int key = 0;
226aff52e68SYiFei Zhu 	int map_fd;
227aff52e68SYiFei Zhu 	int ret;
228aff52e68SYiFei Zhu 	__u32 i;
229aff52e68SYiFei Zhu 
230aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
231aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
232aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
233aff52e68SYiFei Zhu 	if (ret)
234aff52e68SYiFei Zhu 		return NULL;
235aff52e68SYiFei Zhu 
236aff52e68SYiFei Zhu 	if (!prog_info.nr_map_ids)
237aff52e68SYiFei Zhu 		return NULL;
238aff52e68SYiFei Zhu 
239aff52e68SYiFei Zhu 	map_ids = calloc(prog_info.nr_map_ids, sizeof(__u32));
240aff52e68SYiFei Zhu 	if (!map_ids)
241aff52e68SYiFei Zhu 		return NULL;
242aff52e68SYiFei Zhu 
243aff52e68SYiFei Zhu 	nr_maps = prog_info.nr_map_ids;
244aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
245aff52e68SYiFei Zhu 	prog_info.nr_map_ids = nr_maps;
246aff52e68SYiFei Zhu 	prog_info.map_ids = ptr_to_u64(map_ids);
247aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
248aff52e68SYiFei Zhu 
249aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
250aff52e68SYiFei Zhu 	if (ret)
251aff52e68SYiFei Zhu 		goto free_map_ids;
252aff52e68SYiFei Zhu 
253aff52e68SYiFei Zhu 	for (i = 0; i < prog_info.nr_map_ids; i++) {
254aff52e68SYiFei Zhu 		map_fd = bpf_map_get_fd_by_id(map_ids[i]);
255aff52e68SYiFei Zhu 		if (map_fd < 0)
256aff52e68SYiFei Zhu 			goto free_map_ids;
257aff52e68SYiFei Zhu 
258aff52e68SYiFei Zhu 		memset(map_info, 0, sizeof(*map_info));
259aff52e68SYiFei Zhu 		map_info_len = sizeof(*map_info);
260aff52e68SYiFei Zhu 		ret = bpf_obj_get_info_by_fd(map_fd, map_info, &map_info_len);
261aff52e68SYiFei Zhu 		if (ret < 0) {
262aff52e68SYiFei Zhu 			close(map_fd);
263aff52e68SYiFei Zhu 			goto free_map_ids;
264aff52e68SYiFei Zhu 		}
265aff52e68SYiFei Zhu 
266aff52e68SYiFei Zhu 		if (map_info->type != BPF_MAP_TYPE_ARRAY ||
267aff52e68SYiFei Zhu 		    map_info->key_size != sizeof(int) ||
268aff52e68SYiFei Zhu 		    map_info->max_entries != 1 ||
269aff52e68SYiFei Zhu 		    !map_info->btf_value_type_id ||
270aff52e68SYiFei Zhu 		    !strstr(map_info->name, ".rodata")) {
271aff52e68SYiFei Zhu 			close(map_fd);
272aff52e68SYiFei Zhu 			continue;
273aff52e68SYiFei Zhu 		}
274aff52e68SYiFei Zhu 
275aff52e68SYiFei Zhu 		value = malloc(map_info->value_size);
276aff52e68SYiFei Zhu 		if (!value) {
277aff52e68SYiFei Zhu 			close(map_fd);
278aff52e68SYiFei Zhu 			goto free_map_ids;
279aff52e68SYiFei Zhu 		}
280aff52e68SYiFei Zhu 
281aff52e68SYiFei Zhu 		if (bpf_map_lookup_elem(map_fd, &key, value)) {
282aff52e68SYiFei Zhu 			close(map_fd);
283aff52e68SYiFei Zhu 			free(value);
284aff52e68SYiFei Zhu 			value = NULL;
285aff52e68SYiFei Zhu 			goto free_map_ids;
286aff52e68SYiFei Zhu 		}
287aff52e68SYiFei Zhu 
288aff52e68SYiFei Zhu 		close(map_fd);
289aff52e68SYiFei Zhu 		break;
290aff52e68SYiFei Zhu 	}
291aff52e68SYiFei Zhu 
292aff52e68SYiFei Zhu free_map_ids:
293aff52e68SYiFei Zhu 	free(map_ids);
294aff52e68SYiFei Zhu 	return value;
295aff52e68SYiFei Zhu }
296aff52e68SYiFei Zhu 
297aff52e68SYiFei Zhu static bool has_metadata_prefix(const char *s)
298aff52e68SYiFei Zhu {
299aff52e68SYiFei Zhu 	return strncmp(s, BPF_METADATA_PREFIX, BPF_METADATA_PREFIX_LEN) == 0;
300aff52e68SYiFei Zhu }
301aff52e68SYiFei Zhu 
302aff52e68SYiFei Zhu static void show_prog_metadata(int fd, __u32 num_maps)
303aff52e68SYiFei Zhu {
304aff52e68SYiFei Zhu 	const struct btf_type *t_datasec, *t_var;
305aff52e68SYiFei Zhu 	struct bpf_map_info map_info;
306aff52e68SYiFei Zhu 	struct btf_var_secinfo *vsi;
307aff52e68SYiFei Zhu 	bool printed_header = false;
308aff52e68SYiFei Zhu 	unsigned int i, vlen;
309aff52e68SYiFei Zhu 	void *value = NULL;
310aff52e68SYiFei Zhu 	const char *name;
31186f4b7f2SQuentin Monnet 	struct btf *btf;
312aff52e68SYiFei Zhu 	int err;
313aff52e68SYiFei Zhu 
314aff52e68SYiFei Zhu 	if (!num_maps)
315aff52e68SYiFei Zhu 		return;
316aff52e68SYiFei Zhu 
317aff52e68SYiFei Zhu 	memset(&map_info, 0, sizeof(map_info));
318aff52e68SYiFei Zhu 	value = find_metadata(fd, &map_info);
319aff52e68SYiFei Zhu 	if (!value)
320aff52e68SYiFei Zhu 		return;
321aff52e68SYiFei Zhu 
32286f4b7f2SQuentin Monnet 	btf = btf__load_from_kernel_by_id(map_info.btf_id);
32386f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf))
324aff52e68SYiFei Zhu 		goto out_free;
325aff52e68SYiFei Zhu 
326aff52e68SYiFei Zhu 	t_datasec = btf__type_by_id(btf, map_info.btf_value_type_id);
327aff52e68SYiFei Zhu 	if (!btf_is_datasec(t_datasec))
328aff52e68SYiFei Zhu 		goto out_free;
329aff52e68SYiFei Zhu 
330aff52e68SYiFei Zhu 	vlen = btf_vlen(t_datasec);
331aff52e68SYiFei Zhu 	vsi = btf_var_secinfos(t_datasec);
332aff52e68SYiFei Zhu 
333aff52e68SYiFei Zhu 	/* We don't proceed to check the kinds of the elements of the DATASEC.
334aff52e68SYiFei Zhu 	 * The verifier enforces them to be BTF_KIND_VAR.
335aff52e68SYiFei Zhu 	 */
336aff52e68SYiFei Zhu 
337aff52e68SYiFei Zhu 	if (json_output) {
338aff52e68SYiFei Zhu 		struct btf_dumper d = {
339aff52e68SYiFei Zhu 			.btf = btf,
340aff52e68SYiFei Zhu 			.jw = json_wtr,
341aff52e68SYiFei Zhu 			.is_plain_text = false,
342aff52e68SYiFei Zhu 		};
343aff52e68SYiFei Zhu 
344aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
345aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
346aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
347aff52e68SYiFei Zhu 
348aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
349aff52e68SYiFei Zhu 				continue;
350aff52e68SYiFei Zhu 
351aff52e68SYiFei Zhu 			if (!printed_header) {
352aff52e68SYiFei Zhu 				jsonw_name(json_wtr, "metadata");
353aff52e68SYiFei Zhu 				jsonw_start_object(json_wtr);
354aff52e68SYiFei Zhu 				printed_header = true;
355aff52e68SYiFei Zhu 			}
356aff52e68SYiFei Zhu 
357aff52e68SYiFei Zhu 			jsonw_name(json_wtr, name + BPF_METADATA_PREFIX_LEN);
358aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
359aff52e68SYiFei Zhu 			if (err) {
360aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
361aff52e68SYiFei Zhu 				break;
362aff52e68SYiFei Zhu 			}
363aff52e68SYiFei Zhu 		}
364aff52e68SYiFei Zhu 		if (printed_header)
365aff52e68SYiFei Zhu 			jsonw_end_object(json_wtr);
366aff52e68SYiFei Zhu 	} else {
367e89ef634SQuentin Monnet 		json_writer_t *btf_wtr;
368aff52e68SYiFei Zhu 		struct btf_dumper d = {
369aff52e68SYiFei Zhu 			.btf = btf,
370aff52e68SYiFei Zhu 			.is_plain_text = true,
371aff52e68SYiFei Zhu 		};
372aff52e68SYiFei Zhu 
373aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
374aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
375aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
376aff52e68SYiFei Zhu 
377aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
378aff52e68SYiFei Zhu 				continue;
379aff52e68SYiFei Zhu 
380aff52e68SYiFei Zhu 			if (!printed_header) {
381aff52e68SYiFei Zhu 				printf("\tmetadata:");
382e89ef634SQuentin Monnet 
383e89ef634SQuentin Monnet 				btf_wtr = jsonw_new(stdout);
384e89ef634SQuentin Monnet 				if (!btf_wtr) {
385e89ef634SQuentin Monnet 					p_err("jsonw alloc failed");
386e89ef634SQuentin Monnet 					goto out_free;
387e89ef634SQuentin Monnet 				}
388e89ef634SQuentin Monnet 				d.jw = btf_wtr,
389e89ef634SQuentin Monnet 
390aff52e68SYiFei Zhu 				printed_header = true;
391aff52e68SYiFei Zhu 			}
392aff52e68SYiFei Zhu 
393aff52e68SYiFei Zhu 			printf("\n\t\t%s = ", name + BPF_METADATA_PREFIX_LEN);
394aff52e68SYiFei Zhu 
395aff52e68SYiFei Zhu 			jsonw_reset(btf_wtr);
396aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
397aff52e68SYiFei Zhu 			if (err) {
398aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
399aff52e68SYiFei Zhu 				break;
400aff52e68SYiFei Zhu 			}
401aff52e68SYiFei Zhu 		}
402aff52e68SYiFei Zhu 		if (printed_header)
403aff52e68SYiFei Zhu 			jsonw_destroy(&btf_wtr);
404aff52e68SYiFei Zhu 	}
405aff52e68SYiFei Zhu 
406aff52e68SYiFei Zhu out_free:
407aff52e68SYiFei Zhu 	btf__free(btf);
408aff52e68SYiFei Zhu 	free(value);
409aff52e68SYiFei Zhu }
410aff52e68SYiFei Zhu 
411b662000aSRaman Shukhau static void print_prog_header_json(struct bpf_prog_info *info, int fd)
412743cc665SQuentin Monnet {
413b700eeb4SDaniel Müller 	const char *prog_type_str;
414b662000aSRaman Shukhau 	char prog_name[MAX_PROG_FULL_NAME];
415b662000aSRaman Shukhau 
416743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "id", info->id);
417b700eeb4SDaniel Müller 	prog_type_str = libbpf_bpf_prog_type_str(info->type);
418b700eeb4SDaniel Müller 
419b700eeb4SDaniel Müller 	if (prog_type_str)
420b700eeb4SDaniel Müller 		jsonw_string_field(json_wtr, "type", prog_type_str);
42171bb428fSJakub Kicinski 	else
422743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "type", info->type);
42371bb428fSJakub Kicinski 
424b662000aSRaman Shukhau 	if (*info->name) {
425b662000aSRaman Shukhau 		get_prog_full_name(info, fd, prog_name, sizeof(prog_name));
426b662000aSRaman Shukhau 		jsonw_string_field(json_wtr, "name", prog_name);
427b662000aSRaman Shukhau 	}
42871bb428fSJakub Kicinski 
429743cc665SQuentin Monnet 	jsonw_name(json_wtr, "tag");
430743cc665SQuentin Monnet 	jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
431743cc665SQuentin Monnet 		     info->tag[0], info->tag[1], info->tag[2], info->tag[3],
432743cc665SQuentin Monnet 		     info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
43371bb428fSJakub Kicinski 
4349b984a20SJiri Olsa 	jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible);
43588ad472bSAlexei Starovoitov 	if (info->run_time_ns) {
43688ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_time_ns", info->run_time_ns);
43788ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_cnt", info->run_cnt);
43888ad472bSAlexei Starovoitov 	}
4399ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
4409ed9e9baSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "recursion_misses", info->recursion_misses);
441ec202509SPaul Chaignon }
4429b984a20SJiri Olsa 
443ec202509SPaul Chaignon static void print_prog_json(struct bpf_prog_info *info, int fd)
444ec202509SPaul Chaignon {
445ec202509SPaul Chaignon 	char *memlock;
446ec202509SPaul Chaignon 
447ec202509SPaul Chaignon 	jsonw_start_object(json_wtr);
448b662000aSRaman Shukhau 	print_prog_header_json(info, fd);
44952262210SJakub Kicinski 	print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
45052262210SJakub Kicinski 
451743cc665SQuentin Monnet 	if (info->load_time) {
45271bb428fSJakub Kicinski 		char buf[32];
45371bb428fSJakub Kicinski 
454743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
45571bb428fSJakub Kicinski 
45671bb428fSJakub Kicinski 		/* Piggy back on load_time, since 0 uid is a valid one */
457a3fe1f6fSQuentin Monnet 		jsonw_name(json_wtr, "loaded_at");
458a3fe1f6fSQuentin Monnet 		jsonw_printf(json_wtr, "%s", buf);
459743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
46071bb428fSJakub Kicinski 	}
46171bb428fSJakub Kicinski 
462743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
46371bb428fSJakub Kicinski 
464743cc665SQuentin Monnet 	if (info->jited_prog_len) {
465743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", true);
466743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
467743cc665SQuentin Monnet 	} else {
468743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", false);
469743cc665SQuentin Monnet 	}
470743cc665SQuentin Monnet 
471743cc665SQuentin Monnet 	memlock = get_fdinfo(fd, "memlock");
472743cc665SQuentin Monnet 	if (memlock)
473357b3cc3SChris J Arges 		jsonw_int_field(json_wtr, "bytes_memlock", atoll(memlock));
474743cc665SQuentin Monnet 	free(memlock);
475743cc665SQuentin Monnet 
476743cc665SQuentin Monnet 	if (info->nr_map_ids)
477743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
478743cc665SQuentin Monnet 
479569b0c77SPrashant Bhole 	if (info->btf_id)
480569b0c77SPrashant Bhole 		jsonw_int_field(json_wtr, "btf_id", info->btf_id);
481569b0c77SPrashant Bhole 
4828f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
4838f184732SQuentin Monnet 		struct hashmap_entry *entry;
4844990f1f4SPrashant Bhole 
4854990f1f4SPrashant Bhole 		jsonw_name(json_wtr, "pinned");
4864990f1f4SPrashant Bhole 		jsonw_start_array(json_wtr);
4878f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
4888f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
4898f184732SQuentin Monnet 			jsonw_string(json_wtr, entry->value);
4904990f1f4SPrashant Bhole 		jsonw_end_array(json_wtr);
4914990f1f4SPrashant Bhole 	}
4924990f1f4SPrashant Bhole 
493d6699f8eSQuentin Monnet 	emit_obj_refs_json(refs_table, info->id, json_wtr);
494d53dee3fSAndrii Nakryiko 
495aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
496aff52e68SYiFei Zhu 
497743cc665SQuentin Monnet 	jsonw_end_object(json_wtr);
498743cc665SQuentin Monnet }
499743cc665SQuentin Monnet 
500b662000aSRaman Shukhau static void print_prog_header_plain(struct bpf_prog_info *info, int fd)
501743cc665SQuentin Monnet {
502b700eeb4SDaniel Müller 	const char *prog_type_str;
503b662000aSRaman Shukhau 	char prog_name[MAX_PROG_FULL_NAME];
504b662000aSRaman Shukhau 
505743cc665SQuentin Monnet 	printf("%u: ", info->id);
506b700eeb4SDaniel Müller 	prog_type_str = libbpf_bpf_prog_type_str(info->type);
507b700eeb4SDaniel Müller 	if (prog_type_str)
508b700eeb4SDaniel Müller 		printf("%s  ", prog_type_str);
509743cc665SQuentin Monnet 	else
510743cc665SQuentin Monnet 		printf("type %u  ", info->type);
511743cc665SQuentin Monnet 
512b662000aSRaman Shukhau 	if (*info->name) {
513b662000aSRaman Shukhau 		get_prog_full_name(info, fd, prog_name, sizeof(prog_name));
514b662000aSRaman Shukhau 		printf("name %s  ", prog_name);
515b662000aSRaman Shukhau 	}
516743cc665SQuentin Monnet 
517743cc665SQuentin Monnet 	printf("tag ");
518743cc665SQuentin Monnet 	fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
51952262210SJakub Kicinski 	print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
5209b984a20SJiri Olsa 	printf("%s", info->gpl_compatible ? "  gpl" : "");
52188ad472bSAlexei Starovoitov 	if (info->run_time_ns)
52288ad472bSAlexei Starovoitov 		printf(" run_time_ns %lld run_cnt %lld",
52388ad472bSAlexei Starovoitov 		       info->run_time_ns, info->run_cnt);
5249ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
5259ed9e9baSAlexei Starovoitov 		printf(" recursion_misses %lld", info->recursion_misses);
526743cc665SQuentin Monnet 	printf("\n");
527ec202509SPaul Chaignon }
528ec202509SPaul Chaignon 
529ec202509SPaul Chaignon static void print_prog_plain(struct bpf_prog_info *info, int fd)
530ec202509SPaul Chaignon {
531ec202509SPaul Chaignon 	char *memlock;
532ec202509SPaul Chaignon 
533b662000aSRaman Shukhau 	print_prog_header_plain(info, fd);
534743cc665SQuentin Monnet 
535743cc665SQuentin Monnet 	if (info->load_time) {
536743cc665SQuentin Monnet 		char buf[32];
537743cc665SQuentin Monnet 
538743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
539743cc665SQuentin Monnet 
540743cc665SQuentin Monnet 		/* Piggy back on load_time, since 0 uid is a valid one */
541743cc665SQuentin Monnet 		printf("\tloaded_at %s  uid %u\n", buf, info->created_by_uid);
542743cc665SQuentin Monnet 	}
543743cc665SQuentin Monnet 
544743cc665SQuentin Monnet 	printf("\txlated %uB", info->xlated_prog_len);
545743cc665SQuentin Monnet 
546743cc665SQuentin Monnet 	if (info->jited_prog_len)
547743cc665SQuentin Monnet 		printf("  jited %uB", info->jited_prog_len);
54871bb428fSJakub Kicinski 	else
54971bb428fSJakub Kicinski 		printf("  not jited");
55071bb428fSJakub Kicinski 
55171bb428fSJakub Kicinski 	memlock = get_fdinfo(fd, "memlock");
55271bb428fSJakub Kicinski 	if (memlock)
55371bb428fSJakub Kicinski 		printf("  memlock %sB", memlock);
55471bb428fSJakub Kicinski 	free(memlock);
55571bb428fSJakub Kicinski 
556743cc665SQuentin Monnet 	if (info->nr_map_ids)
557743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
55871bb428fSJakub Kicinski 
5598f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
5608f184732SQuentin Monnet 		struct hashmap_entry *entry;
5614990f1f4SPrashant Bhole 
5628f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
5638f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
5648f184732SQuentin Monnet 			printf("\n\tpinned %s", (char *)entry->value);
5654990f1f4SPrashant Bhole 	}
5664990f1f4SPrashant Bhole 
567569b0c77SPrashant Bhole 	if (info->btf_id)
568031ebc1aSQuentin Monnet 		printf("\n\tbtf_id %d", info->btf_id);
569569b0c77SPrashant Bhole 
570d6699f8eSQuentin Monnet 	emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
571d53dee3fSAndrii Nakryiko 
57271bb428fSJakub Kicinski 	printf("\n");
573aff52e68SYiFei Zhu 
574aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
575743cc665SQuentin Monnet }
576743cc665SQuentin Monnet 
577743cc665SQuentin Monnet static int show_prog(int fd)
578743cc665SQuentin Monnet {
579743cc665SQuentin Monnet 	struct bpf_prog_info info = {};
580743cc665SQuentin Monnet 	__u32 len = sizeof(info);
581743cc665SQuentin Monnet 	int err;
582743cc665SQuentin Monnet 
583743cc665SQuentin Monnet 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
584743cc665SQuentin Monnet 	if (err) {
5859a5ab8bfSQuentin Monnet 		p_err("can't get prog info: %s", strerror(errno));
586743cc665SQuentin Monnet 		return -1;
587743cc665SQuentin Monnet 	}
588743cc665SQuentin Monnet 
589743cc665SQuentin Monnet 	if (json_output)
590743cc665SQuentin Monnet 		print_prog_json(&info, fd);
591743cc665SQuentin Monnet 	else
592743cc665SQuentin Monnet 		print_prog_plain(&info, fd);
59371bb428fSJakub Kicinski 
59471bb428fSJakub Kicinski 	return 0;
59571bb428fSJakub Kicinski }
59671bb428fSJakub Kicinski 
597ec202509SPaul Chaignon static int do_show_subset(int argc, char **argv)
598ec202509SPaul Chaignon {
599ec202509SPaul Chaignon 	int *fds = NULL;
600ec202509SPaul Chaignon 	int nb_fds, i;
601ec202509SPaul Chaignon 	int err = -1;
602ec202509SPaul Chaignon 
603ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
604ec202509SPaul Chaignon 	if (!fds) {
605ec202509SPaul Chaignon 		p_err("mem alloc failed");
606ec202509SPaul Chaignon 		return -1;
607ec202509SPaul Chaignon 	}
608ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
609ec202509SPaul Chaignon 	if (nb_fds < 1)
610ec202509SPaul Chaignon 		goto exit_free;
611ec202509SPaul Chaignon 
612ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
613ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
614ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
615ec202509SPaul Chaignon 		err = show_prog(fds[i]);
616ec202509SPaul Chaignon 		if (err) {
617ec202509SPaul Chaignon 			for (; i < nb_fds; i++)
618ec202509SPaul Chaignon 				close(fds[i]);
619ec202509SPaul Chaignon 			break;
620ec202509SPaul Chaignon 		}
621ec202509SPaul Chaignon 		close(fds[i]);
622ec202509SPaul Chaignon 	}
623ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
624ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
625ec202509SPaul Chaignon 
626ec202509SPaul Chaignon exit_free:
627ec202509SPaul Chaignon 	free(fds);
628ec202509SPaul Chaignon 	return err;
629ec202509SPaul Chaignon }
630ec202509SPaul Chaignon 
63171bb428fSJakub Kicinski static int do_show(int argc, char **argv)
632743cc665SQuentin Monnet {
633743cc665SQuentin Monnet 	__u32 id = 0;
63471bb428fSJakub Kicinski 	int err;
63571bb428fSJakub Kicinski 	int fd;
63671bb428fSJakub Kicinski 
63746241271SQuentin Monnet 	if (show_pinned) {
6388f184732SQuentin Monnet 		prog_table = hashmap__new(hash_fn_for_key_as_id,
6398f184732SQuentin Monnet 					  equal_fn_for_key_as_id, NULL);
640622a5b58SMauricio Vásquez 		if (IS_ERR(prog_table)) {
6418f184732SQuentin Monnet 			p_err("failed to create hashmap for pinned paths");
6428f184732SQuentin Monnet 			return -1;
6438f184732SQuentin Monnet 		}
6448f184732SQuentin Monnet 		build_pinned_obj_table(prog_table, BPF_OBJ_PROG);
64546241271SQuentin Monnet 	}
646d53dee3fSAndrii Nakryiko 	build_obj_refs_table(&refs_table, BPF_OBJ_PROG);
6474990f1f4SPrashant Bhole 
648ec202509SPaul Chaignon 	if (argc == 2)
649ec202509SPaul Chaignon 		return do_show_subset(argc, argv);
65071bb428fSJakub Kicinski 
65171bb428fSJakub Kicinski 	if (argc)
65271bb428fSJakub Kicinski 		return BAD_ARG();
65371bb428fSJakub Kicinski 
654743cc665SQuentin Monnet 	if (json_output)
655743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
65671bb428fSJakub Kicinski 	while (true) {
65771bb428fSJakub Kicinski 		err = bpf_prog_get_next_id(id, &id);
65871bb428fSJakub Kicinski 		if (err) {
6591739c26dSQuentin Monnet 			if (errno == ENOENT) {
6601739c26dSQuentin Monnet 				err = 0;
66171bb428fSJakub Kicinski 				break;
6621739c26dSQuentin Monnet 			}
6639a5ab8bfSQuentin Monnet 			p_err("can't get next program: %s%s", strerror(errno),
6649a5ab8bfSQuentin Monnet 			      errno == EINVAL ? " -- kernel too old?" : "");
665743cc665SQuentin Monnet 			err = -1;
666743cc665SQuentin Monnet 			break;
66771bb428fSJakub Kicinski 		}
66871bb428fSJakub Kicinski 
66971bb428fSJakub Kicinski 		fd = bpf_prog_get_fd_by_id(id);
67071bb428fSJakub Kicinski 		if (fd < 0) {
6718207c6ddSJakub Kicinski 			if (errno == ENOENT)
6728207c6ddSJakub Kicinski 				continue;
6739a5ab8bfSQuentin Monnet 			p_err("can't get prog by id (%u): %s",
67471bb428fSJakub Kicinski 			      id, strerror(errno));
675743cc665SQuentin Monnet 			err = -1;
676743cc665SQuentin Monnet 			break;
67771bb428fSJakub Kicinski 		}
67871bb428fSJakub Kicinski 
67971bb428fSJakub Kicinski 		err = show_prog(fd);
68071bb428fSJakub Kicinski 		close(fd);
68171bb428fSJakub Kicinski 		if (err)
682743cc665SQuentin Monnet 			break;
68371bb428fSJakub Kicinski 	}
68471bb428fSJakub Kicinski 
685743cc665SQuentin Monnet 	if (json_output)
686743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
687743cc665SQuentin Monnet 
688d6699f8eSQuentin Monnet 	delete_obj_refs_table(refs_table);
689d53dee3fSAndrii Nakryiko 
69046241271SQuentin Monnet 	if (show_pinned)
6918f184732SQuentin Monnet 		delete_pinned_obj_table(prog_table);
69246241271SQuentin Monnet 
693743cc665SQuentin Monnet 	return err;
69471bb428fSJakub Kicinski }
69571bb428fSJakub Kicinski 
696ec202509SPaul Chaignon static int
697ec202509SPaul Chaignon prog_dump(struct bpf_prog_info *info, enum dump_mode mode,
698ec202509SPaul Chaignon 	  char *filepath, bool opcodes, bool visual, bool linum)
69971bb428fSJakub Kicinski {
700b053b439SMartin KaFai Lau 	struct bpf_prog_linfo *prog_linfo = NULL;
7013ddeac67SJakub Kicinski 	const char *disasm_opt = NULL;
7027105e828SDaniel Borkmann 	struct dump_data dd = {};
703cae73f23SSong Liu 	void *func_info = NULL;
704254471e5SYonghong Song 	struct btf *btf = NULL;
705254471e5SYonghong Song 	char func_sig[1024];
70671bb428fSJakub Kicinski 	unsigned char *buf;
707cae73f23SSong Liu 	__u32 member_len;
708ebbd7f64SQuentin Monnet 	int fd, err = -1;
70971bb428fSJakub Kicinski 	ssize_t n;
71071bb428fSJakub Kicinski 
711cae73f23SSong Liu 	if (mode == DUMP_JITED) {
7125b79bcdfSToke Høiland-Jørgensen 		if (info->jited_prog_len == 0 || !info->jited_prog_insns) {
7139a5ab8bfSQuentin Monnet 			p_info("no instructions returned");
714ec202509SPaul Chaignon 			return -1;
715f84192eeSSandipan Das 		}
71609f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->jited_prog_insns);
717cae73f23SSong Liu 		member_len = info->jited_prog_len;
718cae73f23SSong Liu 	} else {	/* DUMP_XLATED */
719d95f1e8bSToke Høiland-Jørgensen 		if (info->xlated_prog_len == 0 || !info->xlated_prog_insns) {
7207105e828SDaniel Borkmann 			p_err("error retrieving insn dump: kernel.kptr_restrict set?");
721ec202509SPaul Chaignon 			return -1;
7227105e828SDaniel Borkmann 		}
72309f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->xlated_prog_insns);
724cae73f23SSong Liu 		member_len = info->xlated_prog_len;
725cae73f23SSong Liu 	}
7267105e828SDaniel Borkmann 
72786f4b7f2SQuentin Monnet 	if (info->btf_id) {
72886f4b7f2SQuentin Monnet 		btf = btf__load_from_kernel_by_id(info->btf_id);
72986f4b7f2SQuentin Monnet 		if (libbpf_get_error(btf)) {
730254471e5SYonghong Song 			p_err("failed to get btf");
731ec202509SPaul Chaignon 			return -1;
732254471e5SYonghong Song 		}
73386f4b7f2SQuentin Monnet 	}
734254471e5SYonghong Song 
73509f44b75SAndrii Nakryiko 	func_info = u64_to_ptr(info->func_info);
736cae73f23SSong Liu 
737cae73f23SSong Liu 	if (info->nr_line_info) {
738cae73f23SSong Liu 		prog_linfo = bpf_prog_linfo__new(info);
739b053b439SMartin KaFai Lau 		if (!prog_linfo)
74010a5ce98SMartin KaFai Lau 			p_info("error in processing bpf_line_info.  continue without it.");
741b053b439SMartin KaFai Lau 	}
742b053b439SMartin KaFai Lau 
74371bb428fSJakub Kicinski 	if (filepath) {
74471bb428fSJakub Kicinski 		fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
74571bb428fSJakub Kicinski 		if (fd < 0) {
7469a5ab8bfSQuentin Monnet 			p_err("can't open file %s: %s", filepath,
74771bb428fSJakub Kicinski 			      strerror(errno));
748ebbd7f64SQuentin Monnet 			goto exit_free;
74971bb428fSJakub Kicinski 		}
75071bb428fSJakub Kicinski 
751cae73f23SSong Liu 		n = write(fd, buf, member_len);
75271bb428fSJakub Kicinski 		close(fd);
75309f44b75SAndrii Nakryiko 		if (n != (ssize_t)member_len) {
7549a5ab8bfSQuentin Monnet 			p_err("error writing output file: %s",
75571bb428fSJakub Kicinski 			      n < 0 ? strerror(errno) : "short write");
756ebbd7f64SQuentin Monnet 			goto exit_free;
75771bb428fSJakub Kicinski 		}
75852c84d36SQuentin Monnet 
75952c84d36SQuentin Monnet 		if (json_output)
76052c84d36SQuentin Monnet 			jsonw_null(json_wtr);
761cae73f23SSong Liu 	} else if (mode == DUMP_JITED) {
762e6593596SJiong Wang 		const char *name = NULL;
763e6593596SJiong Wang 
764cae73f23SSong Liu 		if (info->ifindex) {
765cae73f23SSong Liu 			name = ifindex_to_bfd_params(info->ifindex,
766cae73f23SSong Liu 						     info->netns_dev,
767cae73f23SSong Liu 						     info->netns_ino,
7683ddeac67SJakub Kicinski 						     &disasm_opt);
769e6593596SJiong Wang 			if (!name)
770ebbd7f64SQuentin Monnet 				goto exit_free;
771e6593596SJiong Wang 		}
772e6593596SJiong Wang 
773cae73f23SSong Liu 		if (info->nr_jited_func_lens && info->jited_func_lens) {
774f7f62c71SSandipan Das 			struct kernel_sym *sym = NULL;
775254471e5SYonghong Song 			struct bpf_func_info *record;
776f7f62c71SSandipan Das 			char sym_name[SYM_MAX_NAME];
777f7f62c71SSandipan Das 			unsigned char *img = buf;
778f7f62c71SSandipan Das 			__u64 *ksyms = NULL;
779f7f62c71SSandipan Das 			__u32 *lens;
780f7f62c71SSandipan Das 			__u32 i;
781cae73f23SSong Liu 			if (info->nr_jited_ksyms) {
782f7f62c71SSandipan Das 				kernel_syms_load(&dd);
78309f44b75SAndrii Nakryiko 				ksyms = u64_to_ptr(info->jited_ksyms);
784f7f62c71SSandipan Das 			}
785f7f62c71SSandipan Das 
786f7f62c71SSandipan Das 			if (json_output)
787f7f62c71SSandipan Das 				jsonw_start_array(json_wtr);
788f7f62c71SSandipan Das 
78909f44b75SAndrii Nakryiko 			lens = u64_to_ptr(info->jited_func_lens);
790cae73f23SSong Liu 			for (i = 0; i < info->nr_jited_func_lens; i++) {
791f7f62c71SSandipan Das 				if (ksyms) {
792f7f62c71SSandipan Das 					sym = kernel_syms_search(&dd, ksyms[i]);
793f7f62c71SSandipan Das 					if (sym)
794f7f62c71SSandipan Das 						sprintf(sym_name, "%s", sym->name);
795f7f62c71SSandipan Das 					else
796f7f62c71SSandipan Das 						sprintf(sym_name, "0x%016llx", ksyms[i]);
797f7f62c71SSandipan Das 				} else {
798f7f62c71SSandipan Das 					strcpy(sym_name, "unknown");
799f7f62c71SSandipan Das 				}
800f7f62c71SSandipan Das 
801254471e5SYonghong Song 				if (func_info) {
802cae73f23SSong Liu 					record = func_info + i * info->func_info_rec_size;
803254471e5SYonghong Song 					btf_dumper_type_only(btf, record->type_id,
804254471e5SYonghong Song 							     func_sig,
805254471e5SYonghong Song 							     sizeof(func_sig));
806254471e5SYonghong Song 				}
807254471e5SYonghong Song 
808f7f62c71SSandipan Das 				if (json_output) {
809f7f62c71SSandipan Das 					jsonw_start_object(json_wtr);
810254471e5SYonghong Song 					if (func_info && func_sig[0] != '\0') {
811254471e5SYonghong Song 						jsonw_name(json_wtr, "proto");
812254471e5SYonghong Song 						jsonw_string(json_wtr, func_sig);
813254471e5SYonghong Song 					}
814f7f62c71SSandipan Das 					jsonw_name(json_wtr, "name");
815f7f62c71SSandipan Das 					jsonw_string(json_wtr, sym_name);
816f7f62c71SSandipan Das 					jsonw_name(json_wtr, "insns");
817f7f62c71SSandipan Das 				} else {
818254471e5SYonghong Song 					if (func_info && func_sig[0] != '\0')
819254471e5SYonghong Song 						printf("%s:\n", func_sig);
820f7f62c71SSandipan Das 					printf("%s:\n", sym_name);
821f7f62c71SSandipan Das 				}
822f7f62c71SSandipan Das 
823b053b439SMartin KaFai Lau 				disasm_print_insn(img, lens[i], opcodes,
824b053b439SMartin KaFai Lau 						  name, disasm_opt, btf,
825b053b439SMartin KaFai Lau 						  prog_linfo, ksyms[i], i,
826b053b439SMartin KaFai Lau 						  linum);
827b053b439SMartin KaFai Lau 
828f7f62c71SSandipan Das 				img += lens[i];
829f7f62c71SSandipan Das 
830f7f62c71SSandipan Das 				if (json_output)
831f7f62c71SSandipan Das 					jsonw_end_object(json_wtr);
832f7f62c71SSandipan Das 				else
833f7f62c71SSandipan Das 					printf("\n");
834f7f62c71SSandipan Das 			}
835f7f62c71SSandipan Das 
836f7f62c71SSandipan Das 			if (json_output)
837f7f62c71SSandipan Das 				jsonw_end_array(json_wtr);
838f7f62c71SSandipan Das 		} else {
839cae73f23SSong Liu 			disasm_print_insn(buf, member_len, opcodes, name,
840b053b439SMartin KaFai Lau 					  disasm_opt, btf, NULL, 0, 0, false);
841f7f62c71SSandipan Das 		}
842b6c1cedbSJiong Wang 	} else if (visual) {
843b6c1cedbSJiong Wang 		if (json_output)
844b6c1cedbSJiong Wang 			jsonw_null(json_wtr);
845b6c1cedbSJiong Wang 		else
846cae73f23SSong Liu 			dump_xlated_cfg(buf, member_len);
8477105e828SDaniel Borkmann 	} else {
8487105e828SDaniel Borkmann 		kernel_syms_load(&dd);
849cae73f23SSong Liu 		dd.nr_jited_ksyms = info->nr_jited_ksyms;
85009f44b75SAndrii Nakryiko 		dd.jited_ksyms = u64_to_ptr(info->jited_ksyms);
851254471e5SYonghong Song 		dd.btf = btf;
852254471e5SYonghong Song 		dd.func_info = func_info;
853cae73f23SSong Liu 		dd.finfo_rec_size = info->func_info_rec_size;
854b053b439SMartin KaFai Lau 		dd.prog_linfo = prog_linfo;
855f84192eeSSandipan Das 
856f05e2c32SQuentin Monnet 		if (json_output)
857cae73f23SSong Liu 			dump_xlated_json(&dd, buf, member_len, opcodes,
858b053b439SMartin KaFai Lau 					 linum);
859f05e2c32SQuentin Monnet 		else
860cae73f23SSong Liu 			dump_xlated_plain(&dd, buf, member_len, opcodes,
861b053b439SMartin KaFai Lau 					  linum);
8627105e828SDaniel Borkmann 		kernel_syms_destroy(&dd);
8637105e828SDaniel Borkmann 	}
86471bb428fSJakub Kicinski 
865ebbd7f64SQuentin Monnet 	err = 0;
866369e955bSQuentin Monnet 
867ebbd7f64SQuentin Monnet exit_free:
868ebbd7f64SQuentin Monnet 	btf__free(btf);
869ebbd7f64SQuentin Monnet 	bpf_prog_linfo__free(prog_linfo);
870ebbd7f64SQuentin Monnet 	return err;
871ec202509SPaul Chaignon }
87271bb428fSJakub Kicinski 
873ec202509SPaul Chaignon static int do_dump(int argc, char **argv)
874ec202509SPaul Chaignon {
875c59765cfSDave Marchevsky 	struct bpf_prog_info info;
876c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
877c59765cfSDave Marchevsky 	size_t info_data_sz = 0;
878c59765cfSDave Marchevsky 	void *info_data = NULL;
879ec202509SPaul Chaignon 	char *filepath = NULL;
880ec202509SPaul Chaignon 	bool opcodes = false;
881ec202509SPaul Chaignon 	bool visual = false;
882ec202509SPaul Chaignon 	enum dump_mode mode;
883ec202509SPaul Chaignon 	bool linum = false;
884ec202509SPaul Chaignon 	int nb_fds, i = 0;
885c59765cfSDave Marchevsky 	int *fds = NULL;
886ec202509SPaul Chaignon 	int err = -1;
887ec202509SPaul Chaignon 
888ec202509SPaul Chaignon 	if (is_prefix(*argv, "jited")) {
889ec202509SPaul Chaignon 		if (disasm_init())
89071bb428fSJakub Kicinski 			return -1;
891ec202509SPaul Chaignon 		mode = DUMP_JITED;
892ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "xlated")) {
893ec202509SPaul Chaignon 		mode = DUMP_XLATED;
894ec202509SPaul Chaignon 	} else {
895ec202509SPaul Chaignon 		p_err("expected 'xlated' or 'jited', got: %s", *argv);
896ec202509SPaul Chaignon 		return -1;
897ec202509SPaul Chaignon 	}
898ec202509SPaul Chaignon 	NEXT_ARG();
899ec202509SPaul Chaignon 
900ec202509SPaul Chaignon 	if (argc < 2)
901ec202509SPaul Chaignon 		usage();
902ec202509SPaul Chaignon 
903ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
904ec202509SPaul Chaignon 	if (!fds) {
905ec202509SPaul Chaignon 		p_err("mem alloc failed");
906ec202509SPaul Chaignon 		return -1;
907ec202509SPaul Chaignon 	}
908ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
909ec202509SPaul Chaignon 	if (nb_fds < 1)
910ec202509SPaul Chaignon 		goto exit_free;
911ec202509SPaul Chaignon 
912ec202509SPaul Chaignon 	if (is_prefix(*argv, "file")) {
913ec202509SPaul Chaignon 		NEXT_ARG();
914ec202509SPaul Chaignon 		if (!argc) {
915ec202509SPaul Chaignon 			p_err("expected file path");
916ec202509SPaul Chaignon 			goto exit_close;
917ec202509SPaul Chaignon 		}
918ec202509SPaul Chaignon 		if (nb_fds > 1) {
919ec202509SPaul Chaignon 			p_err("several programs matched");
920ec202509SPaul Chaignon 			goto exit_close;
921ec202509SPaul Chaignon 		}
922ec202509SPaul Chaignon 
923ec202509SPaul Chaignon 		filepath = *argv;
924ec202509SPaul Chaignon 		NEXT_ARG();
925ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "opcodes")) {
926ec202509SPaul Chaignon 		opcodes = true;
927ec202509SPaul Chaignon 		NEXT_ARG();
928ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "visual")) {
929ec202509SPaul Chaignon 		if (nb_fds > 1) {
930ec202509SPaul Chaignon 			p_err("several programs matched");
931ec202509SPaul Chaignon 			goto exit_close;
932ec202509SPaul Chaignon 		}
933ec202509SPaul Chaignon 
934ec202509SPaul Chaignon 		visual = true;
935ec202509SPaul Chaignon 		NEXT_ARG();
936ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "linum")) {
937ec202509SPaul Chaignon 		linum = true;
938ec202509SPaul Chaignon 		NEXT_ARG();
939ec202509SPaul Chaignon 	}
940ec202509SPaul Chaignon 
941ec202509SPaul Chaignon 	if (argc) {
942ec202509SPaul Chaignon 		usage();
943ec202509SPaul Chaignon 		goto exit_close;
944ec202509SPaul Chaignon 	}
945ec202509SPaul Chaignon 
946ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
947ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
948ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
949c59765cfSDave Marchevsky 		memset(&info, 0, sizeof(info));
950c59765cfSDave Marchevsky 
951c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
952c59765cfSDave Marchevsky 		if (err) {
953c59765cfSDave Marchevsky 			p_err("can't get prog info: %s", strerror(errno));
954c59765cfSDave Marchevsky 			break;
955c59765cfSDave Marchevsky 		}
956c59765cfSDave Marchevsky 
957c59765cfSDave Marchevsky 		err = prep_prog_info(&info, mode, &info_data, &info_data_sz);
958c59765cfSDave Marchevsky 		if (err) {
959c59765cfSDave Marchevsky 			p_err("can't grow prog info_data");
960c59765cfSDave Marchevsky 			break;
961c59765cfSDave Marchevsky 		}
962c59765cfSDave Marchevsky 
963c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
964c59765cfSDave Marchevsky 		if (err) {
965ec202509SPaul Chaignon 			p_err("can't get prog info: %s", strerror(errno));
966ec202509SPaul Chaignon 			break;
967ec202509SPaul Chaignon 		}
968ec202509SPaul Chaignon 
969ec202509SPaul Chaignon 		if (json_output && nb_fds > 1) {
970ec202509SPaul Chaignon 			jsonw_start_object(json_wtr);	/* prog object */
971b662000aSRaman Shukhau 			print_prog_header_json(&info, fds[i]);
972ec202509SPaul Chaignon 			jsonw_name(json_wtr, "insns");
973ec202509SPaul Chaignon 		} else if (nb_fds > 1) {
974b662000aSRaman Shukhau 			print_prog_header_plain(&info, fds[i]);
975ec202509SPaul Chaignon 		}
976ec202509SPaul Chaignon 
977c59765cfSDave Marchevsky 		err = prog_dump(&info, mode, filepath, opcodes, visual, linum);
978ec202509SPaul Chaignon 
979ec202509SPaul Chaignon 		if (json_output && nb_fds > 1)
980ec202509SPaul Chaignon 			jsonw_end_object(json_wtr);	/* prog object */
981ec202509SPaul Chaignon 		else if (i != nb_fds - 1 && nb_fds > 1)
982ec202509SPaul Chaignon 			printf("\n");
983ec202509SPaul Chaignon 
984ec202509SPaul Chaignon 		if (err)
985ec202509SPaul Chaignon 			break;
986ec202509SPaul Chaignon 		close(fds[i]);
987ec202509SPaul Chaignon 	}
988ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
989ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
990ec202509SPaul Chaignon 
991ec202509SPaul Chaignon exit_close:
992ec202509SPaul Chaignon 	for (; i < nb_fds; i++)
993ec202509SPaul Chaignon 		close(fds[i]);
994ec202509SPaul Chaignon exit_free:
995c59765cfSDave Marchevsky 	free(info_data);
996ec202509SPaul Chaignon 	free(fds);
997ec202509SPaul Chaignon 	return err;
99871bb428fSJakub Kicinski }
99971bb428fSJakub Kicinski 
100071bb428fSJakub Kicinski static int do_pin(int argc, char **argv)
100171bb428fSJakub Kicinski {
1002004b45c0SQuentin Monnet 	int err;
1003004b45c0SQuentin Monnet 
100475a1e792SQuentin Monnet 	err = do_pin_any(argc, argv, prog_parse_fd);
1005004b45c0SQuentin Monnet 	if (!err && json_output)
1006004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
1007004b45c0SQuentin Monnet 	return err;
100871bb428fSJakub Kicinski }
100971bb428fSJakub Kicinski 
10103ff5a4dcSJakub Kicinski struct map_replace {
10113ff5a4dcSJakub Kicinski 	int idx;
10123ff5a4dcSJakub Kicinski 	int fd;
10133ff5a4dcSJakub Kicinski 	char *name;
10143ff5a4dcSJakub Kicinski };
10153ff5a4dcSJakub Kicinski 
1016c101189bSQuentin Monnet static int map_replace_compar(const void *p1, const void *p2)
10173ff5a4dcSJakub Kicinski {
10183ff5a4dcSJakub Kicinski 	const struct map_replace *a = p1, *b = p2;
10193ff5a4dcSJakub Kicinski 
10203ff5a4dcSJakub Kicinski 	return a->idx - b->idx;
10213ff5a4dcSJakub Kicinski }
10223ff5a4dcSJakub Kicinski 
1023092f0892SStanislav Fomichev static int parse_attach_detach_args(int argc, char **argv, int *progfd,
1024092f0892SStanislav Fomichev 				    enum bpf_attach_type *attach_type,
1025092f0892SStanislav Fomichev 				    int *mapfd)
1026092f0892SStanislav Fomichev {
1027092f0892SStanislav Fomichev 	if (!REQ_ARGS(3))
1028092f0892SStanislav Fomichev 		return -EINVAL;
1029092f0892SStanislav Fomichev 
1030092f0892SStanislav Fomichev 	*progfd = prog_parse_fd(&argc, &argv);
1031092f0892SStanislav Fomichev 	if (*progfd < 0)
1032092f0892SStanislav Fomichev 		return *progfd;
1033092f0892SStanislav Fomichev 
1034092f0892SStanislav Fomichev 	*attach_type = parse_attach_type(*argv);
1035092f0892SStanislav Fomichev 	if (*attach_type == __MAX_BPF_ATTACH_TYPE) {
1036092f0892SStanislav Fomichev 		p_err("invalid attach/detach type");
1037092f0892SStanislav Fomichev 		return -EINVAL;
1038092f0892SStanislav Fomichev 	}
1039092f0892SStanislav Fomichev 
1040092f0892SStanislav Fomichev 	if (*attach_type == BPF_FLOW_DISSECTOR) {
1041f9b7ff0dSLorenz Bauer 		*mapfd = 0;
1042092f0892SStanislav Fomichev 		return 0;
1043092f0892SStanislav Fomichev 	}
1044092f0892SStanislav Fomichev 
1045092f0892SStanislav Fomichev 	NEXT_ARG();
1046092f0892SStanislav Fomichev 	if (!REQ_ARGS(2))
1047092f0892SStanislav Fomichev 		return -EINVAL;
1048092f0892SStanislav Fomichev 
1049092f0892SStanislav Fomichev 	*mapfd = map_parse_fd(&argc, &argv);
1050092f0892SStanislav Fomichev 	if (*mapfd < 0)
1051092f0892SStanislav Fomichev 		return *mapfd;
1052092f0892SStanislav Fomichev 
1053092f0892SStanislav Fomichev 	return 0;
1054092f0892SStanislav Fomichev }
1055092f0892SStanislav Fomichev 
1056b7d3826cSJohn Fastabend static int do_attach(int argc, char **argv)
1057b7d3826cSJohn Fastabend {
1058b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1059092f0892SStanislav Fomichev 	int err, progfd;
1060092f0892SStanislav Fomichev 	int mapfd;
1061b7d3826cSJohn Fastabend 
1062092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1063092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1064092f0892SStanislav Fomichev 	if (err)
1065092f0892SStanislav Fomichev 		return err;
1066b7d3826cSJohn Fastabend 
1067b7d3826cSJohn Fastabend 	err = bpf_prog_attach(progfd, mapfd, attach_type, 0);
1068b7d3826cSJohn Fastabend 	if (err) {
1069b7d3826cSJohn Fastabend 		p_err("failed prog attach to map");
1070b7d3826cSJohn Fastabend 		return -EINVAL;
1071b7d3826cSJohn Fastabend 	}
1072b7d3826cSJohn Fastabend 
1073b7d3826cSJohn Fastabend 	if (json_output)
1074b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1075b7d3826cSJohn Fastabend 	return 0;
1076b7d3826cSJohn Fastabend }
1077b7d3826cSJohn Fastabend 
1078b7d3826cSJohn Fastabend static int do_detach(int argc, char **argv)
1079b7d3826cSJohn Fastabend {
1080b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1081092f0892SStanislav Fomichev 	int err, progfd;
1082092f0892SStanislav Fomichev 	int mapfd;
1083b7d3826cSJohn Fastabend 
1084092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1085092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1086092f0892SStanislav Fomichev 	if (err)
1087092f0892SStanislav Fomichev 		return err;
1088b7d3826cSJohn Fastabend 
1089b7d3826cSJohn Fastabend 	err = bpf_prog_detach2(progfd, mapfd, attach_type);
1090b7d3826cSJohn Fastabend 	if (err) {
1091b7d3826cSJohn Fastabend 		p_err("failed prog detach from map");
1092b7d3826cSJohn Fastabend 		return -EINVAL;
1093b7d3826cSJohn Fastabend 	}
1094b7d3826cSJohn Fastabend 
1095b7d3826cSJohn Fastabend 	if (json_output)
1096b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1097b7d3826cSJohn Fastabend 	return 0;
1098b7d3826cSJohn Fastabend }
109977380998SStanislav Fomichev 
1100ba95c745SQuentin Monnet static int check_single_stdin(char *file_data_in, char *file_ctx_in)
1101ba95c745SQuentin Monnet {
1102ba95c745SQuentin Monnet 	if (file_data_in && file_ctx_in &&
1103ba95c745SQuentin Monnet 	    !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) {
1104ba95c745SQuentin Monnet 		p_err("cannot use standard input for both data_in and ctx_in");
1105ba95c745SQuentin Monnet 		return -1;
1106ba95c745SQuentin Monnet 	}
1107ba95c745SQuentin Monnet 
1108ba95c745SQuentin Monnet 	return 0;
1109ba95c745SQuentin Monnet }
1110ba95c745SQuentin Monnet 
1111ba95c745SQuentin Monnet static int get_run_data(const char *fname, void **data_ptr, unsigned int *size)
1112ba95c745SQuentin Monnet {
1113ba95c745SQuentin Monnet 	size_t block_size = 256;
1114ba95c745SQuentin Monnet 	size_t buf_size = block_size;
1115ba95c745SQuentin Monnet 	size_t nb_read = 0;
1116ba95c745SQuentin Monnet 	void *tmp;
1117ba95c745SQuentin Monnet 	FILE *f;
1118ba95c745SQuentin Monnet 
1119ba95c745SQuentin Monnet 	if (!fname) {
1120ba95c745SQuentin Monnet 		*data_ptr = NULL;
1121ba95c745SQuentin Monnet 		*size = 0;
1122ba95c745SQuentin Monnet 		return 0;
1123ba95c745SQuentin Monnet 	}
1124ba95c745SQuentin Monnet 
1125ba95c745SQuentin Monnet 	if (!strcmp(fname, "-"))
1126ba95c745SQuentin Monnet 		f = stdin;
1127ba95c745SQuentin Monnet 	else
1128ba95c745SQuentin Monnet 		f = fopen(fname, "r");
1129ba95c745SQuentin Monnet 	if (!f) {
1130ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1131ba95c745SQuentin Monnet 		return -1;
1132ba95c745SQuentin Monnet 	}
1133ba95c745SQuentin Monnet 
1134ba95c745SQuentin Monnet 	*data_ptr = malloc(block_size);
1135ba95c745SQuentin Monnet 	if (!*data_ptr) {
1136ba95c745SQuentin Monnet 		p_err("failed to allocate memory for data_in/ctx_in: %s",
1137ba95c745SQuentin Monnet 		      strerror(errno));
1138ba95c745SQuentin Monnet 		goto err_fclose;
1139ba95c745SQuentin Monnet 	}
1140ba95c745SQuentin Monnet 
1141ba95c745SQuentin Monnet 	while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) {
1142ba95c745SQuentin Monnet 		if (feof(f))
1143ba95c745SQuentin Monnet 			break;
1144ba95c745SQuentin Monnet 		if (ferror(f)) {
1145ba95c745SQuentin Monnet 			p_err("failed to read data_in/ctx_in from %s: %s",
1146ba95c745SQuentin Monnet 			      fname, strerror(errno));
1147ba95c745SQuentin Monnet 			goto err_free;
1148ba95c745SQuentin Monnet 		}
1149ba95c745SQuentin Monnet 		if (nb_read > buf_size - block_size) {
1150ba95c745SQuentin Monnet 			if (buf_size == UINT32_MAX) {
1151ba95c745SQuentin Monnet 				p_err("data_in/ctx_in is too long (max: %d)",
1152ba95c745SQuentin Monnet 				      UINT32_MAX);
1153ba95c745SQuentin Monnet 				goto err_free;
1154ba95c745SQuentin Monnet 			}
1155ba95c745SQuentin Monnet 			/* No space for fread()-ing next chunk; realloc() */
1156ba95c745SQuentin Monnet 			buf_size *= 2;
1157ba95c745SQuentin Monnet 			tmp = realloc(*data_ptr, buf_size);
1158ba95c745SQuentin Monnet 			if (!tmp) {
1159ba95c745SQuentin Monnet 				p_err("failed to reallocate data_in/ctx_in: %s",
1160ba95c745SQuentin Monnet 				      strerror(errno));
1161ba95c745SQuentin Monnet 				goto err_free;
1162ba95c745SQuentin Monnet 			}
1163ba95c745SQuentin Monnet 			*data_ptr = tmp;
1164ba95c745SQuentin Monnet 		}
1165ba95c745SQuentin Monnet 	}
1166ba95c745SQuentin Monnet 	if (f != stdin)
1167ba95c745SQuentin Monnet 		fclose(f);
1168ba95c745SQuentin Monnet 
1169ba95c745SQuentin Monnet 	*size = nb_read;
1170ba95c745SQuentin Monnet 	return 0;
1171ba95c745SQuentin Monnet 
1172ba95c745SQuentin Monnet err_free:
1173ba95c745SQuentin Monnet 	free(*data_ptr);
1174ba95c745SQuentin Monnet 	*data_ptr = NULL;
1175ba95c745SQuentin Monnet err_fclose:
1176ba95c745SQuentin Monnet 	if (f != stdin)
1177ba95c745SQuentin Monnet 		fclose(f);
1178ba95c745SQuentin Monnet 	return -1;
1179ba95c745SQuentin Monnet }
1180ba95c745SQuentin Monnet 
1181ba95c745SQuentin Monnet static void hex_print(void *data, unsigned int size, FILE *f)
1182ba95c745SQuentin Monnet {
1183ba95c745SQuentin Monnet 	size_t i, j;
1184ba95c745SQuentin Monnet 	char c;
1185ba95c745SQuentin Monnet 
1186ba95c745SQuentin Monnet 	for (i = 0; i < size; i += 16) {
1187ba95c745SQuentin Monnet 		/* Row offset */
1188ba95c745SQuentin Monnet 		fprintf(f, "%07zx\t", i);
1189ba95c745SQuentin Monnet 
1190ba95c745SQuentin Monnet 		/* Hexadecimal values */
1191ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++)
1192ba95c745SQuentin Monnet 			fprintf(f, "%02x%s", *(uint8_t *)(data + j),
1193ba95c745SQuentin Monnet 				j % 2 ? " " : "");
1194ba95c745SQuentin Monnet 		for (; j < i + 16; j++)
1195ba95c745SQuentin Monnet 			fprintf(f, "  %s", j % 2 ? " " : "");
1196ba95c745SQuentin Monnet 
1197ba95c745SQuentin Monnet 		/* ASCII values (if relevant), '.' otherwise */
1198ba95c745SQuentin Monnet 		fprintf(f, "| ");
1199ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++) {
1200ba95c745SQuentin Monnet 			c = *(char *)(data + j);
1201ba95c745SQuentin Monnet 			if (c < ' ' || c > '~')
1202ba95c745SQuentin Monnet 				c = '.';
1203ba95c745SQuentin Monnet 			fprintf(f, "%c%s", c, j == i + 7 ? " " : "");
1204ba95c745SQuentin Monnet 		}
1205ba95c745SQuentin Monnet 
1206ba95c745SQuentin Monnet 		fprintf(f, "\n");
1207ba95c745SQuentin Monnet 	}
1208ba95c745SQuentin Monnet }
1209ba95c745SQuentin Monnet 
1210ba95c745SQuentin Monnet static int
1211ba95c745SQuentin Monnet print_run_output(void *data, unsigned int size, const char *fname,
1212ba95c745SQuentin Monnet 		 const char *json_key)
1213ba95c745SQuentin Monnet {
1214ba95c745SQuentin Monnet 	size_t nb_written;
1215ba95c745SQuentin Monnet 	FILE *f;
1216ba95c745SQuentin Monnet 
1217ba95c745SQuentin Monnet 	if (!fname)
1218ba95c745SQuentin Monnet 		return 0;
1219ba95c745SQuentin Monnet 
1220ba95c745SQuentin Monnet 	if (!strcmp(fname, "-")) {
1221ba95c745SQuentin Monnet 		f = stdout;
1222ba95c745SQuentin Monnet 		if (json_output) {
1223ba95c745SQuentin Monnet 			jsonw_name(json_wtr, json_key);
1224ba95c745SQuentin Monnet 			print_data_json(data, size);
1225ba95c745SQuentin Monnet 		} else {
1226ba95c745SQuentin Monnet 			hex_print(data, size, f);
1227ba95c745SQuentin Monnet 		}
1228ba95c745SQuentin Monnet 		return 0;
1229ba95c745SQuentin Monnet 	}
1230ba95c745SQuentin Monnet 
1231ba95c745SQuentin Monnet 	f = fopen(fname, "w");
1232ba95c745SQuentin Monnet 	if (!f) {
1233ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1234ba95c745SQuentin Monnet 		return -1;
1235ba95c745SQuentin Monnet 	}
1236ba95c745SQuentin Monnet 
1237ba95c745SQuentin Monnet 	nb_written = fwrite(data, 1, size, f);
1238ba95c745SQuentin Monnet 	fclose(f);
1239ba95c745SQuentin Monnet 	if (nb_written != size) {
1240ba95c745SQuentin Monnet 		p_err("failed to write output data/ctx: %s", strerror(errno));
1241ba95c745SQuentin Monnet 		return -1;
1242ba95c745SQuentin Monnet 	}
1243ba95c745SQuentin Monnet 
1244ba95c745SQuentin Monnet 	return 0;
1245ba95c745SQuentin Monnet }
1246ba95c745SQuentin Monnet 
1247ba95c745SQuentin Monnet static int alloc_run_data(void **data_ptr, unsigned int size_out)
1248ba95c745SQuentin Monnet {
1249ba95c745SQuentin Monnet 	*data_ptr = calloc(size_out, 1);
1250ba95c745SQuentin Monnet 	if (!*data_ptr) {
1251ba95c745SQuentin Monnet 		p_err("failed to allocate memory for output data/ctx: %s",
1252ba95c745SQuentin Monnet 		      strerror(errno));
1253ba95c745SQuentin Monnet 		return -1;
1254ba95c745SQuentin Monnet 	}
1255ba95c745SQuentin Monnet 
1256ba95c745SQuentin Monnet 	return 0;
1257ba95c745SQuentin Monnet }
1258ba95c745SQuentin Monnet 
1259ba95c745SQuentin Monnet static int do_run(int argc, char **argv)
1260ba95c745SQuentin Monnet {
1261ba95c745SQuentin Monnet 	char *data_fname_in = NULL, *data_fname_out = NULL;
1262ba95c745SQuentin Monnet 	char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
1263ba95c745SQuentin Monnet 	const unsigned int default_size = SZ_32K;
1264ba95c745SQuentin Monnet 	void *data_in = NULL, *data_out = NULL;
1265ba95c745SQuentin Monnet 	void *ctx_in = NULL, *ctx_out = NULL;
1266ba95c745SQuentin Monnet 	unsigned int repeat = 1;
1267ba95c745SQuentin Monnet 	int fd, err;
12689cce5313SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, test_attr);
1269ba95c745SQuentin Monnet 
1270ba95c745SQuentin Monnet 	if (!REQ_ARGS(4))
1271ba95c745SQuentin Monnet 		return -1;
1272ba95c745SQuentin Monnet 
1273ba95c745SQuentin Monnet 	fd = prog_parse_fd(&argc, &argv);
1274ba95c745SQuentin Monnet 	if (fd < 0)
1275ba95c745SQuentin Monnet 		return -1;
1276ba95c745SQuentin Monnet 
1277ba95c745SQuentin Monnet 	while (argc) {
1278ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "data_in", "data_out",
1279ba95c745SQuentin Monnet 					 "data_size_out", NULL))
1280ba95c745SQuentin Monnet 			return -1;
1281ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "ctx_in", "ctx_out",
1282ba95c745SQuentin Monnet 					 "ctx_size_out", NULL))
1283ba95c745SQuentin Monnet 			return -1;
1284ba95c745SQuentin Monnet 
1285ba95c745SQuentin Monnet 		if (is_prefix(*argv, "data_in")) {
1286ba95c745SQuentin Monnet 			NEXT_ARG();
1287ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1288ba95c745SQuentin Monnet 				return -1;
1289ba95c745SQuentin Monnet 
1290ba95c745SQuentin Monnet 			data_fname_in = GET_ARG();
1291ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1292ba95c745SQuentin Monnet 				return -1;
1293ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_out")) {
1294ba95c745SQuentin Monnet 			NEXT_ARG();
1295ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1296ba95c745SQuentin Monnet 				return -1;
1297ba95c745SQuentin Monnet 
1298ba95c745SQuentin Monnet 			data_fname_out = GET_ARG();
1299ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_size_out")) {
1300ba95c745SQuentin Monnet 			char *endptr;
1301ba95c745SQuentin Monnet 
1302ba95c745SQuentin Monnet 			NEXT_ARG();
1303ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1304ba95c745SQuentin Monnet 				return -1;
1305ba95c745SQuentin Monnet 
1306ba95c745SQuentin Monnet 			test_attr.data_size_out = strtoul(*argv, &endptr, 0);
1307ba95c745SQuentin Monnet 			if (*endptr) {
1308ba95c745SQuentin Monnet 				p_err("can't parse %s as output data size",
1309ba95c745SQuentin Monnet 				      *argv);
1310ba95c745SQuentin Monnet 				return -1;
1311ba95c745SQuentin Monnet 			}
1312ba95c745SQuentin Monnet 			NEXT_ARG();
1313ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_in")) {
1314ba95c745SQuentin Monnet 			NEXT_ARG();
1315ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1316ba95c745SQuentin Monnet 				return -1;
1317ba95c745SQuentin Monnet 
1318ba95c745SQuentin Monnet 			ctx_fname_in = GET_ARG();
1319ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1320ba95c745SQuentin Monnet 				return -1;
1321ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_out")) {
1322ba95c745SQuentin Monnet 			NEXT_ARG();
1323ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1324ba95c745SQuentin Monnet 				return -1;
1325ba95c745SQuentin Monnet 
1326ba95c745SQuentin Monnet 			ctx_fname_out = GET_ARG();
1327ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_size_out")) {
1328ba95c745SQuentin Monnet 			char *endptr;
1329ba95c745SQuentin Monnet 
1330ba95c745SQuentin Monnet 			NEXT_ARG();
1331ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1332ba95c745SQuentin Monnet 				return -1;
1333ba95c745SQuentin Monnet 
1334ba95c745SQuentin Monnet 			test_attr.ctx_size_out = strtoul(*argv, &endptr, 0);
1335ba95c745SQuentin Monnet 			if (*endptr) {
1336ba95c745SQuentin Monnet 				p_err("can't parse %s as output context size",
1337ba95c745SQuentin Monnet 				      *argv);
1338ba95c745SQuentin Monnet 				return -1;
1339ba95c745SQuentin Monnet 			}
1340ba95c745SQuentin Monnet 			NEXT_ARG();
1341ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "repeat")) {
1342ba95c745SQuentin Monnet 			char *endptr;
1343ba95c745SQuentin Monnet 
1344ba95c745SQuentin Monnet 			NEXT_ARG();
1345ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1346ba95c745SQuentin Monnet 				return -1;
1347ba95c745SQuentin Monnet 
1348ba95c745SQuentin Monnet 			repeat = strtoul(*argv, &endptr, 0);
1349ba95c745SQuentin Monnet 			if (*endptr) {
1350ba95c745SQuentin Monnet 				p_err("can't parse %s as repeat number",
1351ba95c745SQuentin Monnet 				      *argv);
1352ba95c745SQuentin Monnet 				return -1;
1353ba95c745SQuentin Monnet 			}
1354ba95c745SQuentin Monnet 			NEXT_ARG();
1355ba95c745SQuentin Monnet 		} else {
1356ba95c745SQuentin 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'?",
1357ba95c745SQuentin Monnet 			      *argv);
1358ba95c745SQuentin Monnet 			return -1;
1359ba95c745SQuentin Monnet 		}
1360ba95c745SQuentin Monnet 	}
1361ba95c745SQuentin Monnet 
1362ba95c745SQuentin Monnet 	err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in);
1363ba95c745SQuentin Monnet 	if (err)
1364ba95c745SQuentin Monnet 		return -1;
1365ba95c745SQuentin Monnet 
1366ba95c745SQuentin Monnet 	if (data_in) {
1367ba95c745SQuentin Monnet 		if (!test_attr.data_size_out)
1368ba95c745SQuentin Monnet 			test_attr.data_size_out = default_size;
1369ba95c745SQuentin Monnet 		err = alloc_run_data(&data_out, test_attr.data_size_out);
1370ba95c745SQuentin Monnet 		if (err)
1371ba95c745SQuentin Monnet 			goto free_data_in;
1372ba95c745SQuentin Monnet 	}
1373ba95c745SQuentin Monnet 
1374ba95c745SQuentin Monnet 	err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in);
1375ba95c745SQuentin Monnet 	if (err)
1376ba95c745SQuentin Monnet 		goto free_data_out;
1377ba95c745SQuentin Monnet 
1378ba95c745SQuentin Monnet 	if (ctx_in) {
1379ba95c745SQuentin Monnet 		if (!test_attr.ctx_size_out)
1380ba95c745SQuentin Monnet 			test_attr.ctx_size_out = default_size;
1381ba95c745SQuentin Monnet 		err = alloc_run_data(&ctx_out, test_attr.ctx_size_out);
1382ba95c745SQuentin Monnet 		if (err)
1383ba95c745SQuentin Monnet 			goto free_ctx_in;
1384ba95c745SQuentin Monnet 	}
1385ba95c745SQuentin Monnet 
1386ba95c745SQuentin Monnet 	test_attr.repeat	= repeat;
1387ba95c745SQuentin Monnet 	test_attr.data_in	= data_in;
1388ba95c745SQuentin Monnet 	test_attr.data_out	= data_out;
1389ba95c745SQuentin Monnet 	test_attr.ctx_in	= ctx_in;
1390ba95c745SQuentin Monnet 	test_attr.ctx_out	= ctx_out;
1391ba95c745SQuentin Monnet 
13929cce5313SDelyan Kratunov 	err = bpf_prog_test_run_opts(fd, &test_attr);
1393ba95c745SQuentin Monnet 	if (err) {
1394ba95c745SQuentin Monnet 		p_err("failed to run program: %s", strerror(errno));
1395ba95c745SQuentin Monnet 		goto free_ctx_out;
1396ba95c745SQuentin Monnet 	}
1397ba95c745SQuentin Monnet 
1398ba95c745SQuentin Monnet 	err = 0;
1399ba95c745SQuentin Monnet 
1400ba95c745SQuentin Monnet 	if (json_output)
1401ba95c745SQuentin Monnet 		jsonw_start_object(json_wtr);	/* root */
1402ba95c745SQuentin Monnet 
1403ba95c745SQuentin Monnet 	/* Do not exit on errors occurring when printing output data/context,
1404ba95c745SQuentin Monnet 	 * we still want to print return value and duration for program run.
1405ba95c745SQuentin Monnet 	 */
1406ba95c745SQuentin Monnet 	if (test_attr.data_size_out)
1407ba95c745SQuentin Monnet 		err += print_run_output(test_attr.data_out,
1408ba95c745SQuentin Monnet 					test_attr.data_size_out,
1409ba95c745SQuentin Monnet 					data_fname_out, "data_out");
1410ba95c745SQuentin Monnet 	if (test_attr.ctx_size_out)
1411ba95c745SQuentin Monnet 		err += print_run_output(test_attr.ctx_out,
1412ba95c745SQuentin Monnet 					test_attr.ctx_size_out,
1413ba95c745SQuentin Monnet 					ctx_fname_out, "ctx_out");
1414ba95c745SQuentin Monnet 
1415ba95c745SQuentin Monnet 	if (json_output) {
1416ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "retval", test_attr.retval);
1417ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "duration", test_attr.duration);
1418ba95c745SQuentin Monnet 		jsonw_end_object(json_wtr);	/* root */
1419ba95c745SQuentin Monnet 	} else {
1420ba95c745SQuentin Monnet 		fprintf(stdout, "Return value: %u, duration%s: %uns\n",
1421ba95c745SQuentin Monnet 			test_attr.retval,
1422ba95c745SQuentin Monnet 			repeat > 1 ? " (average)" : "", test_attr.duration);
1423ba95c745SQuentin Monnet 	}
1424ba95c745SQuentin Monnet 
1425ba95c745SQuentin Monnet free_ctx_out:
1426ba95c745SQuentin Monnet 	free(ctx_out);
1427ba95c745SQuentin Monnet free_ctx_in:
1428ba95c745SQuentin Monnet 	free(ctx_in);
1429ba95c745SQuentin Monnet free_data_out:
1430ba95c745SQuentin Monnet 	free(data_out);
1431ba95c745SQuentin Monnet free_data_in:
1432ba95c745SQuentin Monnet 	free(data_in);
1433ba95c745SQuentin Monnet 
1434ba95c745SQuentin Monnet 	return err;
1435ba95c745SQuentin Monnet }
1436ba95c745SQuentin Monnet 
14376ae32b29SQuentin Monnet static int
14386ae32b29SQuentin Monnet get_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
14396ae32b29SQuentin Monnet 		      enum bpf_attach_type *expected_attach_type)
14406ae32b29SQuentin Monnet {
14416ae32b29SQuentin Monnet 	libbpf_print_fn_t print_backup;
14426ae32b29SQuentin Monnet 	int ret;
14436ae32b29SQuentin Monnet 
14446ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14456ae32b29SQuentin Monnet 	if (!ret)
14466ae32b29SQuentin Monnet 		return ret;
14476ae32b29SQuentin Monnet 
14486ae32b29SQuentin Monnet 	/* libbpf_prog_type_by_name() failed, let's re-run with debug level */
14496ae32b29SQuentin Monnet 	print_backup = libbpf_set_print(print_all_levels);
14506ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14516ae32b29SQuentin Monnet 	libbpf_set_print(print_backup);
14526ae32b29SQuentin Monnet 
14536ae32b29SQuentin Monnet 	return ret;
14546ae32b29SQuentin Monnet }
14556ae32b29SQuentin Monnet 
1456*19526e70SWang Yufen static int
1457*19526e70SWang Yufen auto_attach_program(struct bpf_program *prog, const char *path)
1458*19526e70SWang Yufen {
1459*19526e70SWang Yufen 	struct bpf_link *link;
1460*19526e70SWang Yufen 	int err;
1461*19526e70SWang Yufen 
1462*19526e70SWang Yufen 	link = bpf_program__attach(prog);
1463*19526e70SWang Yufen 	if (!link) {
1464*19526e70SWang Yufen 		p_info("Program %s does not support autoattach, falling back to pinning",
1465*19526e70SWang Yufen 		       bpf_program__name(prog));
1466*19526e70SWang Yufen 		return bpf_obj_pin(bpf_program__fd(prog), path);
1467*19526e70SWang Yufen 	}
1468*19526e70SWang Yufen 
1469*19526e70SWang Yufen 	err = bpf_link__pin(link, path);
1470*19526e70SWang Yufen 	bpf_link__destroy(link);
1471*19526e70SWang Yufen 	return err;
1472*19526e70SWang Yufen }
1473*19526e70SWang Yufen 
1474*19526e70SWang Yufen static int pathname_concat(char *buf, size_t buf_sz, const char *path, const char *name)
1475*19526e70SWang Yufen {
1476*19526e70SWang Yufen 	int len;
1477*19526e70SWang Yufen 
1478*19526e70SWang Yufen 	len = snprintf(buf, buf_sz, "%s/%s", path, name);
1479*19526e70SWang Yufen 	if (len < 0)
1480*19526e70SWang Yufen 		return -EINVAL;
1481*19526e70SWang Yufen 	if ((size_t)len >= buf_sz)
1482*19526e70SWang Yufen 		return -ENAMETOOLONG;
1483*19526e70SWang Yufen 
1484*19526e70SWang Yufen 	return 0;
1485*19526e70SWang Yufen }
1486*19526e70SWang Yufen 
1487*19526e70SWang Yufen static int
1488*19526e70SWang Yufen auto_attach_programs(struct bpf_object *obj, const char *path)
1489*19526e70SWang Yufen {
1490*19526e70SWang Yufen 	struct bpf_program *prog;
1491*19526e70SWang Yufen 	char buf[PATH_MAX];
1492*19526e70SWang Yufen 	int err;
1493*19526e70SWang Yufen 
1494*19526e70SWang Yufen 	bpf_object__for_each_program(prog, obj) {
1495*19526e70SWang Yufen 		err = pathname_concat(buf, sizeof(buf), path, bpf_program__name(prog));
1496*19526e70SWang Yufen 		if (err)
1497*19526e70SWang Yufen 			goto err_unpin_programs;
1498*19526e70SWang Yufen 
1499*19526e70SWang Yufen 		err = auto_attach_program(prog, buf);
1500*19526e70SWang Yufen 		if (err)
1501*19526e70SWang Yufen 			goto err_unpin_programs;
1502*19526e70SWang Yufen 	}
1503*19526e70SWang Yufen 
1504*19526e70SWang Yufen 	return 0;
1505*19526e70SWang Yufen 
1506*19526e70SWang Yufen err_unpin_programs:
1507*19526e70SWang Yufen 	while ((prog = bpf_object__prev_program(obj, prog))) {
1508*19526e70SWang Yufen 		if (pathname_concat(buf, sizeof(buf), path, bpf_program__name(prog)))
1509*19526e70SWang Yufen 			continue;
1510*19526e70SWang Yufen 
1511*19526e70SWang Yufen 		bpf_program__unpin(prog, buf);
1512*19526e70SWang Yufen 	}
1513*19526e70SWang Yufen 
1514*19526e70SWang Yufen 	return err;
1515*19526e70SWang Yufen }
1516*19526e70SWang Yufen 
151777380998SStanislav Fomichev static int load_with_options(int argc, char **argv, bool first_prog_only)
151849a086c2SRoman Gushchin {
151932e3e58eSAndrii Nakryiko 	enum bpf_prog_type common_prog_type = BPF_PROG_TYPE_UNSPEC;
1520e00aca65SAndrii Nakryiko 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
1521e00aca65SAndrii Nakryiko 		.relaxed_maps = relaxed_maps,
1522e00aca65SAndrii Nakryiko 	);
152355d77807SQuentin Monnet 	enum bpf_attach_type expected_attach_type;
15243ff5a4dcSJakub Kicinski 	struct map_replace *map_replace = NULL;
152577380998SStanislav Fomichev 	struct bpf_program *prog = NULL, *pos;
15263ff5a4dcSJakub Kicinski 	unsigned int old_map_fds = 0;
15273767a94bSStanislav Fomichev 	const char *pinmaps = NULL;
1528*19526e70SWang Yufen 	bool auto_attach = false;
152949a086c2SRoman Gushchin 	struct bpf_object *obj;
1530c8406848SJakub Kicinski 	struct bpf_map *map;
1531c8406848SJakub Kicinski 	const char *pinfile;
15323ff5a4dcSJakub Kicinski 	unsigned int i, j;
1533c8406848SJakub Kicinski 	__u32 ifindex = 0;
153432e3e58eSAndrii Nakryiko 	const char *file;
15353ff5a4dcSJakub Kicinski 	int idx, err;
153649a086c2SRoman Gushchin 
153732e3e58eSAndrii Nakryiko 
15388d1fc3deSJakub Kicinski 	if (!REQ_ARGS(2))
15398d1fc3deSJakub Kicinski 		return -1;
154032e3e58eSAndrii Nakryiko 	file = GET_ARG();
15418d1fc3deSJakub Kicinski 	pinfile = GET_ARG();
154249a086c2SRoman Gushchin 
1543ba6dd679SJakub Kicinski 	while (argc) {
154449f2cba3SJakub Kicinski 		if (is_prefix(*argv, "type")) {
154549f2cba3SJakub Kicinski 			NEXT_ARG();
154649f2cba3SJakub Kicinski 
154732e3e58eSAndrii Nakryiko 			if (common_prog_type != BPF_PROG_TYPE_UNSPEC) {
154849f2cba3SJakub Kicinski 				p_err("program type already specified");
15493ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
155049f2cba3SJakub Kicinski 			}
155149f2cba3SJakub Kicinski 			if (!REQ_ARGS(1))
15523ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
155349f2cba3SJakub Kicinski 
1554314f14abSStanislav Fomichev 			err = libbpf_prog_type_by_name(*argv, &common_prog_type,
1555314f14abSStanislav Fomichev 						       &expected_attach_type);
1556314f14abSStanislav Fomichev 			if (err < 0) {
155749f2cba3SJakub Kicinski 				/* Put a '/' at the end of type to appease libbpf */
1558314f14abSStanislav Fomichev 				char *type = malloc(strlen(*argv) + 2);
1559314f14abSStanislav Fomichev 
156049f2cba3SJakub Kicinski 				if (!type) {
156149f2cba3SJakub Kicinski 					p_err("mem alloc failed");
15623ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
156349f2cba3SJakub Kicinski 				}
156449f2cba3SJakub Kicinski 				*type = 0;
156549f2cba3SJakub Kicinski 				strcat(type, *argv);
156649f2cba3SJakub Kicinski 				strcat(type, "/");
156749f2cba3SJakub Kicinski 
15686ae32b29SQuentin Monnet 				err = get_prog_type_by_name(type, &common_prog_type,
1569c8406848SJakub Kicinski 							    &expected_attach_type);
157049f2cba3SJakub Kicinski 				free(type);
1571c76e4c22STaeung Song 				if (err < 0)
15723ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
1573314f14abSStanislav Fomichev 			}
1574c76e4c22STaeung Song 
157549f2cba3SJakub Kicinski 			NEXT_ARG();
15763ff5a4dcSJakub Kicinski 		} else if (is_prefix(*argv, "map")) {
1577dde7011aSJakub Kicinski 			void *new_map_replace;
15783ff5a4dcSJakub Kicinski 			char *endptr, *name;
15793ff5a4dcSJakub Kicinski 			int fd;
15803ff5a4dcSJakub Kicinski 
15813ff5a4dcSJakub Kicinski 			NEXT_ARG();
15823ff5a4dcSJakub Kicinski 
15833ff5a4dcSJakub Kicinski 			if (!REQ_ARGS(4))
15843ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15853ff5a4dcSJakub Kicinski 
15863ff5a4dcSJakub Kicinski 			if (is_prefix(*argv, "idx")) {
15873ff5a4dcSJakub Kicinski 				NEXT_ARG();
15883ff5a4dcSJakub Kicinski 
15893ff5a4dcSJakub Kicinski 				idx = strtoul(*argv, &endptr, 0);
15903ff5a4dcSJakub Kicinski 				if (*endptr) {
15913ff5a4dcSJakub Kicinski 					p_err("can't parse %s as IDX", *argv);
15923ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
15933ff5a4dcSJakub Kicinski 				}
15943ff5a4dcSJakub Kicinski 				name = NULL;
15953ff5a4dcSJakub Kicinski 			} else if (is_prefix(*argv, "name")) {
15963ff5a4dcSJakub Kicinski 				NEXT_ARG();
15973ff5a4dcSJakub Kicinski 
15983ff5a4dcSJakub Kicinski 				name = *argv;
15993ff5a4dcSJakub Kicinski 				idx = -1;
16003ff5a4dcSJakub Kicinski 			} else {
16013ff5a4dcSJakub Kicinski 				p_err("expected 'idx' or 'name', got: '%s'?",
16023ff5a4dcSJakub Kicinski 				      *argv);
16033ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
16043ff5a4dcSJakub Kicinski 			}
16053ff5a4dcSJakub Kicinski 			NEXT_ARG();
16063ff5a4dcSJakub Kicinski 
16073ff5a4dcSJakub Kicinski 			fd = map_parse_fd(&argc, &argv);
16083ff5a4dcSJakub Kicinski 			if (fd < 0)
16093ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
16103ff5a4dcSJakub Kicinski 
1611a19df713SMauricio Vásquez 			new_map_replace = libbpf_reallocarray(map_replace,
1612dde7011aSJakub Kicinski 							      old_map_fds + 1,
16133ff5a4dcSJakub Kicinski 							      sizeof(*map_replace));
1614dde7011aSJakub Kicinski 			if (!new_map_replace) {
16153ff5a4dcSJakub Kicinski 				p_err("mem alloc failed");
16163ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
16173ff5a4dcSJakub Kicinski 			}
1618dde7011aSJakub Kicinski 			map_replace = new_map_replace;
1619dde7011aSJakub Kicinski 
16203ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].idx = idx;
16213ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].name = name;
16223ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].fd = fd;
16233ff5a4dcSJakub Kicinski 			old_map_fds++;
162449f2cba3SJakub Kicinski 		} else if (is_prefix(*argv, "dev")) {
1625ba6dd679SJakub Kicinski 			NEXT_ARG();
1626ba6dd679SJakub Kicinski 
1627c8406848SJakub Kicinski 			if (ifindex) {
1628ba6dd679SJakub Kicinski 				p_err("offload device already specified");
16293ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1630ba6dd679SJakub Kicinski 			}
1631ba6dd679SJakub Kicinski 			if (!REQ_ARGS(1))
16323ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1633ba6dd679SJakub Kicinski 
1634c8406848SJakub Kicinski 			ifindex = if_nametoindex(*argv);
1635c8406848SJakub Kicinski 			if (!ifindex) {
1636ba6dd679SJakub Kicinski 				p_err("unrecognized netdevice '%s': %s",
1637ba6dd679SJakub Kicinski 				      *argv, strerror(errno));
16383ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1639ba6dd679SJakub Kicinski 			}
1640ba6dd679SJakub Kicinski 			NEXT_ARG();
16413767a94bSStanislav Fomichev 		} else if (is_prefix(*argv, "pinmaps")) {
16423767a94bSStanislav Fomichev 			NEXT_ARG();
16433767a94bSStanislav Fomichev 
16443767a94bSStanislav Fomichev 			if (!REQ_ARGS(1))
16453767a94bSStanislav Fomichev 				goto err_free_reuse_maps;
16463767a94bSStanislav Fomichev 
16473767a94bSStanislav Fomichev 			pinmaps = GET_ARG();
1648*19526e70SWang Yufen 		} else if (is_prefix(*argv, "autoattach")) {
1649*19526e70SWang Yufen 			auto_attach = true;
1650*19526e70SWang Yufen 			NEXT_ARG();
1651ba6dd679SJakub Kicinski 		} else {
16523ff5a4dcSJakub Kicinski 			p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
1653ba6dd679SJakub Kicinski 			      *argv);
16543ff5a4dcSJakub Kicinski 			goto err_free_reuse_maps;
1655ba6dd679SJakub Kicinski 		}
1656ba6dd679SJakub Kicinski 	}
1657ba6dd679SJakub Kicinski 
16586b4384ffSQuentin Monnet 	set_max_rlimit();
16596b4384ffSQuentin Monnet 
1660b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1661b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1662b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1663b59e4ce8SAndrii Nakryiko 
166432e3e58eSAndrii Nakryiko 	obj = bpf_object__open_file(file, &open_opts);
1665d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1666c8406848SJakub Kicinski 		p_err("failed to open object file");
16673ff5a4dcSJakub Kicinski 		goto err_free_reuse_maps;
166849a086c2SRoman Gushchin 	}
166949a086c2SRoman Gushchin 
167077380998SStanislav Fomichev 	bpf_object__for_each_program(pos, obj) {
167132e3e58eSAndrii Nakryiko 		enum bpf_prog_type prog_type = common_prog_type;
1672c8406848SJakub Kicinski 
167332e3e58eSAndrii Nakryiko 		if (prog_type == BPF_PROG_TYPE_UNSPEC) {
1674fd17e272SAndrii Nakryiko 			const char *sec_name = bpf_program__section_name(pos);
1675c8406848SJakub Kicinski 
16766ae32b29SQuentin Monnet 			err = get_prog_type_by_name(sec_name, &prog_type,
1677c8406848SJakub Kicinski 						    &expected_attach_type);
1678c76e4c22STaeung Song 			if (err < 0)
1679c8406848SJakub Kicinski 				goto err_close_obj;
1680c8406848SJakub Kicinski 		}
168177380998SStanislav Fomichev 
168277380998SStanislav Fomichev 		bpf_program__set_ifindex(pos, ifindex);
168377380998SStanislav Fomichev 		bpf_program__set_type(pos, prog_type);
168477380998SStanislav Fomichev 		bpf_program__set_expected_attach_type(pos, expected_attach_type);
168577380998SStanislav Fomichev 	}
1686c8406848SJakub Kicinski 
16873ff5a4dcSJakub Kicinski 	qsort(map_replace, old_map_fds, sizeof(*map_replace),
16883ff5a4dcSJakub Kicinski 	      map_replace_compar);
16893ff5a4dcSJakub Kicinski 
16903ff5a4dcSJakub Kicinski 	/* After the sort maps by name will be first on the list, because they
16913ff5a4dcSJakub Kicinski 	 * have idx == -1.  Resolve them.
16923ff5a4dcSJakub Kicinski 	 */
16933ff5a4dcSJakub Kicinski 	j = 0;
16943ff5a4dcSJakub Kicinski 	while (j < old_map_fds && map_replace[j].name) {
16953ff5a4dcSJakub Kicinski 		i = 0;
1696f74a53d9SJakub Kicinski 		bpf_object__for_each_map(map, obj) {
16973ff5a4dcSJakub Kicinski 			if (!strcmp(bpf_map__name(map), map_replace[j].name)) {
16983ff5a4dcSJakub Kicinski 				map_replace[j].idx = i;
16993ff5a4dcSJakub Kicinski 				break;
17003ff5a4dcSJakub Kicinski 			}
17013ff5a4dcSJakub Kicinski 			i++;
17023ff5a4dcSJakub Kicinski 		}
17033ff5a4dcSJakub Kicinski 		if (map_replace[j].idx == -1) {
17043ff5a4dcSJakub Kicinski 			p_err("unable to find map '%s'", map_replace[j].name);
17053ff5a4dcSJakub Kicinski 			goto err_close_obj;
17063ff5a4dcSJakub Kicinski 		}
17073ff5a4dcSJakub Kicinski 		j++;
17083ff5a4dcSJakub Kicinski 	}
17093ff5a4dcSJakub Kicinski 	/* Resort if any names were resolved */
17103ff5a4dcSJakub Kicinski 	if (j)
17113ff5a4dcSJakub Kicinski 		qsort(map_replace, old_map_fds, sizeof(*map_replace),
17123ff5a4dcSJakub Kicinski 		      map_replace_compar);
17133ff5a4dcSJakub Kicinski 
17143ff5a4dcSJakub Kicinski 	/* Set ifindex and name reuse */
17153ff5a4dcSJakub Kicinski 	j = 0;
17163ff5a4dcSJakub Kicinski 	idx = 0;
1717f74a53d9SJakub Kicinski 	bpf_object__for_each_map(map, obj) {
17189855c131SChristy Lee 		if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
1719c8406848SJakub Kicinski 			bpf_map__set_ifindex(map, ifindex);
1720c8406848SJakub Kicinski 
17213ff5a4dcSJakub Kicinski 		if (j < old_map_fds && idx == map_replace[j].idx) {
17223ff5a4dcSJakub Kicinski 			err = bpf_map__reuse_fd(map, map_replace[j++].fd);
17233ff5a4dcSJakub Kicinski 			if (err) {
17243ff5a4dcSJakub Kicinski 				p_err("unable to set up map reuse: %d", err);
17253ff5a4dcSJakub Kicinski 				goto err_close_obj;
17263ff5a4dcSJakub Kicinski 			}
17273ff5a4dcSJakub Kicinski 
17283ff5a4dcSJakub Kicinski 			/* Next reuse wants to apply to the same map */
17293ff5a4dcSJakub Kicinski 			if (j < old_map_fds && map_replace[j].idx == idx) {
17303ff5a4dcSJakub Kicinski 				p_err("replacement for map idx %d specified more than once",
17313ff5a4dcSJakub Kicinski 				      idx);
17323ff5a4dcSJakub Kicinski 				goto err_close_obj;
17333ff5a4dcSJakub Kicinski 			}
17343ff5a4dcSJakub Kicinski 		}
17353ff5a4dcSJakub Kicinski 
17363ff5a4dcSJakub Kicinski 		idx++;
17373ff5a4dcSJakub Kicinski 	}
17383ff5a4dcSJakub Kicinski 	if (j < old_map_fds) {
17393ff5a4dcSJakub Kicinski 		p_err("map idx '%d' not used", map_replace[j].idx);
17403ff5a4dcSJakub Kicinski 		goto err_close_obj;
17413ff5a4dcSJakub Kicinski 	}
17423ff5a4dcSJakub Kicinski 
1743b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1744c8406848SJakub Kicinski 	if (err) {
1745c8406848SJakub Kicinski 		p_err("failed to load object file");
1746c8406848SJakub Kicinski 		goto err_close_obj;
1747c8406848SJakub Kicinski 	}
1748c8406848SJakub Kicinski 
174977380998SStanislav Fomichev 	err = mount_bpffs_for_pin(pinfile);
175077380998SStanislav Fomichev 	if (err)
1751bfee71fbSJakub Kicinski 		goto err_close_obj;
175249a086c2SRoman Gushchin 
175377380998SStanislav Fomichev 	if (first_prog_only) {
17546f2b219bSHengqi Chen 		prog = bpf_object__next_program(obj, NULL);
175577380998SStanislav Fomichev 		if (!prog) {
175677380998SStanislav Fomichev 			p_err("object file doesn't contain any bpf program");
175777380998SStanislav Fomichev 			goto err_close_obj;
175877380998SStanislav Fomichev 		}
175977380998SStanislav Fomichev 
1760*19526e70SWang Yufen 		if (auto_attach)
1761*19526e70SWang Yufen 			err = auto_attach_program(prog, pinfile);
1762*19526e70SWang Yufen 		else
176377380998SStanislav Fomichev 			err = bpf_obj_pin(bpf_program__fd(prog), pinfile);
176477380998SStanislav Fomichev 		if (err) {
176577380998SStanislav Fomichev 			p_err("failed to pin program %s",
1766fd17e272SAndrii Nakryiko 			      bpf_program__section_name(prog));
176777380998SStanislav Fomichev 			goto err_close_obj;
176877380998SStanislav Fomichev 		}
176977380998SStanislav Fomichev 	} else {
1770*19526e70SWang Yufen 		if (auto_attach)
1771*19526e70SWang Yufen 			err = auto_attach_programs(obj, pinfile);
1772*19526e70SWang Yufen 		else
177377380998SStanislav Fomichev 			err = bpf_object__pin_programs(obj, pinfile);
177477380998SStanislav Fomichev 		if (err) {
177577380998SStanislav Fomichev 			p_err("failed to pin all programs");
177677380998SStanislav Fomichev 			goto err_close_obj;
177777380998SStanislav Fomichev 		}
177877380998SStanislav Fomichev 	}
177977380998SStanislav Fomichev 
17803767a94bSStanislav Fomichev 	if (pinmaps) {
17813767a94bSStanislav Fomichev 		err = bpf_object__pin_maps(obj, pinmaps);
17823767a94bSStanislav Fomichev 		if (err) {
17833767a94bSStanislav Fomichev 			p_err("failed to pin all maps");
17843767a94bSStanislav Fomichev 			goto err_unpin;
17853767a94bSStanislav Fomichev 		}
17863767a94bSStanislav Fomichev 	}
17873767a94bSStanislav Fomichev 
178849a086c2SRoman Gushchin 	if (json_output)
178949a086c2SRoman Gushchin 		jsonw_null(json_wtr);
179049a086c2SRoman Gushchin 
1791bfee71fbSJakub Kicinski 	bpf_object__close(obj);
17923ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
17933ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
17943ff5a4dcSJakub Kicinski 	free(map_replace);
1795bfee71fbSJakub Kicinski 
179649a086c2SRoman Gushchin 	return 0;
1797bfee71fbSJakub Kicinski 
17983767a94bSStanislav Fomichev err_unpin:
17993767a94bSStanislav Fomichev 	if (first_prog_only)
18003767a94bSStanislav Fomichev 		unlink(pinfile);
18013767a94bSStanislav Fomichev 	else
18023767a94bSStanislav Fomichev 		bpf_object__unpin_programs(obj, pinfile);
1803bfee71fbSJakub Kicinski err_close_obj:
1804314f14abSStanislav Fomichev 	if (!legacy_libbpf) {
1805314f14abSStanislav Fomichev 		p_info("Warning: bpftool is now running in libbpf strict mode and has more stringent requirements about BPF programs.\n"
1806314f14abSStanislav Fomichev 		       "If it used to work for this object file but now doesn't, see --legacy option for more details.\n");
1807314f14abSStanislav Fomichev 	}
1808314f14abSStanislav Fomichev 
1809bfee71fbSJakub Kicinski 	bpf_object__close(obj);
18103ff5a4dcSJakub Kicinski err_free_reuse_maps:
18113ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
18123ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
18133ff5a4dcSJakub Kicinski 	free(map_replace);
1814bfee71fbSJakub Kicinski 	return -1;
181549a086c2SRoman Gushchin }
181649a086c2SRoman Gushchin 
1817d510296dSAlexei Starovoitov static int count_open_fds(void)
1818d510296dSAlexei Starovoitov {
1819d510296dSAlexei Starovoitov 	DIR *dp = opendir("/proc/self/fd");
1820d510296dSAlexei Starovoitov 	struct dirent *de;
1821d510296dSAlexei Starovoitov 	int cnt = -3;
1822d510296dSAlexei Starovoitov 
1823d510296dSAlexei Starovoitov 	if (!dp)
1824d510296dSAlexei Starovoitov 		return -1;
1825d510296dSAlexei Starovoitov 
1826d510296dSAlexei Starovoitov 	while ((de = readdir(dp)))
1827d510296dSAlexei Starovoitov 		cnt++;
1828d510296dSAlexei Starovoitov 
1829d510296dSAlexei Starovoitov 	closedir(dp);
1830d510296dSAlexei Starovoitov 	return cnt;
1831d510296dSAlexei Starovoitov }
1832d510296dSAlexei Starovoitov 
1833d510296dSAlexei Starovoitov static int try_loader(struct gen_loader_opts *gen)
1834d510296dSAlexei Starovoitov {
1835d510296dSAlexei Starovoitov 	struct bpf_load_and_run_opts opts = {};
1836d510296dSAlexei Starovoitov 	struct bpf_loader_ctx *ctx;
1837d510296dSAlexei Starovoitov 	int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
1838d510296dSAlexei Starovoitov 					     sizeof(struct bpf_prog_desc));
1839d510296dSAlexei Starovoitov 	int log_buf_sz = (1u << 24) - 1;
1840d510296dSAlexei Starovoitov 	int err, fds_before, fd_delta;
1841942df4dcSAlexei Starovoitov 	char *log_buf = NULL;
1842d510296dSAlexei Starovoitov 
1843d510296dSAlexei Starovoitov 	ctx = alloca(ctx_sz);
1844d510296dSAlexei Starovoitov 	memset(ctx, 0, ctx_sz);
1845d510296dSAlexei Starovoitov 	ctx->sz = ctx_sz;
1846942df4dcSAlexei Starovoitov 	if (verifier_logs) {
1847942df4dcSAlexei Starovoitov 		ctx->log_level = 1 + 2 + 4;
1848d510296dSAlexei Starovoitov 		ctx->log_size = log_buf_sz;
1849d510296dSAlexei Starovoitov 		log_buf = malloc(log_buf_sz);
1850d510296dSAlexei Starovoitov 		if (!log_buf)
1851d510296dSAlexei Starovoitov 			return -ENOMEM;
1852d510296dSAlexei Starovoitov 		ctx->log_buf = (long) log_buf;
1853942df4dcSAlexei Starovoitov 	}
1854d510296dSAlexei Starovoitov 	opts.ctx = ctx;
1855d510296dSAlexei Starovoitov 	opts.data = gen->data;
1856d510296dSAlexei Starovoitov 	opts.data_sz = gen->data_sz;
1857d510296dSAlexei Starovoitov 	opts.insns = gen->insns;
1858d510296dSAlexei Starovoitov 	opts.insns_sz = gen->insns_sz;
1859d510296dSAlexei Starovoitov 	fds_before = count_open_fds();
1860d510296dSAlexei Starovoitov 	err = bpf_load_and_run(&opts);
1861d510296dSAlexei Starovoitov 	fd_delta = count_open_fds() - fds_before;
1862942df4dcSAlexei Starovoitov 	if (err < 0 || verifier_logs) {
1863d510296dSAlexei Starovoitov 		fprintf(stderr, "err %d\n%s\n%s", err, opts.errstr, log_buf);
1864942df4dcSAlexei Starovoitov 		if (fd_delta && err < 0)
1865d510296dSAlexei Starovoitov 			fprintf(stderr, "loader prog leaked %d FDs\n",
1866d510296dSAlexei Starovoitov 				fd_delta);
1867d510296dSAlexei Starovoitov 	}
1868d510296dSAlexei Starovoitov 	free(log_buf);
1869d510296dSAlexei Starovoitov 	return err;
1870d510296dSAlexei Starovoitov }
1871d510296dSAlexei Starovoitov 
1872d510296dSAlexei Starovoitov static int do_loader(int argc, char **argv)
1873d510296dSAlexei Starovoitov {
1874d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts);
1875d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(gen_loader_opts, gen);
1876d510296dSAlexei Starovoitov 	struct bpf_object *obj;
1877d510296dSAlexei Starovoitov 	const char *file;
1878d510296dSAlexei Starovoitov 	int err = 0;
1879d510296dSAlexei Starovoitov 
1880d510296dSAlexei Starovoitov 	if (!REQ_ARGS(1))
1881d510296dSAlexei Starovoitov 		return -1;
1882d510296dSAlexei Starovoitov 	file = GET_ARG();
1883d510296dSAlexei Starovoitov 
1884b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1885b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1886b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1887b59e4ce8SAndrii Nakryiko 
1888d510296dSAlexei Starovoitov 	obj = bpf_object__open_file(file, &open_opts);
1889d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1890d510296dSAlexei Starovoitov 		p_err("failed to open object file");
1891d510296dSAlexei Starovoitov 		goto err_close_obj;
1892d510296dSAlexei Starovoitov 	}
1893d510296dSAlexei Starovoitov 
1894d510296dSAlexei Starovoitov 	err = bpf_object__gen_loader(obj, &gen);
1895d510296dSAlexei Starovoitov 	if (err)
1896d510296dSAlexei Starovoitov 		goto err_close_obj;
1897d510296dSAlexei Starovoitov 
1898b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1899d510296dSAlexei Starovoitov 	if (err) {
1900d510296dSAlexei Starovoitov 		p_err("failed to load object file");
1901d510296dSAlexei Starovoitov 		goto err_close_obj;
1902d510296dSAlexei Starovoitov 	}
1903d510296dSAlexei Starovoitov 
1904d510296dSAlexei Starovoitov 	if (verifier_logs) {
1905d510296dSAlexei Starovoitov 		struct dump_data dd = {};
1906d510296dSAlexei Starovoitov 
1907d510296dSAlexei Starovoitov 		kernel_syms_load(&dd);
1908d510296dSAlexei Starovoitov 		dump_xlated_plain(&dd, (void *)gen.insns, gen.insns_sz, false, false);
1909d510296dSAlexei Starovoitov 		kernel_syms_destroy(&dd);
1910d510296dSAlexei Starovoitov 	}
1911d510296dSAlexei Starovoitov 	err = try_loader(&gen);
1912d510296dSAlexei Starovoitov err_close_obj:
1913d510296dSAlexei Starovoitov 	bpf_object__close(obj);
1914d510296dSAlexei Starovoitov 	return err;
1915d510296dSAlexei Starovoitov }
1916d510296dSAlexei Starovoitov 
191777380998SStanislav Fomichev static int do_load(int argc, char **argv)
191877380998SStanislav Fomichev {
1919d510296dSAlexei Starovoitov 	if (use_loader)
1920d510296dSAlexei Starovoitov 		return do_loader(argc, argv);
192177380998SStanislav Fomichev 	return load_with_options(argc, argv, true);
192277380998SStanislav Fomichev }
192377380998SStanislav Fomichev 
192477380998SStanislav Fomichev static int do_loadall(int argc, char **argv)
192577380998SStanislav Fomichev {
192677380998SStanislav Fomichev 	return load_with_options(argc, argv, false);
192777380998SStanislav Fomichev }
192877380998SStanislav Fomichev 
192947c09d6aSSong Liu #ifdef BPFTOOL_WITHOUT_SKELETONS
193047c09d6aSSong Liu 
193147c09d6aSSong Liu static int do_profile(int argc, char **argv)
193247c09d6aSSong Liu {
193314e5728fSSong Liu 	p_err("bpftool prog profile command is not supported. Please build bpftool with clang >= 10.0.0");
193447c09d6aSSong Liu 	return 0;
193547c09d6aSSong Liu }
193647c09d6aSSong Liu 
193747c09d6aSSong Liu #else /* BPFTOOL_WITHOUT_SKELETONS */
193847c09d6aSSong Liu 
193947c09d6aSSong Liu #include "profiler.skel.h"
194047c09d6aSSong Liu 
194147c09d6aSSong Liu struct profile_metric {
194247c09d6aSSong Liu 	const char *name;
194347c09d6aSSong Liu 	struct bpf_perf_event_value val;
194447c09d6aSSong Liu 	struct perf_event_attr attr;
194547c09d6aSSong Liu 	bool selected;
194647c09d6aSSong Liu 
194747c09d6aSSong Liu 	/* calculate ratios like instructions per cycle */
194847c09d6aSSong Liu 	const int ratio_metric; /* 0 for N/A, 1 for index 0 (cycles) */
194947c09d6aSSong Liu 	const char *ratio_desc;
195047c09d6aSSong Liu 	const float ratio_mul;
195147c09d6aSSong Liu } metrics[] = {
195247c09d6aSSong Liu 	{
195347c09d6aSSong Liu 		.name = "cycles",
195447c09d6aSSong Liu 		.attr = {
195547c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
195647c09d6aSSong Liu 			.config = PERF_COUNT_HW_CPU_CYCLES,
195747c09d6aSSong Liu 			.exclude_user = 1,
195847c09d6aSSong Liu 		},
195947c09d6aSSong Liu 	},
196047c09d6aSSong Liu 	{
196147c09d6aSSong Liu 		.name = "instructions",
196247c09d6aSSong Liu 		.attr = {
196347c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
196447c09d6aSSong Liu 			.config = PERF_COUNT_HW_INSTRUCTIONS,
196547c09d6aSSong Liu 			.exclude_user = 1,
196647c09d6aSSong Liu 		},
196747c09d6aSSong Liu 		.ratio_metric = 1,
196847c09d6aSSong Liu 		.ratio_desc = "insns per cycle",
196947c09d6aSSong Liu 		.ratio_mul = 1.0,
197047c09d6aSSong Liu 	},
197147c09d6aSSong Liu 	{
197247c09d6aSSong Liu 		.name = "l1d_loads",
197347c09d6aSSong Liu 		.attr = {
197447c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
197547c09d6aSSong Liu 			.config =
197647c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_L1D |
197747c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
197847c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
197947c09d6aSSong Liu 			.exclude_user = 1,
198047c09d6aSSong Liu 		},
198147c09d6aSSong Liu 	},
198247c09d6aSSong Liu 	{
198347c09d6aSSong Liu 		.name = "llc_misses",
198447c09d6aSSong Liu 		.attr = {
198547c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
198647c09d6aSSong Liu 			.config =
198747c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_LL |
198847c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
198947c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
199047c09d6aSSong Liu 			.exclude_user = 1
199147c09d6aSSong Liu 		},
199247c09d6aSSong Liu 		.ratio_metric = 2,
199347c09d6aSSong Liu 		.ratio_desc = "LLC misses per million insns",
199447c09d6aSSong Liu 		.ratio_mul = 1e6,
199547c09d6aSSong Liu 	},
1996450d060eSYonghong Song 	{
1997450d060eSYonghong Song 		.name = "itlb_misses",
1998450d060eSYonghong Song 		.attr = {
1999450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
2000450d060eSYonghong Song 			.config =
2001450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_ITLB |
2002450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
2003450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
2004450d060eSYonghong Song 			.exclude_user = 1
2005450d060eSYonghong Song 		},
2006450d060eSYonghong Song 		.ratio_metric = 2,
2007450d060eSYonghong Song 		.ratio_desc = "itlb misses per million insns",
2008450d060eSYonghong Song 		.ratio_mul = 1e6,
2009450d060eSYonghong Song 	},
2010450d060eSYonghong Song 	{
2011450d060eSYonghong Song 		.name = "dtlb_misses",
2012450d060eSYonghong Song 		.attr = {
2013450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
2014450d060eSYonghong Song 			.config =
2015450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_DTLB |
2016450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
2017450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
2018450d060eSYonghong Song 			.exclude_user = 1
2019450d060eSYonghong Song 		},
2020450d060eSYonghong Song 		.ratio_metric = 2,
2021450d060eSYonghong Song 		.ratio_desc = "dtlb misses per million insns",
2022450d060eSYonghong Song 		.ratio_mul = 1e6,
2023450d060eSYonghong Song 	},
202447c09d6aSSong Liu };
202547c09d6aSSong Liu 
202647c09d6aSSong Liu static __u64 profile_total_count;
202747c09d6aSSong Liu 
202847c09d6aSSong Liu #define MAX_NUM_PROFILE_METRICS 4
202947c09d6aSSong Liu 
203047c09d6aSSong Liu static int profile_parse_metrics(int argc, char **argv)
203147c09d6aSSong Liu {
203247c09d6aSSong Liu 	unsigned int metric_cnt;
203347c09d6aSSong Liu 	int selected_cnt = 0;
203447c09d6aSSong Liu 	unsigned int i;
203547c09d6aSSong Liu 
20365eff8c18SRongguang Wei 	metric_cnt = ARRAY_SIZE(metrics);
203747c09d6aSSong Liu 
203847c09d6aSSong Liu 	while (argc > 0) {
203947c09d6aSSong Liu 		for (i = 0; i < metric_cnt; i++) {
204047c09d6aSSong Liu 			if (is_prefix(argv[0], metrics[i].name)) {
204147c09d6aSSong Liu 				if (!metrics[i].selected)
204247c09d6aSSong Liu 					selected_cnt++;
204347c09d6aSSong Liu 				metrics[i].selected = true;
204447c09d6aSSong Liu 				break;
204547c09d6aSSong Liu 			}
204647c09d6aSSong Liu 		}
204747c09d6aSSong Liu 		if (i == metric_cnt) {
204847c09d6aSSong Liu 			p_err("unknown metric %s", argv[0]);
204947c09d6aSSong Liu 			return -1;
205047c09d6aSSong Liu 		}
205147c09d6aSSong Liu 		NEXT_ARG();
205247c09d6aSSong Liu 	}
205347c09d6aSSong Liu 	if (selected_cnt > MAX_NUM_PROFILE_METRICS) {
205447c09d6aSSong Liu 		p_err("too many (%d) metrics, please specify no more than %d metrics at at time",
205547c09d6aSSong Liu 		      selected_cnt, MAX_NUM_PROFILE_METRICS);
205647c09d6aSSong Liu 		return -1;
205747c09d6aSSong Liu 	}
205847c09d6aSSong Liu 	return selected_cnt;
205947c09d6aSSong Liu }
206047c09d6aSSong Liu 
206147c09d6aSSong Liu static void profile_read_values(struct profiler_bpf *obj)
206247c09d6aSSong Liu {
206347c09d6aSSong Liu 	__u32 m, cpu, num_cpu = obj->rodata->num_cpu;
206447c09d6aSSong Liu 	int reading_map_fd, count_map_fd;
206547c09d6aSSong Liu 	__u64 counts[num_cpu];
206647c09d6aSSong Liu 	__u32 key = 0;
206747c09d6aSSong Liu 	int err;
206847c09d6aSSong Liu 
206947c09d6aSSong Liu 	reading_map_fd = bpf_map__fd(obj->maps.accum_readings);
207047c09d6aSSong Liu 	count_map_fd = bpf_map__fd(obj->maps.counts);
207147c09d6aSSong Liu 	if (reading_map_fd < 0 || count_map_fd < 0) {
207247c09d6aSSong Liu 		p_err("failed to get fd for map");
207347c09d6aSSong Liu 		return;
207447c09d6aSSong Liu 	}
207547c09d6aSSong Liu 
207647c09d6aSSong Liu 	err = bpf_map_lookup_elem(count_map_fd, &key, counts);
207747c09d6aSSong Liu 	if (err) {
207847c09d6aSSong Liu 		p_err("failed to read count_map: %s", strerror(errno));
207947c09d6aSSong Liu 		return;
208047c09d6aSSong Liu 	}
208147c09d6aSSong Liu 
208247c09d6aSSong Liu 	profile_total_count = 0;
208347c09d6aSSong Liu 	for (cpu = 0; cpu < num_cpu; cpu++)
208447c09d6aSSong Liu 		profile_total_count += counts[cpu];
208547c09d6aSSong Liu 
208647c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
208747c09d6aSSong Liu 		struct bpf_perf_event_value values[num_cpu];
208847c09d6aSSong Liu 
208947c09d6aSSong Liu 		if (!metrics[m].selected)
209047c09d6aSSong Liu 			continue;
209147c09d6aSSong Liu 
209247c09d6aSSong Liu 		err = bpf_map_lookup_elem(reading_map_fd, &key, values);
209347c09d6aSSong Liu 		if (err) {
209447c09d6aSSong Liu 			p_err("failed to read reading_map: %s",
209547c09d6aSSong Liu 			      strerror(errno));
209647c09d6aSSong Liu 			return;
209747c09d6aSSong Liu 		}
209847c09d6aSSong Liu 		for (cpu = 0; cpu < num_cpu; cpu++) {
209947c09d6aSSong Liu 			metrics[m].val.counter += values[cpu].counter;
210047c09d6aSSong Liu 			metrics[m].val.enabled += values[cpu].enabled;
210147c09d6aSSong Liu 			metrics[m].val.running += values[cpu].running;
210247c09d6aSSong Liu 		}
210347c09d6aSSong Liu 		key++;
210447c09d6aSSong Liu 	}
210547c09d6aSSong Liu }
210647c09d6aSSong Liu 
210747c09d6aSSong Liu static void profile_print_readings_json(void)
210847c09d6aSSong Liu {
210947c09d6aSSong Liu 	__u32 m;
211047c09d6aSSong Liu 
211147c09d6aSSong Liu 	jsonw_start_array(json_wtr);
211247c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
211347c09d6aSSong Liu 		if (!metrics[m].selected)
211447c09d6aSSong Liu 			continue;
211547c09d6aSSong Liu 		jsonw_start_object(json_wtr);
211647c09d6aSSong Liu 		jsonw_string_field(json_wtr, "metric", metrics[m].name);
211747c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "run_cnt", profile_total_count);
211847c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "value", metrics[m].val.counter);
211947c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "enabled", metrics[m].val.enabled);
212047c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "running", metrics[m].val.running);
212147c09d6aSSong Liu 
212247c09d6aSSong Liu 		jsonw_end_object(json_wtr);
212347c09d6aSSong Liu 	}
212447c09d6aSSong Liu 	jsonw_end_array(json_wtr);
212547c09d6aSSong Liu }
212647c09d6aSSong Liu 
212747c09d6aSSong Liu static void profile_print_readings_plain(void)
212847c09d6aSSong Liu {
212947c09d6aSSong Liu 	__u32 m;
213047c09d6aSSong Liu 
213147c09d6aSSong Liu 	printf("\n%18llu %-20s\n", profile_total_count, "run_cnt");
213247c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
213347c09d6aSSong Liu 		struct bpf_perf_event_value *val = &metrics[m].val;
213447c09d6aSSong Liu 		int r;
213547c09d6aSSong Liu 
213647c09d6aSSong Liu 		if (!metrics[m].selected)
213747c09d6aSSong Liu 			continue;
213847c09d6aSSong Liu 		printf("%18llu %-20s", val->counter, metrics[m].name);
213947c09d6aSSong Liu 
214047c09d6aSSong Liu 		r = metrics[m].ratio_metric - 1;
214147c09d6aSSong Liu 		if (r >= 0 && metrics[r].selected &&
214247c09d6aSSong Liu 		    metrics[r].val.counter > 0) {
214347c09d6aSSong Liu 			printf("# %8.2f %-30s",
214447c09d6aSSong Liu 			       val->counter * metrics[m].ratio_mul /
214547c09d6aSSong Liu 			       metrics[r].val.counter,
214647c09d6aSSong Liu 			       metrics[m].ratio_desc);
214747c09d6aSSong Liu 		} else {
214847c09d6aSSong Liu 			printf("%-41s", "");
214947c09d6aSSong Liu 		}
215047c09d6aSSong Liu 
215147c09d6aSSong Liu 		if (val->enabled > val->running)
215247c09d6aSSong Liu 			printf("(%4.2f%%)",
215347c09d6aSSong Liu 			       val->running * 100.0 / val->enabled);
215447c09d6aSSong Liu 		printf("\n");
215547c09d6aSSong Liu 	}
215647c09d6aSSong Liu }
215747c09d6aSSong Liu 
215847c09d6aSSong Liu static void profile_print_readings(void)
215947c09d6aSSong Liu {
216047c09d6aSSong Liu 	if (json_output)
216147c09d6aSSong Liu 		profile_print_readings_json();
216247c09d6aSSong Liu 	else
216347c09d6aSSong Liu 		profile_print_readings_plain();
216447c09d6aSSong Liu }
216547c09d6aSSong Liu 
216647c09d6aSSong Liu static char *profile_target_name(int tgt_fd)
216747c09d6aSSong Liu {
2168c59765cfSDave Marchevsky 	struct bpf_func_info func_info;
2169c59765cfSDave Marchevsky 	struct bpf_prog_info info = {};
2170c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
217147c09d6aSSong Liu 	const struct btf_type *t;
2172c59765cfSDave Marchevsky 	__u32 func_info_rec_size;
2173369e955bSQuentin Monnet 	struct btf *btf = NULL;
217447c09d6aSSong Liu 	char *name = NULL;
2175c59765cfSDave Marchevsky 	int err;
217647c09d6aSSong Liu 
2177c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2178c59765cfSDave Marchevsky 	if (err) {
2179c59765cfSDave Marchevsky 		p_err("failed to bpf_obj_get_info_by_fd for prog FD %d", tgt_fd);
2180c59765cfSDave Marchevsky 		goto out;
218147c09d6aSSong Liu 	}
218247c09d6aSSong Liu 
2183c59765cfSDave Marchevsky 	if (info.btf_id == 0) {
218447c09d6aSSong Liu 		p_err("prog FD %d doesn't have valid btf", tgt_fd);
218547c09d6aSSong Liu 		goto out;
218647c09d6aSSong Liu 	}
218747c09d6aSSong Liu 
2188c59765cfSDave Marchevsky 	func_info_rec_size = info.func_info_rec_size;
2189c59765cfSDave Marchevsky 	if (info.nr_func_info == 0) {
2190c59765cfSDave Marchevsky 		p_err("bpf_obj_get_info_by_fd for prog FD %d found 0 func_info", tgt_fd);
2191c59765cfSDave Marchevsky 		goto out;
2192c59765cfSDave Marchevsky 	}
2193c59765cfSDave Marchevsky 
2194c59765cfSDave Marchevsky 	memset(&info, 0, sizeof(info));
2195c59765cfSDave Marchevsky 	info.nr_func_info = 1;
2196c59765cfSDave Marchevsky 	info.func_info_rec_size = func_info_rec_size;
2197c59765cfSDave Marchevsky 	info.func_info = ptr_to_u64(&func_info);
2198c59765cfSDave Marchevsky 
2199c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2200c59765cfSDave Marchevsky 	if (err) {
2201c59765cfSDave Marchevsky 		p_err("failed to get func_info for prog FD %d", tgt_fd);
2202c59765cfSDave Marchevsky 		goto out;
2203c59765cfSDave Marchevsky 	}
2204c59765cfSDave Marchevsky 
2205c59765cfSDave Marchevsky 	btf = btf__load_from_kernel_by_id(info.btf_id);
220686f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf)) {
220786f4b7f2SQuentin Monnet 		p_err("failed to load btf for prog FD %d", tgt_fd);
220886f4b7f2SQuentin Monnet 		goto out;
220986f4b7f2SQuentin Monnet 	}
221086f4b7f2SQuentin Monnet 
2211c59765cfSDave Marchevsky 	t = btf__type_by_id(btf, func_info.type_id);
221247c09d6aSSong Liu 	if (!t) {
221347c09d6aSSong Liu 		p_err("btf %d doesn't have type %d",
2214c59765cfSDave Marchevsky 		      info.btf_id, func_info.type_id);
221547c09d6aSSong Liu 		goto out;
221647c09d6aSSong Liu 	}
221747c09d6aSSong Liu 	name = strdup(btf__name_by_offset(btf, t->name_off));
221847c09d6aSSong Liu out:
2219369e955bSQuentin Monnet 	btf__free(btf);
222047c09d6aSSong Liu 	return name;
222147c09d6aSSong Liu }
222247c09d6aSSong Liu 
222347c09d6aSSong Liu static struct profiler_bpf *profile_obj;
222447c09d6aSSong Liu static int profile_tgt_fd = -1;
222547c09d6aSSong Liu static char *profile_tgt_name;
222647c09d6aSSong Liu static int *profile_perf_events;
222747c09d6aSSong Liu static int profile_perf_event_cnt;
222847c09d6aSSong Liu 
222947c09d6aSSong Liu static void profile_close_perf_events(struct profiler_bpf *obj)
223047c09d6aSSong Liu {
223147c09d6aSSong Liu 	int i;
223247c09d6aSSong Liu 
223347c09d6aSSong Liu 	for (i = profile_perf_event_cnt - 1; i >= 0; i--)
223447c09d6aSSong Liu 		close(profile_perf_events[i]);
223547c09d6aSSong Liu 
223647c09d6aSSong Liu 	free(profile_perf_events);
223747c09d6aSSong Liu 	profile_perf_event_cnt = 0;
223847c09d6aSSong Liu }
223947c09d6aSSong Liu 
224047c09d6aSSong Liu static int profile_open_perf_events(struct profiler_bpf *obj)
224147c09d6aSSong Liu {
224247c09d6aSSong Liu 	unsigned int cpu, m;
224347c09d6aSSong Liu 	int map_fd, pmu_fd;
224447c09d6aSSong Liu 
224547c09d6aSSong Liu 	profile_perf_events = calloc(
224647c09d6aSSong Liu 		sizeof(int), obj->rodata->num_cpu * obj->rodata->num_metric);
224747c09d6aSSong Liu 	if (!profile_perf_events) {
224847c09d6aSSong Liu 		p_err("failed to allocate memory for perf_event array: %s",
224947c09d6aSSong Liu 		      strerror(errno));
225047c09d6aSSong Liu 		return -1;
225147c09d6aSSong Liu 	}
225247c09d6aSSong Liu 	map_fd = bpf_map__fd(obj->maps.events);
225347c09d6aSSong Liu 	if (map_fd < 0) {
225447c09d6aSSong Liu 		p_err("failed to get fd for events map");
225547c09d6aSSong Liu 		return -1;
225647c09d6aSSong Liu 	}
225747c09d6aSSong Liu 
225847c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
225947c09d6aSSong Liu 		if (!metrics[m].selected)
226047c09d6aSSong Liu 			continue;
226147c09d6aSSong Liu 		for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) {
226247c09d6aSSong Liu 			pmu_fd = syscall(__NR_perf_event_open, &metrics[m].attr,
226347c09d6aSSong Liu 					 -1/*pid*/, cpu, -1/*group_fd*/, 0);
226447c09d6aSSong Liu 			if (pmu_fd < 0 ||
226547c09d6aSSong Liu 			    bpf_map_update_elem(map_fd, &profile_perf_event_cnt,
226647c09d6aSSong Liu 						&pmu_fd, BPF_ANY) ||
226747c09d6aSSong Liu 			    ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
226847c09d6aSSong Liu 				p_err("failed to create event %s on cpu %d",
226947c09d6aSSong Liu 				      metrics[m].name, cpu);
227047c09d6aSSong Liu 				return -1;
227147c09d6aSSong Liu 			}
227247c09d6aSSong Liu 			profile_perf_events[profile_perf_event_cnt++] = pmu_fd;
227347c09d6aSSong Liu 		}
227447c09d6aSSong Liu 	}
227547c09d6aSSong Liu 	return 0;
227647c09d6aSSong Liu }
227747c09d6aSSong Liu 
227847c09d6aSSong Liu static void profile_print_and_cleanup(void)
227947c09d6aSSong Liu {
228047c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
228147c09d6aSSong Liu 	profile_read_values(profile_obj);
228247c09d6aSSong Liu 	profile_print_readings();
228347c09d6aSSong Liu 	profiler_bpf__destroy(profile_obj);
228447c09d6aSSong Liu 
228547c09d6aSSong Liu 	close(profile_tgt_fd);
228647c09d6aSSong Liu 	free(profile_tgt_name);
228747c09d6aSSong Liu }
228847c09d6aSSong Liu 
228947c09d6aSSong Liu static void int_exit(int signo)
229047c09d6aSSong Liu {
229147c09d6aSSong Liu 	profile_print_and_cleanup();
229247c09d6aSSong Liu 	exit(0);
229347c09d6aSSong Liu }
229447c09d6aSSong Liu 
229547c09d6aSSong Liu static int do_profile(int argc, char **argv)
229647c09d6aSSong Liu {
229747c09d6aSSong Liu 	int num_metric, num_cpu, err = -1;
229847c09d6aSSong Liu 	struct bpf_program *prog;
229947c09d6aSSong Liu 	unsigned long duration;
230047c09d6aSSong Liu 	char *endptr;
230147c09d6aSSong Liu 
230247c09d6aSSong Liu 	/* we at least need two args for the prog and one metric */
230347c09d6aSSong Liu 	if (!REQ_ARGS(3))
230447c09d6aSSong Liu 		return -EINVAL;
230547c09d6aSSong Liu 
230647c09d6aSSong Liu 	/* parse target fd */
230747c09d6aSSong Liu 	profile_tgt_fd = prog_parse_fd(&argc, &argv);
230847c09d6aSSong Liu 	if (profile_tgt_fd < 0) {
230947c09d6aSSong Liu 		p_err("failed to parse fd");
231047c09d6aSSong Liu 		return -1;
231147c09d6aSSong Liu 	}
231247c09d6aSSong Liu 
231347c09d6aSSong Liu 	/* parse profiling optional duration */
231447c09d6aSSong Liu 	if (argc > 2 && is_prefix(argv[0], "duration")) {
231547c09d6aSSong Liu 		NEXT_ARG();
231647c09d6aSSong Liu 		duration = strtoul(*argv, &endptr, 0);
231747c09d6aSSong Liu 		if (*endptr)
231847c09d6aSSong Liu 			usage();
231947c09d6aSSong Liu 		NEXT_ARG();
232047c09d6aSSong Liu 	} else {
232147c09d6aSSong Liu 		duration = UINT_MAX;
232247c09d6aSSong Liu 	}
232347c09d6aSSong Liu 
232447c09d6aSSong Liu 	num_metric = profile_parse_metrics(argc, argv);
232547c09d6aSSong Liu 	if (num_metric <= 0)
232647c09d6aSSong Liu 		goto out;
232747c09d6aSSong Liu 
232847c09d6aSSong Liu 	num_cpu = libbpf_num_possible_cpus();
232947c09d6aSSong Liu 	if (num_cpu <= 0) {
233047c09d6aSSong Liu 		p_err("failed to identify number of CPUs");
233147c09d6aSSong Liu 		goto out;
233247c09d6aSSong Liu 	}
233347c09d6aSSong Liu 
233447c09d6aSSong Liu 	profile_obj = profiler_bpf__open();
233547c09d6aSSong Liu 	if (!profile_obj) {
233647c09d6aSSong Liu 		p_err("failed to open and/or load BPF object");
233747c09d6aSSong Liu 		goto out;
233847c09d6aSSong Liu 	}
233947c09d6aSSong Liu 
234047c09d6aSSong Liu 	profile_obj->rodata->num_cpu = num_cpu;
234147c09d6aSSong Liu 	profile_obj->rodata->num_metric = num_metric;
234247c09d6aSSong Liu 
234347c09d6aSSong Liu 	/* adjust map sizes */
234439748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.events, num_metric * num_cpu);
234539748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.fentry_readings, num_metric);
234639748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.accum_readings, num_metric);
234739748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.counts, 1);
234847c09d6aSSong Liu 
234947c09d6aSSong Liu 	/* change target name */
235047c09d6aSSong Liu 	profile_tgt_name = profile_target_name(profile_tgt_fd);
235147c09d6aSSong Liu 	if (!profile_tgt_name)
235247c09d6aSSong Liu 		goto out;
235347c09d6aSSong Liu 
235447c09d6aSSong Liu 	bpf_object__for_each_program(prog, profile_obj->obj) {
235547c09d6aSSong Liu 		err = bpf_program__set_attach_target(prog, profile_tgt_fd,
235647c09d6aSSong Liu 						     profile_tgt_name);
235747c09d6aSSong Liu 		if (err) {
235847c09d6aSSong Liu 			p_err("failed to set attach target\n");
235947c09d6aSSong Liu 			goto out;
236047c09d6aSSong Liu 		}
236147c09d6aSSong Liu 	}
236247c09d6aSSong Liu 
23636b4384ffSQuentin Monnet 	set_max_rlimit();
236447c09d6aSSong Liu 	err = profiler_bpf__load(profile_obj);
236547c09d6aSSong Liu 	if (err) {
236647c09d6aSSong Liu 		p_err("failed to load profile_obj");
236747c09d6aSSong Liu 		goto out;
236847c09d6aSSong Liu 	}
236947c09d6aSSong Liu 
237047c09d6aSSong Liu 	err = profile_open_perf_events(profile_obj);
237147c09d6aSSong Liu 	if (err)
237247c09d6aSSong Liu 		goto out;
237347c09d6aSSong Liu 
237447c09d6aSSong Liu 	err = profiler_bpf__attach(profile_obj);
237547c09d6aSSong Liu 	if (err) {
237647c09d6aSSong Liu 		p_err("failed to attach profile_obj");
237747c09d6aSSong Liu 		goto out;
237847c09d6aSSong Liu 	}
237947c09d6aSSong Liu 	signal(SIGINT, int_exit);
238047c09d6aSSong Liu 
238147c09d6aSSong Liu 	sleep(duration);
238247c09d6aSSong Liu 	profile_print_and_cleanup();
238347c09d6aSSong Liu 	return 0;
238447c09d6aSSong Liu 
238547c09d6aSSong Liu out:
238647c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
238747c09d6aSSong Liu 	if (profile_obj)
238847c09d6aSSong Liu 		profiler_bpf__destroy(profile_obj);
238947c09d6aSSong Liu 	close(profile_tgt_fd);
239047c09d6aSSong Liu 	free(profile_tgt_name);
239147c09d6aSSong Liu 	return err;
239247c09d6aSSong Liu }
239347c09d6aSSong Liu 
239447c09d6aSSong Liu #endif /* BPFTOOL_WITHOUT_SKELETONS */
239547c09d6aSSong Liu 
239671bb428fSJakub Kicinski static int do_help(int argc, char **argv)
239771bb428fSJakub Kicinski {
2398004b45c0SQuentin Monnet 	if (json_output) {
2399004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
2400004b45c0SQuentin Monnet 		return 0;
2401004b45c0SQuentin Monnet 	}
2402004b45c0SQuentin Monnet 
240371bb428fSJakub Kicinski 	fprintf(stderr,
240490040351SQuentin Monnet 		"Usage: %1$s %2$s { show | list } [PROG]\n"
240590040351SQuentin Monnet 		"       %1$s %2$s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n"
240690040351SQuentin Monnet 		"       %1$s %2$s dump jited  PROG [{ file FILE | opcodes | linum }]\n"
240790040351SQuentin Monnet 		"       %1$s %2$s pin   PROG FILE\n"
240890040351SQuentin Monnet 		"       %1$s %2$s { load | loadall } OBJ  PATH \\\n"
240977380998SStanislav Fomichev 		"                         [type TYPE] [dev NAME] \\\n"
24103767a94bSStanislav Fomichev 		"                         [map { idx IDX | name NAME } MAP]\\\n"
24113767a94bSStanislav Fomichev 		"                         [pinmaps MAP_DIR]\n"
2412*19526e70SWang Yufen 		"                         [autoattach]\n"
241390040351SQuentin Monnet 		"       %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n"
241490040351SQuentin Monnet 		"       %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n"
241590040351SQuentin Monnet 		"       %1$s %2$s run PROG \\\n"
2416ba95c745SQuentin Monnet 		"                         data_in FILE \\\n"
2417ba95c745SQuentin Monnet 		"                         [data_out FILE [data_size_out L]] \\\n"
2418ba95c745SQuentin Monnet 		"                         [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
2419ba95c745SQuentin Monnet 		"                         [repeat N]\n"
242090040351SQuentin Monnet 		"       %1$s %2$s profile PROG [duration DURATION] METRICs\n"
242190040351SQuentin Monnet 		"       %1$s %2$s tracelog\n"
242290040351SQuentin Monnet 		"       %1$s %2$s help\n"
242371bb428fSJakub Kicinski 		"\n"
24243ff5a4dcSJakub Kicinski 		"       " HELP_SPEC_MAP "\n"
242571bb428fSJakub Kicinski 		"       " HELP_SPEC_PROGRAM "\n"
242649f2cba3SJakub Kicinski 		"       TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
242749f2cba3SJakub Kicinski 		"                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
242849f2cba3SJakub Kicinski 		"                 cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
242949f2cba3SJakub Kicinski 		"                 lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
2430f25377eeSAndrey Ignatov 		"                 sk_reuseport | flow_dissector | cgroup/sysctl |\n"
243149f2cba3SJakub Kicinski 		"                 cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
243249f2cba3SJakub Kicinski 		"                 cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
243305ee19c1SDaniel Borkmann 		"                 cgroup/getpeername4 | cgroup/getpeername6 |\n"
243405ee19c1SDaniel Borkmann 		"                 cgroup/getsockname4 | cgroup/getsockname6 | cgroup/sendmsg4 |\n"
243505ee19c1SDaniel Borkmann 		"                 cgroup/sendmsg6 | cgroup/recvmsg4 | cgroup/recvmsg6 |\n"
2436a8deba85SLiu Jian 		"                 cgroup/getsockopt | cgroup/setsockopt | cgroup/sock_release |\n"
243793a3545dSJakub Sitnicki 		"                 struct_ops | fentry | fexit | freplace | sk_lookup }\n"
24381ba5ad36SDaniel Müller 		"       ATTACH_TYPE := { sk_msg_verdict | sk_skb_verdict | sk_skb_stream_verdict |\n"
24391ba5ad36SDaniel Müller 		"                        sk_skb_stream_parser | flow_dissector }\n"
2440450d060eSYonghong Song 		"       METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
2441c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " |\n"
24428cc8c635SQuentin Monnet 		"                    {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
24438cc8c635SQuentin Monnet 		"                    {-L|--use-loader} }\n"
244471bb428fSJakub Kicinski 		"",
244590040351SQuentin Monnet 		bin_name, argv[-2]);
244671bb428fSJakub Kicinski 
244771bb428fSJakub Kicinski 	return 0;
244871bb428fSJakub Kicinski }
244971bb428fSJakub Kicinski 
245071bb428fSJakub Kicinski static const struct cmd cmds[] = {
245171bb428fSJakub Kicinski 	{ "show",	do_show },
24526ebe6dbdSJakub Kicinski 	{ "list",	do_show },
24539f606179SQuentin Monnet 	{ "help",	do_help },
245471bb428fSJakub Kicinski 	{ "dump",	do_dump },
245571bb428fSJakub Kicinski 	{ "pin",	do_pin },
245649a086c2SRoman Gushchin 	{ "load",	do_load },
245777380998SStanislav Fomichev 	{ "loadall",	do_loadall },
2458b7d3826cSJohn Fastabend 	{ "attach",	do_attach },
2459b7d3826cSJohn Fastabend 	{ "detach",	do_detach },
246030da46b5SQuentin Monnet 	{ "tracelog",	do_tracelog },
2461ba95c745SQuentin Monnet 	{ "run",	do_run },
246247c09d6aSSong Liu 	{ "profile",	do_profile },
246371bb428fSJakub Kicinski 	{ 0 }
246471bb428fSJakub Kicinski };
246571bb428fSJakub Kicinski 
246671bb428fSJakub Kicinski int do_prog(int argc, char **argv)
246771bb428fSJakub Kicinski {
246871bb428fSJakub Kicinski 	return cmd_select(cmds, argc, argv, do_help);
246971bb428fSJakub Kicinski }
2470