162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <errno.h>
362306a36Sopenharmony_ci#include <stdlib.h>
462306a36Sopenharmony_ci#include <bpf/bpf.h>
562306a36Sopenharmony_ci#include <bpf/btf.h>
662306a36Sopenharmony_ci#include <bpf/libbpf.h>
762306a36Sopenharmony_ci#include <linux/btf.h>
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/string.h>
1062306a36Sopenharmony_ci#include <internal/lib.h>
1162306a36Sopenharmony_ci#include <symbol/kallsyms.h>
1262306a36Sopenharmony_ci#include "bpf-event.h"
1362306a36Sopenharmony_ci#include "bpf-utils.h"
1462306a36Sopenharmony_ci#include "debug.h"
1562306a36Sopenharmony_ci#include "dso.h"
1662306a36Sopenharmony_ci#include "symbol.h"
1762306a36Sopenharmony_ci#include "machine.h"
1862306a36Sopenharmony_ci#include "env.h"
1962306a36Sopenharmony_ci#include "session.h"
2062306a36Sopenharmony_ci#include "map.h"
2162306a36Sopenharmony_ci#include "evlist.h"
2262306a36Sopenharmony_ci#include "record.h"
2362306a36Sopenharmony_ci#include "util/synthetic-events.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	int ret = 0;
2862306a36Sopenharmony_ci	size_t i;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	for (i = 0; i < len; i++)
3162306a36Sopenharmony_ci		ret += snprintf(buf + ret, size - ret, "%02x", data[i]);
3262306a36Sopenharmony_ci	return ret;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int machine__process_bpf_event_load(struct machine *machine,
3662306a36Sopenharmony_ci					   union perf_event *event,
3762306a36Sopenharmony_ci					   struct perf_sample *sample __maybe_unused)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct bpf_prog_info_node *info_node;
4062306a36Sopenharmony_ci	struct perf_env *env = machine->env;
4162306a36Sopenharmony_ci	struct perf_bpil *info_linear;
4262306a36Sopenharmony_ci	int id = event->bpf.id;
4362306a36Sopenharmony_ci	unsigned int i;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* perf-record, no need to handle bpf-event */
4662306a36Sopenharmony_ci	if (env == NULL)
4762306a36Sopenharmony_ci		return 0;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	info_node = perf_env__find_bpf_prog_info(env, id);
5062306a36Sopenharmony_ci	if (!info_node)
5162306a36Sopenharmony_ci		return 0;
5262306a36Sopenharmony_ci	info_linear = info_node->info_linear;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (i = 0; i < info_linear->info.nr_jited_ksyms; i++) {
5562306a36Sopenharmony_ci		u64 *addrs = (u64 *)(uintptr_t)(info_linear->info.jited_ksyms);
5662306a36Sopenharmony_ci		u64 addr = addrs[i];
5762306a36Sopenharmony_ci		struct map *map = maps__find(machine__kernel_maps(machine), addr);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		if (map) {
6062306a36Sopenharmony_ci			struct dso *dso = map__dso(map);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci			dso->binary_type = DSO_BINARY_TYPE__BPF_PROG_INFO;
6362306a36Sopenharmony_ci			dso->bpf_prog.id = id;
6462306a36Sopenharmony_ci			dso->bpf_prog.sub_id = i;
6562306a36Sopenharmony_ci			dso->bpf_prog.env = env;
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciint machine__process_bpf(struct machine *machine, union perf_event *event,
7262306a36Sopenharmony_ci			 struct perf_sample *sample)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	if (dump_trace)
7562306a36Sopenharmony_ci		perf_event__fprintf_bpf(event, stdout);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	switch (event->bpf.type) {
7862306a36Sopenharmony_ci	case PERF_BPF_EVENT_PROG_LOAD:
7962306a36Sopenharmony_ci		return machine__process_bpf_event_load(machine, event, sample);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	case PERF_BPF_EVENT_PROG_UNLOAD:
8262306a36Sopenharmony_ci		/*
8362306a36Sopenharmony_ci		 * Do not free bpf_prog_info and btf of the program here,
8462306a36Sopenharmony_ci		 * as annotation still need them. They will be freed at
8562306a36Sopenharmony_ci		 * the end of the session.
8662306a36Sopenharmony_ci		 */
8762306a36Sopenharmony_ci		break;
8862306a36Sopenharmony_ci	default:
8962306a36Sopenharmony_ci		pr_debug("unexpected bpf event type of %d\n", event->bpf.type);
9062306a36Sopenharmony_ci		break;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	return 0;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int perf_env__fetch_btf(struct perf_env *env,
9662306a36Sopenharmony_ci			       u32 btf_id,
9762306a36Sopenharmony_ci			       struct btf *btf)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct btf_node *node;
10062306a36Sopenharmony_ci	u32 data_size;
10162306a36Sopenharmony_ci	const void *data;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	data = btf__raw_data(btf, &data_size);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	node = malloc(data_size + sizeof(struct btf_node));
10662306a36Sopenharmony_ci	if (!node)
10762306a36Sopenharmony_ci		return -1;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	node->id = btf_id;
11062306a36Sopenharmony_ci	node->data_size = data_size;
11162306a36Sopenharmony_ci	memcpy(node->data, data, data_size);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (!perf_env__insert_btf(env, node)) {
11462306a36Sopenharmony_ci		/* Insertion failed because of a duplicate. */
11562306a36Sopenharmony_ci		free(node);
11662306a36Sopenharmony_ci		return -1;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	return 0;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int synthesize_bpf_prog_name(char *buf, int size,
12262306a36Sopenharmony_ci				    struct bpf_prog_info *info,
12362306a36Sopenharmony_ci				    struct btf *btf,
12462306a36Sopenharmony_ci				    u32 sub_id)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	u8 (*prog_tags)[BPF_TAG_SIZE] = (void *)(uintptr_t)(info->prog_tags);
12762306a36Sopenharmony_ci	void *func_infos = (void *)(uintptr_t)(info->func_info);
12862306a36Sopenharmony_ci	u32 sub_prog_cnt = info->nr_jited_ksyms;
12962306a36Sopenharmony_ci	const struct bpf_func_info *finfo;
13062306a36Sopenharmony_ci	const char *short_name = NULL;
13162306a36Sopenharmony_ci	const struct btf_type *t;
13262306a36Sopenharmony_ci	int name_len;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	name_len = snprintf(buf, size, "bpf_prog_");
13562306a36Sopenharmony_ci	name_len += snprintf_hex(buf + name_len, size - name_len,
13662306a36Sopenharmony_ci				 prog_tags[sub_id], BPF_TAG_SIZE);
13762306a36Sopenharmony_ci	if (btf) {
13862306a36Sopenharmony_ci		finfo = func_infos + sub_id * info->func_info_rec_size;
13962306a36Sopenharmony_ci		t = btf__type_by_id(btf, finfo->type_id);
14062306a36Sopenharmony_ci		short_name = btf__name_by_offset(btf, t->name_off);
14162306a36Sopenharmony_ci	} else if (sub_id == 0 && sub_prog_cnt == 1) {
14262306a36Sopenharmony_ci		/* no subprog */
14362306a36Sopenharmony_ci		if (info->name[0])
14462306a36Sopenharmony_ci			short_name = info->name;
14562306a36Sopenharmony_ci	} else
14662306a36Sopenharmony_ci		short_name = "F";
14762306a36Sopenharmony_ci	if (short_name)
14862306a36Sopenharmony_ci		name_len += snprintf(buf + name_len, size - name_len,
14962306a36Sopenharmony_ci				     "_%s", short_name);
15062306a36Sopenharmony_ci	return name_len;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/*
15462306a36Sopenharmony_ci * Synthesize PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT for one bpf
15562306a36Sopenharmony_ci * program. One PERF_RECORD_BPF_EVENT is generated for the program. And
15662306a36Sopenharmony_ci * one PERF_RECORD_KSYMBOL is generated for each sub program.
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci * Returns:
15962306a36Sopenharmony_ci *    0 for success;
16062306a36Sopenharmony_ci *   -1 for failures;
16162306a36Sopenharmony_ci *   -2 for lack of kernel support.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
16462306a36Sopenharmony_ci					       perf_event__handler_t process,
16562306a36Sopenharmony_ci					       struct machine *machine,
16662306a36Sopenharmony_ci					       int fd,
16762306a36Sopenharmony_ci					       union perf_event *event,
16862306a36Sopenharmony_ci					       struct record_opts *opts)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct perf_record_ksymbol *ksymbol_event = &event->ksymbol;
17162306a36Sopenharmony_ci	struct perf_record_bpf_event *bpf_event = &event->bpf;
17262306a36Sopenharmony_ci	struct perf_tool *tool = session->tool;
17362306a36Sopenharmony_ci	struct bpf_prog_info_node *info_node;
17462306a36Sopenharmony_ci	struct perf_bpil *info_linear;
17562306a36Sopenharmony_ci	struct bpf_prog_info *info;
17662306a36Sopenharmony_ci	struct btf *btf = NULL;
17762306a36Sopenharmony_ci	struct perf_env *env;
17862306a36Sopenharmony_ci	u32 sub_prog_cnt, i;
17962306a36Sopenharmony_ci	int err = 0;
18062306a36Sopenharmony_ci	u64 arrays;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/*
18362306a36Sopenharmony_ci	 * for perf-record and perf-report use header.env;
18462306a36Sopenharmony_ci	 * otherwise, use global perf_env.
18562306a36Sopenharmony_ci	 */
18662306a36Sopenharmony_ci	env = session->data ? &session->header.env : &perf_env;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	arrays = 1UL << PERF_BPIL_JITED_KSYMS;
18962306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_JITED_FUNC_LENS;
19062306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_FUNC_INFO;
19162306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_PROG_TAGS;
19262306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_JITED_INSNS;
19362306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_LINE_INFO;
19462306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_JITED_LINE_INFO;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	info_linear = get_bpf_prog_info_linear(fd, arrays);
19762306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(info_linear)) {
19862306a36Sopenharmony_ci		info_linear = NULL;
19962306a36Sopenharmony_ci		pr_debug("%s: failed to get BPF program info. aborting\n", __func__);
20062306a36Sopenharmony_ci		return -1;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (info_linear->info_len < offsetof(struct bpf_prog_info, prog_tags)) {
20462306a36Sopenharmony_ci		free(info_linear);
20562306a36Sopenharmony_ci		pr_debug("%s: the kernel is too old, aborting\n", __func__);
20662306a36Sopenharmony_ci		return -2;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	info = &info_linear->info;
21062306a36Sopenharmony_ci	if (!info->jited_ksyms) {
21162306a36Sopenharmony_ci		free(info_linear);
21262306a36Sopenharmony_ci		return -1;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* number of ksyms, func_lengths, and tags should match */
21662306a36Sopenharmony_ci	sub_prog_cnt = info->nr_jited_ksyms;
21762306a36Sopenharmony_ci	if (sub_prog_cnt != info->nr_prog_tags ||
21862306a36Sopenharmony_ci	    sub_prog_cnt != info->nr_jited_func_lens) {
21962306a36Sopenharmony_ci		free(info_linear);
22062306a36Sopenharmony_ci		return -1;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* check BTF func info support */
22462306a36Sopenharmony_ci	if (info->btf_id && info->nr_func_info && info->func_info_rec_size) {
22562306a36Sopenharmony_ci		/* btf func info number should be same as sub_prog_cnt */
22662306a36Sopenharmony_ci		if (sub_prog_cnt != info->nr_func_info) {
22762306a36Sopenharmony_ci			pr_debug("%s: mismatch in BPF sub program count and BTF function info count, aborting\n", __func__);
22862306a36Sopenharmony_ci			free(info_linear);
22962306a36Sopenharmony_ci			return -1;
23062306a36Sopenharmony_ci		}
23162306a36Sopenharmony_ci		btf = btf__load_from_kernel_by_id(info->btf_id);
23262306a36Sopenharmony_ci		if (libbpf_get_error(btf)) {
23362306a36Sopenharmony_ci			pr_debug("%s: failed to get BTF of id %u, aborting\n", __func__, info->btf_id);
23462306a36Sopenharmony_ci			err = -1;
23562306a36Sopenharmony_ci			goto out;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci		perf_env__fetch_btf(env, info->btf_id, btf);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* Synthesize PERF_RECORD_KSYMBOL */
24162306a36Sopenharmony_ci	for (i = 0; i < sub_prog_cnt; i++) {
24262306a36Sopenharmony_ci		__u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens);
24362306a36Sopenharmony_ci		__u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms);
24462306a36Sopenharmony_ci		int name_len;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		*ksymbol_event = (struct perf_record_ksymbol) {
24762306a36Sopenharmony_ci			.header = {
24862306a36Sopenharmony_ci				.type = PERF_RECORD_KSYMBOL,
24962306a36Sopenharmony_ci				.size = offsetof(struct perf_record_ksymbol, name),
25062306a36Sopenharmony_ci			},
25162306a36Sopenharmony_ci			.addr = prog_addrs[i],
25262306a36Sopenharmony_ci			.len = prog_lens[i],
25362306a36Sopenharmony_ci			.ksym_type = PERF_RECORD_KSYMBOL_TYPE_BPF,
25462306a36Sopenharmony_ci			.flags = 0,
25562306a36Sopenharmony_ci		};
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		name_len = synthesize_bpf_prog_name(ksymbol_event->name,
25862306a36Sopenharmony_ci						    KSYM_NAME_LEN, info, btf, i);
25962306a36Sopenharmony_ci		ksymbol_event->header.size += PERF_ALIGN(name_len + 1,
26062306a36Sopenharmony_ci							 sizeof(u64));
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		memset((void *)event + event->header.size, 0, machine->id_hdr_size);
26362306a36Sopenharmony_ci		event->header.size += machine->id_hdr_size;
26462306a36Sopenharmony_ci		err = perf_tool__process_synth_event(tool, event,
26562306a36Sopenharmony_ci						     machine, process);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (!opts->no_bpf_event) {
26962306a36Sopenharmony_ci		/* Synthesize PERF_RECORD_BPF_EVENT */
27062306a36Sopenharmony_ci		*bpf_event = (struct perf_record_bpf_event) {
27162306a36Sopenharmony_ci			.header = {
27262306a36Sopenharmony_ci				.type = PERF_RECORD_BPF_EVENT,
27362306a36Sopenharmony_ci				.size = sizeof(struct perf_record_bpf_event),
27462306a36Sopenharmony_ci			},
27562306a36Sopenharmony_ci			.type = PERF_BPF_EVENT_PROG_LOAD,
27662306a36Sopenharmony_ci			.flags = 0,
27762306a36Sopenharmony_ci			.id = info->id,
27862306a36Sopenharmony_ci		};
27962306a36Sopenharmony_ci		memcpy(bpf_event->tag, info->tag, BPF_TAG_SIZE);
28062306a36Sopenharmony_ci		memset((void *)event + event->header.size, 0, machine->id_hdr_size);
28162306a36Sopenharmony_ci		event->header.size += machine->id_hdr_size;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/* save bpf_prog_info to env */
28462306a36Sopenharmony_ci		info_node = malloc(sizeof(struct bpf_prog_info_node));
28562306a36Sopenharmony_ci		if (!info_node) {
28662306a36Sopenharmony_ci			err = -1;
28762306a36Sopenharmony_ci			goto out;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		info_node->info_linear = info_linear;
29162306a36Sopenharmony_ci		perf_env__insert_bpf_prog_info(env, info_node);
29262306a36Sopenharmony_ci		info_linear = NULL;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		/*
29562306a36Sopenharmony_ci		 * process after saving bpf_prog_info to env, so that
29662306a36Sopenharmony_ci		 * required information is ready for look up
29762306a36Sopenharmony_ci		 */
29862306a36Sopenharmony_ci		err = perf_tool__process_synth_event(tool, event,
29962306a36Sopenharmony_ci						     machine, process);
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ciout:
30362306a36Sopenharmony_ci	free(info_linear);
30462306a36Sopenharmony_ci	btf__free(btf);
30562306a36Sopenharmony_ci	return err ? -1 : 0;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistruct kallsyms_parse {
30962306a36Sopenharmony_ci	union perf_event	*event;
31062306a36Sopenharmony_ci	perf_event__handler_t	 process;
31162306a36Sopenharmony_ci	struct machine		*machine;
31262306a36Sopenharmony_ci	struct perf_tool	*tool;
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int
31662306a36Sopenharmony_ciprocess_bpf_image(char *name, u64 addr, struct kallsyms_parse *data)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct machine *machine = data->machine;
31962306a36Sopenharmony_ci	union perf_event *event = data->event;
32062306a36Sopenharmony_ci	struct perf_record_ksymbol *ksymbol;
32162306a36Sopenharmony_ci	int len;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	ksymbol = &event->ksymbol;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	*ksymbol = (struct perf_record_ksymbol) {
32662306a36Sopenharmony_ci		.header = {
32762306a36Sopenharmony_ci			.type = PERF_RECORD_KSYMBOL,
32862306a36Sopenharmony_ci			.size = offsetof(struct perf_record_ksymbol, name),
32962306a36Sopenharmony_ci		},
33062306a36Sopenharmony_ci		.addr      = addr,
33162306a36Sopenharmony_ci		.len       = page_size,
33262306a36Sopenharmony_ci		.ksym_type = PERF_RECORD_KSYMBOL_TYPE_BPF,
33362306a36Sopenharmony_ci		.flags     = 0,
33462306a36Sopenharmony_ci	};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	len = scnprintf(ksymbol->name, KSYM_NAME_LEN, "%s", name);
33762306a36Sopenharmony_ci	ksymbol->header.size += PERF_ALIGN(len + 1, sizeof(u64));
33862306a36Sopenharmony_ci	memset((void *) event + event->header.size, 0, machine->id_hdr_size);
33962306a36Sopenharmony_ci	event->header.size += machine->id_hdr_size;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return perf_tool__process_synth_event(data->tool, event, machine,
34262306a36Sopenharmony_ci					      data->process);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int
34662306a36Sopenharmony_cikallsyms_process_symbol(void *data, const char *_name,
34762306a36Sopenharmony_ci			char type __maybe_unused, u64 start)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	char disp[KSYM_NAME_LEN];
35062306a36Sopenharmony_ci	char *module, *name;
35162306a36Sopenharmony_ci	unsigned long id;
35262306a36Sopenharmony_ci	int err = 0;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	module = strchr(_name, '\t');
35562306a36Sopenharmony_ci	if (!module)
35662306a36Sopenharmony_ci		return 0;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* We are going after [bpf] module ... */
35962306a36Sopenharmony_ci	if (strcmp(module + 1, "[bpf]"))
36062306a36Sopenharmony_ci		return 0;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	name = memdup(_name, (module - _name) + 1);
36362306a36Sopenharmony_ci	if (!name)
36462306a36Sopenharmony_ci		return -ENOMEM;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	name[module - _name] = 0;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* .. and only for trampolines and dispatchers */
36962306a36Sopenharmony_ci	if ((sscanf(name, "bpf_trampoline_%lu", &id) == 1) ||
37062306a36Sopenharmony_ci	    (sscanf(name, "bpf_dispatcher_%s", disp) == 1))
37162306a36Sopenharmony_ci		err = process_bpf_image(name, start, data);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	free(name);
37462306a36Sopenharmony_ci	return err;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ciint perf_event__synthesize_bpf_events(struct perf_session *session,
37862306a36Sopenharmony_ci				      perf_event__handler_t process,
37962306a36Sopenharmony_ci				      struct machine *machine,
38062306a36Sopenharmony_ci				      struct record_opts *opts)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	const char *kallsyms_filename = "/proc/kallsyms";
38362306a36Sopenharmony_ci	struct kallsyms_parse arg;
38462306a36Sopenharmony_ci	union perf_event *event;
38562306a36Sopenharmony_ci	__u32 id = 0;
38662306a36Sopenharmony_ci	int err;
38762306a36Sopenharmony_ci	int fd;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	event = malloc(sizeof(event->bpf) + KSYM_NAME_LEN + machine->id_hdr_size);
39062306a36Sopenharmony_ci	if (!event)
39162306a36Sopenharmony_ci		return -1;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* Synthesize all the bpf programs in system. */
39462306a36Sopenharmony_ci	while (true) {
39562306a36Sopenharmony_ci		err = bpf_prog_get_next_id(id, &id);
39662306a36Sopenharmony_ci		if (err) {
39762306a36Sopenharmony_ci			if (errno == ENOENT) {
39862306a36Sopenharmony_ci				err = 0;
39962306a36Sopenharmony_ci				break;
40062306a36Sopenharmony_ci			}
40162306a36Sopenharmony_ci			pr_debug("%s: can't get next program: %s%s\n",
40262306a36Sopenharmony_ci				 __func__, strerror(errno),
40362306a36Sopenharmony_ci				 errno == EINVAL ? " -- kernel too old?" : "");
40462306a36Sopenharmony_ci			/* don't report error on old kernel or EPERM  */
40562306a36Sopenharmony_ci			err = (errno == EINVAL || errno == EPERM) ? 0 : -1;
40662306a36Sopenharmony_ci			break;
40762306a36Sopenharmony_ci		}
40862306a36Sopenharmony_ci		fd = bpf_prog_get_fd_by_id(id);
40962306a36Sopenharmony_ci		if (fd < 0) {
41062306a36Sopenharmony_ci			pr_debug("%s: failed to get fd for prog_id %u\n",
41162306a36Sopenharmony_ci				 __func__, id);
41262306a36Sopenharmony_ci			continue;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		err = perf_event__synthesize_one_bpf_prog(session, process,
41662306a36Sopenharmony_ci							  machine, fd,
41762306a36Sopenharmony_ci							  event, opts);
41862306a36Sopenharmony_ci		close(fd);
41962306a36Sopenharmony_ci		if (err) {
42062306a36Sopenharmony_ci			/* do not return error for old kernel */
42162306a36Sopenharmony_ci			if (err == -2)
42262306a36Sopenharmony_ci				err = 0;
42362306a36Sopenharmony_ci			break;
42462306a36Sopenharmony_ci		}
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* Synthesize all the bpf images - trampolines/dispatchers. */
42862306a36Sopenharmony_ci	if (symbol_conf.kallsyms_name != NULL)
42962306a36Sopenharmony_ci		kallsyms_filename = symbol_conf.kallsyms_name;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	arg = (struct kallsyms_parse) {
43262306a36Sopenharmony_ci		.event   = event,
43362306a36Sopenharmony_ci		.process = process,
43462306a36Sopenharmony_ci		.machine = machine,
43562306a36Sopenharmony_ci		.tool    = session->tool,
43662306a36Sopenharmony_ci	};
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (kallsyms__parse(kallsyms_filename, &arg, kallsyms_process_symbol)) {
43962306a36Sopenharmony_ci		pr_err("%s: failed to synthesize bpf images: %s\n",
44062306a36Sopenharmony_ci		       __func__, strerror(errno));
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	free(event);
44462306a36Sopenharmony_ci	return err;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic void perf_env__add_bpf_info(struct perf_env *env, u32 id)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct bpf_prog_info_node *info_node;
45062306a36Sopenharmony_ci	struct perf_bpil *info_linear;
45162306a36Sopenharmony_ci	struct btf *btf = NULL;
45262306a36Sopenharmony_ci	u64 arrays;
45362306a36Sopenharmony_ci	u32 btf_id;
45462306a36Sopenharmony_ci	int fd;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	fd = bpf_prog_get_fd_by_id(id);
45762306a36Sopenharmony_ci	if (fd < 0)
45862306a36Sopenharmony_ci		return;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	arrays = 1UL << PERF_BPIL_JITED_KSYMS;
46162306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_JITED_FUNC_LENS;
46262306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_FUNC_INFO;
46362306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_PROG_TAGS;
46462306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_JITED_INSNS;
46562306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_LINE_INFO;
46662306a36Sopenharmony_ci	arrays |= 1UL << PERF_BPIL_JITED_LINE_INFO;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	info_linear = get_bpf_prog_info_linear(fd, arrays);
46962306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(info_linear)) {
47062306a36Sopenharmony_ci		pr_debug("%s: failed to get BPF program info. aborting\n", __func__);
47162306a36Sopenharmony_ci		goto out;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	btf_id = info_linear->info.btf_id;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	info_node = malloc(sizeof(struct bpf_prog_info_node));
47762306a36Sopenharmony_ci	if (info_node) {
47862306a36Sopenharmony_ci		info_node->info_linear = info_linear;
47962306a36Sopenharmony_ci		perf_env__insert_bpf_prog_info(env, info_node);
48062306a36Sopenharmony_ci	} else
48162306a36Sopenharmony_ci		free(info_linear);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (btf_id == 0)
48462306a36Sopenharmony_ci		goto out;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	btf = btf__load_from_kernel_by_id(btf_id);
48762306a36Sopenharmony_ci	if (libbpf_get_error(btf)) {
48862306a36Sopenharmony_ci		pr_debug("%s: failed to get BTF of id %u, aborting\n",
48962306a36Sopenharmony_ci			 __func__, btf_id);
49062306a36Sopenharmony_ci		goto out;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci	perf_env__fetch_btf(env, btf_id, btf);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ciout:
49562306a36Sopenharmony_ci	btf__free(btf);
49662306a36Sopenharmony_ci	close(fd);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int bpf_event__sb_cb(union perf_event *event, void *data)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct perf_env *env = data;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (event->header.type != PERF_RECORD_BPF_EVENT)
50462306a36Sopenharmony_ci		return -1;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	switch (event->bpf.type) {
50762306a36Sopenharmony_ci	case PERF_BPF_EVENT_PROG_LOAD:
50862306a36Sopenharmony_ci		perf_env__add_bpf_info(env, event->bpf.id);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	case PERF_BPF_EVENT_PROG_UNLOAD:
51162306a36Sopenharmony_ci		/*
51262306a36Sopenharmony_ci		 * Do not free bpf_prog_info and btf of the program here,
51362306a36Sopenharmony_ci		 * as annotation still need them. They will be freed at
51462306a36Sopenharmony_ci		 * the end of the session.
51562306a36Sopenharmony_ci		 */
51662306a36Sopenharmony_ci		break;
51762306a36Sopenharmony_ci	default:
51862306a36Sopenharmony_ci		pr_debug("unexpected bpf event type of %d\n", event->bpf.type);
51962306a36Sopenharmony_ci		break;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	return 0;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ciint evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct perf_event_attr attr = {
52862306a36Sopenharmony_ci		.type	          = PERF_TYPE_SOFTWARE,
52962306a36Sopenharmony_ci		.config           = PERF_COUNT_SW_DUMMY,
53062306a36Sopenharmony_ci		.sample_id_all    = 1,
53162306a36Sopenharmony_ci		.watermark        = 1,
53262306a36Sopenharmony_ci		.bpf_event        = 1,
53362306a36Sopenharmony_ci		.size	   = sizeof(attr), /* to capture ABI version */
53462306a36Sopenharmony_ci	};
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/*
53762306a36Sopenharmony_ci	 * Older gcc versions don't support designated initializers, like above,
53862306a36Sopenharmony_ci	 * for unnamed union members, such as the following:
53962306a36Sopenharmony_ci	 */
54062306a36Sopenharmony_ci	attr.wakeup_watermark = 1;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return evlist__add_sb_event(evlist, &attr, bpf_event__sb_cb, env);
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_civoid __bpf_event__print_bpf_prog_info(struct bpf_prog_info *info,
54662306a36Sopenharmony_ci				      struct perf_env *env,
54762306a36Sopenharmony_ci				      FILE *fp)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	__u32 *prog_lens = (__u32 *)(uintptr_t)(info->jited_func_lens);
55062306a36Sopenharmony_ci	__u64 *prog_addrs = (__u64 *)(uintptr_t)(info->jited_ksyms);
55162306a36Sopenharmony_ci	char name[KSYM_NAME_LEN];
55262306a36Sopenharmony_ci	struct btf *btf = NULL;
55362306a36Sopenharmony_ci	u32 sub_prog_cnt, i;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	sub_prog_cnt = info->nr_jited_ksyms;
55662306a36Sopenharmony_ci	if (sub_prog_cnt != info->nr_prog_tags ||
55762306a36Sopenharmony_ci	    sub_prog_cnt != info->nr_jited_func_lens)
55862306a36Sopenharmony_ci		return;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (info->btf_id) {
56162306a36Sopenharmony_ci		struct btf_node *node;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		node = __perf_env__find_btf(env, info->btf_id);
56462306a36Sopenharmony_ci		if (node)
56562306a36Sopenharmony_ci			btf = btf__new((__u8 *)(node->data),
56662306a36Sopenharmony_ci				       node->data_size);
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (sub_prog_cnt == 1) {
57062306a36Sopenharmony_ci		synthesize_bpf_prog_name(name, KSYM_NAME_LEN, info, btf, 0);
57162306a36Sopenharmony_ci		fprintf(fp, "# bpf_prog_info %u: %s addr 0x%llx size %u\n",
57262306a36Sopenharmony_ci			info->id, name, prog_addrs[0], prog_lens[0]);
57362306a36Sopenharmony_ci		goto out;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	fprintf(fp, "# bpf_prog_info %u:\n", info->id);
57762306a36Sopenharmony_ci	for (i = 0; i < sub_prog_cnt; i++) {
57862306a36Sopenharmony_ci		synthesize_bpf_prog_name(name, KSYM_NAME_LEN, info, btf, i);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		fprintf(fp, "# \tsub_prog %u: %s addr 0x%llx size %u\n",
58162306a36Sopenharmony_ci			i, name, prog_addrs[i], prog_lens[i]);
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ciout:
58462306a36Sopenharmony_ci	btf__free(btf);
58562306a36Sopenharmony_ci}
586