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