projects/linux·blob master · refs

212 lines · 4.1 KiB

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