xref: /kernel/linux/linux-6.6/tools/bpf/bpftool/link.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci/* Copyright (C) 2020 Facebook */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <errno.h>
562306a36Sopenharmony_ci#include <linux/err.h>
662306a36Sopenharmony_ci#include <linux/netfilter.h>
762306a36Sopenharmony_ci#include <linux/netfilter_arp.h>
862306a36Sopenharmony_ci#include <linux/perf_event.h>
962306a36Sopenharmony_ci#include <net/if.h>
1062306a36Sopenharmony_ci#include <stdio.h>
1162306a36Sopenharmony_ci#include <unistd.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <bpf/bpf.h>
1462306a36Sopenharmony_ci#include <bpf/hashmap.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "json_writer.h"
1762306a36Sopenharmony_ci#include "main.h"
1862306a36Sopenharmony_ci#include "xlated_dumper.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define PERF_HW_CACHE_LEN 128
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic struct hashmap *link_table;
2362306a36Sopenharmony_cistatic struct dump_data dd;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic const char *perf_type_name[PERF_TYPE_MAX] = {
2662306a36Sopenharmony_ci	[PERF_TYPE_HARDWARE]			= "hardware",
2762306a36Sopenharmony_ci	[PERF_TYPE_SOFTWARE]			= "software",
2862306a36Sopenharmony_ci	[PERF_TYPE_TRACEPOINT]			= "tracepoint",
2962306a36Sopenharmony_ci	[PERF_TYPE_HW_CACHE]			= "hw-cache",
3062306a36Sopenharmony_ci	[PERF_TYPE_RAW]				= "raw",
3162306a36Sopenharmony_ci	[PERF_TYPE_BREAKPOINT]			= "breakpoint",
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciconst char *event_symbols_hw[PERF_COUNT_HW_MAX] = {
3562306a36Sopenharmony_ci	[PERF_COUNT_HW_CPU_CYCLES]		= "cpu-cycles",
3662306a36Sopenharmony_ci	[PERF_COUNT_HW_INSTRUCTIONS]		= "instructions",
3762306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_REFERENCES]	= "cache-references",
3862306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_MISSES]		= "cache-misses",
3962306a36Sopenharmony_ci	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS]	= "branch-instructions",
4062306a36Sopenharmony_ci	[PERF_COUNT_HW_BRANCH_MISSES]		= "branch-misses",
4162306a36Sopenharmony_ci	[PERF_COUNT_HW_BUS_CYCLES]		= "bus-cycles",
4262306a36Sopenharmony_ci	[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND]	= "stalled-cycles-frontend",
4362306a36Sopenharmony_ci	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= "stalled-cycles-backend",
4462306a36Sopenharmony_ci	[PERF_COUNT_HW_REF_CPU_CYCLES]		= "ref-cycles",
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciconst char *event_symbols_sw[PERF_COUNT_SW_MAX] = {
4862306a36Sopenharmony_ci	[PERF_COUNT_SW_CPU_CLOCK]		= "cpu-clock",
4962306a36Sopenharmony_ci	[PERF_COUNT_SW_TASK_CLOCK]		= "task-clock",
5062306a36Sopenharmony_ci	[PERF_COUNT_SW_PAGE_FAULTS]		= "page-faults",
5162306a36Sopenharmony_ci	[PERF_COUNT_SW_CONTEXT_SWITCHES]	= "context-switches",
5262306a36Sopenharmony_ci	[PERF_COUNT_SW_CPU_MIGRATIONS]		= "cpu-migrations",
5362306a36Sopenharmony_ci	[PERF_COUNT_SW_PAGE_FAULTS_MIN]		= "minor-faults",
5462306a36Sopenharmony_ci	[PERF_COUNT_SW_PAGE_FAULTS_MAJ]		= "major-faults",
5562306a36Sopenharmony_ci	[PERF_COUNT_SW_ALIGNMENT_FAULTS]	= "alignment-faults",
5662306a36Sopenharmony_ci	[PERF_COUNT_SW_EMULATION_FAULTS]	= "emulation-faults",
5762306a36Sopenharmony_ci	[PERF_COUNT_SW_DUMMY]			= "dummy",
5862306a36Sopenharmony_ci	[PERF_COUNT_SW_BPF_OUTPUT]		= "bpf-output",
5962306a36Sopenharmony_ci	[PERF_COUNT_SW_CGROUP_SWITCHES]		= "cgroup-switches",
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciconst char *evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX] = {
6362306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_L1D]		= "L1-dcache",
6462306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_L1I]		= "L1-icache",
6562306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_LL]		= "LLC",
6662306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_DTLB]		= "dTLB",
6762306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_ITLB]		= "iTLB",
6862306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_BPU]		= "branch",
6962306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_NODE]		= "node",
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciconst char *evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX] = {
7362306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_OP_READ]		= "load",
7462306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_OP_WRITE]		= "store",
7562306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_OP_PREFETCH]	= "prefetch",
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ciconst char *evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
7962306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_RESULT_ACCESS]	= "refs",
8062306a36Sopenharmony_ci	[PERF_COUNT_HW_CACHE_RESULT_MISS]	= "misses",
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define perf_event_name(array, id) ({			\
8462306a36Sopenharmony_ci	const char *event_str = NULL;			\
8562306a36Sopenharmony_ci							\
8662306a36Sopenharmony_ci	if ((id) < ARRAY_SIZE(array))			\
8762306a36Sopenharmony_ci		event_str = array[id];			\
8862306a36Sopenharmony_ci	event_str;					\
8962306a36Sopenharmony_ci})
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int link_parse_fd(int *argc, char ***argv)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	int fd;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (is_prefix(**argv, "id")) {
9662306a36Sopenharmony_ci		unsigned int id;
9762306a36Sopenharmony_ci		char *endptr;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		NEXT_ARGP();
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		id = strtoul(**argv, &endptr, 0);
10262306a36Sopenharmony_ci		if (*endptr) {
10362306a36Sopenharmony_ci			p_err("can't parse %s as ID", **argv);
10462306a36Sopenharmony_ci			return -1;
10562306a36Sopenharmony_ci		}
10662306a36Sopenharmony_ci		NEXT_ARGP();
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		fd = bpf_link_get_fd_by_id(id);
10962306a36Sopenharmony_ci		if (fd < 0)
11062306a36Sopenharmony_ci			p_err("failed to get link with ID %d: %s", id, strerror(errno));
11162306a36Sopenharmony_ci		return fd;
11262306a36Sopenharmony_ci	} else if (is_prefix(**argv, "pinned")) {
11362306a36Sopenharmony_ci		char *path;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		NEXT_ARGP();
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		path = **argv;
11862306a36Sopenharmony_ci		NEXT_ARGP();
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		return open_obj_pinned_any(path, BPF_OBJ_LINK);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
12462306a36Sopenharmony_ci	return -1;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void
12862306a36Sopenharmony_cishow_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	const char *link_type_str;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	jsonw_uint_field(wtr, "id", info->id);
13362306a36Sopenharmony_ci	link_type_str = libbpf_bpf_link_type_str(info->type);
13462306a36Sopenharmony_ci	if (link_type_str)
13562306a36Sopenharmony_ci		jsonw_string_field(wtr, "type", link_type_str);
13662306a36Sopenharmony_ci	else
13762306a36Sopenharmony_ci		jsonw_uint_field(wtr, "type", info->type);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void show_link_attach_type_json(__u32 attach_type, json_writer_t *wtr)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	const char *attach_type_str;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	attach_type_str = libbpf_bpf_attach_type_str(attach_type);
14762306a36Sopenharmony_ci	if (attach_type_str)
14862306a36Sopenharmony_ci		jsonw_string_field(wtr, "attach_type", attach_type_str);
14962306a36Sopenharmony_ci	else
15062306a36Sopenharmony_ci		jsonw_uint_field(wtr, "attach_type", attach_type);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void show_link_ifindex_json(__u32 ifindex, json_writer_t *wtr)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	char devname[IF_NAMESIZE] = "(unknown)";
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (ifindex)
15862306a36Sopenharmony_ci		if_indextoname(ifindex, devname);
15962306a36Sopenharmony_ci	else
16062306a36Sopenharmony_ci		snprintf(devname, sizeof(devname), "(detached)");
16162306a36Sopenharmony_ci	jsonw_string_field(wtr, "devname", devname);
16262306a36Sopenharmony_ci	jsonw_uint_field(wtr, "ifindex", ifindex);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic bool is_iter_map_target(const char *target_name)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	return strcmp(target_name, "bpf_map_elem") == 0 ||
16862306a36Sopenharmony_ci	       strcmp(target_name, "bpf_sk_storage_map") == 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic bool is_iter_cgroup_target(const char *target_name)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	return strcmp(target_name, "cgroup") == 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic const char *cgroup_order_string(__u32 order)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	switch (order) {
17962306a36Sopenharmony_ci	case BPF_CGROUP_ITER_ORDER_UNSPEC:
18062306a36Sopenharmony_ci		return "order_unspec";
18162306a36Sopenharmony_ci	case BPF_CGROUP_ITER_SELF_ONLY:
18262306a36Sopenharmony_ci		return "self_only";
18362306a36Sopenharmony_ci	case BPF_CGROUP_ITER_DESCENDANTS_PRE:
18462306a36Sopenharmony_ci		return "descendants_pre";
18562306a36Sopenharmony_ci	case BPF_CGROUP_ITER_DESCENDANTS_POST:
18662306a36Sopenharmony_ci		return "descendants_post";
18762306a36Sopenharmony_ci	case BPF_CGROUP_ITER_ANCESTORS_UP:
18862306a36Sopenharmony_ci		return "ancestors_up";
18962306a36Sopenharmony_ci	default: /* won't happen */
19062306a36Sopenharmony_ci		return "unknown";
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic bool is_iter_task_target(const char *target_name)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	return strcmp(target_name, "task") == 0 ||
19762306a36Sopenharmony_ci		strcmp(target_name, "task_file") == 0 ||
19862306a36Sopenharmony_ci		strcmp(target_name, "task_vma") == 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void show_iter_json(struct bpf_link_info *info, json_writer_t *wtr)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	const char *target_name = u64_to_ptr(info->iter.target_name);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	jsonw_string_field(wtr, "target_name", target_name);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (is_iter_map_target(target_name))
20862306a36Sopenharmony_ci		jsonw_uint_field(wtr, "map_id", info->iter.map.map_id);
20962306a36Sopenharmony_ci	else if (is_iter_task_target(target_name)) {
21062306a36Sopenharmony_ci		if (info->iter.task.tid)
21162306a36Sopenharmony_ci			jsonw_uint_field(wtr, "tid", info->iter.task.tid);
21262306a36Sopenharmony_ci		else if (info->iter.task.pid)
21362306a36Sopenharmony_ci			jsonw_uint_field(wtr, "pid", info->iter.task.pid);
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (is_iter_cgroup_target(target_name)) {
21762306a36Sopenharmony_ci		jsonw_lluint_field(wtr, "cgroup_id", info->iter.cgroup.cgroup_id);
21862306a36Sopenharmony_ci		jsonw_string_field(wtr, "order",
21962306a36Sopenharmony_ci				   cgroup_order_string(info->iter.cgroup.order));
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_civoid netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	jsonw_uint_field(json_wtr, "pf",
22662306a36Sopenharmony_ci			 info->netfilter.pf);
22762306a36Sopenharmony_ci	jsonw_uint_field(json_wtr, "hook",
22862306a36Sopenharmony_ci			 info->netfilter.hooknum);
22962306a36Sopenharmony_ci	jsonw_int_field(json_wtr, "prio",
23062306a36Sopenharmony_ci			 info->netfilter.priority);
23162306a36Sopenharmony_ci	jsonw_uint_field(json_wtr, "flags",
23262306a36Sopenharmony_ci			 info->netfilter.flags);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int get_prog_info(int prog_id, struct bpf_prog_info *info)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	__u32 len = sizeof(*info);
23862306a36Sopenharmony_ci	int err, prog_fd;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	prog_fd = bpf_prog_get_fd_by_id(prog_id);
24162306a36Sopenharmony_ci	if (prog_fd < 0)
24262306a36Sopenharmony_ci		return prog_fd;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	memset(info, 0, sizeof(*info));
24562306a36Sopenharmony_ci	err = bpf_prog_get_info_by_fd(prog_fd, info, &len);
24662306a36Sopenharmony_ci	if (err)
24762306a36Sopenharmony_ci		p_err("can't get prog info: %s", strerror(errno));
24862306a36Sopenharmony_ci	close(prog_fd);
24962306a36Sopenharmony_ci	return err;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int cmp_u64(const void *A, const void *B)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	const __u64 *a = A, *b = B;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return *a - *b;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void
26062306a36Sopenharmony_cishow_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	__u32 i, j = 0;
26362306a36Sopenharmony_ci	__u64 *addrs;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	jsonw_bool_field(json_wtr, "retprobe",
26662306a36Sopenharmony_ci			 info->kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN);
26762306a36Sopenharmony_ci	jsonw_uint_field(json_wtr, "func_cnt", info->kprobe_multi.count);
26862306a36Sopenharmony_ci	jsonw_name(json_wtr, "funcs");
26962306a36Sopenharmony_ci	jsonw_start_array(json_wtr);
27062306a36Sopenharmony_ci	addrs = u64_to_ptr(info->kprobe_multi.addrs);
27162306a36Sopenharmony_ci	qsort(addrs, info->kprobe_multi.count, sizeof(addrs[0]), cmp_u64);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Load it once for all. */
27462306a36Sopenharmony_ci	if (!dd.sym_count)
27562306a36Sopenharmony_ci		kernel_syms_load(&dd);
27662306a36Sopenharmony_ci	for (i = 0; i < dd.sym_count; i++) {
27762306a36Sopenharmony_ci		if (dd.sym_mapping[i].address != addrs[j])
27862306a36Sopenharmony_ci			continue;
27962306a36Sopenharmony_ci		jsonw_start_object(json_wtr);
28062306a36Sopenharmony_ci		jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
28162306a36Sopenharmony_ci		jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
28262306a36Sopenharmony_ci		/* Print null if it is vmlinux */
28362306a36Sopenharmony_ci		if (dd.sym_mapping[i].module[0] == '\0') {
28462306a36Sopenharmony_ci			jsonw_name(json_wtr, "module");
28562306a36Sopenharmony_ci			jsonw_null(json_wtr);
28662306a36Sopenharmony_ci		} else {
28762306a36Sopenharmony_ci			jsonw_string_field(json_wtr, "module", dd.sym_mapping[i].module);
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci		jsonw_end_object(json_wtr);
29062306a36Sopenharmony_ci		if (j++ == info->kprobe_multi.count)
29162306a36Sopenharmony_ci			break;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	jsonw_end_array(json_wtr);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void
29762306a36Sopenharmony_cishow_perf_event_kprobe_json(struct bpf_link_info *info, json_writer_t *wtr)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	jsonw_bool_field(wtr, "retprobe", info->perf_event.type == BPF_PERF_EVENT_KRETPROBE);
30062306a36Sopenharmony_ci	jsonw_uint_field(wtr, "addr", info->perf_event.kprobe.addr);
30162306a36Sopenharmony_ci	jsonw_string_field(wtr, "func",
30262306a36Sopenharmony_ci			   u64_to_ptr(info->perf_event.kprobe.func_name));
30362306a36Sopenharmony_ci	jsonw_uint_field(wtr, "offset", info->perf_event.kprobe.offset);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void
30762306a36Sopenharmony_cishow_perf_event_uprobe_json(struct bpf_link_info *info, json_writer_t *wtr)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	jsonw_bool_field(wtr, "retprobe", info->perf_event.type == BPF_PERF_EVENT_URETPROBE);
31062306a36Sopenharmony_ci	jsonw_string_field(wtr, "file",
31162306a36Sopenharmony_ci			   u64_to_ptr(info->perf_event.uprobe.file_name));
31262306a36Sopenharmony_ci	jsonw_uint_field(wtr, "offset", info->perf_event.uprobe.offset);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void
31662306a36Sopenharmony_cishow_perf_event_tracepoint_json(struct bpf_link_info *info, json_writer_t *wtr)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	jsonw_string_field(wtr, "tracepoint",
31962306a36Sopenharmony_ci			   u64_to_ptr(info->perf_event.tracepoint.tp_name));
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic char *perf_config_hw_cache_str(__u64 config)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	const char *hw_cache, *result, *op;
32562306a36Sopenharmony_ci	char *str = malloc(PERF_HW_CACHE_LEN);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (!str) {
32862306a36Sopenharmony_ci		p_err("mem alloc failed");
32962306a36Sopenharmony_ci		return NULL;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	hw_cache = perf_event_name(evsel__hw_cache, config & 0xff);
33362306a36Sopenharmony_ci	if (hw_cache)
33462306a36Sopenharmony_ci		snprintf(str, PERF_HW_CACHE_LEN, "%s-", hw_cache);
33562306a36Sopenharmony_ci	else
33662306a36Sopenharmony_ci		snprintf(str, PERF_HW_CACHE_LEN, "%lld-", config & 0xff);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	op = perf_event_name(evsel__hw_cache_op, (config >> 8) & 0xff);
33962306a36Sopenharmony_ci	if (op)
34062306a36Sopenharmony_ci		snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
34162306a36Sopenharmony_ci			 "%s-", op);
34262306a36Sopenharmony_ci	else
34362306a36Sopenharmony_ci		snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
34462306a36Sopenharmony_ci			 "%lld-", (config >> 8) & 0xff);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	result = perf_event_name(evsel__hw_cache_result, config >> 16);
34762306a36Sopenharmony_ci	if (result)
34862306a36Sopenharmony_ci		snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
34962306a36Sopenharmony_ci			 "%s", result);
35062306a36Sopenharmony_ci	else
35162306a36Sopenharmony_ci		snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
35262306a36Sopenharmony_ci			 "%lld", config >> 16);
35362306a36Sopenharmony_ci	return str;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic const char *perf_config_str(__u32 type, __u64 config)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	const char *perf_config;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	switch (type) {
36162306a36Sopenharmony_ci	case PERF_TYPE_HARDWARE:
36262306a36Sopenharmony_ci		perf_config = perf_event_name(event_symbols_hw, config);
36362306a36Sopenharmony_ci		break;
36462306a36Sopenharmony_ci	case PERF_TYPE_SOFTWARE:
36562306a36Sopenharmony_ci		perf_config = perf_event_name(event_symbols_sw, config);
36662306a36Sopenharmony_ci		break;
36762306a36Sopenharmony_ci	case PERF_TYPE_HW_CACHE:
36862306a36Sopenharmony_ci		perf_config = perf_config_hw_cache_str(config);
36962306a36Sopenharmony_ci		break;
37062306a36Sopenharmony_ci	default:
37162306a36Sopenharmony_ci		perf_config = NULL;
37262306a36Sopenharmony_ci		break;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	return perf_config;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void
37862306a36Sopenharmony_cishow_perf_event_event_json(struct bpf_link_info *info, json_writer_t *wtr)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	__u64 config = info->perf_event.event.config;
38162306a36Sopenharmony_ci	__u32 type = info->perf_event.event.type;
38262306a36Sopenharmony_ci	const char *perf_type, *perf_config;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	perf_type = perf_event_name(perf_type_name, type);
38562306a36Sopenharmony_ci	if (perf_type)
38662306a36Sopenharmony_ci		jsonw_string_field(wtr, "event_type", perf_type);
38762306a36Sopenharmony_ci	else
38862306a36Sopenharmony_ci		jsonw_uint_field(wtr, "event_type", type);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	perf_config = perf_config_str(type, config);
39162306a36Sopenharmony_ci	if (perf_config)
39262306a36Sopenharmony_ci		jsonw_string_field(wtr, "event_config", perf_config);
39362306a36Sopenharmony_ci	else
39462306a36Sopenharmony_ci		jsonw_uint_field(wtr, "event_config", config);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (type == PERF_TYPE_HW_CACHE && perf_config)
39762306a36Sopenharmony_ci		free((void *)perf_config);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int show_link_close_json(int fd, struct bpf_link_info *info)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct bpf_prog_info prog_info;
40362306a36Sopenharmony_ci	const char *prog_type_str;
40462306a36Sopenharmony_ci	int err;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	jsonw_start_object(json_wtr);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	show_link_header_json(info, json_wtr);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	switch (info->type) {
41162306a36Sopenharmony_ci	case BPF_LINK_TYPE_RAW_TRACEPOINT:
41262306a36Sopenharmony_ci		jsonw_string_field(json_wtr, "tp_name",
41362306a36Sopenharmony_ci				   u64_to_ptr(info->raw_tracepoint.tp_name));
41462306a36Sopenharmony_ci		break;
41562306a36Sopenharmony_ci	case BPF_LINK_TYPE_TRACING:
41662306a36Sopenharmony_ci		err = get_prog_info(info->prog_id, &prog_info);
41762306a36Sopenharmony_ci		if (err)
41862306a36Sopenharmony_ci			return err;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);
42162306a36Sopenharmony_ci		/* libbpf will return NULL for variants unknown to it. */
42262306a36Sopenharmony_ci		if (prog_type_str)
42362306a36Sopenharmony_ci			jsonw_string_field(json_wtr, "prog_type", prog_type_str);
42462306a36Sopenharmony_ci		else
42562306a36Sopenharmony_ci			jsonw_uint_field(json_wtr, "prog_type", prog_info.type);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		show_link_attach_type_json(info->tracing.attach_type,
42862306a36Sopenharmony_ci					   json_wtr);
42962306a36Sopenharmony_ci		jsonw_uint_field(json_wtr, "target_obj_id", info->tracing.target_obj_id);
43062306a36Sopenharmony_ci		jsonw_uint_field(json_wtr, "target_btf_id", info->tracing.target_btf_id);
43162306a36Sopenharmony_ci		break;
43262306a36Sopenharmony_ci	case BPF_LINK_TYPE_CGROUP:
43362306a36Sopenharmony_ci		jsonw_lluint_field(json_wtr, "cgroup_id",
43462306a36Sopenharmony_ci				   info->cgroup.cgroup_id);
43562306a36Sopenharmony_ci		show_link_attach_type_json(info->cgroup.attach_type, json_wtr);
43662306a36Sopenharmony_ci		break;
43762306a36Sopenharmony_ci	case BPF_LINK_TYPE_ITER:
43862306a36Sopenharmony_ci		show_iter_json(info, json_wtr);
43962306a36Sopenharmony_ci		break;
44062306a36Sopenharmony_ci	case BPF_LINK_TYPE_NETNS:
44162306a36Sopenharmony_ci		jsonw_uint_field(json_wtr, "netns_ino",
44262306a36Sopenharmony_ci				 info->netns.netns_ino);
44362306a36Sopenharmony_ci		show_link_attach_type_json(info->netns.attach_type, json_wtr);
44462306a36Sopenharmony_ci		break;
44562306a36Sopenharmony_ci	case BPF_LINK_TYPE_NETFILTER:
44662306a36Sopenharmony_ci		netfilter_dump_json(info, json_wtr);
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	case BPF_LINK_TYPE_TCX:
44962306a36Sopenharmony_ci		show_link_ifindex_json(info->tcx.ifindex, json_wtr);
45062306a36Sopenharmony_ci		show_link_attach_type_json(info->tcx.attach_type, json_wtr);
45162306a36Sopenharmony_ci		break;
45262306a36Sopenharmony_ci	case BPF_LINK_TYPE_XDP:
45362306a36Sopenharmony_ci		show_link_ifindex_json(info->xdp.ifindex, json_wtr);
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case BPF_LINK_TYPE_STRUCT_OPS:
45662306a36Sopenharmony_ci		jsonw_uint_field(json_wtr, "map_id",
45762306a36Sopenharmony_ci				 info->struct_ops.map_id);
45862306a36Sopenharmony_ci		break;
45962306a36Sopenharmony_ci	case BPF_LINK_TYPE_KPROBE_MULTI:
46062306a36Sopenharmony_ci		show_kprobe_multi_json(info, json_wtr);
46162306a36Sopenharmony_ci		break;
46262306a36Sopenharmony_ci	case BPF_LINK_TYPE_PERF_EVENT:
46362306a36Sopenharmony_ci		switch (info->perf_event.type) {
46462306a36Sopenharmony_ci		case BPF_PERF_EVENT_EVENT:
46562306a36Sopenharmony_ci			show_perf_event_event_json(info, json_wtr);
46662306a36Sopenharmony_ci			break;
46762306a36Sopenharmony_ci		case BPF_PERF_EVENT_TRACEPOINT:
46862306a36Sopenharmony_ci			show_perf_event_tracepoint_json(info, json_wtr);
46962306a36Sopenharmony_ci			break;
47062306a36Sopenharmony_ci		case BPF_PERF_EVENT_KPROBE:
47162306a36Sopenharmony_ci		case BPF_PERF_EVENT_KRETPROBE:
47262306a36Sopenharmony_ci			show_perf_event_kprobe_json(info, json_wtr);
47362306a36Sopenharmony_ci			break;
47462306a36Sopenharmony_ci		case BPF_PERF_EVENT_UPROBE:
47562306a36Sopenharmony_ci		case BPF_PERF_EVENT_URETPROBE:
47662306a36Sopenharmony_ci			show_perf_event_uprobe_json(info, json_wtr);
47762306a36Sopenharmony_ci			break;
47862306a36Sopenharmony_ci		default:
47962306a36Sopenharmony_ci			break;
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci		break;
48262306a36Sopenharmony_ci	default:
48362306a36Sopenharmony_ci		break;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (!hashmap__empty(link_table)) {
48762306a36Sopenharmony_ci		struct hashmap_entry *entry;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		jsonw_name(json_wtr, "pinned");
49062306a36Sopenharmony_ci		jsonw_start_array(json_wtr);
49162306a36Sopenharmony_ci		hashmap__for_each_key_entry(link_table, entry, info->id)
49262306a36Sopenharmony_ci			jsonw_string(json_wtr, entry->pvalue);
49362306a36Sopenharmony_ci		jsonw_end_array(json_wtr);
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	emit_obj_refs_json(refs_table, info->id, json_wtr);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	jsonw_end_object(json_wtr);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return 0;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic void show_link_header_plain(struct bpf_link_info *info)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	const char *link_type_str;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	printf("%u: ", info->id);
50862306a36Sopenharmony_ci	link_type_str = libbpf_bpf_link_type_str(info->type);
50962306a36Sopenharmony_ci	if (link_type_str)
51062306a36Sopenharmony_ci		printf("%s  ", link_type_str);
51162306a36Sopenharmony_ci	else
51262306a36Sopenharmony_ci		printf("type %u  ", info->type);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (info->type == BPF_LINK_TYPE_STRUCT_OPS)
51562306a36Sopenharmony_ci		printf("map %u  ", info->struct_ops.map_id);
51662306a36Sopenharmony_ci	else
51762306a36Sopenharmony_ci		printf("prog %u  ", info->prog_id);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic void show_link_attach_type_plain(__u32 attach_type)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	const char *attach_type_str;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	attach_type_str = libbpf_bpf_attach_type_str(attach_type);
52562306a36Sopenharmony_ci	if (attach_type_str)
52662306a36Sopenharmony_ci		printf("attach_type %s  ", attach_type_str);
52762306a36Sopenharmony_ci	else
52862306a36Sopenharmony_ci		printf("attach_type %u  ", attach_type);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic void show_link_ifindex_plain(__u32 ifindex)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	char devname[IF_NAMESIZE * 2] = "(unknown)";
53462306a36Sopenharmony_ci	char tmpname[IF_NAMESIZE];
53562306a36Sopenharmony_ci	char *ret = NULL;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (ifindex)
53862306a36Sopenharmony_ci		ret = if_indextoname(ifindex, tmpname);
53962306a36Sopenharmony_ci	else
54062306a36Sopenharmony_ci		snprintf(devname, sizeof(devname), "(detached)");
54162306a36Sopenharmony_ci	if (ret)
54262306a36Sopenharmony_ci		snprintf(devname, sizeof(devname), "%s(%d)",
54362306a36Sopenharmony_ci			 tmpname, ifindex);
54462306a36Sopenharmony_ci	printf("ifindex %s  ", devname);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic void show_iter_plain(struct bpf_link_info *info)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	const char *target_name = u64_to_ptr(info->iter.target_name);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	printf("target_name %s  ", target_name);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (is_iter_map_target(target_name))
55462306a36Sopenharmony_ci		printf("map_id %u  ", info->iter.map.map_id);
55562306a36Sopenharmony_ci	else if (is_iter_task_target(target_name)) {
55662306a36Sopenharmony_ci		if (info->iter.task.tid)
55762306a36Sopenharmony_ci			printf("tid %u ", info->iter.task.tid);
55862306a36Sopenharmony_ci		else if (info->iter.task.pid)
55962306a36Sopenharmony_ci			printf("pid %u ", info->iter.task.pid);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (is_iter_cgroup_target(target_name)) {
56362306a36Sopenharmony_ci		printf("cgroup_id %llu  ", info->iter.cgroup.cgroup_id);
56462306a36Sopenharmony_ci		printf("order %s  ",
56562306a36Sopenharmony_ci		       cgroup_order_string(info->iter.cgroup.order));
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic const char * const pf2name[] = {
57062306a36Sopenharmony_ci	[NFPROTO_INET] = "inet",
57162306a36Sopenharmony_ci	[NFPROTO_IPV4] = "ip",
57262306a36Sopenharmony_ci	[NFPROTO_ARP] = "arp",
57362306a36Sopenharmony_ci	[NFPROTO_NETDEV] = "netdev",
57462306a36Sopenharmony_ci	[NFPROTO_BRIDGE] = "bridge",
57562306a36Sopenharmony_ci	[NFPROTO_IPV6] = "ip6",
57662306a36Sopenharmony_ci};
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic const char * const inethook2name[] = {
57962306a36Sopenharmony_ci	[NF_INET_PRE_ROUTING] = "prerouting",
58062306a36Sopenharmony_ci	[NF_INET_LOCAL_IN] = "input",
58162306a36Sopenharmony_ci	[NF_INET_FORWARD] = "forward",
58262306a36Sopenharmony_ci	[NF_INET_LOCAL_OUT] = "output",
58362306a36Sopenharmony_ci	[NF_INET_POST_ROUTING] = "postrouting",
58462306a36Sopenharmony_ci};
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic const char * const arphook2name[] = {
58762306a36Sopenharmony_ci	[NF_ARP_IN] = "input",
58862306a36Sopenharmony_ci	[NF_ARP_OUT] = "output",
58962306a36Sopenharmony_ci};
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_civoid netfilter_dump_plain(const struct bpf_link_info *info)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	const char *hookname = NULL, *pfname = NULL;
59462306a36Sopenharmony_ci	unsigned int hook = info->netfilter.hooknum;
59562306a36Sopenharmony_ci	unsigned int pf = info->netfilter.pf;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (pf < ARRAY_SIZE(pf2name))
59862306a36Sopenharmony_ci		pfname = pf2name[pf];
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	switch (pf) {
60162306a36Sopenharmony_ci	case NFPROTO_BRIDGE: /* bridge shares numbers with enum nf_inet_hooks */
60262306a36Sopenharmony_ci	case NFPROTO_IPV4:
60362306a36Sopenharmony_ci	case NFPROTO_IPV6:
60462306a36Sopenharmony_ci	case NFPROTO_INET:
60562306a36Sopenharmony_ci		if (hook < ARRAY_SIZE(inethook2name))
60662306a36Sopenharmony_ci			hookname = inethook2name[hook];
60762306a36Sopenharmony_ci		break;
60862306a36Sopenharmony_ci	case NFPROTO_ARP:
60962306a36Sopenharmony_ci		if (hook < ARRAY_SIZE(arphook2name))
61062306a36Sopenharmony_ci			hookname = arphook2name[hook];
61162306a36Sopenharmony_ci	default:
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (pfname)
61662306a36Sopenharmony_ci		printf("\n\t%s", pfname);
61762306a36Sopenharmony_ci	else
61862306a36Sopenharmony_ci		printf("\n\tpf: %d", pf);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (hookname)
62162306a36Sopenharmony_ci		printf(" %s", hookname);
62262306a36Sopenharmony_ci	else
62362306a36Sopenharmony_ci		printf(", hook %u,", hook);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	printf(" prio %d", info->netfilter.priority);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (info->netfilter.flags)
62862306a36Sopenharmony_ci		printf(" flags 0x%x", info->netfilter.flags);
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic void show_kprobe_multi_plain(struct bpf_link_info *info)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	__u32 i, j = 0;
63462306a36Sopenharmony_ci	__u64 *addrs;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (!info->kprobe_multi.count)
63762306a36Sopenharmony_ci		return;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (info->kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN)
64062306a36Sopenharmony_ci		printf("\n\tkretprobe.multi  ");
64162306a36Sopenharmony_ci	else
64262306a36Sopenharmony_ci		printf("\n\tkprobe.multi  ");
64362306a36Sopenharmony_ci	printf("func_cnt %u  ", info->kprobe_multi.count);
64462306a36Sopenharmony_ci	addrs = (__u64 *)u64_to_ptr(info->kprobe_multi.addrs);
64562306a36Sopenharmony_ci	qsort(addrs, info->kprobe_multi.count, sizeof(__u64), cmp_u64);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/* Load it once for all. */
64862306a36Sopenharmony_ci	if (!dd.sym_count)
64962306a36Sopenharmony_ci		kernel_syms_load(&dd);
65062306a36Sopenharmony_ci	if (!dd.sym_count)
65162306a36Sopenharmony_ci		return;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	printf("\n\t%-16s %s", "addr", "func [module]");
65462306a36Sopenharmony_ci	for (i = 0; i < dd.sym_count; i++) {
65562306a36Sopenharmony_ci		if (dd.sym_mapping[i].address != addrs[j])
65662306a36Sopenharmony_ci			continue;
65762306a36Sopenharmony_ci		printf("\n\t%016lx %s",
65862306a36Sopenharmony_ci		       dd.sym_mapping[i].address, dd.sym_mapping[i].name);
65962306a36Sopenharmony_ci		if (dd.sym_mapping[i].module[0] != '\0')
66062306a36Sopenharmony_ci			printf(" [%s]  ", dd.sym_mapping[i].module);
66162306a36Sopenharmony_ci		else
66262306a36Sopenharmony_ci			printf("  ");
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		if (j++ == info->kprobe_multi.count)
66562306a36Sopenharmony_ci			break;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic void show_perf_event_kprobe_plain(struct bpf_link_info *info)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	const char *buf;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	buf = u64_to_ptr(info->perf_event.kprobe.func_name);
67462306a36Sopenharmony_ci	if (buf[0] == '\0' && !info->perf_event.kprobe.addr)
67562306a36Sopenharmony_ci		return;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (info->perf_event.type == BPF_PERF_EVENT_KRETPROBE)
67862306a36Sopenharmony_ci		printf("\n\tkretprobe ");
67962306a36Sopenharmony_ci	else
68062306a36Sopenharmony_ci		printf("\n\tkprobe ");
68162306a36Sopenharmony_ci	if (info->perf_event.kprobe.addr)
68262306a36Sopenharmony_ci		printf("%llx ", info->perf_event.kprobe.addr);
68362306a36Sopenharmony_ci	printf("%s", buf);
68462306a36Sopenharmony_ci	if (info->perf_event.kprobe.offset)
68562306a36Sopenharmony_ci		printf("+%#x", info->perf_event.kprobe.offset);
68662306a36Sopenharmony_ci	printf("  ");
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic void show_perf_event_uprobe_plain(struct bpf_link_info *info)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	const char *buf;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	buf = u64_to_ptr(info->perf_event.uprobe.file_name);
69462306a36Sopenharmony_ci	if (buf[0] == '\0')
69562306a36Sopenharmony_ci		return;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (info->perf_event.type == BPF_PERF_EVENT_URETPROBE)
69862306a36Sopenharmony_ci		printf("\n\turetprobe ");
69962306a36Sopenharmony_ci	else
70062306a36Sopenharmony_ci		printf("\n\tuprobe ");
70162306a36Sopenharmony_ci	printf("%s+%#x  ", buf, info->perf_event.uprobe.offset);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic void show_perf_event_tracepoint_plain(struct bpf_link_info *info)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	const char *buf;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	buf = u64_to_ptr(info->perf_event.tracepoint.tp_name);
70962306a36Sopenharmony_ci	if (buf[0] == '\0')
71062306a36Sopenharmony_ci		return;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	printf("\n\ttracepoint %s  ", buf);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void show_perf_event_event_plain(struct bpf_link_info *info)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	__u64 config = info->perf_event.event.config;
71862306a36Sopenharmony_ci	__u32 type = info->perf_event.event.type;
71962306a36Sopenharmony_ci	const char *perf_type, *perf_config;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	printf("\n\tevent ");
72262306a36Sopenharmony_ci	perf_type = perf_event_name(perf_type_name, type);
72362306a36Sopenharmony_ci	if (perf_type)
72462306a36Sopenharmony_ci		printf("%s:", perf_type);
72562306a36Sopenharmony_ci	else
72662306a36Sopenharmony_ci		printf("%u :", type);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	perf_config = perf_config_str(type, config);
72962306a36Sopenharmony_ci	if (perf_config)
73062306a36Sopenharmony_ci		printf("%s  ", perf_config);
73162306a36Sopenharmony_ci	else
73262306a36Sopenharmony_ci		printf("%llu  ", config);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (type == PERF_TYPE_HW_CACHE && perf_config)
73562306a36Sopenharmony_ci		free((void *)perf_config);
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic int show_link_close_plain(int fd, struct bpf_link_info *info)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct bpf_prog_info prog_info;
74162306a36Sopenharmony_ci	const char *prog_type_str;
74262306a36Sopenharmony_ci	int err;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	show_link_header_plain(info);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	switch (info->type) {
74762306a36Sopenharmony_ci	case BPF_LINK_TYPE_RAW_TRACEPOINT:
74862306a36Sopenharmony_ci		printf("\n\ttp '%s'  ",
74962306a36Sopenharmony_ci		       (const char *)u64_to_ptr(info->raw_tracepoint.tp_name));
75062306a36Sopenharmony_ci		break;
75162306a36Sopenharmony_ci	case BPF_LINK_TYPE_TRACING:
75262306a36Sopenharmony_ci		err = get_prog_info(info->prog_id, &prog_info);
75362306a36Sopenharmony_ci		if (err)
75462306a36Sopenharmony_ci			return err;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci		prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);
75762306a36Sopenharmony_ci		/* libbpf will return NULL for variants unknown to it. */
75862306a36Sopenharmony_ci		if (prog_type_str)
75962306a36Sopenharmony_ci			printf("\n\tprog_type %s  ", prog_type_str);
76062306a36Sopenharmony_ci		else
76162306a36Sopenharmony_ci			printf("\n\tprog_type %u  ", prog_info.type);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci		show_link_attach_type_plain(info->tracing.attach_type);
76462306a36Sopenharmony_ci		if (info->tracing.target_obj_id || info->tracing.target_btf_id)
76562306a36Sopenharmony_ci			printf("\n\ttarget_obj_id %u  target_btf_id %u  ",
76662306a36Sopenharmony_ci			       info->tracing.target_obj_id,
76762306a36Sopenharmony_ci			       info->tracing.target_btf_id);
76862306a36Sopenharmony_ci		break;
76962306a36Sopenharmony_ci	case BPF_LINK_TYPE_CGROUP:
77062306a36Sopenharmony_ci		printf("\n\tcgroup_id %zu  ", (size_t)info->cgroup.cgroup_id);
77162306a36Sopenharmony_ci		show_link_attach_type_plain(info->cgroup.attach_type);
77262306a36Sopenharmony_ci		break;
77362306a36Sopenharmony_ci	case BPF_LINK_TYPE_ITER:
77462306a36Sopenharmony_ci		show_iter_plain(info);
77562306a36Sopenharmony_ci		break;
77662306a36Sopenharmony_ci	case BPF_LINK_TYPE_NETNS:
77762306a36Sopenharmony_ci		printf("\n\tnetns_ino %u  ", info->netns.netns_ino);
77862306a36Sopenharmony_ci		show_link_attach_type_plain(info->netns.attach_type);
77962306a36Sopenharmony_ci		break;
78062306a36Sopenharmony_ci	case BPF_LINK_TYPE_NETFILTER:
78162306a36Sopenharmony_ci		netfilter_dump_plain(info);
78262306a36Sopenharmony_ci		break;
78362306a36Sopenharmony_ci	case BPF_LINK_TYPE_TCX:
78462306a36Sopenharmony_ci		printf("\n\t");
78562306a36Sopenharmony_ci		show_link_ifindex_plain(info->tcx.ifindex);
78662306a36Sopenharmony_ci		show_link_attach_type_plain(info->tcx.attach_type);
78762306a36Sopenharmony_ci		break;
78862306a36Sopenharmony_ci	case BPF_LINK_TYPE_XDP:
78962306a36Sopenharmony_ci		printf("\n\t");
79062306a36Sopenharmony_ci		show_link_ifindex_plain(info->xdp.ifindex);
79162306a36Sopenharmony_ci		break;
79262306a36Sopenharmony_ci	case BPF_LINK_TYPE_KPROBE_MULTI:
79362306a36Sopenharmony_ci		show_kprobe_multi_plain(info);
79462306a36Sopenharmony_ci		break;
79562306a36Sopenharmony_ci	case BPF_LINK_TYPE_PERF_EVENT:
79662306a36Sopenharmony_ci		switch (info->perf_event.type) {
79762306a36Sopenharmony_ci		case BPF_PERF_EVENT_EVENT:
79862306a36Sopenharmony_ci			show_perf_event_event_plain(info);
79962306a36Sopenharmony_ci			break;
80062306a36Sopenharmony_ci		case BPF_PERF_EVENT_TRACEPOINT:
80162306a36Sopenharmony_ci			show_perf_event_tracepoint_plain(info);
80262306a36Sopenharmony_ci			break;
80362306a36Sopenharmony_ci		case BPF_PERF_EVENT_KPROBE:
80462306a36Sopenharmony_ci		case BPF_PERF_EVENT_KRETPROBE:
80562306a36Sopenharmony_ci			show_perf_event_kprobe_plain(info);
80662306a36Sopenharmony_ci			break;
80762306a36Sopenharmony_ci		case BPF_PERF_EVENT_UPROBE:
80862306a36Sopenharmony_ci		case BPF_PERF_EVENT_URETPROBE:
80962306a36Sopenharmony_ci			show_perf_event_uprobe_plain(info);
81062306a36Sopenharmony_ci			break;
81162306a36Sopenharmony_ci		default:
81262306a36Sopenharmony_ci			break;
81362306a36Sopenharmony_ci		}
81462306a36Sopenharmony_ci		break;
81562306a36Sopenharmony_ci	default:
81662306a36Sopenharmony_ci		break;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (!hashmap__empty(link_table)) {
82062306a36Sopenharmony_ci		struct hashmap_entry *entry;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci		hashmap__for_each_key_entry(link_table, entry, info->id)
82362306a36Sopenharmony_ci			printf("\n\tpinned %s", (char *)entry->pvalue);
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci	emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	printf("\n");
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	return 0;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic int do_show_link(int fd)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	struct bpf_link_info info;
83562306a36Sopenharmony_ci	__u32 len = sizeof(info);
83662306a36Sopenharmony_ci	__u64 *addrs = NULL;
83762306a36Sopenharmony_ci	char buf[PATH_MAX];
83862306a36Sopenharmony_ci	int count;
83962306a36Sopenharmony_ci	int err;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	memset(&info, 0, sizeof(info));
84262306a36Sopenharmony_ci	buf[0] = '\0';
84362306a36Sopenharmony_ciagain:
84462306a36Sopenharmony_ci	err = bpf_link_get_info_by_fd(fd, &info, &len);
84562306a36Sopenharmony_ci	if (err) {
84662306a36Sopenharmony_ci		p_err("can't get link info: %s",
84762306a36Sopenharmony_ci		      strerror(errno));
84862306a36Sopenharmony_ci		close(fd);
84962306a36Sopenharmony_ci		return err;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci	if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
85262306a36Sopenharmony_ci	    !info.raw_tracepoint.tp_name) {
85362306a36Sopenharmony_ci		info.raw_tracepoint.tp_name = ptr_to_u64(&buf);
85462306a36Sopenharmony_ci		info.raw_tracepoint.tp_name_len = sizeof(buf);
85562306a36Sopenharmony_ci		goto again;
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci	if (info.type == BPF_LINK_TYPE_ITER &&
85862306a36Sopenharmony_ci	    !info.iter.target_name) {
85962306a36Sopenharmony_ci		info.iter.target_name = ptr_to_u64(&buf);
86062306a36Sopenharmony_ci		info.iter.target_name_len = sizeof(buf);
86162306a36Sopenharmony_ci		goto again;
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci	if (info.type == BPF_LINK_TYPE_KPROBE_MULTI &&
86462306a36Sopenharmony_ci	    !info.kprobe_multi.addrs) {
86562306a36Sopenharmony_ci		count = info.kprobe_multi.count;
86662306a36Sopenharmony_ci		if (count) {
86762306a36Sopenharmony_ci			addrs = calloc(count, sizeof(__u64));
86862306a36Sopenharmony_ci			if (!addrs) {
86962306a36Sopenharmony_ci				p_err("mem alloc failed");
87062306a36Sopenharmony_ci				close(fd);
87162306a36Sopenharmony_ci				return -ENOMEM;
87262306a36Sopenharmony_ci			}
87362306a36Sopenharmony_ci			info.kprobe_multi.addrs = ptr_to_u64(addrs);
87462306a36Sopenharmony_ci			goto again;
87562306a36Sopenharmony_ci		}
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci	if (info.type == BPF_LINK_TYPE_PERF_EVENT) {
87862306a36Sopenharmony_ci		switch (info.perf_event.type) {
87962306a36Sopenharmony_ci		case BPF_PERF_EVENT_TRACEPOINT:
88062306a36Sopenharmony_ci			if (!info.perf_event.tracepoint.tp_name) {
88162306a36Sopenharmony_ci				info.perf_event.tracepoint.tp_name = ptr_to_u64(&buf);
88262306a36Sopenharmony_ci				info.perf_event.tracepoint.name_len = sizeof(buf);
88362306a36Sopenharmony_ci				goto again;
88462306a36Sopenharmony_ci			}
88562306a36Sopenharmony_ci			break;
88662306a36Sopenharmony_ci		case BPF_PERF_EVENT_KPROBE:
88762306a36Sopenharmony_ci		case BPF_PERF_EVENT_KRETPROBE:
88862306a36Sopenharmony_ci			if (!info.perf_event.kprobe.func_name) {
88962306a36Sopenharmony_ci				info.perf_event.kprobe.func_name = ptr_to_u64(&buf);
89062306a36Sopenharmony_ci				info.perf_event.kprobe.name_len = sizeof(buf);
89162306a36Sopenharmony_ci				goto again;
89262306a36Sopenharmony_ci			}
89362306a36Sopenharmony_ci			break;
89462306a36Sopenharmony_ci		case BPF_PERF_EVENT_UPROBE:
89562306a36Sopenharmony_ci		case BPF_PERF_EVENT_URETPROBE:
89662306a36Sopenharmony_ci			if (!info.perf_event.uprobe.file_name) {
89762306a36Sopenharmony_ci				info.perf_event.uprobe.file_name = ptr_to_u64(&buf);
89862306a36Sopenharmony_ci				info.perf_event.uprobe.name_len = sizeof(buf);
89962306a36Sopenharmony_ci				goto again;
90062306a36Sopenharmony_ci			}
90162306a36Sopenharmony_ci			break;
90262306a36Sopenharmony_ci		default:
90362306a36Sopenharmony_ci			break;
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (json_output)
90862306a36Sopenharmony_ci		show_link_close_json(fd, &info);
90962306a36Sopenharmony_ci	else
91062306a36Sopenharmony_ci		show_link_close_plain(fd, &info);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	if (addrs)
91362306a36Sopenharmony_ci		free(addrs);
91462306a36Sopenharmony_ci	close(fd);
91562306a36Sopenharmony_ci	return 0;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cistatic int do_show(int argc, char **argv)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	__u32 id = 0;
92162306a36Sopenharmony_ci	int err, fd;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (show_pinned) {
92462306a36Sopenharmony_ci		link_table = hashmap__new(hash_fn_for_key_as_id,
92562306a36Sopenharmony_ci					  equal_fn_for_key_as_id, NULL);
92662306a36Sopenharmony_ci		if (IS_ERR(link_table)) {
92762306a36Sopenharmony_ci			p_err("failed to create hashmap for pinned paths");
92862306a36Sopenharmony_ci			return -1;
92962306a36Sopenharmony_ci		}
93062306a36Sopenharmony_ci		build_pinned_obj_table(link_table, BPF_OBJ_LINK);
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci	build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	if (argc == 2) {
93562306a36Sopenharmony_ci		fd = link_parse_fd(&argc, &argv);
93662306a36Sopenharmony_ci		if (fd < 0)
93762306a36Sopenharmony_ci			return fd;
93862306a36Sopenharmony_ci		do_show_link(fd);
93962306a36Sopenharmony_ci		goto out;
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (argc)
94362306a36Sopenharmony_ci		return BAD_ARG();
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (json_output)
94662306a36Sopenharmony_ci		jsonw_start_array(json_wtr);
94762306a36Sopenharmony_ci	while (true) {
94862306a36Sopenharmony_ci		err = bpf_link_get_next_id(id, &id);
94962306a36Sopenharmony_ci		if (err) {
95062306a36Sopenharmony_ci			if (errno == ENOENT)
95162306a36Sopenharmony_ci				break;
95262306a36Sopenharmony_ci			p_err("can't get next link: %s%s", strerror(errno),
95362306a36Sopenharmony_ci			      errno == EINVAL ? " -- kernel too old?" : "");
95462306a36Sopenharmony_ci			break;
95562306a36Sopenharmony_ci		}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci		fd = bpf_link_get_fd_by_id(id);
95862306a36Sopenharmony_ci		if (fd < 0) {
95962306a36Sopenharmony_ci			if (errno == ENOENT)
96062306a36Sopenharmony_ci				continue;
96162306a36Sopenharmony_ci			p_err("can't get link by id (%u): %s",
96262306a36Sopenharmony_ci			      id, strerror(errno));
96362306a36Sopenharmony_ci			break;
96462306a36Sopenharmony_ci		}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci		err = do_show_link(fd);
96762306a36Sopenharmony_ci		if (err)
96862306a36Sopenharmony_ci			break;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci	if (json_output)
97162306a36Sopenharmony_ci		jsonw_end_array(json_wtr);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	delete_obj_refs_table(refs_table);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (show_pinned)
97662306a36Sopenharmony_ci		delete_pinned_obj_table(link_table);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ciout:
97962306a36Sopenharmony_ci	if (dd.sym_count)
98062306a36Sopenharmony_ci		kernel_syms_destroy(&dd);
98162306a36Sopenharmony_ci	return errno == ENOENT ? 0 : -1;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic int do_pin(int argc, char **argv)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	int err;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	err = do_pin_any(argc, argv, link_parse_fd);
98962306a36Sopenharmony_ci	if (!err && json_output)
99062306a36Sopenharmony_ci		jsonw_null(json_wtr);
99162306a36Sopenharmony_ci	return err;
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic int do_detach(int argc, char **argv)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	int err, fd;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	if (argc != 2) {
99962306a36Sopenharmony_ci		p_err("link specifier is invalid or missing\n");
100062306a36Sopenharmony_ci		return 1;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	fd = link_parse_fd(&argc, &argv);
100462306a36Sopenharmony_ci	if (fd < 0)
100562306a36Sopenharmony_ci		return 1;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	err = bpf_link_detach(fd);
100862306a36Sopenharmony_ci	if (err)
100962306a36Sopenharmony_ci		err = -errno;
101062306a36Sopenharmony_ci	close(fd);
101162306a36Sopenharmony_ci	if (err) {
101262306a36Sopenharmony_ci		p_err("failed link detach: %s", strerror(-err));
101362306a36Sopenharmony_ci		return 1;
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	if (json_output)
101762306a36Sopenharmony_ci		jsonw_null(json_wtr);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	return 0;
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_cistatic int do_help(int argc, char **argv)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	if (json_output) {
102562306a36Sopenharmony_ci		jsonw_null(json_wtr);
102662306a36Sopenharmony_ci		return 0;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	fprintf(stderr,
103062306a36Sopenharmony_ci		"Usage: %1$s %2$s { show | list }   [LINK]\n"
103162306a36Sopenharmony_ci		"       %1$s %2$s pin        LINK  FILE\n"
103262306a36Sopenharmony_ci		"       %1$s %2$s detach     LINK\n"
103362306a36Sopenharmony_ci		"       %1$s %2$s help\n"
103462306a36Sopenharmony_ci		"\n"
103562306a36Sopenharmony_ci		"       " HELP_SPEC_LINK "\n"
103662306a36Sopenharmony_ci		"       " HELP_SPEC_OPTIONS " |\n"
103762306a36Sopenharmony_ci		"                    {-f|--bpffs} | {-n|--nomount} }\n"
103862306a36Sopenharmony_ci		"",
103962306a36Sopenharmony_ci		bin_name, argv[-2]);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	return 0;
104262306a36Sopenharmony_ci}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_cistatic const struct cmd cmds[] = {
104562306a36Sopenharmony_ci	{ "show",	do_show },
104662306a36Sopenharmony_ci	{ "list",	do_show },
104762306a36Sopenharmony_ci	{ "help",	do_help },
104862306a36Sopenharmony_ci	{ "pin",	do_pin },
104962306a36Sopenharmony_ci	{ "detach",	do_detach },
105062306a36Sopenharmony_ci	{ 0 }
105162306a36Sopenharmony_ci};
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ciint do_link(int argc, char **argv)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	return cmd_select(cmds, argc, argv, do_help);
105662306a36Sopenharmony_ci}
1057