xref: /openbmc/linux/tools/bpf/bpftool/prog.c (revision ce4f660862359f2172ff4e3df30e641df5475cf6)
102ff58dcSJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
202ff58dcSJakub Kicinski /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
371bb428fSJakub Kicinski 
4b3d84af7SQuentin Monnet #ifndef _GNU_SOURCE
53ff5a4dcSJakub Kicinski #define _GNU_SOURCE
6b3d84af7SQuentin Monnet #endif
771bb428fSJakub Kicinski #include <errno.h>
871bb428fSJakub Kicinski #include <fcntl.h>
947c09d6aSSong Liu #include <signal.h>
10c9c35995SJakub Kicinski #include <stdarg.h>
1171bb428fSJakub Kicinski #include <stdio.h>
1271bb428fSJakub Kicinski #include <stdlib.h>
1371bb428fSJakub Kicinski #include <string.h>
1471bb428fSJakub Kicinski #include <time.h>
1571bb428fSJakub Kicinski #include <unistd.h>
16ba6dd679SJakub Kicinski #include <net/if.h>
1747c09d6aSSong Liu #include <sys/ioctl.h>
1871bb428fSJakub Kicinski #include <sys/types.h>
1971bb428fSJakub Kicinski #include <sys/stat.h>
2047c09d6aSSong Liu #include <sys/syscall.h>
21d510296dSAlexei Starovoitov #include <dirent.h>
2271bb428fSJakub Kicinski 
23c8406848SJakub Kicinski #include <linux/err.h>
2447c09d6aSSong Liu #include <linux/perf_event.h>
25ba95c745SQuentin Monnet #include <linux/sizes.h>
26c8406848SJakub Kicinski 
27229c3b47SToke Høiland-Jørgensen #include <bpf/bpf.h>
28229c3b47SToke Høiland-Jørgensen #include <bpf/btf.h>
298f184732SQuentin Monnet #include <bpf/hashmap.h>
30229c3b47SToke Høiland-Jørgensen #include <bpf/libbpf.h>
31a19df713SMauricio Vásquez #include <bpf/libbpf_internal.h>
32d510296dSAlexei Starovoitov #include <bpf/skel_internal.h>
3371bb428fSJakub Kicinski 
34b6c1cedbSJiong Wang #include "cfg.h"
3571bb428fSJakub Kicinski #include "main.h"
3673bb5b4fSJiong Wang #include "xlated_dumper.h"
3771bb428fSJakub Kicinski 
38aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX "bpf_metadata_"
39aff52e68SYiFei Zhu #define BPF_METADATA_PREFIX_LEN (sizeof(BPF_METADATA_PREFIX) - 1)
40aff52e68SYiFei Zhu 
41ec202509SPaul Chaignon enum dump_mode {
42ec202509SPaul Chaignon 	DUMP_JITED,
43ec202509SPaul Chaignon 	DUMP_XLATED,
44ec202509SPaul Chaignon };
45ec202509SPaul Chaignon 
461ba5ad36SDaniel Müller static const bool attach_types[] = {
471ba5ad36SDaniel Müller 	[BPF_SK_SKB_STREAM_PARSER] = true,
481ba5ad36SDaniel Müller 	[BPF_SK_SKB_STREAM_VERDICT] = true,
491ba5ad36SDaniel Müller 	[BPF_SK_SKB_VERDICT] = true,
501ba5ad36SDaniel Müller 	[BPF_SK_MSG_VERDICT] = true,
511ba5ad36SDaniel Müller 	[BPF_FLOW_DISSECTOR] = true,
521ba5ad36SDaniel Müller 	[__MAX_BPF_ATTACH_TYPE] = false,
531ba5ad36SDaniel Müller };
541ba5ad36SDaniel Müller 
551ba5ad36SDaniel Müller /* Textual representations traditionally used by the program and kept around
561ba5ad36SDaniel Müller  * for the sake of backwards compatibility.
571ba5ad36SDaniel Müller  */
58b7d3826cSJohn Fastabend static const char * const attach_type_strings[] = {
59b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_PARSER] = "stream_parser",
60b7d3826cSJohn Fastabend 	[BPF_SK_SKB_STREAM_VERDICT] = "stream_verdict",
61a7ba4558SCong Wang 	[BPF_SK_SKB_VERDICT] = "skb_verdict",
62b7d3826cSJohn Fastabend 	[BPF_SK_MSG_VERDICT] = "msg_verdict",
63b7d3826cSJohn Fastabend 	[__MAX_BPF_ATTACH_TYPE] = NULL,
64b7d3826cSJohn Fastabend };
65b7d3826cSJohn Fastabend 
668f184732SQuentin Monnet static struct hashmap *prog_table;
6746241271SQuentin Monnet 
68c101189bSQuentin Monnet static enum bpf_attach_type parse_attach_type(const char *str)
69b7d3826cSJohn Fastabend {
70b7d3826cSJohn Fastabend 	enum bpf_attach_type type;
71b7d3826cSJohn Fastabend 
72b7d3826cSJohn Fastabend 	for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
731ba5ad36SDaniel Müller 		if (attach_types[type]) {
741ba5ad36SDaniel Müller 			const char *attach_type_str;
751ba5ad36SDaniel Müller 
761ba5ad36SDaniel Müller 			attach_type_str = libbpf_bpf_attach_type_str(type);
771ba5ad36SDaniel Müller 			if (!strcmp(str, attach_type_str))
781ba5ad36SDaniel Müller 				return type;
791ba5ad36SDaniel Müller 		}
801ba5ad36SDaniel Müller 
81b7d3826cSJohn Fastabend 		if (attach_type_strings[type] &&
82b7d3826cSJohn Fastabend 		    is_prefix(str, attach_type_strings[type]))
83b7d3826cSJohn Fastabend 			return type;
84b7d3826cSJohn Fastabend 	}
85b7d3826cSJohn Fastabend 
86b7d3826cSJohn Fastabend 	return __MAX_BPF_ATTACH_TYPE;
87b7d3826cSJohn Fastabend }
88b7d3826cSJohn Fastabend 
89c59765cfSDave Marchevsky static int prep_prog_info(struct bpf_prog_info *const info, enum dump_mode mode,
90c59765cfSDave Marchevsky 			  void **info_data, size_t *const info_data_sz)
91c59765cfSDave Marchevsky {
92c59765cfSDave Marchevsky 	struct bpf_prog_info holder = {};
93c59765cfSDave Marchevsky 	size_t needed = 0;
94c59765cfSDave Marchevsky 	void *ptr;
95c59765cfSDave Marchevsky 
96c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
97c59765cfSDave Marchevsky 		holder.jited_prog_len = info->jited_prog_len;
98c59765cfSDave Marchevsky 		needed += info->jited_prog_len;
99c59765cfSDave Marchevsky 	} else {
100c59765cfSDave Marchevsky 		holder.xlated_prog_len = info->xlated_prog_len;
101c59765cfSDave Marchevsky 		needed += info->xlated_prog_len;
102c59765cfSDave Marchevsky 	}
103c59765cfSDave Marchevsky 
104c59765cfSDave Marchevsky 	holder.nr_jited_ksyms = info->nr_jited_ksyms;
105c59765cfSDave Marchevsky 	needed += info->nr_jited_ksyms * sizeof(__u64);
106c59765cfSDave Marchevsky 
107c59765cfSDave Marchevsky 	holder.nr_jited_func_lens = info->nr_jited_func_lens;
108c59765cfSDave Marchevsky 	needed += info->nr_jited_func_lens * sizeof(__u32);
109c59765cfSDave Marchevsky 
110c59765cfSDave Marchevsky 	holder.nr_func_info = info->nr_func_info;
111c59765cfSDave Marchevsky 	holder.func_info_rec_size = info->func_info_rec_size;
112c59765cfSDave Marchevsky 	needed += info->nr_func_info * info->func_info_rec_size;
113c59765cfSDave Marchevsky 
114c59765cfSDave Marchevsky 	holder.nr_line_info = info->nr_line_info;
115c59765cfSDave Marchevsky 	holder.line_info_rec_size = info->line_info_rec_size;
116c59765cfSDave Marchevsky 	needed += info->nr_line_info * info->line_info_rec_size;
117c59765cfSDave Marchevsky 
118c59765cfSDave Marchevsky 	holder.nr_jited_line_info = info->nr_jited_line_info;
119c59765cfSDave Marchevsky 	holder.jited_line_info_rec_size = info->jited_line_info_rec_size;
120c59765cfSDave Marchevsky 	needed += info->nr_jited_line_info * info->jited_line_info_rec_size;
121c59765cfSDave Marchevsky 
122c59765cfSDave Marchevsky 	if (needed > *info_data_sz) {
123c59765cfSDave Marchevsky 		ptr = realloc(*info_data, needed);
124c59765cfSDave Marchevsky 		if (!ptr)
125c59765cfSDave Marchevsky 			return -1;
126c59765cfSDave Marchevsky 
127c59765cfSDave Marchevsky 		*info_data = ptr;
128c59765cfSDave Marchevsky 		*info_data_sz = needed;
129c59765cfSDave Marchevsky 	}
130c59765cfSDave Marchevsky 	ptr = *info_data;
131c59765cfSDave Marchevsky 
132c59765cfSDave Marchevsky 	if (mode == DUMP_JITED) {
133c59765cfSDave Marchevsky 		holder.jited_prog_insns = ptr_to_u64(ptr);
134c59765cfSDave Marchevsky 		ptr += holder.jited_prog_len;
135c59765cfSDave Marchevsky 	} else {
136c59765cfSDave Marchevsky 		holder.xlated_prog_insns = ptr_to_u64(ptr);
137c59765cfSDave Marchevsky 		ptr += holder.xlated_prog_len;
138c59765cfSDave Marchevsky 	}
139c59765cfSDave Marchevsky 
140c59765cfSDave Marchevsky 	holder.jited_ksyms = ptr_to_u64(ptr);
141c59765cfSDave Marchevsky 	ptr += holder.nr_jited_ksyms * sizeof(__u64);
142c59765cfSDave Marchevsky 
143c59765cfSDave Marchevsky 	holder.jited_func_lens = ptr_to_u64(ptr);
144c59765cfSDave Marchevsky 	ptr += holder.nr_jited_func_lens * sizeof(__u32);
145c59765cfSDave Marchevsky 
146c59765cfSDave Marchevsky 	holder.func_info = ptr_to_u64(ptr);
147c59765cfSDave Marchevsky 	ptr += holder.nr_func_info * holder.func_info_rec_size;
148c59765cfSDave Marchevsky 
149c59765cfSDave Marchevsky 	holder.line_info = ptr_to_u64(ptr);
150c59765cfSDave Marchevsky 	ptr += holder.nr_line_info * holder.line_info_rec_size;
151c59765cfSDave Marchevsky 
152c59765cfSDave Marchevsky 	holder.jited_line_info = ptr_to_u64(ptr);
153c59765cfSDave Marchevsky 	ptr += holder.nr_jited_line_info * holder.jited_line_info_rec_size;
154c59765cfSDave Marchevsky 
155c59765cfSDave Marchevsky 	*info = holder;
156c59765cfSDave Marchevsky 	return 0;
157c59765cfSDave Marchevsky }
158c59765cfSDave Marchevsky 
15971bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
16071bb428fSJakub Kicinski {
16171bb428fSJakub Kicinski 	struct timespec real_time_ts, boot_time_ts;
16271bb428fSJakub Kicinski 	time_t wallclock_secs;
16371bb428fSJakub Kicinski 	struct tm load_tm;
16471bb428fSJakub Kicinski 
16571bb428fSJakub Kicinski 	buf[--size] = '\0';
16671bb428fSJakub Kicinski 
16771bb428fSJakub Kicinski 	if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
16871bb428fSJakub Kicinski 	    clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
16971bb428fSJakub Kicinski 		perror("Can't read clocks");
17071bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
17171bb428fSJakub Kicinski 		return;
17271bb428fSJakub Kicinski 	}
17371bb428fSJakub Kicinski 
17471bb428fSJakub Kicinski 	wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
17507480cbcSJakub Kicinski 		(real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) /
17607480cbcSJakub Kicinski 		1000000000;
17707480cbcSJakub Kicinski 
17871bb428fSJakub Kicinski 
17971bb428fSJakub Kicinski 	if (!localtime_r(&wallclock_secs, &load_tm)) {
18071bb428fSJakub Kicinski 		snprintf(buf, size, "%llu", nsecs / 1000000000);
18171bb428fSJakub Kicinski 		return;
18271bb428fSJakub Kicinski 	}
18371bb428fSJakub Kicinski 
184a3fe1f6fSQuentin Monnet 	if (json_output)
185a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%s", &load_tm);
186a3fe1f6fSQuentin Monnet 	else
187a3fe1f6fSQuentin Monnet 		strftime(buf, size, "%FT%T%z", &load_tm);
18871bb428fSJakub Kicinski }
18971bb428fSJakub Kicinski 
1906e7e034eSQuentin Monnet static void show_prog_maps(int fd, __u32 num_maps)
19171bb428fSJakub Kicinski {
19271bb428fSJakub Kicinski 	struct bpf_prog_info info = {};
19371bb428fSJakub Kicinski 	__u32 len = sizeof(info);
19471bb428fSJakub Kicinski 	__u32 map_ids[num_maps];
19571bb428fSJakub Kicinski 	unsigned int i;
19671bb428fSJakub Kicinski 	int err;
19771bb428fSJakub Kicinski 
19871bb428fSJakub Kicinski 	info.nr_map_ids = num_maps;
19971bb428fSJakub Kicinski 	info.map_ids = ptr_to_u64(map_ids);
20071bb428fSJakub Kicinski 
20171bb428fSJakub Kicinski 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
20271bb428fSJakub Kicinski 	if (err || !info.nr_map_ids)
20371bb428fSJakub Kicinski 		return;
20471bb428fSJakub Kicinski 
205743cc665SQuentin Monnet 	if (json_output) {
206743cc665SQuentin Monnet 		jsonw_name(json_wtr, "map_ids");
207743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
208743cc665SQuentin Monnet 		for (i = 0; i < info.nr_map_ids; i++)
209743cc665SQuentin Monnet 			jsonw_uint(json_wtr, map_ids[i]);
210743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
211743cc665SQuentin Monnet 	} else {
21271bb428fSJakub Kicinski 		printf("  map_ids ");
21371bb428fSJakub Kicinski 		for (i = 0; i < info.nr_map_ids; i++)
21471bb428fSJakub Kicinski 			printf("%u%s", map_ids[i],
21571bb428fSJakub Kicinski 			       i == info.nr_map_ids - 1 ? "" : ",");
21671bb428fSJakub Kicinski 	}
21771bb428fSJakub Kicinski }
21871bb428fSJakub Kicinski 
219aff52e68SYiFei Zhu static void *find_metadata(int prog_fd, struct bpf_map_info *map_info)
220aff52e68SYiFei Zhu {
221aff52e68SYiFei Zhu 	struct bpf_prog_info prog_info;
222aff52e68SYiFei Zhu 	__u32 prog_info_len;
223aff52e68SYiFei Zhu 	__u32 map_info_len;
224aff52e68SYiFei Zhu 	void *value = NULL;
225aff52e68SYiFei Zhu 	__u32 *map_ids;
226aff52e68SYiFei Zhu 	int nr_maps;
227aff52e68SYiFei Zhu 	int key = 0;
228aff52e68SYiFei Zhu 	int map_fd;
229aff52e68SYiFei Zhu 	int ret;
230aff52e68SYiFei Zhu 	__u32 i;
231aff52e68SYiFei Zhu 
232aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
233aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
234aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
235aff52e68SYiFei Zhu 	if (ret)
236aff52e68SYiFei Zhu 		return NULL;
237aff52e68SYiFei Zhu 
238aff52e68SYiFei Zhu 	if (!prog_info.nr_map_ids)
239aff52e68SYiFei Zhu 		return NULL;
240aff52e68SYiFei Zhu 
241aff52e68SYiFei Zhu 	map_ids = calloc(prog_info.nr_map_ids, sizeof(__u32));
242aff52e68SYiFei Zhu 	if (!map_ids)
243aff52e68SYiFei Zhu 		return NULL;
244aff52e68SYiFei Zhu 
245aff52e68SYiFei Zhu 	nr_maps = prog_info.nr_map_ids;
246aff52e68SYiFei Zhu 	memset(&prog_info, 0, sizeof(prog_info));
247aff52e68SYiFei Zhu 	prog_info.nr_map_ids = nr_maps;
248aff52e68SYiFei Zhu 	prog_info.map_ids = ptr_to_u64(map_ids);
249aff52e68SYiFei Zhu 	prog_info_len = sizeof(prog_info);
250aff52e68SYiFei Zhu 
251aff52e68SYiFei Zhu 	ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len);
252aff52e68SYiFei Zhu 	if (ret)
253aff52e68SYiFei Zhu 		goto free_map_ids;
254aff52e68SYiFei Zhu 
255aff52e68SYiFei Zhu 	for (i = 0; i < prog_info.nr_map_ids; i++) {
256aff52e68SYiFei Zhu 		map_fd = bpf_map_get_fd_by_id(map_ids[i]);
257aff52e68SYiFei Zhu 		if (map_fd < 0)
258aff52e68SYiFei Zhu 			goto free_map_ids;
259aff52e68SYiFei Zhu 
260aff52e68SYiFei Zhu 		memset(map_info, 0, sizeof(*map_info));
261aff52e68SYiFei Zhu 		map_info_len = sizeof(*map_info);
262aff52e68SYiFei Zhu 		ret = bpf_obj_get_info_by_fd(map_fd, map_info, &map_info_len);
263aff52e68SYiFei Zhu 		if (ret < 0) {
264aff52e68SYiFei Zhu 			close(map_fd);
265aff52e68SYiFei Zhu 			goto free_map_ids;
266aff52e68SYiFei Zhu 		}
267aff52e68SYiFei Zhu 
268aff52e68SYiFei Zhu 		if (map_info->type != BPF_MAP_TYPE_ARRAY ||
269aff52e68SYiFei Zhu 		    map_info->key_size != sizeof(int) ||
270aff52e68SYiFei Zhu 		    map_info->max_entries != 1 ||
271aff52e68SYiFei Zhu 		    !map_info->btf_value_type_id ||
272aff52e68SYiFei Zhu 		    !strstr(map_info->name, ".rodata")) {
273aff52e68SYiFei Zhu 			close(map_fd);
274aff52e68SYiFei Zhu 			continue;
275aff52e68SYiFei Zhu 		}
276aff52e68SYiFei Zhu 
277aff52e68SYiFei Zhu 		value = malloc(map_info->value_size);
278aff52e68SYiFei Zhu 		if (!value) {
279aff52e68SYiFei Zhu 			close(map_fd);
280aff52e68SYiFei Zhu 			goto free_map_ids;
281aff52e68SYiFei Zhu 		}
282aff52e68SYiFei Zhu 
283aff52e68SYiFei Zhu 		if (bpf_map_lookup_elem(map_fd, &key, value)) {
284aff52e68SYiFei Zhu 			close(map_fd);
285aff52e68SYiFei Zhu 			free(value);
286aff52e68SYiFei Zhu 			value = NULL;
287aff52e68SYiFei Zhu 			goto free_map_ids;
288aff52e68SYiFei Zhu 		}
289aff52e68SYiFei Zhu 
290aff52e68SYiFei Zhu 		close(map_fd);
291aff52e68SYiFei Zhu 		break;
292aff52e68SYiFei Zhu 	}
293aff52e68SYiFei Zhu 
294aff52e68SYiFei Zhu free_map_ids:
295aff52e68SYiFei Zhu 	free(map_ids);
296aff52e68SYiFei Zhu 	return value;
297aff52e68SYiFei Zhu }
298aff52e68SYiFei Zhu 
299aff52e68SYiFei Zhu static bool has_metadata_prefix(const char *s)
300aff52e68SYiFei Zhu {
301aff52e68SYiFei Zhu 	return strncmp(s, BPF_METADATA_PREFIX, BPF_METADATA_PREFIX_LEN) == 0;
302aff52e68SYiFei Zhu }
303aff52e68SYiFei Zhu 
304aff52e68SYiFei Zhu static void show_prog_metadata(int fd, __u32 num_maps)
305aff52e68SYiFei Zhu {
306aff52e68SYiFei Zhu 	const struct btf_type *t_datasec, *t_var;
307aff52e68SYiFei Zhu 	struct bpf_map_info map_info;
308aff52e68SYiFei Zhu 	struct btf_var_secinfo *vsi;
309aff52e68SYiFei Zhu 	bool printed_header = false;
310aff52e68SYiFei Zhu 	unsigned int i, vlen;
311aff52e68SYiFei Zhu 	void *value = NULL;
312aff52e68SYiFei Zhu 	const char *name;
31386f4b7f2SQuentin Monnet 	struct btf *btf;
314aff52e68SYiFei Zhu 	int err;
315aff52e68SYiFei Zhu 
316aff52e68SYiFei Zhu 	if (!num_maps)
317aff52e68SYiFei Zhu 		return;
318aff52e68SYiFei Zhu 
319aff52e68SYiFei Zhu 	memset(&map_info, 0, sizeof(map_info));
320aff52e68SYiFei Zhu 	value = find_metadata(fd, &map_info);
321aff52e68SYiFei Zhu 	if (!value)
322aff52e68SYiFei Zhu 		return;
323aff52e68SYiFei Zhu 
32486f4b7f2SQuentin Monnet 	btf = btf__load_from_kernel_by_id(map_info.btf_id);
32586f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf))
326aff52e68SYiFei Zhu 		goto out_free;
327aff52e68SYiFei Zhu 
328aff52e68SYiFei Zhu 	t_datasec = btf__type_by_id(btf, map_info.btf_value_type_id);
329aff52e68SYiFei Zhu 	if (!btf_is_datasec(t_datasec))
330aff52e68SYiFei Zhu 		goto out_free;
331aff52e68SYiFei Zhu 
332aff52e68SYiFei Zhu 	vlen = btf_vlen(t_datasec);
333aff52e68SYiFei Zhu 	vsi = btf_var_secinfos(t_datasec);
334aff52e68SYiFei Zhu 
335aff52e68SYiFei Zhu 	/* We don't proceed to check the kinds of the elements of the DATASEC.
336aff52e68SYiFei Zhu 	 * The verifier enforces them to be BTF_KIND_VAR.
337aff52e68SYiFei Zhu 	 */
338aff52e68SYiFei Zhu 
339aff52e68SYiFei Zhu 	if (json_output) {
340aff52e68SYiFei Zhu 		struct btf_dumper d = {
341aff52e68SYiFei Zhu 			.btf = btf,
342aff52e68SYiFei Zhu 			.jw = json_wtr,
343aff52e68SYiFei Zhu 			.is_plain_text = false,
344aff52e68SYiFei Zhu 		};
345aff52e68SYiFei Zhu 
346aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
347aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
348aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
349aff52e68SYiFei Zhu 
350aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
351aff52e68SYiFei Zhu 				continue;
352aff52e68SYiFei Zhu 
353aff52e68SYiFei Zhu 			if (!printed_header) {
354aff52e68SYiFei Zhu 				jsonw_name(json_wtr, "metadata");
355aff52e68SYiFei Zhu 				jsonw_start_object(json_wtr);
356aff52e68SYiFei Zhu 				printed_header = true;
357aff52e68SYiFei Zhu 			}
358aff52e68SYiFei Zhu 
359aff52e68SYiFei Zhu 			jsonw_name(json_wtr, name + BPF_METADATA_PREFIX_LEN);
360aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
361aff52e68SYiFei Zhu 			if (err) {
362aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
363aff52e68SYiFei Zhu 				break;
364aff52e68SYiFei Zhu 			}
365aff52e68SYiFei Zhu 		}
366aff52e68SYiFei Zhu 		if (printed_header)
367aff52e68SYiFei Zhu 			jsonw_end_object(json_wtr);
368aff52e68SYiFei Zhu 	} else {
369e89ef634SQuentin Monnet 		json_writer_t *btf_wtr;
370aff52e68SYiFei Zhu 		struct btf_dumper d = {
371aff52e68SYiFei Zhu 			.btf = btf,
372aff52e68SYiFei Zhu 			.is_plain_text = true,
373aff52e68SYiFei Zhu 		};
374aff52e68SYiFei Zhu 
375aff52e68SYiFei Zhu 		for (i = 0; i < vlen; i++, vsi++) {
376aff52e68SYiFei Zhu 			t_var = btf__type_by_id(btf, vsi->type);
377aff52e68SYiFei Zhu 			name = btf__name_by_offset(btf, t_var->name_off);
378aff52e68SYiFei Zhu 
379aff52e68SYiFei Zhu 			if (!has_metadata_prefix(name))
380aff52e68SYiFei Zhu 				continue;
381aff52e68SYiFei Zhu 
382aff52e68SYiFei Zhu 			if (!printed_header) {
383aff52e68SYiFei Zhu 				printf("\tmetadata:");
384e89ef634SQuentin Monnet 
385e89ef634SQuentin Monnet 				btf_wtr = jsonw_new(stdout);
386e89ef634SQuentin Monnet 				if (!btf_wtr) {
387e89ef634SQuentin Monnet 					p_err("jsonw alloc failed");
388e89ef634SQuentin Monnet 					goto out_free;
389e89ef634SQuentin Monnet 				}
390e89ef634SQuentin Monnet 				d.jw = btf_wtr,
391e89ef634SQuentin Monnet 
392aff52e68SYiFei Zhu 				printed_header = true;
393aff52e68SYiFei Zhu 			}
394aff52e68SYiFei Zhu 
395aff52e68SYiFei Zhu 			printf("\n\t\t%s = ", name + BPF_METADATA_PREFIX_LEN);
396aff52e68SYiFei Zhu 
397aff52e68SYiFei Zhu 			jsonw_reset(btf_wtr);
398aff52e68SYiFei Zhu 			err = btf_dumper_type(&d, t_var->type, value + vsi->offset);
399aff52e68SYiFei Zhu 			if (err) {
400aff52e68SYiFei Zhu 				p_err("btf dump failed: %d", err);
401aff52e68SYiFei Zhu 				break;
402aff52e68SYiFei Zhu 			}
403aff52e68SYiFei Zhu 		}
404aff52e68SYiFei Zhu 		if (printed_header)
405aff52e68SYiFei Zhu 			jsonw_destroy(&btf_wtr);
406aff52e68SYiFei Zhu 	}
407aff52e68SYiFei Zhu 
408aff52e68SYiFei Zhu out_free:
409aff52e68SYiFei Zhu 	btf__free(btf);
410aff52e68SYiFei Zhu 	free(value);
411aff52e68SYiFei Zhu }
412aff52e68SYiFei Zhu 
413b662000aSRaman Shukhau static void print_prog_header_json(struct bpf_prog_info *info, int fd)
414743cc665SQuentin Monnet {
415b700eeb4SDaniel Müller 	const char *prog_type_str;
416b662000aSRaman Shukhau 	char prog_name[MAX_PROG_FULL_NAME];
417b662000aSRaman Shukhau 
418743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "id", info->id);
419b700eeb4SDaniel Müller 	prog_type_str = libbpf_bpf_prog_type_str(info->type);
420b700eeb4SDaniel Müller 
421b700eeb4SDaniel Müller 	if (prog_type_str)
422b700eeb4SDaniel Müller 		jsonw_string_field(json_wtr, "type", prog_type_str);
42371bb428fSJakub Kicinski 	else
424743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "type", info->type);
42571bb428fSJakub Kicinski 
426b662000aSRaman Shukhau 	if (*info->name) {
427b662000aSRaman Shukhau 		get_prog_full_name(info, fd, prog_name, sizeof(prog_name));
428b662000aSRaman Shukhau 		jsonw_string_field(json_wtr, "name", prog_name);
429b662000aSRaman Shukhau 	}
43071bb428fSJakub Kicinski 
431743cc665SQuentin Monnet 	jsonw_name(json_wtr, "tag");
432743cc665SQuentin Monnet 	jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
433743cc665SQuentin Monnet 		     info->tag[0], info->tag[1], info->tag[2], info->tag[3],
434743cc665SQuentin Monnet 		     info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
43571bb428fSJakub Kicinski 
4369b984a20SJiri Olsa 	jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible);
43788ad472bSAlexei Starovoitov 	if (info->run_time_ns) {
43888ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_time_ns", info->run_time_ns);
43988ad472bSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "run_cnt", info->run_cnt);
44088ad472bSAlexei Starovoitov 	}
4419ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
4429ed9e9baSAlexei Starovoitov 		jsonw_uint_field(json_wtr, "recursion_misses", info->recursion_misses);
443ec202509SPaul Chaignon }
4449b984a20SJiri Olsa 
445ec202509SPaul Chaignon static void print_prog_json(struct bpf_prog_info *info, int fd)
446ec202509SPaul Chaignon {
447ec202509SPaul Chaignon 	char *memlock;
448ec202509SPaul Chaignon 
449ec202509SPaul Chaignon 	jsonw_start_object(json_wtr);
450b662000aSRaman Shukhau 	print_prog_header_json(info, fd);
45152262210SJakub Kicinski 	print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
45252262210SJakub Kicinski 
453743cc665SQuentin Monnet 	if (info->load_time) {
45471bb428fSJakub Kicinski 		char buf[32];
45571bb428fSJakub Kicinski 
456743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
45771bb428fSJakub Kicinski 
45871bb428fSJakub Kicinski 		/* Piggy back on load_time, since 0 uid is a valid one */
459a3fe1f6fSQuentin Monnet 		jsonw_name(json_wtr, "loaded_at");
460a3fe1f6fSQuentin Monnet 		jsonw_printf(json_wtr, "%s", buf);
461743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
46271bb428fSJakub Kicinski 	}
46371bb428fSJakub Kicinski 
464743cc665SQuentin Monnet 	jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
46571bb428fSJakub Kicinski 
466743cc665SQuentin Monnet 	if (info->jited_prog_len) {
467743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", true);
468743cc665SQuentin Monnet 		jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
469743cc665SQuentin Monnet 	} else {
470743cc665SQuentin Monnet 		jsonw_bool_field(json_wtr, "jited", false);
471743cc665SQuentin Monnet 	}
472743cc665SQuentin Monnet 
473743cc665SQuentin Monnet 	memlock = get_fdinfo(fd, "memlock");
474743cc665SQuentin Monnet 	if (memlock)
475357b3cc3SChris J Arges 		jsonw_int_field(json_wtr, "bytes_memlock", atoll(memlock));
476743cc665SQuentin Monnet 	free(memlock);
477743cc665SQuentin Monnet 
478743cc665SQuentin Monnet 	if (info->nr_map_ids)
479743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
480743cc665SQuentin Monnet 
481569b0c77SPrashant Bhole 	if (info->btf_id)
482569b0c77SPrashant Bhole 		jsonw_int_field(json_wtr, "btf_id", info->btf_id);
483569b0c77SPrashant Bhole 
4848f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
4858f184732SQuentin Monnet 		struct hashmap_entry *entry;
4864990f1f4SPrashant Bhole 
4874990f1f4SPrashant Bhole 		jsonw_name(json_wtr, "pinned");
4884990f1f4SPrashant Bhole 		jsonw_start_array(json_wtr);
4898f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
4908f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
4918f184732SQuentin Monnet 			jsonw_string(json_wtr, entry->value);
4924990f1f4SPrashant Bhole 		jsonw_end_array(json_wtr);
4934990f1f4SPrashant Bhole 	}
4944990f1f4SPrashant Bhole 
495d6699f8eSQuentin Monnet 	emit_obj_refs_json(refs_table, info->id, json_wtr);
496d53dee3fSAndrii Nakryiko 
497aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
498aff52e68SYiFei Zhu 
499743cc665SQuentin Monnet 	jsonw_end_object(json_wtr);
500743cc665SQuentin Monnet }
501743cc665SQuentin Monnet 
502b662000aSRaman Shukhau static void print_prog_header_plain(struct bpf_prog_info *info, int fd)
503743cc665SQuentin Monnet {
504b700eeb4SDaniel Müller 	const char *prog_type_str;
505b662000aSRaman Shukhau 	char prog_name[MAX_PROG_FULL_NAME];
506b662000aSRaman Shukhau 
507743cc665SQuentin Monnet 	printf("%u: ", info->id);
508b700eeb4SDaniel Müller 	prog_type_str = libbpf_bpf_prog_type_str(info->type);
509b700eeb4SDaniel Müller 	if (prog_type_str)
510b700eeb4SDaniel Müller 		printf("%s  ", prog_type_str);
511743cc665SQuentin Monnet 	else
512743cc665SQuentin Monnet 		printf("type %u  ", info->type);
513743cc665SQuentin Monnet 
514b662000aSRaman Shukhau 	if (*info->name) {
515b662000aSRaman Shukhau 		get_prog_full_name(info, fd, prog_name, sizeof(prog_name));
516b662000aSRaman Shukhau 		printf("name %s  ", prog_name);
517b662000aSRaman Shukhau 	}
518743cc665SQuentin Monnet 
519743cc665SQuentin Monnet 	printf("tag ");
520743cc665SQuentin Monnet 	fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
52152262210SJakub Kicinski 	print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
5229b984a20SJiri Olsa 	printf("%s", info->gpl_compatible ? "  gpl" : "");
52388ad472bSAlexei Starovoitov 	if (info->run_time_ns)
52488ad472bSAlexei Starovoitov 		printf(" run_time_ns %lld run_cnt %lld",
52588ad472bSAlexei Starovoitov 		       info->run_time_ns, info->run_cnt);
5269ed9e9baSAlexei Starovoitov 	if (info->recursion_misses)
5279ed9e9baSAlexei Starovoitov 		printf(" recursion_misses %lld", info->recursion_misses);
528743cc665SQuentin Monnet 	printf("\n");
529ec202509SPaul Chaignon }
530ec202509SPaul Chaignon 
531ec202509SPaul Chaignon static void print_prog_plain(struct bpf_prog_info *info, int fd)
532ec202509SPaul Chaignon {
533ec202509SPaul Chaignon 	char *memlock;
534ec202509SPaul Chaignon 
535b662000aSRaman Shukhau 	print_prog_header_plain(info, fd);
536743cc665SQuentin Monnet 
537743cc665SQuentin Monnet 	if (info->load_time) {
538743cc665SQuentin Monnet 		char buf[32];
539743cc665SQuentin Monnet 
540743cc665SQuentin Monnet 		print_boot_time(info->load_time, buf, sizeof(buf));
541743cc665SQuentin Monnet 
542743cc665SQuentin Monnet 		/* Piggy back on load_time, since 0 uid is a valid one */
543743cc665SQuentin Monnet 		printf("\tloaded_at %s  uid %u\n", buf, info->created_by_uid);
544743cc665SQuentin Monnet 	}
545743cc665SQuentin Monnet 
546743cc665SQuentin Monnet 	printf("\txlated %uB", info->xlated_prog_len);
547743cc665SQuentin Monnet 
548743cc665SQuentin Monnet 	if (info->jited_prog_len)
549743cc665SQuentin Monnet 		printf("  jited %uB", info->jited_prog_len);
55071bb428fSJakub Kicinski 	else
55171bb428fSJakub Kicinski 		printf("  not jited");
55271bb428fSJakub Kicinski 
55371bb428fSJakub Kicinski 	memlock = get_fdinfo(fd, "memlock");
55471bb428fSJakub Kicinski 	if (memlock)
55571bb428fSJakub Kicinski 		printf("  memlock %sB", memlock);
55671bb428fSJakub Kicinski 	free(memlock);
55771bb428fSJakub Kicinski 
558743cc665SQuentin Monnet 	if (info->nr_map_ids)
559743cc665SQuentin Monnet 		show_prog_maps(fd, info->nr_map_ids);
56071bb428fSJakub Kicinski 
5618f184732SQuentin Monnet 	if (!hashmap__empty(prog_table)) {
5628f184732SQuentin Monnet 		struct hashmap_entry *entry;
5634990f1f4SPrashant Bhole 
5648f184732SQuentin Monnet 		hashmap__for_each_key_entry(prog_table, entry,
5658f184732SQuentin Monnet 					    u32_as_hash_field(info->id))
5668f184732SQuentin Monnet 			printf("\n\tpinned %s", (char *)entry->value);
5674990f1f4SPrashant Bhole 	}
5684990f1f4SPrashant Bhole 
569569b0c77SPrashant Bhole 	if (info->btf_id)
570031ebc1aSQuentin Monnet 		printf("\n\tbtf_id %d", info->btf_id);
571569b0c77SPrashant Bhole 
572d6699f8eSQuentin Monnet 	emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
573d53dee3fSAndrii Nakryiko 
57471bb428fSJakub Kicinski 	printf("\n");
575aff52e68SYiFei Zhu 
576aff52e68SYiFei Zhu 	show_prog_metadata(fd, info->nr_map_ids);
577743cc665SQuentin Monnet }
578743cc665SQuentin Monnet 
579743cc665SQuentin Monnet static int show_prog(int fd)
580743cc665SQuentin Monnet {
581743cc665SQuentin Monnet 	struct bpf_prog_info info = {};
582743cc665SQuentin Monnet 	__u32 len = sizeof(info);
583743cc665SQuentin Monnet 	int err;
584743cc665SQuentin Monnet 
585743cc665SQuentin Monnet 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
586743cc665SQuentin Monnet 	if (err) {
5879a5ab8bfSQuentin Monnet 		p_err("can't get prog info: %s", strerror(errno));
588743cc665SQuentin Monnet 		return -1;
589743cc665SQuentin Monnet 	}
590743cc665SQuentin Monnet 
591743cc665SQuentin Monnet 	if (json_output)
592743cc665SQuentin Monnet 		print_prog_json(&info, fd);
593743cc665SQuentin Monnet 	else
594743cc665SQuentin Monnet 		print_prog_plain(&info, fd);
59571bb428fSJakub Kicinski 
59671bb428fSJakub Kicinski 	return 0;
59771bb428fSJakub Kicinski }
59871bb428fSJakub Kicinski 
599ec202509SPaul Chaignon static int do_show_subset(int argc, char **argv)
600ec202509SPaul Chaignon {
601ec202509SPaul Chaignon 	int *fds = NULL;
602ec202509SPaul Chaignon 	int nb_fds, i;
603ec202509SPaul Chaignon 	int err = -1;
604ec202509SPaul Chaignon 
605ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
606ec202509SPaul Chaignon 	if (!fds) {
607ec202509SPaul Chaignon 		p_err("mem alloc failed");
608ec202509SPaul Chaignon 		return -1;
609ec202509SPaul Chaignon 	}
610ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
611ec202509SPaul Chaignon 	if (nb_fds < 1)
612ec202509SPaul Chaignon 		goto exit_free;
613ec202509SPaul Chaignon 
614ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
615ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
616ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
617ec202509SPaul Chaignon 		err = show_prog(fds[i]);
618ec202509SPaul Chaignon 		if (err) {
619ec202509SPaul Chaignon 			for (; i < nb_fds; i++)
620ec202509SPaul Chaignon 				close(fds[i]);
621ec202509SPaul Chaignon 			break;
622ec202509SPaul Chaignon 		}
623ec202509SPaul Chaignon 		close(fds[i]);
624ec202509SPaul Chaignon 	}
625ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
626ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
627ec202509SPaul Chaignon 
628ec202509SPaul Chaignon exit_free:
629ec202509SPaul Chaignon 	free(fds);
630ec202509SPaul Chaignon 	return err;
631ec202509SPaul Chaignon }
632ec202509SPaul Chaignon 
63371bb428fSJakub Kicinski static int do_show(int argc, char **argv)
634743cc665SQuentin Monnet {
635743cc665SQuentin Monnet 	__u32 id = 0;
63671bb428fSJakub Kicinski 	int err;
63771bb428fSJakub Kicinski 	int fd;
63871bb428fSJakub Kicinski 
63946241271SQuentin Monnet 	if (show_pinned) {
6408f184732SQuentin Monnet 		prog_table = hashmap__new(hash_fn_for_key_as_id,
6418f184732SQuentin Monnet 					  equal_fn_for_key_as_id, NULL);
642622a5b58SMauricio Vásquez 		if (IS_ERR(prog_table)) {
6438f184732SQuentin Monnet 			p_err("failed to create hashmap for pinned paths");
6448f184732SQuentin Monnet 			return -1;
6458f184732SQuentin Monnet 		}
6468f184732SQuentin Monnet 		build_pinned_obj_table(prog_table, BPF_OBJ_PROG);
64746241271SQuentin Monnet 	}
648d53dee3fSAndrii Nakryiko 	build_obj_refs_table(&refs_table, BPF_OBJ_PROG);
6494990f1f4SPrashant Bhole 
650ec202509SPaul Chaignon 	if (argc == 2)
651ec202509SPaul Chaignon 		return do_show_subset(argc, argv);
65271bb428fSJakub Kicinski 
65371bb428fSJakub Kicinski 	if (argc)
65471bb428fSJakub Kicinski 		return BAD_ARG();
65571bb428fSJakub Kicinski 
656743cc665SQuentin Monnet 	if (json_output)
657743cc665SQuentin Monnet 		jsonw_start_array(json_wtr);
65871bb428fSJakub Kicinski 	while (true) {
65971bb428fSJakub Kicinski 		err = bpf_prog_get_next_id(id, &id);
66071bb428fSJakub Kicinski 		if (err) {
6611739c26dSQuentin Monnet 			if (errno == ENOENT) {
6621739c26dSQuentin Monnet 				err = 0;
66371bb428fSJakub Kicinski 				break;
6641739c26dSQuentin Monnet 			}
6659a5ab8bfSQuentin Monnet 			p_err("can't get next program: %s%s", strerror(errno),
6669a5ab8bfSQuentin Monnet 			      errno == EINVAL ? " -- kernel too old?" : "");
667743cc665SQuentin Monnet 			err = -1;
668743cc665SQuentin Monnet 			break;
66971bb428fSJakub Kicinski 		}
67071bb428fSJakub Kicinski 
67171bb428fSJakub Kicinski 		fd = bpf_prog_get_fd_by_id(id);
67271bb428fSJakub Kicinski 		if (fd < 0) {
6738207c6ddSJakub Kicinski 			if (errno == ENOENT)
6748207c6ddSJakub Kicinski 				continue;
6759a5ab8bfSQuentin Monnet 			p_err("can't get prog by id (%u): %s",
67671bb428fSJakub Kicinski 			      id, strerror(errno));
677743cc665SQuentin Monnet 			err = -1;
678743cc665SQuentin Monnet 			break;
67971bb428fSJakub Kicinski 		}
68071bb428fSJakub Kicinski 
68171bb428fSJakub Kicinski 		err = show_prog(fd);
68271bb428fSJakub Kicinski 		close(fd);
68371bb428fSJakub Kicinski 		if (err)
684743cc665SQuentin Monnet 			break;
68571bb428fSJakub Kicinski 	}
68671bb428fSJakub Kicinski 
687743cc665SQuentin Monnet 	if (json_output)
688743cc665SQuentin Monnet 		jsonw_end_array(json_wtr);
689743cc665SQuentin Monnet 
690d6699f8eSQuentin Monnet 	delete_obj_refs_table(refs_table);
691d53dee3fSAndrii Nakryiko 
69246241271SQuentin Monnet 	if (show_pinned)
6938f184732SQuentin Monnet 		delete_pinned_obj_table(prog_table);
69446241271SQuentin Monnet 
695743cc665SQuentin Monnet 	return err;
69671bb428fSJakub Kicinski }
69771bb428fSJakub Kicinski 
698ec202509SPaul Chaignon static int
699ec202509SPaul Chaignon prog_dump(struct bpf_prog_info *info, enum dump_mode mode,
700ec202509SPaul Chaignon 	  char *filepath, bool opcodes, bool visual, bool linum)
70171bb428fSJakub Kicinski {
702b053b439SMartin KaFai Lau 	struct bpf_prog_linfo *prog_linfo = NULL;
7033ddeac67SJakub Kicinski 	const char *disasm_opt = NULL;
7047105e828SDaniel Borkmann 	struct dump_data dd = {};
705cae73f23SSong Liu 	void *func_info = NULL;
706254471e5SYonghong Song 	struct btf *btf = NULL;
707254471e5SYonghong Song 	char func_sig[1024];
70871bb428fSJakub Kicinski 	unsigned char *buf;
709cae73f23SSong Liu 	__u32 member_len;
710ebbd7f64SQuentin Monnet 	int fd, err = -1;
71171bb428fSJakub Kicinski 	ssize_t n;
71271bb428fSJakub Kicinski 
713cae73f23SSong Liu 	if (mode == DUMP_JITED) {
7145b79bcdfSToke Høiland-Jørgensen 		if (info->jited_prog_len == 0 || !info->jited_prog_insns) {
7159a5ab8bfSQuentin Monnet 			p_info("no instructions returned");
716ec202509SPaul Chaignon 			return -1;
717f84192eeSSandipan Das 		}
71809f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->jited_prog_insns);
719cae73f23SSong Liu 		member_len = info->jited_prog_len;
720cae73f23SSong Liu 	} else {	/* DUMP_XLATED */
721d95f1e8bSToke Høiland-Jørgensen 		if (info->xlated_prog_len == 0 || !info->xlated_prog_insns) {
7227105e828SDaniel Borkmann 			p_err("error retrieving insn dump: kernel.kptr_restrict set?");
723ec202509SPaul Chaignon 			return -1;
7247105e828SDaniel Borkmann 		}
72509f44b75SAndrii Nakryiko 		buf = u64_to_ptr(info->xlated_prog_insns);
726cae73f23SSong Liu 		member_len = info->xlated_prog_len;
727cae73f23SSong Liu 	}
7287105e828SDaniel Borkmann 
72986f4b7f2SQuentin Monnet 	if (info->btf_id) {
73086f4b7f2SQuentin Monnet 		btf = btf__load_from_kernel_by_id(info->btf_id);
73186f4b7f2SQuentin Monnet 		if (libbpf_get_error(btf)) {
732254471e5SYonghong Song 			p_err("failed to get btf");
733ec202509SPaul Chaignon 			return -1;
734254471e5SYonghong Song 		}
73586f4b7f2SQuentin Monnet 	}
736254471e5SYonghong Song 
73709f44b75SAndrii Nakryiko 	func_info = u64_to_ptr(info->func_info);
738cae73f23SSong Liu 
739cae73f23SSong Liu 	if (info->nr_line_info) {
740cae73f23SSong Liu 		prog_linfo = bpf_prog_linfo__new(info);
741b053b439SMartin KaFai Lau 		if (!prog_linfo)
74210a5ce98SMartin KaFai Lau 			p_info("error in processing bpf_line_info.  continue without it.");
743b053b439SMartin KaFai Lau 	}
744b053b439SMartin KaFai Lau 
74571bb428fSJakub Kicinski 	if (filepath) {
74671bb428fSJakub Kicinski 		fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
74771bb428fSJakub Kicinski 		if (fd < 0) {
7489a5ab8bfSQuentin Monnet 			p_err("can't open file %s: %s", filepath,
74971bb428fSJakub Kicinski 			      strerror(errno));
750ebbd7f64SQuentin Monnet 			goto exit_free;
75171bb428fSJakub Kicinski 		}
75271bb428fSJakub Kicinski 
753cae73f23SSong Liu 		n = write(fd, buf, member_len);
75471bb428fSJakub Kicinski 		close(fd);
75509f44b75SAndrii Nakryiko 		if (n != (ssize_t)member_len) {
7569a5ab8bfSQuentin Monnet 			p_err("error writing output file: %s",
75771bb428fSJakub Kicinski 			      n < 0 ? strerror(errno) : "short write");
758ebbd7f64SQuentin Monnet 			goto exit_free;
75971bb428fSJakub Kicinski 		}
76052c84d36SQuentin Monnet 
76152c84d36SQuentin Monnet 		if (json_output)
76252c84d36SQuentin Monnet 			jsonw_null(json_wtr);
763cae73f23SSong Liu 	} else if (mode == DUMP_JITED) {
764e6593596SJiong Wang 		const char *name = NULL;
765e6593596SJiong Wang 
766cae73f23SSong Liu 		if (info->ifindex) {
767*ce4f6608SQuentin Monnet 			name = ifindex_to_arch(info->ifindex, info->netns_dev,
768*ce4f6608SQuentin Monnet 					       info->netns_ino, &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 
82355b4de58SQuentin Monnet 				if (disasm_print_insn(img, lens[i], opcodes,
824b053b439SMartin KaFai Lau 						      name, disasm_opt, btf,
825b053b439SMartin KaFai Lau 						      prog_linfo, ksyms[i], i,
82655b4de58SQuentin Monnet 						      linum))
82755b4de58SQuentin Monnet 					goto exit_free;
828b053b439SMartin KaFai Lau 
829f7f62c71SSandipan Das 				img += lens[i];
830f7f62c71SSandipan Das 
831f7f62c71SSandipan Das 				if (json_output)
832f7f62c71SSandipan Das 					jsonw_end_object(json_wtr);
833f7f62c71SSandipan Das 				else
834f7f62c71SSandipan Das 					printf("\n");
835f7f62c71SSandipan Das 			}
836f7f62c71SSandipan Das 
837f7f62c71SSandipan Das 			if (json_output)
838f7f62c71SSandipan Das 				jsonw_end_array(json_wtr);
839f7f62c71SSandipan Das 		} else {
84055b4de58SQuentin Monnet 			if (disasm_print_insn(buf, member_len, opcodes, name,
84155b4de58SQuentin Monnet 					      disasm_opt, btf, NULL, 0, 0,
84255b4de58SQuentin Monnet 					      false))
84355b4de58SQuentin Monnet 				goto exit_free;
844f7f62c71SSandipan Das 		}
845b6c1cedbSJiong Wang 	} else if (visual) {
846b6c1cedbSJiong Wang 		if (json_output)
847b6c1cedbSJiong Wang 			jsonw_null(json_wtr);
848b6c1cedbSJiong Wang 		else
849cae73f23SSong Liu 			dump_xlated_cfg(buf, member_len);
8507105e828SDaniel Borkmann 	} else {
8517105e828SDaniel Borkmann 		kernel_syms_load(&dd);
852cae73f23SSong Liu 		dd.nr_jited_ksyms = info->nr_jited_ksyms;
85309f44b75SAndrii Nakryiko 		dd.jited_ksyms = u64_to_ptr(info->jited_ksyms);
854254471e5SYonghong Song 		dd.btf = btf;
855254471e5SYonghong Song 		dd.func_info = func_info;
856cae73f23SSong Liu 		dd.finfo_rec_size = info->func_info_rec_size;
857b053b439SMartin KaFai Lau 		dd.prog_linfo = prog_linfo;
858f84192eeSSandipan Das 
859f05e2c32SQuentin Monnet 		if (json_output)
860cae73f23SSong Liu 			dump_xlated_json(&dd, buf, member_len, opcodes,
861b053b439SMartin KaFai Lau 					 linum);
862f05e2c32SQuentin Monnet 		else
863cae73f23SSong Liu 			dump_xlated_plain(&dd, buf, member_len, opcodes,
864b053b439SMartin KaFai Lau 					  linum);
8657105e828SDaniel Borkmann 		kernel_syms_destroy(&dd);
8667105e828SDaniel Borkmann 	}
86771bb428fSJakub Kicinski 
868ebbd7f64SQuentin Monnet 	err = 0;
869369e955bSQuentin Monnet 
870ebbd7f64SQuentin Monnet exit_free:
871ebbd7f64SQuentin Monnet 	btf__free(btf);
872ebbd7f64SQuentin Monnet 	bpf_prog_linfo__free(prog_linfo);
873ebbd7f64SQuentin Monnet 	return err;
874ec202509SPaul Chaignon }
87571bb428fSJakub Kicinski 
876ec202509SPaul Chaignon static int do_dump(int argc, char **argv)
877ec202509SPaul Chaignon {
878c59765cfSDave Marchevsky 	struct bpf_prog_info info;
879c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
880c59765cfSDave Marchevsky 	size_t info_data_sz = 0;
881c59765cfSDave Marchevsky 	void *info_data = NULL;
882ec202509SPaul Chaignon 	char *filepath = NULL;
883ec202509SPaul Chaignon 	bool opcodes = false;
884ec202509SPaul Chaignon 	bool visual = false;
885ec202509SPaul Chaignon 	enum dump_mode mode;
886ec202509SPaul Chaignon 	bool linum = false;
887ec202509SPaul Chaignon 	int nb_fds, i = 0;
888c59765cfSDave Marchevsky 	int *fds = NULL;
889ec202509SPaul Chaignon 	int err = -1;
890ec202509SPaul Chaignon 
891ec202509SPaul Chaignon 	if (is_prefix(*argv, "jited")) {
892ec202509SPaul Chaignon 		if (disasm_init())
89371bb428fSJakub Kicinski 			return -1;
894ec202509SPaul Chaignon 		mode = DUMP_JITED;
895ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "xlated")) {
896ec202509SPaul Chaignon 		mode = DUMP_XLATED;
897ec202509SPaul Chaignon 	} else {
898ec202509SPaul Chaignon 		p_err("expected 'xlated' or 'jited', got: %s", *argv);
899ec202509SPaul Chaignon 		return -1;
900ec202509SPaul Chaignon 	}
901ec202509SPaul Chaignon 	NEXT_ARG();
902ec202509SPaul Chaignon 
903ec202509SPaul Chaignon 	if (argc < 2)
904ec202509SPaul Chaignon 		usage();
905ec202509SPaul Chaignon 
906ec202509SPaul Chaignon 	fds = malloc(sizeof(int));
907ec202509SPaul Chaignon 	if (!fds) {
908ec202509SPaul Chaignon 		p_err("mem alloc failed");
909ec202509SPaul Chaignon 		return -1;
910ec202509SPaul Chaignon 	}
911ec202509SPaul Chaignon 	nb_fds = prog_parse_fds(&argc, &argv, &fds);
912ec202509SPaul Chaignon 	if (nb_fds < 1)
913ec202509SPaul Chaignon 		goto exit_free;
914ec202509SPaul Chaignon 
915ec202509SPaul Chaignon 	if (is_prefix(*argv, "file")) {
916ec202509SPaul Chaignon 		NEXT_ARG();
917ec202509SPaul Chaignon 		if (!argc) {
918ec202509SPaul Chaignon 			p_err("expected file path");
919ec202509SPaul Chaignon 			goto exit_close;
920ec202509SPaul Chaignon 		}
921ec202509SPaul Chaignon 		if (nb_fds > 1) {
922ec202509SPaul Chaignon 			p_err("several programs matched");
923ec202509SPaul Chaignon 			goto exit_close;
924ec202509SPaul Chaignon 		}
925ec202509SPaul Chaignon 
926ec202509SPaul Chaignon 		filepath = *argv;
927ec202509SPaul Chaignon 		NEXT_ARG();
928ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "opcodes")) {
929ec202509SPaul Chaignon 		opcodes = true;
930ec202509SPaul Chaignon 		NEXT_ARG();
931ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "visual")) {
932ec202509SPaul Chaignon 		if (nb_fds > 1) {
933ec202509SPaul Chaignon 			p_err("several programs matched");
934ec202509SPaul Chaignon 			goto exit_close;
935ec202509SPaul Chaignon 		}
936ec202509SPaul Chaignon 
937ec202509SPaul Chaignon 		visual = true;
938ec202509SPaul Chaignon 		NEXT_ARG();
939ec202509SPaul Chaignon 	} else if (is_prefix(*argv, "linum")) {
940ec202509SPaul Chaignon 		linum = true;
941ec202509SPaul Chaignon 		NEXT_ARG();
942ec202509SPaul Chaignon 	}
943ec202509SPaul Chaignon 
944ec202509SPaul Chaignon 	if (argc) {
945ec202509SPaul Chaignon 		usage();
946ec202509SPaul Chaignon 		goto exit_close;
947ec202509SPaul Chaignon 	}
948ec202509SPaul Chaignon 
949ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
950ec202509SPaul Chaignon 		jsonw_start_array(json_wtr);	/* root array */
951ec202509SPaul Chaignon 	for (i = 0; i < nb_fds; i++) {
952c59765cfSDave Marchevsky 		memset(&info, 0, sizeof(info));
953c59765cfSDave Marchevsky 
954c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
955c59765cfSDave Marchevsky 		if (err) {
956c59765cfSDave Marchevsky 			p_err("can't get prog info: %s", strerror(errno));
957c59765cfSDave Marchevsky 			break;
958c59765cfSDave Marchevsky 		}
959c59765cfSDave Marchevsky 
960c59765cfSDave Marchevsky 		err = prep_prog_info(&info, mode, &info_data, &info_data_sz);
961c59765cfSDave Marchevsky 		if (err) {
962c59765cfSDave Marchevsky 			p_err("can't grow prog info_data");
963c59765cfSDave Marchevsky 			break;
964c59765cfSDave Marchevsky 		}
965c59765cfSDave Marchevsky 
966c59765cfSDave Marchevsky 		err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
967c59765cfSDave Marchevsky 		if (err) {
968ec202509SPaul Chaignon 			p_err("can't get prog info: %s", strerror(errno));
969ec202509SPaul Chaignon 			break;
970ec202509SPaul Chaignon 		}
971ec202509SPaul Chaignon 
972ec202509SPaul Chaignon 		if (json_output && nb_fds > 1) {
973ec202509SPaul Chaignon 			jsonw_start_object(json_wtr);	/* prog object */
974b662000aSRaman Shukhau 			print_prog_header_json(&info, fds[i]);
975ec202509SPaul Chaignon 			jsonw_name(json_wtr, "insns");
976ec202509SPaul Chaignon 		} else if (nb_fds > 1) {
977b662000aSRaman Shukhau 			print_prog_header_plain(&info, fds[i]);
978ec202509SPaul Chaignon 		}
979ec202509SPaul Chaignon 
980c59765cfSDave Marchevsky 		err = prog_dump(&info, mode, filepath, opcodes, visual, linum);
981ec202509SPaul Chaignon 
982ec202509SPaul Chaignon 		if (json_output && nb_fds > 1)
983ec202509SPaul Chaignon 			jsonw_end_object(json_wtr);	/* prog object */
984ec202509SPaul Chaignon 		else if (i != nb_fds - 1 && nb_fds > 1)
985ec202509SPaul Chaignon 			printf("\n");
986ec202509SPaul Chaignon 
987ec202509SPaul Chaignon 		if (err)
988ec202509SPaul Chaignon 			break;
989ec202509SPaul Chaignon 		close(fds[i]);
990ec202509SPaul Chaignon 	}
991ec202509SPaul Chaignon 	if (json_output && nb_fds > 1)
992ec202509SPaul Chaignon 		jsonw_end_array(json_wtr);	/* root array */
993ec202509SPaul Chaignon 
994ec202509SPaul Chaignon exit_close:
995ec202509SPaul Chaignon 	for (; i < nb_fds; i++)
996ec202509SPaul Chaignon 		close(fds[i]);
997ec202509SPaul Chaignon exit_free:
998c59765cfSDave Marchevsky 	free(info_data);
999ec202509SPaul Chaignon 	free(fds);
1000ec202509SPaul Chaignon 	return err;
100171bb428fSJakub Kicinski }
100271bb428fSJakub Kicinski 
100371bb428fSJakub Kicinski static int do_pin(int argc, char **argv)
100471bb428fSJakub Kicinski {
1005004b45c0SQuentin Monnet 	int err;
1006004b45c0SQuentin Monnet 
100775a1e792SQuentin Monnet 	err = do_pin_any(argc, argv, prog_parse_fd);
1008004b45c0SQuentin Monnet 	if (!err && json_output)
1009004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
1010004b45c0SQuentin Monnet 	return err;
101171bb428fSJakub Kicinski }
101271bb428fSJakub Kicinski 
10133ff5a4dcSJakub Kicinski struct map_replace {
10143ff5a4dcSJakub Kicinski 	int idx;
10153ff5a4dcSJakub Kicinski 	int fd;
10163ff5a4dcSJakub Kicinski 	char *name;
10173ff5a4dcSJakub Kicinski };
10183ff5a4dcSJakub Kicinski 
1019c101189bSQuentin Monnet static int map_replace_compar(const void *p1, const void *p2)
10203ff5a4dcSJakub Kicinski {
10213ff5a4dcSJakub Kicinski 	const struct map_replace *a = p1, *b = p2;
10223ff5a4dcSJakub Kicinski 
10233ff5a4dcSJakub Kicinski 	return a->idx - b->idx;
10243ff5a4dcSJakub Kicinski }
10253ff5a4dcSJakub Kicinski 
1026092f0892SStanislav Fomichev static int parse_attach_detach_args(int argc, char **argv, int *progfd,
1027092f0892SStanislav Fomichev 				    enum bpf_attach_type *attach_type,
1028092f0892SStanislav Fomichev 				    int *mapfd)
1029092f0892SStanislav Fomichev {
1030092f0892SStanislav Fomichev 	if (!REQ_ARGS(3))
1031092f0892SStanislav Fomichev 		return -EINVAL;
1032092f0892SStanislav Fomichev 
1033092f0892SStanislav Fomichev 	*progfd = prog_parse_fd(&argc, &argv);
1034092f0892SStanislav Fomichev 	if (*progfd < 0)
1035092f0892SStanislav Fomichev 		return *progfd;
1036092f0892SStanislav Fomichev 
1037092f0892SStanislav Fomichev 	*attach_type = parse_attach_type(*argv);
1038092f0892SStanislav Fomichev 	if (*attach_type == __MAX_BPF_ATTACH_TYPE) {
1039092f0892SStanislav Fomichev 		p_err("invalid attach/detach type");
1040092f0892SStanislav Fomichev 		return -EINVAL;
1041092f0892SStanislav Fomichev 	}
1042092f0892SStanislav Fomichev 
1043092f0892SStanislav Fomichev 	if (*attach_type == BPF_FLOW_DISSECTOR) {
1044f9b7ff0dSLorenz Bauer 		*mapfd = 0;
1045092f0892SStanislav Fomichev 		return 0;
1046092f0892SStanislav Fomichev 	}
1047092f0892SStanislav Fomichev 
1048092f0892SStanislav Fomichev 	NEXT_ARG();
1049092f0892SStanislav Fomichev 	if (!REQ_ARGS(2))
1050092f0892SStanislav Fomichev 		return -EINVAL;
1051092f0892SStanislav Fomichev 
1052092f0892SStanislav Fomichev 	*mapfd = map_parse_fd(&argc, &argv);
1053092f0892SStanislav Fomichev 	if (*mapfd < 0)
1054092f0892SStanislav Fomichev 		return *mapfd;
1055092f0892SStanislav Fomichev 
1056092f0892SStanislav Fomichev 	return 0;
1057092f0892SStanislav Fomichev }
1058092f0892SStanislav Fomichev 
1059b7d3826cSJohn Fastabend static int do_attach(int argc, char **argv)
1060b7d3826cSJohn Fastabend {
1061b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1062092f0892SStanislav Fomichev 	int err, progfd;
1063092f0892SStanislav Fomichev 	int mapfd;
1064b7d3826cSJohn Fastabend 
1065092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1066092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1067092f0892SStanislav Fomichev 	if (err)
1068092f0892SStanislav Fomichev 		return err;
1069b7d3826cSJohn Fastabend 
1070b7d3826cSJohn Fastabend 	err = bpf_prog_attach(progfd, mapfd, attach_type, 0);
1071b7d3826cSJohn Fastabend 	if (err) {
1072b7d3826cSJohn Fastabend 		p_err("failed prog attach to map");
1073b7d3826cSJohn Fastabend 		return -EINVAL;
1074b7d3826cSJohn Fastabend 	}
1075b7d3826cSJohn Fastabend 
1076b7d3826cSJohn Fastabend 	if (json_output)
1077b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1078b7d3826cSJohn Fastabend 	return 0;
1079b7d3826cSJohn Fastabend }
1080b7d3826cSJohn Fastabend 
1081b7d3826cSJohn Fastabend static int do_detach(int argc, char **argv)
1082b7d3826cSJohn Fastabend {
1083b7d3826cSJohn Fastabend 	enum bpf_attach_type attach_type;
1084092f0892SStanislav Fomichev 	int err, progfd;
1085092f0892SStanislav Fomichev 	int mapfd;
1086b7d3826cSJohn Fastabend 
1087092f0892SStanislav Fomichev 	err = parse_attach_detach_args(argc, argv,
1088092f0892SStanislav Fomichev 				       &progfd, &attach_type, &mapfd);
1089092f0892SStanislav Fomichev 	if (err)
1090092f0892SStanislav Fomichev 		return err;
1091b7d3826cSJohn Fastabend 
1092b7d3826cSJohn Fastabend 	err = bpf_prog_detach2(progfd, mapfd, attach_type);
1093b7d3826cSJohn Fastabend 	if (err) {
1094b7d3826cSJohn Fastabend 		p_err("failed prog detach from map");
1095b7d3826cSJohn Fastabend 		return -EINVAL;
1096b7d3826cSJohn Fastabend 	}
1097b7d3826cSJohn Fastabend 
1098b7d3826cSJohn Fastabend 	if (json_output)
1099b7d3826cSJohn Fastabend 		jsonw_null(json_wtr);
1100b7d3826cSJohn Fastabend 	return 0;
1101b7d3826cSJohn Fastabend }
110277380998SStanislav Fomichev 
1103ba95c745SQuentin Monnet static int check_single_stdin(char *file_data_in, char *file_ctx_in)
1104ba95c745SQuentin Monnet {
1105ba95c745SQuentin Monnet 	if (file_data_in && file_ctx_in &&
1106ba95c745SQuentin Monnet 	    !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) {
1107ba95c745SQuentin Monnet 		p_err("cannot use standard input for both data_in and ctx_in");
1108ba95c745SQuentin Monnet 		return -1;
1109ba95c745SQuentin Monnet 	}
1110ba95c745SQuentin Monnet 
1111ba95c745SQuentin Monnet 	return 0;
1112ba95c745SQuentin Monnet }
1113ba95c745SQuentin Monnet 
1114ba95c745SQuentin Monnet static int get_run_data(const char *fname, void **data_ptr, unsigned int *size)
1115ba95c745SQuentin Monnet {
1116ba95c745SQuentin Monnet 	size_t block_size = 256;
1117ba95c745SQuentin Monnet 	size_t buf_size = block_size;
1118ba95c745SQuentin Monnet 	size_t nb_read = 0;
1119ba95c745SQuentin Monnet 	void *tmp;
1120ba95c745SQuentin Monnet 	FILE *f;
1121ba95c745SQuentin Monnet 
1122ba95c745SQuentin Monnet 	if (!fname) {
1123ba95c745SQuentin Monnet 		*data_ptr = NULL;
1124ba95c745SQuentin Monnet 		*size = 0;
1125ba95c745SQuentin Monnet 		return 0;
1126ba95c745SQuentin Monnet 	}
1127ba95c745SQuentin Monnet 
1128ba95c745SQuentin Monnet 	if (!strcmp(fname, "-"))
1129ba95c745SQuentin Monnet 		f = stdin;
1130ba95c745SQuentin Monnet 	else
1131ba95c745SQuentin Monnet 		f = fopen(fname, "r");
1132ba95c745SQuentin Monnet 	if (!f) {
1133ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1134ba95c745SQuentin Monnet 		return -1;
1135ba95c745SQuentin Monnet 	}
1136ba95c745SQuentin Monnet 
1137ba95c745SQuentin Monnet 	*data_ptr = malloc(block_size);
1138ba95c745SQuentin Monnet 	if (!*data_ptr) {
1139ba95c745SQuentin Monnet 		p_err("failed to allocate memory for data_in/ctx_in: %s",
1140ba95c745SQuentin Monnet 		      strerror(errno));
1141ba95c745SQuentin Monnet 		goto err_fclose;
1142ba95c745SQuentin Monnet 	}
1143ba95c745SQuentin Monnet 
1144ba95c745SQuentin Monnet 	while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) {
1145ba95c745SQuentin Monnet 		if (feof(f))
1146ba95c745SQuentin Monnet 			break;
1147ba95c745SQuentin Monnet 		if (ferror(f)) {
1148ba95c745SQuentin Monnet 			p_err("failed to read data_in/ctx_in from %s: %s",
1149ba95c745SQuentin Monnet 			      fname, strerror(errno));
1150ba95c745SQuentin Monnet 			goto err_free;
1151ba95c745SQuentin Monnet 		}
1152ba95c745SQuentin Monnet 		if (nb_read > buf_size - block_size) {
1153ba95c745SQuentin Monnet 			if (buf_size == UINT32_MAX) {
1154ba95c745SQuentin Monnet 				p_err("data_in/ctx_in is too long (max: %d)",
1155ba95c745SQuentin Monnet 				      UINT32_MAX);
1156ba95c745SQuentin Monnet 				goto err_free;
1157ba95c745SQuentin Monnet 			}
1158ba95c745SQuentin Monnet 			/* No space for fread()-ing next chunk; realloc() */
1159ba95c745SQuentin Monnet 			buf_size *= 2;
1160ba95c745SQuentin Monnet 			tmp = realloc(*data_ptr, buf_size);
1161ba95c745SQuentin Monnet 			if (!tmp) {
1162ba95c745SQuentin Monnet 				p_err("failed to reallocate data_in/ctx_in: %s",
1163ba95c745SQuentin Monnet 				      strerror(errno));
1164ba95c745SQuentin Monnet 				goto err_free;
1165ba95c745SQuentin Monnet 			}
1166ba95c745SQuentin Monnet 			*data_ptr = tmp;
1167ba95c745SQuentin Monnet 		}
1168ba95c745SQuentin Monnet 	}
1169ba95c745SQuentin Monnet 	if (f != stdin)
1170ba95c745SQuentin Monnet 		fclose(f);
1171ba95c745SQuentin Monnet 
1172ba95c745SQuentin Monnet 	*size = nb_read;
1173ba95c745SQuentin Monnet 	return 0;
1174ba95c745SQuentin Monnet 
1175ba95c745SQuentin Monnet err_free:
1176ba95c745SQuentin Monnet 	free(*data_ptr);
1177ba95c745SQuentin Monnet 	*data_ptr = NULL;
1178ba95c745SQuentin Monnet err_fclose:
1179ba95c745SQuentin Monnet 	if (f != stdin)
1180ba95c745SQuentin Monnet 		fclose(f);
1181ba95c745SQuentin Monnet 	return -1;
1182ba95c745SQuentin Monnet }
1183ba95c745SQuentin Monnet 
1184ba95c745SQuentin Monnet static void hex_print(void *data, unsigned int size, FILE *f)
1185ba95c745SQuentin Monnet {
1186ba95c745SQuentin Monnet 	size_t i, j;
1187ba95c745SQuentin Monnet 	char c;
1188ba95c745SQuentin Monnet 
1189ba95c745SQuentin Monnet 	for (i = 0; i < size; i += 16) {
1190ba95c745SQuentin Monnet 		/* Row offset */
1191ba95c745SQuentin Monnet 		fprintf(f, "%07zx\t", i);
1192ba95c745SQuentin Monnet 
1193ba95c745SQuentin Monnet 		/* Hexadecimal values */
1194ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++)
1195ba95c745SQuentin Monnet 			fprintf(f, "%02x%s", *(uint8_t *)(data + j),
1196ba95c745SQuentin Monnet 				j % 2 ? " " : "");
1197ba95c745SQuentin Monnet 		for (; j < i + 16; j++)
1198ba95c745SQuentin Monnet 			fprintf(f, "  %s", j % 2 ? " " : "");
1199ba95c745SQuentin Monnet 
1200ba95c745SQuentin Monnet 		/* ASCII values (if relevant), '.' otherwise */
1201ba95c745SQuentin Monnet 		fprintf(f, "| ");
1202ba95c745SQuentin Monnet 		for (j = i; j < i + 16 && j < size; j++) {
1203ba95c745SQuentin Monnet 			c = *(char *)(data + j);
1204ba95c745SQuentin Monnet 			if (c < ' ' || c > '~')
1205ba95c745SQuentin Monnet 				c = '.';
1206ba95c745SQuentin Monnet 			fprintf(f, "%c%s", c, j == i + 7 ? " " : "");
1207ba95c745SQuentin Monnet 		}
1208ba95c745SQuentin Monnet 
1209ba95c745SQuentin Monnet 		fprintf(f, "\n");
1210ba95c745SQuentin Monnet 	}
1211ba95c745SQuentin Monnet }
1212ba95c745SQuentin Monnet 
1213ba95c745SQuentin Monnet static int
1214ba95c745SQuentin Monnet print_run_output(void *data, unsigned int size, const char *fname,
1215ba95c745SQuentin Monnet 		 const char *json_key)
1216ba95c745SQuentin Monnet {
1217ba95c745SQuentin Monnet 	size_t nb_written;
1218ba95c745SQuentin Monnet 	FILE *f;
1219ba95c745SQuentin Monnet 
1220ba95c745SQuentin Monnet 	if (!fname)
1221ba95c745SQuentin Monnet 		return 0;
1222ba95c745SQuentin Monnet 
1223ba95c745SQuentin Monnet 	if (!strcmp(fname, "-")) {
1224ba95c745SQuentin Monnet 		f = stdout;
1225ba95c745SQuentin Monnet 		if (json_output) {
1226ba95c745SQuentin Monnet 			jsonw_name(json_wtr, json_key);
1227ba95c745SQuentin Monnet 			print_data_json(data, size);
1228ba95c745SQuentin Monnet 		} else {
1229ba95c745SQuentin Monnet 			hex_print(data, size, f);
1230ba95c745SQuentin Monnet 		}
1231ba95c745SQuentin Monnet 		return 0;
1232ba95c745SQuentin Monnet 	}
1233ba95c745SQuentin Monnet 
1234ba95c745SQuentin Monnet 	f = fopen(fname, "w");
1235ba95c745SQuentin Monnet 	if (!f) {
1236ba95c745SQuentin Monnet 		p_err("failed to open %s: %s", fname, strerror(errno));
1237ba95c745SQuentin Monnet 		return -1;
1238ba95c745SQuentin Monnet 	}
1239ba95c745SQuentin Monnet 
1240ba95c745SQuentin Monnet 	nb_written = fwrite(data, 1, size, f);
1241ba95c745SQuentin Monnet 	fclose(f);
1242ba95c745SQuentin Monnet 	if (nb_written != size) {
1243ba95c745SQuentin Monnet 		p_err("failed to write output data/ctx: %s", strerror(errno));
1244ba95c745SQuentin Monnet 		return -1;
1245ba95c745SQuentin Monnet 	}
1246ba95c745SQuentin Monnet 
1247ba95c745SQuentin Monnet 	return 0;
1248ba95c745SQuentin Monnet }
1249ba95c745SQuentin Monnet 
1250ba95c745SQuentin Monnet static int alloc_run_data(void **data_ptr, unsigned int size_out)
1251ba95c745SQuentin Monnet {
1252ba95c745SQuentin Monnet 	*data_ptr = calloc(size_out, 1);
1253ba95c745SQuentin Monnet 	if (!*data_ptr) {
1254ba95c745SQuentin Monnet 		p_err("failed to allocate memory for output data/ctx: %s",
1255ba95c745SQuentin Monnet 		      strerror(errno));
1256ba95c745SQuentin Monnet 		return -1;
1257ba95c745SQuentin Monnet 	}
1258ba95c745SQuentin Monnet 
1259ba95c745SQuentin Monnet 	return 0;
1260ba95c745SQuentin Monnet }
1261ba95c745SQuentin Monnet 
1262ba95c745SQuentin Monnet static int do_run(int argc, char **argv)
1263ba95c745SQuentin Monnet {
1264ba95c745SQuentin Monnet 	char *data_fname_in = NULL, *data_fname_out = NULL;
1265ba95c745SQuentin Monnet 	char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
1266ba95c745SQuentin Monnet 	const unsigned int default_size = SZ_32K;
1267ba95c745SQuentin Monnet 	void *data_in = NULL, *data_out = NULL;
1268ba95c745SQuentin Monnet 	void *ctx_in = NULL, *ctx_out = NULL;
1269ba95c745SQuentin Monnet 	unsigned int repeat = 1;
1270ba95c745SQuentin Monnet 	int fd, err;
12719cce5313SDelyan Kratunov 	LIBBPF_OPTS(bpf_test_run_opts, test_attr);
1272ba95c745SQuentin Monnet 
1273ba95c745SQuentin Monnet 	if (!REQ_ARGS(4))
1274ba95c745SQuentin Monnet 		return -1;
1275ba95c745SQuentin Monnet 
1276ba95c745SQuentin Monnet 	fd = prog_parse_fd(&argc, &argv);
1277ba95c745SQuentin Monnet 	if (fd < 0)
1278ba95c745SQuentin Monnet 		return -1;
1279ba95c745SQuentin Monnet 
1280ba95c745SQuentin Monnet 	while (argc) {
1281ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "data_in", "data_out",
1282ba95c745SQuentin Monnet 					 "data_size_out", NULL))
1283ba95c745SQuentin Monnet 			return -1;
1284ba95c745SQuentin Monnet 		if (detect_common_prefix(*argv, "ctx_in", "ctx_out",
1285ba95c745SQuentin Monnet 					 "ctx_size_out", NULL))
1286ba95c745SQuentin Monnet 			return -1;
1287ba95c745SQuentin Monnet 
1288ba95c745SQuentin Monnet 		if (is_prefix(*argv, "data_in")) {
1289ba95c745SQuentin Monnet 			NEXT_ARG();
1290ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1291ba95c745SQuentin Monnet 				return -1;
1292ba95c745SQuentin Monnet 
1293ba95c745SQuentin Monnet 			data_fname_in = GET_ARG();
1294ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1295ba95c745SQuentin Monnet 				return -1;
1296ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_out")) {
1297ba95c745SQuentin Monnet 			NEXT_ARG();
1298ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1299ba95c745SQuentin Monnet 				return -1;
1300ba95c745SQuentin Monnet 
1301ba95c745SQuentin Monnet 			data_fname_out = GET_ARG();
1302ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "data_size_out")) {
1303ba95c745SQuentin Monnet 			char *endptr;
1304ba95c745SQuentin Monnet 
1305ba95c745SQuentin Monnet 			NEXT_ARG();
1306ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1307ba95c745SQuentin Monnet 				return -1;
1308ba95c745SQuentin Monnet 
1309ba95c745SQuentin Monnet 			test_attr.data_size_out = strtoul(*argv, &endptr, 0);
1310ba95c745SQuentin Monnet 			if (*endptr) {
1311ba95c745SQuentin Monnet 				p_err("can't parse %s as output data size",
1312ba95c745SQuentin Monnet 				      *argv);
1313ba95c745SQuentin Monnet 				return -1;
1314ba95c745SQuentin Monnet 			}
1315ba95c745SQuentin Monnet 			NEXT_ARG();
1316ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_in")) {
1317ba95c745SQuentin Monnet 			NEXT_ARG();
1318ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1319ba95c745SQuentin Monnet 				return -1;
1320ba95c745SQuentin Monnet 
1321ba95c745SQuentin Monnet 			ctx_fname_in = GET_ARG();
1322ba95c745SQuentin Monnet 			if (check_single_stdin(data_fname_in, ctx_fname_in))
1323ba95c745SQuentin Monnet 				return -1;
1324ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_out")) {
1325ba95c745SQuentin Monnet 			NEXT_ARG();
1326ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1327ba95c745SQuentin Monnet 				return -1;
1328ba95c745SQuentin Monnet 
1329ba95c745SQuentin Monnet 			ctx_fname_out = GET_ARG();
1330ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "ctx_size_out")) {
1331ba95c745SQuentin Monnet 			char *endptr;
1332ba95c745SQuentin Monnet 
1333ba95c745SQuentin Monnet 			NEXT_ARG();
1334ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1335ba95c745SQuentin Monnet 				return -1;
1336ba95c745SQuentin Monnet 
1337ba95c745SQuentin Monnet 			test_attr.ctx_size_out = strtoul(*argv, &endptr, 0);
1338ba95c745SQuentin Monnet 			if (*endptr) {
1339ba95c745SQuentin Monnet 				p_err("can't parse %s as output context size",
1340ba95c745SQuentin Monnet 				      *argv);
1341ba95c745SQuentin Monnet 				return -1;
1342ba95c745SQuentin Monnet 			}
1343ba95c745SQuentin Monnet 			NEXT_ARG();
1344ba95c745SQuentin Monnet 		} else if (is_prefix(*argv, "repeat")) {
1345ba95c745SQuentin Monnet 			char *endptr;
1346ba95c745SQuentin Monnet 
1347ba95c745SQuentin Monnet 			NEXT_ARG();
1348ba95c745SQuentin Monnet 			if (!REQ_ARGS(1))
1349ba95c745SQuentin Monnet 				return -1;
1350ba95c745SQuentin Monnet 
1351ba95c745SQuentin Monnet 			repeat = strtoul(*argv, &endptr, 0);
1352ba95c745SQuentin Monnet 			if (*endptr) {
1353ba95c745SQuentin Monnet 				p_err("can't parse %s as repeat number",
1354ba95c745SQuentin Monnet 				      *argv);
1355ba95c745SQuentin Monnet 				return -1;
1356ba95c745SQuentin Monnet 			}
1357ba95c745SQuentin Monnet 			NEXT_ARG();
1358ba95c745SQuentin Monnet 		} else {
1359ba95c745SQuentin 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'?",
1360ba95c745SQuentin Monnet 			      *argv);
1361ba95c745SQuentin Monnet 			return -1;
1362ba95c745SQuentin Monnet 		}
1363ba95c745SQuentin Monnet 	}
1364ba95c745SQuentin Monnet 
1365ba95c745SQuentin Monnet 	err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in);
1366ba95c745SQuentin Monnet 	if (err)
1367ba95c745SQuentin Monnet 		return -1;
1368ba95c745SQuentin Monnet 
1369ba95c745SQuentin Monnet 	if (data_in) {
1370ba95c745SQuentin Monnet 		if (!test_attr.data_size_out)
1371ba95c745SQuentin Monnet 			test_attr.data_size_out = default_size;
1372ba95c745SQuentin Monnet 		err = alloc_run_data(&data_out, test_attr.data_size_out);
1373ba95c745SQuentin Monnet 		if (err)
1374ba95c745SQuentin Monnet 			goto free_data_in;
1375ba95c745SQuentin Monnet 	}
1376ba95c745SQuentin Monnet 
1377ba95c745SQuentin Monnet 	err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in);
1378ba95c745SQuentin Monnet 	if (err)
1379ba95c745SQuentin Monnet 		goto free_data_out;
1380ba95c745SQuentin Monnet 
1381ba95c745SQuentin Monnet 	if (ctx_in) {
1382ba95c745SQuentin Monnet 		if (!test_attr.ctx_size_out)
1383ba95c745SQuentin Monnet 			test_attr.ctx_size_out = default_size;
1384ba95c745SQuentin Monnet 		err = alloc_run_data(&ctx_out, test_attr.ctx_size_out);
1385ba95c745SQuentin Monnet 		if (err)
1386ba95c745SQuentin Monnet 			goto free_ctx_in;
1387ba95c745SQuentin Monnet 	}
1388ba95c745SQuentin Monnet 
1389ba95c745SQuentin Monnet 	test_attr.repeat	= repeat;
1390ba95c745SQuentin Monnet 	test_attr.data_in	= data_in;
1391ba95c745SQuentin Monnet 	test_attr.data_out	= data_out;
1392ba95c745SQuentin Monnet 	test_attr.ctx_in	= ctx_in;
1393ba95c745SQuentin Monnet 	test_attr.ctx_out	= ctx_out;
1394ba95c745SQuentin Monnet 
13959cce5313SDelyan Kratunov 	err = bpf_prog_test_run_opts(fd, &test_attr);
1396ba95c745SQuentin Monnet 	if (err) {
1397ba95c745SQuentin Monnet 		p_err("failed to run program: %s", strerror(errno));
1398ba95c745SQuentin Monnet 		goto free_ctx_out;
1399ba95c745SQuentin Monnet 	}
1400ba95c745SQuentin Monnet 
1401ba95c745SQuentin Monnet 	err = 0;
1402ba95c745SQuentin Monnet 
1403ba95c745SQuentin Monnet 	if (json_output)
1404ba95c745SQuentin Monnet 		jsonw_start_object(json_wtr);	/* root */
1405ba95c745SQuentin Monnet 
1406ba95c745SQuentin Monnet 	/* Do not exit on errors occurring when printing output data/context,
1407ba95c745SQuentin Monnet 	 * we still want to print return value and duration for program run.
1408ba95c745SQuentin Monnet 	 */
1409ba95c745SQuentin Monnet 	if (test_attr.data_size_out)
1410ba95c745SQuentin Monnet 		err += print_run_output(test_attr.data_out,
1411ba95c745SQuentin Monnet 					test_attr.data_size_out,
1412ba95c745SQuentin Monnet 					data_fname_out, "data_out");
1413ba95c745SQuentin Monnet 	if (test_attr.ctx_size_out)
1414ba95c745SQuentin Monnet 		err += print_run_output(test_attr.ctx_out,
1415ba95c745SQuentin Monnet 					test_attr.ctx_size_out,
1416ba95c745SQuentin Monnet 					ctx_fname_out, "ctx_out");
1417ba95c745SQuentin Monnet 
1418ba95c745SQuentin Monnet 	if (json_output) {
1419ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "retval", test_attr.retval);
1420ba95c745SQuentin Monnet 		jsonw_uint_field(json_wtr, "duration", test_attr.duration);
1421ba95c745SQuentin Monnet 		jsonw_end_object(json_wtr);	/* root */
1422ba95c745SQuentin Monnet 	} else {
1423ba95c745SQuentin Monnet 		fprintf(stdout, "Return value: %u, duration%s: %uns\n",
1424ba95c745SQuentin Monnet 			test_attr.retval,
1425ba95c745SQuentin Monnet 			repeat > 1 ? " (average)" : "", test_attr.duration);
1426ba95c745SQuentin Monnet 	}
1427ba95c745SQuentin Monnet 
1428ba95c745SQuentin Monnet free_ctx_out:
1429ba95c745SQuentin Monnet 	free(ctx_out);
1430ba95c745SQuentin Monnet free_ctx_in:
1431ba95c745SQuentin Monnet 	free(ctx_in);
1432ba95c745SQuentin Monnet free_data_out:
1433ba95c745SQuentin Monnet 	free(data_out);
1434ba95c745SQuentin Monnet free_data_in:
1435ba95c745SQuentin Monnet 	free(data_in);
1436ba95c745SQuentin Monnet 
1437ba95c745SQuentin Monnet 	return err;
1438ba95c745SQuentin Monnet }
1439ba95c745SQuentin Monnet 
14406ae32b29SQuentin Monnet static int
14416ae32b29SQuentin Monnet get_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
14426ae32b29SQuentin Monnet 		      enum bpf_attach_type *expected_attach_type)
14436ae32b29SQuentin Monnet {
14446ae32b29SQuentin Monnet 	libbpf_print_fn_t print_backup;
14456ae32b29SQuentin Monnet 	int ret;
14466ae32b29SQuentin Monnet 
14476ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14486ae32b29SQuentin Monnet 	if (!ret)
14496ae32b29SQuentin Monnet 		return ret;
14506ae32b29SQuentin Monnet 
14516ae32b29SQuentin Monnet 	/* libbpf_prog_type_by_name() failed, let's re-run with debug level */
14526ae32b29SQuentin Monnet 	print_backup = libbpf_set_print(print_all_levels);
14536ae32b29SQuentin Monnet 	ret = libbpf_prog_type_by_name(name, prog_type, expected_attach_type);
14546ae32b29SQuentin Monnet 	libbpf_set_print(print_backup);
14556ae32b29SQuentin Monnet 
14566ae32b29SQuentin Monnet 	return ret;
14576ae32b29SQuentin Monnet }
14586ae32b29SQuentin Monnet 
145919526e70SWang Yufen static int
146019526e70SWang Yufen auto_attach_program(struct bpf_program *prog, const char *path)
146119526e70SWang Yufen {
146219526e70SWang Yufen 	struct bpf_link *link;
146319526e70SWang Yufen 	int err;
146419526e70SWang Yufen 
146519526e70SWang Yufen 	link = bpf_program__attach(prog);
146619526e70SWang Yufen 	if (!link) {
146719526e70SWang Yufen 		p_info("Program %s does not support autoattach, falling back to pinning",
146819526e70SWang Yufen 		       bpf_program__name(prog));
146919526e70SWang Yufen 		return bpf_obj_pin(bpf_program__fd(prog), path);
147019526e70SWang Yufen 	}
147119526e70SWang Yufen 
147219526e70SWang Yufen 	err = bpf_link__pin(link, path);
147319526e70SWang Yufen 	bpf_link__destroy(link);
147419526e70SWang Yufen 	return err;
147519526e70SWang Yufen }
147619526e70SWang Yufen 
147719526e70SWang Yufen static int pathname_concat(char *buf, size_t buf_sz, const char *path, const char *name)
147819526e70SWang Yufen {
147919526e70SWang Yufen 	int len;
148019526e70SWang Yufen 
148119526e70SWang Yufen 	len = snprintf(buf, buf_sz, "%s/%s", path, name);
148219526e70SWang Yufen 	if (len < 0)
148319526e70SWang Yufen 		return -EINVAL;
148419526e70SWang Yufen 	if ((size_t)len >= buf_sz)
148519526e70SWang Yufen 		return -ENAMETOOLONG;
148619526e70SWang Yufen 
148719526e70SWang Yufen 	return 0;
148819526e70SWang Yufen }
148919526e70SWang Yufen 
149019526e70SWang Yufen static int
149119526e70SWang Yufen auto_attach_programs(struct bpf_object *obj, const char *path)
149219526e70SWang Yufen {
149319526e70SWang Yufen 	struct bpf_program *prog;
149419526e70SWang Yufen 	char buf[PATH_MAX];
149519526e70SWang Yufen 	int err;
149619526e70SWang Yufen 
149719526e70SWang Yufen 	bpf_object__for_each_program(prog, obj) {
149819526e70SWang Yufen 		err = pathname_concat(buf, sizeof(buf), path, bpf_program__name(prog));
149919526e70SWang Yufen 		if (err)
150019526e70SWang Yufen 			goto err_unpin_programs;
150119526e70SWang Yufen 
150219526e70SWang Yufen 		err = auto_attach_program(prog, buf);
150319526e70SWang Yufen 		if (err)
150419526e70SWang Yufen 			goto err_unpin_programs;
150519526e70SWang Yufen 	}
150619526e70SWang Yufen 
150719526e70SWang Yufen 	return 0;
150819526e70SWang Yufen 
150919526e70SWang Yufen err_unpin_programs:
151019526e70SWang Yufen 	while ((prog = bpf_object__prev_program(obj, prog))) {
151119526e70SWang Yufen 		if (pathname_concat(buf, sizeof(buf), path, bpf_program__name(prog)))
151219526e70SWang Yufen 			continue;
151319526e70SWang Yufen 
151419526e70SWang Yufen 		bpf_program__unpin(prog, buf);
151519526e70SWang Yufen 	}
151619526e70SWang Yufen 
151719526e70SWang Yufen 	return err;
151819526e70SWang Yufen }
151919526e70SWang Yufen 
152077380998SStanislav Fomichev static int load_with_options(int argc, char **argv, bool first_prog_only)
152149a086c2SRoman Gushchin {
152232e3e58eSAndrii Nakryiko 	enum bpf_prog_type common_prog_type = BPF_PROG_TYPE_UNSPEC;
1523e00aca65SAndrii Nakryiko 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
1524e00aca65SAndrii Nakryiko 		.relaxed_maps = relaxed_maps,
1525e00aca65SAndrii Nakryiko 	);
152655d77807SQuentin Monnet 	enum bpf_attach_type expected_attach_type;
15273ff5a4dcSJakub Kicinski 	struct map_replace *map_replace = NULL;
152877380998SStanislav Fomichev 	struct bpf_program *prog = NULL, *pos;
15293ff5a4dcSJakub Kicinski 	unsigned int old_map_fds = 0;
15303767a94bSStanislav Fomichev 	const char *pinmaps = NULL;
153119526e70SWang Yufen 	bool auto_attach = false;
153249a086c2SRoman Gushchin 	struct bpf_object *obj;
1533c8406848SJakub Kicinski 	struct bpf_map *map;
1534c8406848SJakub Kicinski 	const char *pinfile;
15353ff5a4dcSJakub Kicinski 	unsigned int i, j;
1536c8406848SJakub Kicinski 	__u32 ifindex = 0;
153732e3e58eSAndrii Nakryiko 	const char *file;
15383ff5a4dcSJakub Kicinski 	int idx, err;
153949a086c2SRoman Gushchin 
154032e3e58eSAndrii Nakryiko 
15418d1fc3deSJakub Kicinski 	if (!REQ_ARGS(2))
15428d1fc3deSJakub Kicinski 		return -1;
154332e3e58eSAndrii Nakryiko 	file = GET_ARG();
15448d1fc3deSJakub Kicinski 	pinfile = GET_ARG();
154549a086c2SRoman Gushchin 
1546ba6dd679SJakub Kicinski 	while (argc) {
154749f2cba3SJakub Kicinski 		if (is_prefix(*argv, "type")) {
154849f2cba3SJakub Kicinski 			NEXT_ARG();
154949f2cba3SJakub Kicinski 
155032e3e58eSAndrii Nakryiko 			if (common_prog_type != BPF_PROG_TYPE_UNSPEC) {
155149f2cba3SJakub Kicinski 				p_err("program type already specified");
15523ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
155349f2cba3SJakub Kicinski 			}
155449f2cba3SJakub Kicinski 			if (!REQ_ARGS(1))
15553ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
155649f2cba3SJakub Kicinski 
1557314f14abSStanislav Fomichev 			err = libbpf_prog_type_by_name(*argv, &common_prog_type,
1558314f14abSStanislav Fomichev 						       &expected_attach_type);
1559314f14abSStanislav Fomichev 			if (err < 0) {
156049f2cba3SJakub Kicinski 				/* Put a '/' at the end of type to appease libbpf */
1561314f14abSStanislav Fomichev 				char *type = malloc(strlen(*argv) + 2);
1562314f14abSStanislav Fomichev 
156349f2cba3SJakub Kicinski 				if (!type) {
156449f2cba3SJakub Kicinski 					p_err("mem alloc failed");
15653ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
156649f2cba3SJakub Kicinski 				}
156749f2cba3SJakub Kicinski 				*type = 0;
156849f2cba3SJakub Kicinski 				strcat(type, *argv);
156949f2cba3SJakub Kicinski 				strcat(type, "/");
157049f2cba3SJakub Kicinski 
15716ae32b29SQuentin Monnet 				err = get_prog_type_by_name(type, &common_prog_type,
1572c8406848SJakub Kicinski 							    &expected_attach_type);
157349f2cba3SJakub Kicinski 				free(type);
1574c76e4c22STaeung Song 				if (err < 0)
15753ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
1576314f14abSStanislav Fomichev 			}
1577c76e4c22STaeung Song 
157849f2cba3SJakub Kicinski 			NEXT_ARG();
15793ff5a4dcSJakub Kicinski 		} else if (is_prefix(*argv, "map")) {
1580dde7011aSJakub Kicinski 			void *new_map_replace;
15813ff5a4dcSJakub Kicinski 			char *endptr, *name;
15823ff5a4dcSJakub Kicinski 			int fd;
15833ff5a4dcSJakub Kicinski 
15843ff5a4dcSJakub Kicinski 			NEXT_ARG();
15853ff5a4dcSJakub Kicinski 
15863ff5a4dcSJakub Kicinski 			if (!REQ_ARGS(4))
15873ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
15883ff5a4dcSJakub Kicinski 
15893ff5a4dcSJakub Kicinski 			if (is_prefix(*argv, "idx")) {
15903ff5a4dcSJakub Kicinski 				NEXT_ARG();
15913ff5a4dcSJakub Kicinski 
15923ff5a4dcSJakub Kicinski 				idx = strtoul(*argv, &endptr, 0);
15933ff5a4dcSJakub Kicinski 				if (*endptr) {
15943ff5a4dcSJakub Kicinski 					p_err("can't parse %s as IDX", *argv);
15953ff5a4dcSJakub Kicinski 					goto err_free_reuse_maps;
15963ff5a4dcSJakub Kicinski 				}
15973ff5a4dcSJakub Kicinski 				name = NULL;
15983ff5a4dcSJakub Kicinski 			} else if (is_prefix(*argv, "name")) {
15993ff5a4dcSJakub Kicinski 				NEXT_ARG();
16003ff5a4dcSJakub Kicinski 
16013ff5a4dcSJakub Kicinski 				name = *argv;
16023ff5a4dcSJakub Kicinski 				idx = -1;
16033ff5a4dcSJakub Kicinski 			} else {
16043ff5a4dcSJakub Kicinski 				p_err("expected 'idx' or 'name', got: '%s'?",
16053ff5a4dcSJakub Kicinski 				      *argv);
16063ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
16073ff5a4dcSJakub Kicinski 			}
16083ff5a4dcSJakub Kicinski 			NEXT_ARG();
16093ff5a4dcSJakub Kicinski 
16103ff5a4dcSJakub Kicinski 			fd = map_parse_fd(&argc, &argv);
16113ff5a4dcSJakub Kicinski 			if (fd < 0)
16123ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
16133ff5a4dcSJakub Kicinski 
1614a19df713SMauricio Vásquez 			new_map_replace = libbpf_reallocarray(map_replace,
1615dde7011aSJakub Kicinski 							      old_map_fds + 1,
16163ff5a4dcSJakub Kicinski 							      sizeof(*map_replace));
1617dde7011aSJakub Kicinski 			if (!new_map_replace) {
16183ff5a4dcSJakub Kicinski 				p_err("mem alloc failed");
16193ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
16203ff5a4dcSJakub Kicinski 			}
1621dde7011aSJakub Kicinski 			map_replace = new_map_replace;
1622dde7011aSJakub Kicinski 
16233ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].idx = idx;
16243ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].name = name;
16253ff5a4dcSJakub Kicinski 			map_replace[old_map_fds].fd = fd;
16263ff5a4dcSJakub Kicinski 			old_map_fds++;
162749f2cba3SJakub Kicinski 		} else if (is_prefix(*argv, "dev")) {
1628ba6dd679SJakub Kicinski 			NEXT_ARG();
1629ba6dd679SJakub Kicinski 
1630c8406848SJakub Kicinski 			if (ifindex) {
1631ba6dd679SJakub Kicinski 				p_err("offload device already specified");
16323ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1633ba6dd679SJakub Kicinski 			}
1634ba6dd679SJakub Kicinski 			if (!REQ_ARGS(1))
16353ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1636ba6dd679SJakub Kicinski 
1637c8406848SJakub Kicinski 			ifindex = if_nametoindex(*argv);
1638c8406848SJakub Kicinski 			if (!ifindex) {
1639ba6dd679SJakub Kicinski 				p_err("unrecognized netdevice '%s': %s",
1640ba6dd679SJakub Kicinski 				      *argv, strerror(errno));
16413ff5a4dcSJakub Kicinski 				goto err_free_reuse_maps;
1642ba6dd679SJakub Kicinski 			}
1643ba6dd679SJakub Kicinski 			NEXT_ARG();
16443767a94bSStanislav Fomichev 		} else if (is_prefix(*argv, "pinmaps")) {
16453767a94bSStanislav Fomichev 			NEXT_ARG();
16463767a94bSStanislav Fomichev 
16473767a94bSStanislav Fomichev 			if (!REQ_ARGS(1))
16483767a94bSStanislav Fomichev 				goto err_free_reuse_maps;
16493767a94bSStanislav Fomichev 
16503767a94bSStanislav Fomichev 			pinmaps = GET_ARG();
165119526e70SWang Yufen 		} else if (is_prefix(*argv, "autoattach")) {
165219526e70SWang Yufen 			auto_attach = true;
165319526e70SWang Yufen 			NEXT_ARG();
1654ba6dd679SJakub Kicinski 		} else {
16553ff5a4dcSJakub Kicinski 			p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
1656ba6dd679SJakub Kicinski 			      *argv);
16573ff5a4dcSJakub Kicinski 			goto err_free_reuse_maps;
1658ba6dd679SJakub Kicinski 		}
1659ba6dd679SJakub Kicinski 	}
1660ba6dd679SJakub Kicinski 
16616b4384ffSQuentin Monnet 	set_max_rlimit();
16626b4384ffSQuentin Monnet 
1663b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1664b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1665b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1666b59e4ce8SAndrii Nakryiko 
166732e3e58eSAndrii Nakryiko 	obj = bpf_object__open_file(file, &open_opts);
1668d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1669c8406848SJakub Kicinski 		p_err("failed to open object file");
16703ff5a4dcSJakub Kicinski 		goto err_free_reuse_maps;
167149a086c2SRoman Gushchin 	}
167249a086c2SRoman Gushchin 
167377380998SStanislav Fomichev 	bpf_object__for_each_program(pos, obj) {
167432e3e58eSAndrii Nakryiko 		enum bpf_prog_type prog_type = common_prog_type;
1675c8406848SJakub Kicinski 
167632e3e58eSAndrii Nakryiko 		if (prog_type == BPF_PROG_TYPE_UNSPEC) {
1677fd17e272SAndrii Nakryiko 			const char *sec_name = bpf_program__section_name(pos);
1678c8406848SJakub Kicinski 
16796ae32b29SQuentin Monnet 			err = get_prog_type_by_name(sec_name, &prog_type,
1680c8406848SJakub Kicinski 						    &expected_attach_type);
1681c76e4c22STaeung Song 			if (err < 0)
1682c8406848SJakub Kicinski 				goto err_close_obj;
1683c8406848SJakub Kicinski 		}
168477380998SStanislav Fomichev 
168577380998SStanislav Fomichev 		bpf_program__set_ifindex(pos, ifindex);
168677380998SStanislav Fomichev 		bpf_program__set_type(pos, prog_type);
168777380998SStanislav Fomichev 		bpf_program__set_expected_attach_type(pos, expected_attach_type);
168877380998SStanislav Fomichev 	}
1689c8406848SJakub Kicinski 
16903ff5a4dcSJakub Kicinski 	qsort(map_replace, old_map_fds, sizeof(*map_replace),
16913ff5a4dcSJakub Kicinski 	      map_replace_compar);
16923ff5a4dcSJakub Kicinski 
16933ff5a4dcSJakub Kicinski 	/* After the sort maps by name will be first on the list, because they
16943ff5a4dcSJakub Kicinski 	 * have idx == -1.  Resolve them.
16953ff5a4dcSJakub Kicinski 	 */
16963ff5a4dcSJakub Kicinski 	j = 0;
16973ff5a4dcSJakub Kicinski 	while (j < old_map_fds && map_replace[j].name) {
16983ff5a4dcSJakub Kicinski 		i = 0;
1699f74a53d9SJakub Kicinski 		bpf_object__for_each_map(map, obj) {
17003ff5a4dcSJakub Kicinski 			if (!strcmp(bpf_map__name(map), map_replace[j].name)) {
17013ff5a4dcSJakub Kicinski 				map_replace[j].idx = i;
17023ff5a4dcSJakub Kicinski 				break;
17033ff5a4dcSJakub Kicinski 			}
17043ff5a4dcSJakub Kicinski 			i++;
17053ff5a4dcSJakub Kicinski 		}
17063ff5a4dcSJakub Kicinski 		if (map_replace[j].idx == -1) {
17073ff5a4dcSJakub Kicinski 			p_err("unable to find map '%s'", map_replace[j].name);
17083ff5a4dcSJakub Kicinski 			goto err_close_obj;
17093ff5a4dcSJakub Kicinski 		}
17103ff5a4dcSJakub Kicinski 		j++;
17113ff5a4dcSJakub Kicinski 	}
17123ff5a4dcSJakub Kicinski 	/* Resort if any names were resolved */
17133ff5a4dcSJakub Kicinski 	if (j)
17143ff5a4dcSJakub Kicinski 		qsort(map_replace, old_map_fds, sizeof(*map_replace),
17153ff5a4dcSJakub Kicinski 		      map_replace_compar);
17163ff5a4dcSJakub Kicinski 
17173ff5a4dcSJakub Kicinski 	/* Set ifindex and name reuse */
17183ff5a4dcSJakub Kicinski 	j = 0;
17193ff5a4dcSJakub Kicinski 	idx = 0;
1720f74a53d9SJakub Kicinski 	bpf_object__for_each_map(map, obj) {
17219855c131SChristy Lee 		if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
1722c8406848SJakub Kicinski 			bpf_map__set_ifindex(map, ifindex);
1723c8406848SJakub Kicinski 
17243ff5a4dcSJakub Kicinski 		if (j < old_map_fds && idx == map_replace[j].idx) {
17253ff5a4dcSJakub Kicinski 			err = bpf_map__reuse_fd(map, map_replace[j++].fd);
17263ff5a4dcSJakub Kicinski 			if (err) {
17273ff5a4dcSJakub Kicinski 				p_err("unable to set up map reuse: %d", err);
17283ff5a4dcSJakub Kicinski 				goto err_close_obj;
17293ff5a4dcSJakub Kicinski 			}
17303ff5a4dcSJakub Kicinski 
17313ff5a4dcSJakub Kicinski 			/* Next reuse wants to apply to the same map */
17323ff5a4dcSJakub Kicinski 			if (j < old_map_fds && map_replace[j].idx == idx) {
17333ff5a4dcSJakub Kicinski 				p_err("replacement for map idx %d specified more than once",
17343ff5a4dcSJakub Kicinski 				      idx);
17353ff5a4dcSJakub Kicinski 				goto err_close_obj;
17363ff5a4dcSJakub Kicinski 			}
17373ff5a4dcSJakub Kicinski 		}
17383ff5a4dcSJakub Kicinski 
17393ff5a4dcSJakub Kicinski 		idx++;
17403ff5a4dcSJakub Kicinski 	}
17413ff5a4dcSJakub Kicinski 	if (j < old_map_fds) {
17423ff5a4dcSJakub Kicinski 		p_err("map idx '%d' not used", map_replace[j].idx);
17433ff5a4dcSJakub Kicinski 		goto err_close_obj;
17443ff5a4dcSJakub Kicinski 	}
17453ff5a4dcSJakub Kicinski 
1746b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1747c8406848SJakub Kicinski 	if (err) {
1748c8406848SJakub Kicinski 		p_err("failed to load object file");
1749c8406848SJakub Kicinski 		goto err_close_obj;
1750c8406848SJakub Kicinski 	}
1751c8406848SJakub Kicinski 
175277380998SStanislav Fomichev 	err = mount_bpffs_for_pin(pinfile);
175377380998SStanislav Fomichev 	if (err)
1754bfee71fbSJakub Kicinski 		goto err_close_obj;
175549a086c2SRoman Gushchin 
175677380998SStanislav Fomichev 	if (first_prog_only) {
17576f2b219bSHengqi Chen 		prog = bpf_object__next_program(obj, NULL);
175877380998SStanislav Fomichev 		if (!prog) {
175977380998SStanislav Fomichev 			p_err("object file doesn't contain any bpf program");
176077380998SStanislav Fomichev 			goto err_close_obj;
176177380998SStanislav Fomichev 		}
176277380998SStanislav Fomichev 
176319526e70SWang Yufen 		if (auto_attach)
176419526e70SWang Yufen 			err = auto_attach_program(prog, pinfile);
176519526e70SWang Yufen 		else
176677380998SStanislav Fomichev 			err = bpf_obj_pin(bpf_program__fd(prog), pinfile);
176777380998SStanislav Fomichev 		if (err) {
176877380998SStanislav Fomichev 			p_err("failed to pin program %s",
1769fd17e272SAndrii Nakryiko 			      bpf_program__section_name(prog));
177077380998SStanislav Fomichev 			goto err_close_obj;
177177380998SStanislav Fomichev 		}
177277380998SStanislav Fomichev 	} else {
177319526e70SWang Yufen 		if (auto_attach)
177419526e70SWang Yufen 			err = auto_attach_programs(obj, pinfile);
177519526e70SWang Yufen 		else
177677380998SStanislav Fomichev 			err = bpf_object__pin_programs(obj, pinfile);
177777380998SStanislav Fomichev 		if (err) {
177877380998SStanislav Fomichev 			p_err("failed to pin all programs");
177977380998SStanislav Fomichev 			goto err_close_obj;
178077380998SStanislav Fomichev 		}
178177380998SStanislav Fomichev 	}
178277380998SStanislav Fomichev 
17833767a94bSStanislav Fomichev 	if (pinmaps) {
17843767a94bSStanislav Fomichev 		err = bpf_object__pin_maps(obj, pinmaps);
17853767a94bSStanislav Fomichev 		if (err) {
17863767a94bSStanislav Fomichev 			p_err("failed to pin all maps");
17873767a94bSStanislav Fomichev 			goto err_unpin;
17883767a94bSStanislav Fomichev 		}
17893767a94bSStanislav Fomichev 	}
17903767a94bSStanislav Fomichev 
179149a086c2SRoman Gushchin 	if (json_output)
179249a086c2SRoman Gushchin 		jsonw_null(json_wtr);
179349a086c2SRoman Gushchin 
1794bfee71fbSJakub Kicinski 	bpf_object__close(obj);
17953ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
17963ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
17973ff5a4dcSJakub Kicinski 	free(map_replace);
1798bfee71fbSJakub Kicinski 
179949a086c2SRoman Gushchin 	return 0;
1800bfee71fbSJakub Kicinski 
18013767a94bSStanislav Fomichev err_unpin:
18023767a94bSStanislav Fomichev 	if (first_prog_only)
18033767a94bSStanislav Fomichev 		unlink(pinfile);
18043767a94bSStanislav Fomichev 	else
18053767a94bSStanislav Fomichev 		bpf_object__unpin_programs(obj, pinfile);
1806bfee71fbSJakub Kicinski err_close_obj:
1807314f14abSStanislav Fomichev 	if (!legacy_libbpf) {
1808314f14abSStanislav Fomichev 		p_info("Warning: bpftool is now running in libbpf strict mode and has more stringent requirements about BPF programs.\n"
1809314f14abSStanislav Fomichev 		       "If it used to work for this object file but now doesn't, see --legacy option for more details.\n");
1810314f14abSStanislav Fomichev 	}
1811314f14abSStanislav Fomichev 
1812bfee71fbSJakub Kicinski 	bpf_object__close(obj);
18133ff5a4dcSJakub Kicinski err_free_reuse_maps:
18143ff5a4dcSJakub Kicinski 	for (i = 0; i < old_map_fds; i++)
18153ff5a4dcSJakub Kicinski 		close(map_replace[i].fd);
18163ff5a4dcSJakub Kicinski 	free(map_replace);
1817bfee71fbSJakub Kicinski 	return -1;
181849a086c2SRoman Gushchin }
181949a086c2SRoman Gushchin 
1820d510296dSAlexei Starovoitov static int count_open_fds(void)
1821d510296dSAlexei Starovoitov {
1822d510296dSAlexei Starovoitov 	DIR *dp = opendir("/proc/self/fd");
1823d510296dSAlexei Starovoitov 	struct dirent *de;
1824d510296dSAlexei Starovoitov 	int cnt = -3;
1825d510296dSAlexei Starovoitov 
1826d510296dSAlexei Starovoitov 	if (!dp)
1827d510296dSAlexei Starovoitov 		return -1;
1828d510296dSAlexei Starovoitov 
1829d510296dSAlexei Starovoitov 	while ((de = readdir(dp)))
1830d510296dSAlexei Starovoitov 		cnt++;
1831d510296dSAlexei Starovoitov 
1832d510296dSAlexei Starovoitov 	closedir(dp);
1833d510296dSAlexei Starovoitov 	return cnt;
1834d510296dSAlexei Starovoitov }
1835d510296dSAlexei Starovoitov 
1836d510296dSAlexei Starovoitov static int try_loader(struct gen_loader_opts *gen)
1837d510296dSAlexei Starovoitov {
1838d510296dSAlexei Starovoitov 	struct bpf_load_and_run_opts opts = {};
1839d510296dSAlexei Starovoitov 	struct bpf_loader_ctx *ctx;
1840d510296dSAlexei Starovoitov 	int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
1841d510296dSAlexei Starovoitov 					     sizeof(struct bpf_prog_desc));
1842d510296dSAlexei Starovoitov 	int log_buf_sz = (1u << 24) - 1;
1843d510296dSAlexei Starovoitov 	int err, fds_before, fd_delta;
1844942df4dcSAlexei Starovoitov 	char *log_buf = NULL;
1845d510296dSAlexei Starovoitov 
1846d510296dSAlexei Starovoitov 	ctx = alloca(ctx_sz);
1847d510296dSAlexei Starovoitov 	memset(ctx, 0, ctx_sz);
1848d510296dSAlexei Starovoitov 	ctx->sz = ctx_sz;
1849942df4dcSAlexei Starovoitov 	if (verifier_logs) {
1850942df4dcSAlexei Starovoitov 		ctx->log_level = 1 + 2 + 4;
1851d510296dSAlexei Starovoitov 		ctx->log_size = log_buf_sz;
1852d510296dSAlexei Starovoitov 		log_buf = malloc(log_buf_sz);
1853d510296dSAlexei Starovoitov 		if (!log_buf)
1854d510296dSAlexei Starovoitov 			return -ENOMEM;
1855d510296dSAlexei Starovoitov 		ctx->log_buf = (long) log_buf;
1856942df4dcSAlexei Starovoitov 	}
1857d510296dSAlexei Starovoitov 	opts.ctx = ctx;
1858d510296dSAlexei Starovoitov 	opts.data = gen->data;
1859d510296dSAlexei Starovoitov 	opts.data_sz = gen->data_sz;
1860d510296dSAlexei Starovoitov 	opts.insns = gen->insns;
1861d510296dSAlexei Starovoitov 	opts.insns_sz = gen->insns_sz;
1862d510296dSAlexei Starovoitov 	fds_before = count_open_fds();
1863d510296dSAlexei Starovoitov 	err = bpf_load_and_run(&opts);
1864d510296dSAlexei Starovoitov 	fd_delta = count_open_fds() - fds_before;
1865942df4dcSAlexei Starovoitov 	if (err < 0 || verifier_logs) {
1866d510296dSAlexei Starovoitov 		fprintf(stderr, "err %d\n%s\n%s", err, opts.errstr, log_buf);
1867942df4dcSAlexei Starovoitov 		if (fd_delta && err < 0)
1868d510296dSAlexei Starovoitov 			fprintf(stderr, "loader prog leaked %d FDs\n",
1869d510296dSAlexei Starovoitov 				fd_delta);
1870d510296dSAlexei Starovoitov 	}
1871d510296dSAlexei Starovoitov 	free(log_buf);
1872d510296dSAlexei Starovoitov 	return err;
1873d510296dSAlexei Starovoitov }
1874d510296dSAlexei Starovoitov 
1875d510296dSAlexei Starovoitov static int do_loader(int argc, char **argv)
1876d510296dSAlexei Starovoitov {
1877d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts);
1878d510296dSAlexei Starovoitov 	DECLARE_LIBBPF_OPTS(gen_loader_opts, gen);
1879d510296dSAlexei Starovoitov 	struct bpf_object *obj;
1880d510296dSAlexei Starovoitov 	const char *file;
1881d510296dSAlexei Starovoitov 	int err = 0;
1882d510296dSAlexei Starovoitov 
1883d510296dSAlexei Starovoitov 	if (!REQ_ARGS(1))
1884d510296dSAlexei Starovoitov 		return -1;
1885d510296dSAlexei Starovoitov 	file = GET_ARG();
1886d510296dSAlexei Starovoitov 
1887b59e4ce8SAndrii Nakryiko 	if (verifier_logs)
1888b59e4ce8SAndrii Nakryiko 		/* log_level1 + log_level2 + stats, but not stable UAPI */
1889b59e4ce8SAndrii Nakryiko 		open_opts.kernel_log_level = 1 + 2 + 4;
1890b59e4ce8SAndrii Nakryiko 
1891d510296dSAlexei Starovoitov 	obj = bpf_object__open_file(file, &open_opts);
1892d510296dSAlexei Starovoitov 	if (libbpf_get_error(obj)) {
1893d510296dSAlexei Starovoitov 		p_err("failed to open object file");
1894d510296dSAlexei Starovoitov 		goto err_close_obj;
1895d510296dSAlexei Starovoitov 	}
1896d510296dSAlexei Starovoitov 
1897d510296dSAlexei Starovoitov 	err = bpf_object__gen_loader(obj, &gen);
1898d510296dSAlexei Starovoitov 	if (err)
1899d510296dSAlexei Starovoitov 		goto err_close_obj;
1900d510296dSAlexei Starovoitov 
1901b59e4ce8SAndrii Nakryiko 	err = bpf_object__load(obj);
1902d510296dSAlexei Starovoitov 	if (err) {
1903d510296dSAlexei Starovoitov 		p_err("failed to load object file");
1904d510296dSAlexei Starovoitov 		goto err_close_obj;
1905d510296dSAlexei Starovoitov 	}
1906d510296dSAlexei Starovoitov 
1907d510296dSAlexei Starovoitov 	if (verifier_logs) {
1908d510296dSAlexei Starovoitov 		struct dump_data dd = {};
1909d510296dSAlexei Starovoitov 
1910d510296dSAlexei Starovoitov 		kernel_syms_load(&dd);
1911d510296dSAlexei Starovoitov 		dump_xlated_plain(&dd, (void *)gen.insns, gen.insns_sz, false, false);
1912d510296dSAlexei Starovoitov 		kernel_syms_destroy(&dd);
1913d510296dSAlexei Starovoitov 	}
1914d510296dSAlexei Starovoitov 	err = try_loader(&gen);
1915d510296dSAlexei Starovoitov err_close_obj:
1916d510296dSAlexei Starovoitov 	bpf_object__close(obj);
1917d510296dSAlexei Starovoitov 	return err;
1918d510296dSAlexei Starovoitov }
1919d510296dSAlexei Starovoitov 
192077380998SStanislav Fomichev static int do_load(int argc, char **argv)
192177380998SStanislav Fomichev {
1922d510296dSAlexei Starovoitov 	if (use_loader)
1923d510296dSAlexei Starovoitov 		return do_loader(argc, argv);
192477380998SStanislav Fomichev 	return load_with_options(argc, argv, true);
192577380998SStanislav Fomichev }
192677380998SStanislav Fomichev 
192777380998SStanislav Fomichev static int do_loadall(int argc, char **argv)
192877380998SStanislav Fomichev {
192977380998SStanislav Fomichev 	return load_with_options(argc, argv, false);
193077380998SStanislav Fomichev }
193177380998SStanislav Fomichev 
193247c09d6aSSong Liu #ifdef BPFTOOL_WITHOUT_SKELETONS
193347c09d6aSSong Liu 
193447c09d6aSSong Liu static int do_profile(int argc, char **argv)
193547c09d6aSSong Liu {
193614e5728fSSong Liu 	p_err("bpftool prog profile command is not supported. Please build bpftool with clang >= 10.0.0");
193747c09d6aSSong Liu 	return 0;
193847c09d6aSSong Liu }
193947c09d6aSSong Liu 
194047c09d6aSSong Liu #else /* BPFTOOL_WITHOUT_SKELETONS */
194147c09d6aSSong Liu 
194247c09d6aSSong Liu #include "profiler.skel.h"
194347c09d6aSSong Liu 
194447c09d6aSSong Liu struct profile_metric {
194547c09d6aSSong Liu 	const char *name;
194647c09d6aSSong Liu 	struct bpf_perf_event_value val;
194747c09d6aSSong Liu 	struct perf_event_attr attr;
194847c09d6aSSong Liu 	bool selected;
194947c09d6aSSong Liu 
195047c09d6aSSong Liu 	/* calculate ratios like instructions per cycle */
195147c09d6aSSong Liu 	const int ratio_metric; /* 0 for N/A, 1 for index 0 (cycles) */
195247c09d6aSSong Liu 	const char *ratio_desc;
195347c09d6aSSong Liu 	const float ratio_mul;
195447c09d6aSSong Liu } metrics[] = {
195547c09d6aSSong Liu 	{
195647c09d6aSSong Liu 		.name = "cycles",
195747c09d6aSSong Liu 		.attr = {
195847c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
195947c09d6aSSong Liu 			.config = PERF_COUNT_HW_CPU_CYCLES,
196047c09d6aSSong Liu 			.exclude_user = 1,
196147c09d6aSSong Liu 		},
196247c09d6aSSong Liu 	},
196347c09d6aSSong Liu 	{
196447c09d6aSSong Liu 		.name = "instructions",
196547c09d6aSSong Liu 		.attr = {
196647c09d6aSSong Liu 			.type = PERF_TYPE_HARDWARE,
196747c09d6aSSong Liu 			.config = PERF_COUNT_HW_INSTRUCTIONS,
196847c09d6aSSong Liu 			.exclude_user = 1,
196947c09d6aSSong Liu 		},
197047c09d6aSSong Liu 		.ratio_metric = 1,
197147c09d6aSSong Liu 		.ratio_desc = "insns per cycle",
197247c09d6aSSong Liu 		.ratio_mul = 1.0,
197347c09d6aSSong Liu 	},
197447c09d6aSSong Liu 	{
197547c09d6aSSong Liu 		.name = "l1d_loads",
197647c09d6aSSong Liu 		.attr = {
197747c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
197847c09d6aSSong Liu 			.config =
197947c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_L1D |
198047c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
198147c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
198247c09d6aSSong Liu 			.exclude_user = 1,
198347c09d6aSSong Liu 		},
198447c09d6aSSong Liu 	},
198547c09d6aSSong Liu 	{
198647c09d6aSSong Liu 		.name = "llc_misses",
198747c09d6aSSong Liu 		.attr = {
198847c09d6aSSong Liu 			.type = PERF_TYPE_HW_CACHE,
198947c09d6aSSong Liu 			.config =
199047c09d6aSSong Liu 				PERF_COUNT_HW_CACHE_LL |
199147c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
199247c09d6aSSong Liu 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
199347c09d6aSSong Liu 			.exclude_user = 1
199447c09d6aSSong Liu 		},
199547c09d6aSSong Liu 		.ratio_metric = 2,
199647c09d6aSSong Liu 		.ratio_desc = "LLC misses per million insns",
199747c09d6aSSong Liu 		.ratio_mul = 1e6,
199847c09d6aSSong Liu 	},
1999450d060eSYonghong Song 	{
2000450d060eSYonghong Song 		.name = "itlb_misses",
2001450d060eSYonghong Song 		.attr = {
2002450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
2003450d060eSYonghong Song 			.config =
2004450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_ITLB |
2005450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
2006450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
2007450d060eSYonghong Song 			.exclude_user = 1
2008450d060eSYonghong Song 		},
2009450d060eSYonghong Song 		.ratio_metric = 2,
2010450d060eSYonghong Song 		.ratio_desc = "itlb misses per million insns",
2011450d060eSYonghong Song 		.ratio_mul = 1e6,
2012450d060eSYonghong Song 	},
2013450d060eSYonghong Song 	{
2014450d060eSYonghong Song 		.name = "dtlb_misses",
2015450d060eSYonghong Song 		.attr = {
2016450d060eSYonghong Song 			.type = PERF_TYPE_HW_CACHE,
2017450d060eSYonghong Song 			.config =
2018450d060eSYonghong Song 				PERF_COUNT_HW_CACHE_DTLB |
2019450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
2020450d060eSYonghong Song 				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
2021450d060eSYonghong Song 			.exclude_user = 1
2022450d060eSYonghong Song 		},
2023450d060eSYonghong Song 		.ratio_metric = 2,
2024450d060eSYonghong Song 		.ratio_desc = "dtlb misses per million insns",
2025450d060eSYonghong Song 		.ratio_mul = 1e6,
2026450d060eSYonghong Song 	},
202747c09d6aSSong Liu };
202847c09d6aSSong Liu 
202947c09d6aSSong Liu static __u64 profile_total_count;
203047c09d6aSSong Liu 
203147c09d6aSSong Liu #define MAX_NUM_PROFILE_METRICS 4
203247c09d6aSSong Liu 
203347c09d6aSSong Liu static int profile_parse_metrics(int argc, char **argv)
203447c09d6aSSong Liu {
203547c09d6aSSong Liu 	unsigned int metric_cnt;
203647c09d6aSSong Liu 	int selected_cnt = 0;
203747c09d6aSSong Liu 	unsigned int i;
203847c09d6aSSong Liu 
20395eff8c18SRongguang Wei 	metric_cnt = ARRAY_SIZE(metrics);
204047c09d6aSSong Liu 
204147c09d6aSSong Liu 	while (argc > 0) {
204247c09d6aSSong Liu 		for (i = 0; i < metric_cnt; i++) {
204347c09d6aSSong Liu 			if (is_prefix(argv[0], metrics[i].name)) {
204447c09d6aSSong Liu 				if (!metrics[i].selected)
204547c09d6aSSong Liu 					selected_cnt++;
204647c09d6aSSong Liu 				metrics[i].selected = true;
204747c09d6aSSong Liu 				break;
204847c09d6aSSong Liu 			}
204947c09d6aSSong Liu 		}
205047c09d6aSSong Liu 		if (i == metric_cnt) {
205147c09d6aSSong Liu 			p_err("unknown metric %s", argv[0]);
205247c09d6aSSong Liu 			return -1;
205347c09d6aSSong Liu 		}
205447c09d6aSSong Liu 		NEXT_ARG();
205547c09d6aSSong Liu 	}
205647c09d6aSSong Liu 	if (selected_cnt > MAX_NUM_PROFILE_METRICS) {
205747c09d6aSSong Liu 		p_err("too many (%d) metrics, please specify no more than %d metrics at at time",
205847c09d6aSSong Liu 		      selected_cnt, MAX_NUM_PROFILE_METRICS);
205947c09d6aSSong Liu 		return -1;
206047c09d6aSSong Liu 	}
206147c09d6aSSong Liu 	return selected_cnt;
206247c09d6aSSong Liu }
206347c09d6aSSong Liu 
206447c09d6aSSong Liu static void profile_read_values(struct profiler_bpf *obj)
206547c09d6aSSong Liu {
206647c09d6aSSong Liu 	__u32 m, cpu, num_cpu = obj->rodata->num_cpu;
206747c09d6aSSong Liu 	int reading_map_fd, count_map_fd;
206847c09d6aSSong Liu 	__u64 counts[num_cpu];
206947c09d6aSSong Liu 	__u32 key = 0;
207047c09d6aSSong Liu 	int err;
207147c09d6aSSong Liu 
207247c09d6aSSong Liu 	reading_map_fd = bpf_map__fd(obj->maps.accum_readings);
207347c09d6aSSong Liu 	count_map_fd = bpf_map__fd(obj->maps.counts);
207447c09d6aSSong Liu 	if (reading_map_fd < 0 || count_map_fd < 0) {
207547c09d6aSSong Liu 		p_err("failed to get fd for map");
207647c09d6aSSong Liu 		return;
207747c09d6aSSong Liu 	}
207847c09d6aSSong Liu 
207947c09d6aSSong Liu 	err = bpf_map_lookup_elem(count_map_fd, &key, counts);
208047c09d6aSSong Liu 	if (err) {
208147c09d6aSSong Liu 		p_err("failed to read count_map: %s", strerror(errno));
208247c09d6aSSong Liu 		return;
208347c09d6aSSong Liu 	}
208447c09d6aSSong Liu 
208547c09d6aSSong Liu 	profile_total_count = 0;
208647c09d6aSSong Liu 	for (cpu = 0; cpu < num_cpu; cpu++)
208747c09d6aSSong Liu 		profile_total_count += counts[cpu];
208847c09d6aSSong Liu 
208947c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
209047c09d6aSSong Liu 		struct bpf_perf_event_value values[num_cpu];
209147c09d6aSSong Liu 
209247c09d6aSSong Liu 		if (!metrics[m].selected)
209347c09d6aSSong Liu 			continue;
209447c09d6aSSong Liu 
209547c09d6aSSong Liu 		err = bpf_map_lookup_elem(reading_map_fd, &key, values);
209647c09d6aSSong Liu 		if (err) {
209747c09d6aSSong Liu 			p_err("failed to read reading_map: %s",
209847c09d6aSSong Liu 			      strerror(errno));
209947c09d6aSSong Liu 			return;
210047c09d6aSSong Liu 		}
210147c09d6aSSong Liu 		for (cpu = 0; cpu < num_cpu; cpu++) {
210247c09d6aSSong Liu 			metrics[m].val.counter += values[cpu].counter;
210347c09d6aSSong Liu 			metrics[m].val.enabled += values[cpu].enabled;
210447c09d6aSSong Liu 			metrics[m].val.running += values[cpu].running;
210547c09d6aSSong Liu 		}
210647c09d6aSSong Liu 		key++;
210747c09d6aSSong Liu 	}
210847c09d6aSSong Liu }
210947c09d6aSSong Liu 
211047c09d6aSSong Liu static void profile_print_readings_json(void)
211147c09d6aSSong Liu {
211247c09d6aSSong Liu 	__u32 m;
211347c09d6aSSong Liu 
211447c09d6aSSong Liu 	jsonw_start_array(json_wtr);
211547c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
211647c09d6aSSong Liu 		if (!metrics[m].selected)
211747c09d6aSSong Liu 			continue;
211847c09d6aSSong Liu 		jsonw_start_object(json_wtr);
211947c09d6aSSong Liu 		jsonw_string_field(json_wtr, "metric", metrics[m].name);
212047c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "run_cnt", profile_total_count);
212147c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "value", metrics[m].val.counter);
212247c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "enabled", metrics[m].val.enabled);
212347c09d6aSSong Liu 		jsonw_lluint_field(json_wtr, "running", metrics[m].val.running);
212447c09d6aSSong Liu 
212547c09d6aSSong Liu 		jsonw_end_object(json_wtr);
212647c09d6aSSong Liu 	}
212747c09d6aSSong Liu 	jsonw_end_array(json_wtr);
212847c09d6aSSong Liu }
212947c09d6aSSong Liu 
213047c09d6aSSong Liu static void profile_print_readings_plain(void)
213147c09d6aSSong Liu {
213247c09d6aSSong Liu 	__u32 m;
213347c09d6aSSong Liu 
213447c09d6aSSong Liu 	printf("\n%18llu %-20s\n", profile_total_count, "run_cnt");
213547c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
213647c09d6aSSong Liu 		struct bpf_perf_event_value *val = &metrics[m].val;
213747c09d6aSSong Liu 		int r;
213847c09d6aSSong Liu 
213947c09d6aSSong Liu 		if (!metrics[m].selected)
214047c09d6aSSong Liu 			continue;
214147c09d6aSSong Liu 		printf("%18llu %-20s", val->counter, metrics[m].name);
214247c09d6aSSong Liu 
214347c09d6aSSong Liu 		r = metrics[m].ratio_metric - 1;
214447c09d6aSSong Liu 		if (r >= 0 && metrics[r].selected &&
214547c09d6aSSong Liu 		    metrics[r].val.counter > 0) {
214647c09d6aSSong Liu 			printf("# %8.2f %-30s",
214747c09d6aSSong Liu 			       val->counter * metrics[m].ratio_mul /
214847c09d6aSSong Liu 			       metrics[r].val.counter,
214947c09d6aSSong Liu 			       metrics[m].ratio_desc);
215047c09d6aSSong Liu 		} else {
215147c09d6aSSong Liu 			printf("%-41s", "");
215247c09d6aSSong Liu 		}
215347c09d6aSSong Liu 
215447c09d6aSSong Liu 		if (val->enabled > val->running)
215547c09d6aSSong Liu 			printf("(%4.2f%%)",
215647c09d6aSSong Liu 			       val->running * 100.0 / val->enabled);
215747c09d6aSSong Liu 		printf("\n");
215847c09d6aSSong Liu 	}
215947c09d6aSSong Liu }
216047c09d6aSSong Liu 
216147c09d6aSSong Liu static void profile_print_readings(void)
216247c09d6aSSong Liu {
216347c09d6aSSong Liu 	if (json_output)
216447c09d6aSSong Liu 		profile_print_readings_json();
216547c09d6aSSong Liu 	else
216647c09d6aSSong Liu 		profile_print_readings_plain();
216747c09d6aSSong Liu }
216847c09d6aSSong Liu 
216947c09d6aSSong Liu static char *profile_target_name(int tgt_fd)
217047c09d6aSSong Liu {
2171c59765cfSDave Marchevsky 	struct bpf_func_info func_info;
2172c59765cfSDave Marchevsky 	struct bpf_prog_info info = {};
2173c59765cfSDave Marchevsky 	__u32 info_len = sizeof(info);
217447c09d6aSSong Liu 	const struct btf_type *t;
2175c59765cfSDave Marchevsky 	__u32 func_info_rec_size;
2176369e955bSQuentin Monnet 	struct btf *btf = NULL;
217747c09d6aSSong Liu 	char *name = NULL;
2178c59765cfSDave Marchevsky 	int err;
217947c09d6aSSong Liu 
2180c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2181c59765cfSDave Marchevsky 	if (err) {
2182c59765cfSDave Marchevsky 		p_err("failed to bpf_obj_get_info_by_fd for prog FD %d", tgt_fd);
2183c59765cfSDave Marchevsky 		goto out;
218447c09d6aSSong Liu 	}
218547c09d6aSSong Liu 
2186c59765cfSDave Marchevsky 	if (info.btf_id == 0) {
218747c09d6aSSong Liu 		p_err("prog FD %d doesn't have valid btf", tgt_fd);
218847c09d6aSSong Liu 		goto out;
218947c09d6aSSong Liu 	}
219047c09d6aSSong Liu 
2191c59765cfSDave Marchevsky 	func_info_rec_size = info.func_info_rec_size;
2192c59765cfSDave Marchevsky 	if (info.nr_func_info == 0) {
2193c59765cfSDave Marchevsky 		p_err("bpf_obj_get_info_by_fd for prog FD %d found 0 func_info", tgt_fd);
2194c59765cfSDave Marchevsky 		goto out;
2195c59765cfSDave Marchevsky 	}
2196c59765cfSDave Marchevsky 
2197c59765cfSDave Marchevsky 	memset(&info, 0, sizeof(info));
2198c59765cfSDave Marchevsky 	info.nr_func_info = 1;
2199c59765cfSDave Marchevsky 	info.func_info_rec_size = func_info_rec_size;
2200c59765cfSDave Marchevsky 	info.func_info = ptr_to_u64(&func_info);
2201c59765cfSDave Marchevsky 
2202c59765cfSDave Marchevsky 	err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
2203c59765cfSDave Marchevsky 	if (err) {
2204c59765cfSDave Marchevsky 		p_err("failed to get func_info for prog FD %d", tgt_fd);
2205c59765cfSDave Marchevsky 		goto out;
2206c59765cfSDave Marchevsky 	}
2207c59765cfSDave Marchevsky 
2208c59765cfSDave Marchevsky 	btf = btf__load_from_kernel_by_id(info.btf_id);
220986f4b7f2SQuentin Monnet 	if (libbpf_get_error(btf)) {
221086f4b7f2SQuentin Monnet 		p_err("failed to load btf for prog FD %d", tgt_fd);
221186f4b7f2SQuentin Monnet 		goto out;
221286f4b7f2SQuentin Monnet 	}
221386f4b7f2SQuentin Monnet 
2214c59765cfSDave Marchevsky 	t = btf__type_by_id(btf, func_info.type_id);
221547c09d6aSSong Liu 	if (!t) {
221647c09d6aSSong Liu 		p_err("btf %d doesn't have type %d",
2217c59765cfSDave Marchevsky 		      info.btf_id, func_info.type_id);
221847c09d6aSSong Liu 		goto out;
221947c09d6aSSong Liu 	}
222047c09d6aSSong Liu 	name = strdup(btf__name_by_offset(btf, t->name_off));
222147c09d6aSSong Liu out:
2222369e955bSQuentin Monnet 	btf__free(btf);
222347c09d6aSSong Liu 	return name;
222447c09d6aSSong Liu }
222547c09d6aSSong Liu 
222647c09d6aSSong Liu static struct profiler_bpf *profile_obj;
222747c09d6aSSong Liu static int profile_tgt_fd = -1;
222847c09d6aSSong Liu static char *profile_tgt_name;
222947c09d6aSSong Liu static int *profile_perf_events;
223047c09d6aSSong Liu static int profile_perf_event_cnt;
223147c09d6aSSong Liu 
223247c09d6aSSong Liu static void profile_close_perf_events(struct profiler_bpf *obj)
223347c09d6aSSong Liu {
223447c09d6aSSong Liu 	int i;
223547c09d6aSSong Liu 
223647c09d6aSSong Liu 	for (i = profile_perf_event_cnt - 1; i >= 0; i--)
223747c09d6aSSong Liu 		close(profile_perf_events[i]);
223847c09d6aSSong Liu 
223947c09d6aSSong Liu 	free(profile_perf_events);
224047c09d6aSSong Liu 	profile_perf_event_cnt = 0;
224147c09d6aSSong Liu }
224247c09d6aSSong Liu 
224347c09d6aSSong Liu static int profile_open_perf_events(struct profiler_bpf *obj)
224447c09d6aSSong Liu {
224547c09d6aSSong Liu 	unsigned int cpu, m;
224647c09d6aSSong Liu 	int map_fd, pmu_fd;
224747c09d6aSSong Liu 
224847c09d6aSSong Liu 	profile_perf_events = calloc(
224947c09d6aSSong Liu 		sizeof(int), obj->rodata->num_cpu * obj->rodata->num_metric);
225047c09d6aSSong Liu 	if (!profile_perf_events) {
225147c09d6aSSong Liu 		p_err("failed to allocate memory for perf_event array: %s",
225247c09d6aSSong Liu 		      strerror(errno));
225347c09d6aSSong Liu 		return -1;
225447c09d6aSSong Liu 	}
225547c09d6aSSong Liu 	map_fd = bpf_map__fd(obj->maps.events);
225647c09d6aSSong Liu 	if (map_fd < 0) {
225747c09d6aSSong Liu 		p_err("failed to get fd for events map");
225847c09d6aSSong Liu 		return -1;
225947c09d6aSSong Liu 	}
226047c09d6aSSong Liu 
226147c09d6aSSong Liu 	for (m = 0; m < ARRAY_SIZE(metrics); m++) {
226247c09d6aSSong Liu 		if (!metrics[m].selected)
226347c09d6aSSong Liu 			continue;
226447c09d6aSSong Liu 		for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) {
226547c09d6aSSong Liu 			pmu_fd = syscall(__NR_perf_event_open, &metrics[m].attr,
226647c09d6aSSong Liu 					 -1/*pid*/, cpu, -1/*group_fd*/, 0);
226747c09d6aSSong Liu 			if (pmu_fd < 0 ||
226847c09d6aSSong Liu 			    bpf_map_update_elem(map_fd, &profile_perf_event_cnt,
226947c09d6aSSong Liu 						&pmu_fd, BPF_ANY) ||
227047c09d6aSSong Liu 			    ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
227147c09d6aSSong Liu 				p_err("failed to create event %s on cpu %d",
227247c09d6aSSong Liu 				      metrics[m].name, cpu);
227347c09d6aSSong Liu 				return -1;
227447c09d6aSSong Liu 			}
227547c09d6aSSong Liu 			profile_perf_events[profile_perf_event_cnt++] = pmu_fd;
227647c09d6aSSong Liu 		}
227747c09d6aSSong Liu 	}
227847c09d6aSSong Liu 	return 0;
227947c09d6aSSong Liu }
228047c09d6aSSong Liu 
228147c09d6aSSong Liu static void profile_print_and_cleanup(void)
228247c09d6aSSong Liu {
228347c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
228447c09d6aSSong Liu 	profile_read_values(profile_obj);
228547c09d6aSSong Liu 	profile_print_readings();
228647c09d6aSSong Liu 	profiler_bpf__destroy(profile_obj);
228747c09d6aSSong Liu 
228847c09d6aSSong Liu 	close(profile_tgt_fd);
228947c09d6aSSong Liu 	free(profile_tgt_name);
229047c09d6aSSong Liu }
229147c09d6aSSong Liu 
229247c09d6aSSong Liu static void int_exit(int signo)
229347c09d6aSSong Liu {
229447c09d6aSSong Liu 	profile_print_and_cleanup();
229547c09d6aSSong Liu 	exit(0);
229647c09d6aSSong Liu }
229747c09d6aSSong Liu 
229847c09d6aSSong Liu static int do_profile(int argc, char **argv)
229947c09d6aSSong Liu {
230047c09d6aSSong Liu 	int num_metric, num_cpu, err = -1;
230147c09d6aSSong Liu 	struct bpf_program *prog;
230247c09d6aSSong Liu 	unsigned long duration;
230347c09d6aSSong Liu 	char *endptr;
230447c09d6aSSong Liu 
230547c09d6aSSong Liu 	/* we at least need two args for the prog and one metric */
230647c09d6aSSong Liu 	if (!REQ_ARGS(3))
230747c09d6aSSong Liu 		return -EINVAL;
230847c09d6aSSong Liu 
230947c09d6aSSong Liu 	/* parse target fd */
231047c09d6aSSong Liu 	profile_tgt_fd = prog_parse_fd(&argc, &argv);
231147c09d6aSSong Liu 	if (profile_tgt_fd < 0) {
231247c09d6aSSong Liu 		p_err("failed to parse fd");
231347c09d6aSSong Liu 		return -1;
231447c09d6aSSong Liu 	}
231547c09d6aSSong Liu 
231647c09d6aSSong Liu 	/* parse profiling optional duration */
231747c09d6aSSong Liu 	if (argc > 2 && is_prefix(argv[0], "duration")) {
231847c09d6aSSong Liu 		NEXT_ARG();
231947c09d6aSSong Liu 		duration = strtoul(*argv, &endptr, 0);
232047c09d6aSSong Liu 		if (*endptr)
232147c09d6aSSong Liu 			usage();
232247c09d6aSSong Liu 		NEXT_ARG();
232347c09d6aSSong Liu 	} else {
232447c09d6aSSong Liu 		duration = UINT_MAX;
232547c09d6aSSong Liu 	}
232647c09d6aSSong Liu 
232747c09d6aSSong Liu 	num_metric = profile_parse_metrics(argc, argv);
232847c09d6aSSong Liu 	if (num_metric <= 0)
232947c09d6aSSong Liu 		goto out;
233047c09d6aSSong Liu 
233147c09d6aSSong Liu 	num_cpu = libbpf_num_possible_cpus();
233247c09d6aSSong Liu 	if (num_cpu <= 0) {
233347c09d6aSSong Liu 		p_err("failed to identify number of CPUs");
233447c09d6aSSong Liu 		goto out;
233547c09d6aSSong Liu 	}
233647c09d6aSSong Liu 
233747c09d6aSSong Liu 	profile_obj = profiler_bpf__open();
233847c09d6aSSong Liu 	if (!profile_obj) {
233947c09d6aSSong Liu 		p_err("failed to open and/or load BPF object");
234047c09d6aSSong Liu 		goto out;
234147c09d6aSSong Liu 	}
234247c09d6aSSong Liu 
234347c09d6aSSong Liu 	profile_obj->rodata->num_cpu = num_cpu;
234447c09d6aSSong Liu 	profile_obj->rodata->num_metric = num_metric;
234547c09d6aSSong Liu 
234647c09d6aSSong Liu 	/* adjust map sizes */
234739748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.events, num_metric * num_cpu);
234839748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.fentry_readings, num_metric);
234939748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.accum_readings, num_metric);
235039748db1SAndrii Nakryiko 	bpf_map__set_max_entries(profile_obj->maps.counts, 1);
235147c09d6aSSong Liu 
235247c09d6aSSong Liu 	/* change target name */
235347c09d6aSSong Liu 	profile_tgt_name = profile_target_name(profile_tgt_fd);
235447c09d6aSSong Liu 	if (!profile_tgt_name)
235547c09d6aSSong Liu 		goto out;
235647c09d6aSSong Liu 
235747c09d6aSSong Liu 	bpf_object__for_each_program(prog, profile_obj->obj) {
235847c09d6aSSong Liu 		err = bpf_program__set_attach_target(prog, profile_tgt_fd,
235947c09d6aSSong Liu 						     profile_tgt_name);
236047c09d6aSSong Liu 		if (err) {
236147c09d6aSSong Liu 			p_err("failed to set attach target\n");
236247c09d6aSSong Liu 			goto out;
236347c09d6aSSong Liu 		}
236447c09d6aSSong Liu 	}
236547c09d6aSSong Liu 
23666b4384ffSQuentin Monnet 	set_max_rlimit();
236747c09d6aSSong Liu 	err = profiler_bpf__load(profile_obj);
236847c09d6aSSong Liu 	if (err) {
236947c09d6aSSong Liu 		p_err("failed to load profile_obj");
237047c09d6aSSong Liu 		goto out;
237147c09d6aSSong Liu 	}
237247c09d6aSSong Liu 
237347c09d6aSSong Liu 	err = profile_open_perf_events(profile_obj);
237447c09d6aSSong Liu 	if (err)
237547c09d6aSSong Liu 		goto out;
237647c09d6aSSong Liu 
237747c09d6aSSong Liu 	err = profiler_bpf__attach(profile_obj);
237847c09d6aSSong Liu 	if (err) {
237947c09d6aSSong Liu 		p_err("failed to attach profile_obj");
238047c09d6aSSong Liu 		goto out;
238147c09d6aSSong Liu 	}
238247c09d6aSSong Liu 	signal(SIGINT, int_exit);
238347c09d6aSSong Liu 
238447c09d6aSSong Liu 	sleep(duration);
238547c09d6aSSong Liu 	profile_print_and_cleanup();
238647c09d6aSSong Liu 	return 0;
238747c09d6aSSong Liu 
238847c09d6aSSong Liu out:
238947c09d6aSSong Liu 	profile_close_perf_events(profile_obj);
239047c09d6aSSong Liu 	if (profile_obj)
239147c09d6aSSong Liu 		profiler_bpf__destroy(profile_obj);
239247c09d6aSSong Liu 	close(profile_tgt_fd);
239347c09d6aSSong Liu 	free(profile_tgt_name);
239447c09d6aSSong Liu 	return err;
239547c09d6aSSong Liu }
239647c09d6aSSong Liu 
239747c09d6aSSong Liu #endif /* BPFTOOL_WITHOUT_SKELETONS */
239847c09d6aSSong Liu 
239971bb428fSJakub Kicinski static int do_help(int argc, char **argv)
240071bb428fSJakub Kicinski {
2401004b45c0SQuentin Monnet 	if (json_output) {
2402004b45c0SQuentin Monnet 		jsonw_null(json_wtr);
2403004b45c0SQuentin Monnet 		return 0;
2404004b45c0SQuentin Monnet 	}
2405004b45c0SQuentin Monnet 
240671bb428fSJakub Kicinski 	fprintf(stderr,
240790040351SQuentin Monnet 		"Usage: %1$s %2$s { show | list } [PROG]\n"
240890040351SQuentin Monnet 		"       %1$s %2$s dump xlated PROG [{ file FILE | opcodes | visual | linum }]\n"
240990040351SQuentin Monnet 		"       %1$s %2$s dump jited  PROG [{ file FILE | opcodes | linum }]\n"
241090040351SQuentin Monnet 		"       %1$s %2$s pin   PROG FILE\n"
241190040351SQuentin Monnet 		"       %1$s %2$s { load | loadall } OBJ  PATH \\\n"
241277380998SStanislav Fomichev 		"                         [type TYPE] [dev NAME] \\\n"
24133767a94bSStanislav Fomichev 		"                         [map { idx IDX | name NAME } MAP]\\\n"
24143767a94bSStanislav Fomichev 		"                         [pinmaps MAP_DIR]\n"
241519526e70SWang Yufen 		"                         [autoattach]\n"
241690040351SQuentin Monnet 		"       %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n"
241790040351SQuentin Monnet 		"       %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n"
241890040351SQuentin Monnet 		"       %1$s %2$s run PROG \\\n"
2419ba95c745SQuentin Monnet 		"                         data_in FILE \\\n"
2420ba95c745SQuentin Monnet 		"                         [data_out FILE [data_size_out L]] \\\n"
2421ba95c745SQuentin Monnet 		"                         [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
2422ba95c745SQuentin Monnet 		"                         [repeat N]\n"
242390040351SQuentin Monnet 		"       %1$s %2$s profile PROG [duration DURATION] METRICs\n"
242490040351SQuentin Monnet 		"       %1$s %2$s tracelog\n"
242590040351SQuentin Monnet 		"       %1$s %2$s help\n"
242671bb428fSJakub Kicinski 		"\n"
24273ff5a4dcSJakub Kicinski 		"       " HELP_SPEC_MAP "\n"
242871bb428fSJakub Kicinski 		"       " HELP_SPEC_PROGRAM "\n"
242949f2cba3SJakub Kicinski 		"       TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
243049f2cba3SJakub Kicinski 		"                 tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
243149f2cba3SJakub Kicinski 		"                 cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
243249f2cba3SJakub Kicinski 		"                 lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
2433f25377eeSAndrey Ignatov 		"                 sk_reuseport | flow_dissector | cgroup/sysctl |\n"
243449f2cba3SJakub Kicinski 		"                 cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
243549f2cba3SJakub Kicinski 		"                 cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
243605ee19c1SDaniel Borkmann 		"                 cgroup/getpeername4 | cgroup/getpeername6 |\n"
243705ee19c1SDaniel Borkmann 		"                 cgroup/getsockname4 | cgroup/getsockname6 | cgroup/sendmsg4 |\n"
243805ee19c1SDaniel Borkmann 		"                 cgroup/sendmsg6 | cgroup/recvmsg4 | cgroup/recvmsg6 |\n"
2439a8deba85SLiu Jian 		"                 cgroup/getsockopt | cgroup/setsockopt | cgroup/sock_release |\n"
244093a3545dSJakub Sitnicki 		"                 struct_ops | fentry | fexit | freplace | sk_lookup }\n"
24411ba5ad36SDaniel Müller 		"       ATTACH_TYPE := { sk_msg_verdict | sk_skb_verdict | sk_skb_stream_verdict |\n"
24421ba5ad36SDaniel Müller 		"                        sk_skb_stream_parser | flow_dissector }\n"
2443450d060eSYonghong Song 		"       METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
2444c07ba629SQuentin Monnet 		"       " HELP_SPEC_OPTIONS " |\n"
24458cc8c635SQuentin Monnet 		"                    {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
24468cc8c635SQuentin Monnet 		"                    {-L|--use-loader} }\n"
244771bb428fSJakub Kicinski 		"",
244890040351SQuentin Monnet 		bin_name, argv[-2]);
244971bb428fSJakub Kicinski 
245071bb428fSJakub Kicinski 	return 0;
245171bb428fSJakub Kicinski }
245271bb428fSJakub Kicinski 
245371bb428fSJakub Kicinski static const struct cmd cmds[] = {
245471bb428fSJakub Kicinski 	{ "show",	do_show },
24556ebe6dbdSJakub Kicinski 	{ "list",	do_show },
24569f606179SQuentin Monnet 	{ "help",	do_help },
245771bb428fSJakub Kicinski 	{ "dump",	do_dump },
245871bb428fSJakub Kicinski 	{ "pin",	do_pin },
245949a086c2SRoman Gushchin 	{ "load",	do_load },
246077380998SStanislav Fomichev 	{ "loadall",	do_loadall },
2461b7d3826cSJohn Fastabend 	{ "attach",	do_attach },
2462b7d3826cSJohn Fastabend 	{ "detach",	do_detach },
246330da46b5SQuentin Monnet 	{ "tracelog",	do_tracelog },
2464ba95c745SQuentin Monnet 	{ "run",	do_run },
246547c09d6aSSong Liu 	{ "profile",	do_profile },
246671bb428fSJakub Kicinski 	{ 0 }
246771bb428fSJakub Kicinski };
246871bb428fSJakub Kicinski 
246971bb428fSJakub Kicinski int do_prog(int argc, char **argv)
247071bb428fSJakub Kicinski {
247171bb428fSJakub Kicinski 	return cmd_select(cmds, argc, argv, do_help);
247271bb428fSJakub Kicinski }
2473