xref: /linux/tools/perf/util/perf_api_probe.c (revision 9e906a9dead17d81d6c2687f65e159231d0e3286)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 
3 #include "perf-sys.h"
4 #include "util/cloexec.h"
5 #include "util/evlist.h"
6 #include "util/evsel.h"
7 #include "util/parse-events.h"
8 #include "util/perf_api_probe.h"
9 #include <perf/cpumap.h>
10 #include <errno.h>
11 
12 typedef void (*setup_probe_fn_t)(struct evsel *evsel);
13 
perf_do_probe_api(setup_probe_fn_t fn,struct perf_cpu cpu,const char * str)14 static int perf_do_probe_api(setup_probe_fn_t fn, struct perf_cpu cpu, const char *str)
15 {
16 	struct evlist *evlist;
17 	struct evsel *evsel;
18 	unsigned long flags = perf_event_open_cloexec_flag();
19 	int err = -EAGAIN, fd;
20 	static pid_t pid = -1;
21 
22 	evlist = evlist__new();
23 	if (!evlist)
24 		return -ENOMEM;
25 
26 	if (parse_event(evlist, str))
27 		goto out_delete;
28 
29 	evsel = evlist__first(evlist);
30 
31 	while (1) {
32 		fd = sys_perf_event_open(&evsel->core.attr, pid, cpu.cpu, -1, flags);
33 		if (fd < 0) {
34 			if (pid == -1 && errno == EACCES) {
35 				pid = 0;
36 				continue;
37 			}
38 			goto out_delete;
39 		}
40 		break;
41 	}
42 	close(fd);
43 
44 	fn(evsel);
45 
46 	fd = sys_perf_event_open(&evsel->core.attr, pid, cpu.cpu, -1, flags);
47 	if (fd < 0) {
48 		if (errno == EINVAL)
49 			err = -EINVAL;
50 		goto out_delete;
51 	}
52 	close(fd);
53 	err = 0;
54 
55 out_delete:
56 	evlist__delete(evlist);
57 	return err;
58 }
59 
perf_probe_api(setup_probe_fn_t fn)60 static bool perf_probe_api(setup_probe_fn_t fn)
61 {
62 	struct perf_pmu *pmu;
63 	struct perf_cpu_map *cpus;
64 	struct perf_cpu cpu;
65 	int ret = 0;
66 
67 	cpus = perf_cpu_map__new_online_cpus();
68 	if (!cpus)
69 		return false;
70 	cpu = perf_cpu_map__cpu(cpus, 0);
71 	perf_cpu_map__put(cpus);
72 
73 	ret = perf_do_probe_api(fn, cpu, "software/cpu-clock/u");
74 	if (!ret)
75 		return true;
76 
77 	pmu = perf_pmus__scan_core(/*pmu=*/NULL);
78 	if (pmu) {
79 		const char *try[] = {"cycles", "instructions", NULL};
80 		char buf[256];
81 		int i = 0;
82 
83 		while (ret == -EAGAIN && try[i]) {
84 			snprintf(buf, sizeof(buf), "%s/%s/u", pmu->name, try[i++]);
85 			ret = perf_do_probe_api(fn, cpu, buf);
86 			if (!ret)
87 				return true;
88 		}
89 	}
90 	return false;
91 }
92 
perf_probe_sample_identifier(struct evsel * evsel)93 static void perf_probe_sample_identifier(struct evsel *evsel)
94 {
95 	evsel->core.attr.sample_type |= PERF_SAMPLE_IDENTIFIER;
96 }
97 
perf_probe_comm_exec(struct evsel * evsel)98 static void perf_probe_comm_exec(struct evsel *evsel)
99 {
100 	evsel->core.attr.comm_exec = 1;
101 }
102 
perf_probe_context_switch(struct evsel * evsel)103 static void perf_probe_context_switch(struct evsel *evsel)
104 {
105 	evsel->core.attr.context_switch = 1;
106 }
107 
perf_probe_text_poke(struct evsel * evsel)108 static void perf_probe_text_poke(struct evsel *evsel)
109 {
110 	evsel->core.attr.text_poke = 1;
111 }
112 
perf_probe_build_id(struct evsel * evsel)113 static void perf_probe_build_id(struct evsel *evsel)
114 {
115 	evsel->core.attr.build_id = 1;
116 }
117 
perf_probe_cgroup(struct evsel * evsel)118 static void perf_probe_cgroup(struct evsel *evsel)
119 {
120 	evsel->core.attr.cgroup = 1;
121 }
122 
perf_can_sample_identifier(void)123 bool perf_can_sample_identifier(void)
124 {
125 	return perf_probe_api(perf_probe_sample_identifier);
126 }
127 
perf_can_comm_exec(void)128 bool perf_can_comm_exec(void)
129 {
130 	return perf_probe_api(perf_probe_comm_exec);
131 }
132 
perf_can_record_switch_events(void)133 bool perf_can_record_switch_events(void)
134 {
135 	return perf_probe_api(perf_probe_context_switch);
136 }
137 
perf_can_record_text_poke_events(void)138 bool perf_can_record_text_poke_events(void)
139 {
140 	return perf_probe_api(perf_probe_text_poke);
141 }
142 
perf_can_record_cpu_wide(void)143 bool perf_can_record_cpu_wide(void)
144 {
145 	struct perf_event_attr attr = {
146 		.type = PERF_TYPE_SOFTWARE,
147 		.config = PERF_COUNT_SW_CPU_CLOCK,
148 		.exclude_kernel = 1,
149 	};
150 	struct perf_cpu_map *cpus;
151 	struct perf_cpu cpu;
152 	int fd;
153 
154 	cpus = perf_cpu_map__new_online_cpus();
155 	if (!cpus)
156 		return false;
157 
158 	cpu = perf_cpu_map__cpu(cpus, 0);
159 	perf_cpu_map__put(cpus);
160 
161 	fd = sys_perf_event_open(&attr, -1, cpu.cpu, -1, 0);
162 	if (fd < 0)
163 		return false;
164 	close(fd);
165 
166 	return true;
167 }
168 
169 /*
170  * Architectures are expected to know if AUX area sampling is supported by the
171  * hardware. Here we check for kernel support.
172  */
perf_can_aux_sample(void)173 bool perf_can_aux_sample(void)
174 {
175 	struct perf_event_attr attr = {
176 		.size = sizeof(struct perf_event_attr),
177 		.exclude_kernel = 1,
178 		/*
179 		 * Non-zero value causes the kernel to calculate the effective
180 		 * attribute size up to that byte.
181 		 */
182 		.aux_sample_size = 1,
183 	};
184 	int fd;
185 
186 	fd = sys_perf_event_open(&attr, -1, 0, -1, 0);
187 	/*
188 	 * If the kernel attribute is big enough to contain aux_sample_size
189 	 * then we assume that it is supported. We are relying on the kernel to
190 	 * validate the attribute size before anything else that could be wrong.
191 	 */
192 	if (fd < 0 && errno == E2BIG)
193 		return false;
194 	if (fd >= 0)
195 		close(fd);
196 
197 	return true;
198 }
199 
perf_can_record_build_id(void)200 bool perf_can_record_build_id(void)
201 {
202 	return perf_probe_api(perf_probe_build_id);
203 }
204 
perf_can_record_cgroup(void)205 bool perf_can_record_cgroup(void)
206 {
207 	return perf_probe_api(perf_probe_cgroup);
208 }
209