162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include "perf-sys.h"
462306a36Sopenharmony_ci#include "util/cloexec.h"
562306a36Sopenharmony_ci#include "util/evlist.h"
662306a36Sopenharmony_ci#include "util/evsel.h"
762306a36Sopenharmony_ci#include "util/parse-events.h"
862306a36Sopenharmony_ci#include "util/perf_api_probe.h"
962306a36Sopenharmony_ci#include <perf/cpumap.h>
1062306a36Sopenharmony_ci#include <errno.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_citypedef void (*setup_probe_fn_t)(struct evsel *evsel);
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int perf_do_probe_api(setup_probe_fn_t fn, struct perf_cpu cpu, const char *str)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct evlist *evlist;
1762306a36Sopenharmony_ci	struct evsel *evsel;
1862306a36Sopenharmony_ci	unsigned long flags = perf_event_open_cloexec_flag();
1962306a36Sopenharmony_ci	int err = -EAGAIN, fd;
2062306a36Sopenharmony_ci	static pid_t pid = -1;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	evlist = evlist__new();
2362306a36Sopenharmony_ci	if (!evlist)
2462306a36Sopenharmony_ci		return -ENOMEM;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	if (parse_event(evlist, str))
2762306a36Sopenharmony_ci		goto out_delete;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	evsel = evlist__first(evlist);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	while (1) {
3262306a36Sopenharmony_ci		fd = sys_perf_event_open(&evsel->core.attr, pid, cpu.cpu, -1, flags);
3362306a36Sopenharmony_ci		if (fd < 0) {
3462306a36Sopenharmony_ci			if (pid == -1 && errno == EACCES) {
3562306a36Sopenharmony_ci				pid = 0;
3662306a36Sopenharmony_ci				continue;
3762306a36Sopenharmony_ci			}
3862306a36Sopenharmony_ci			goto out_delete;
3962306a36Sopenharmony_ci		}
4062306a36Sopenharmony_ci		break;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci	close(fd);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	fn(evsel);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	fd = sys_perf_event_open(&evsel->core.attr, pid, cpu.cpu, -1, flags);
4762306a36Sopenharmony_ci	if (fd < 0) {
4862306a36Sopenharmony_ci		if (errno == EINVAL)
4962306a36Sopenharmony_ci			err = -EINVAL;
5062306a36Sopenharmony_ci		goto out_delete;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci	close(fd);
5362306a36Sopenharmony_ci	err = 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciout_delete:
5662306a36Sopenharmony_ci	evlist__delete(evlist);
5762306a36Sopenharmony_ci	return err;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic bool perf_probe_api(setup_probe_fn_t fn)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL};
6362306a36Sopenharmony_ci	struct perf_cpu_map *cpus;
6462306a36Sopenharmony_ci	struct perf_cpu cpu;
6562306a36Sopenharmony_ci	int ret, i = 0;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	cpus = perf_cpu_map__new(NULL);
6862306a36Sopenharmony_ci	if (!cpus)
6962306a36Sopenharmony_ci		return false;
7062306a36Sopenharmony_ci	cpu = perf_cpu_map__cpu(cpus, 0);
7162306a36Sopenharmony_ci	perf_cpu_map__put(cpus);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	do {
7462306a36Sopenharmony_ci		ret = perf_do_probe_api(fn, cpu, try[i++]);
7562306a36Sopenharmony_ci		if (!ret)
7662306a36Sopenharmony_ci			return true;
7762306a36Sopenharmony_ci	} while (ret == -EAGAIN && try[i]);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return false;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic void perf_probe_sample_identifier(struct evsel *evsel)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	evsel->core.attr.sample_type |= PERF_SAMPLE_IDENTIFIER;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void perf_probe_comm_exec(struct evsel *evsel)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	evsel->core.attr.comm_exec = 1;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void perf_probe_context_switch(struct evsel *evsel)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	evsel->core.attr.context_switch = 1;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void perf_probe_text_poke(struct evsel *evsel)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	evsel->core.attr.text_poke = 1;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void perf_probe_build_id(struct evsel *evsel)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	evsel->core.attr.build_id = 1;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void perf_probe_cgroup(struct evsel *evsel)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	evsel->core.attr.cgroup = 1;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cibool perf_can_sample_identifier(void)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	return perf_probe_api(perf_probe_sample_identifier);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cibool perf_can_comm_exec(void)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	return perf_probe_api(perf_probe_comm_exec);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cibool perf_can_record_switch_events(void)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	return perf_probe_api(perf_probe_context_switch);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cibool perf_can_record_text_poke_events(void)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	return perf_probe_api(perf_probe_text_poke);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cibool perf_can_record_cpu_wide(void)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct perf_event_attr attr = {
13562306a36Sopenharmony_ci		.type = PERF_TYPE_SOFTWARE,
13662306a36Sopenharmony_ci		.config = PERF_COUNT_SW_CPU_CLOCK,
13762306a36Sopenharmony_ci		.exclude_kernel = 1,
13862306a36Sopenharmony_ci	};
13962306a36Sopenharmony_ci	struct perf_cpu_map *cpus;
14062306a36Sopenharmony_ci	struct perf_cpu cpu;
14162306a36Sopenharmony_ci	int fd;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	cpus = perf_cpu_map__new(NULL);
14462306a36Sopenharmony_ci	if (!cpus)
14562306a36Sopenharmony_ci		return false;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	cpu = perf_cpu_map__cpu(cpus, 0);
14862306a36Sopenharmony_ci	perf_cpu_map__put(cpus);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	fd = sys_perf_event_open(&attr, -1, cpu.cpu, -1, 0);
15162306a36Sopenharmony_ci	if (fd < 0)
15262306a36Sopenharmony_ci		return false;
15362306a36Sopenharmony_ci	close(fd);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return true;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * Architectures are expected to know if AUX area sampling is supported by the
16062306a36Sopenharmony_ci * hardware. Here we check for kernel support.
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_cibool perf_can_aux_sample(void)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct perf_event_attr attr = {
16562306a36Sopenharmony_ci		.size = sizeof(struct perf_event_attr),
16662306a36Sopenharmony_ci		.exclude_kernel = 1,
16762306a36Sopenharmony_ci		/*
16862306a36Sopenharmony_ci		 * Non-zero value causes the kernel to calculate the effective
16962306a36Sopenharmony_ci		 * attribute size up to that byte.
17062306a36Sopenharmony_ci		 */
17162306a36Sopenharmony_ci		.aux_sample_size = 1,
17262306a36Sopenharmony_ci	};
17362306a36Sopenharmony_ci	int fd;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	fd = sys_perf_event_open(&attr, -1, 0, -1, 0);
17662306a36Sopenharmony_ci	/*
17762306a36Sopenharmony_ci	 * If the kernel attribute is big enough to contain aux_sample_size
17862306a36Sopenharmony_ci	 * then we assume that it is supported. We are relying on the kernel to
17962306a36Sopenharmony_ci	 * validate the attribute size before anything else that could be wrong.
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	if (fd < 0 && errno == E2BIG)
18262306a36Sopenharmony_ci		return false;
18362306a36Sopenharmony_ci	if (fd >= 0)
18462306a36Sopenharmony_ci		close(fd);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return true;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cibool perf_can_record_build_id(void)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	return perf_probe_api(perf_probe_build_id);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cibool perf_can_record_cgroup(void)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	return perf_probe_api(perf_probe_cgroup);
19762306a36Sopenharmony_ci}
198