GNU Linux-libre 5.19-rc6-gnu
[releases.git] / tools / perf / util / perf_api_probe.c
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
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_events(evlist, str, NULL))
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
60 static bool perf_probe_api(setup_probe_fn_t fn)
61 {
62         const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL};
63         struct perf_cpu_map *cpus;
64         struct perf_cpu cpu;
65         int ret, i = 0;
66
67         cpus = perf_cpu_map__new(NULL);
68         if (!cpus)
69                 return false;
70         cpu = perf_cpu_map__cpu(cpus, 0);
71         perf_cpu_map__put(cpus);
72
73         do {
74                 ret = perf_do_probe_api(fn, cpu, try[i++]);
75                 if (!ret)
76                         return true;
77         } while (ret == -EAGAIN && try[i]);
78
79         return false;
80 }
81
82 static void perf_probe_sample_identifier(struct evsel *evsel)
83 {
84         evsel->core.attr.sample_type |= PERF_SAMPLE_IDENTIFIER;
85 }
86
87 static void perf_probe_comm_exec(struct evsel *evsel)
88 {
89         evsel->core.attr.comm_exec = 1;
90 }
91
92 static void perf_probe_context_switch(struct evsel *evsel)
93 {
94         evsel->core.attr.context_switch = 1;
95 }
96
97 static void perf_probe_text_poke(struct evsel *evsel)
98 {
99         evsel->core.attr.text_poke = 1;
100 }
101
102 static void perf_probe_build_id(struct evsel *evsel)
103 {
104         evsel->core.attr.build_id = 1;
105 }
106
107 static void perf_probe_cgroup(struct evsel *evsel)
108 {
109         evsel->core.attr.cgroup = 1;
110 }
111
112 bool perf_can_sample_identifier(void)
113 {
114         return perf_probe_api(perf_probe_sample_identifier);
115 }
116
117 bool perf_can_comm_exec(void)
118 {
119         return perf_probe_api(perf_probe_comm_exec);
120 }
121
122 bool perf_can_record_switch_events(void)
123 {
124         return perf_probe_api(perf_probe_context_switch);
125 }
126
127 bool perf_can_record_text_poke_events(void)
128 {
129         return perf_probe_api(perf_probe_text_poke);
130 }
131
132 bool perf_can_record_cpu_wide(void)
133 {
134         struct perf_event_attr attr = {
135                 .type = PERF_TYPE_SOFTWARE,
136                 .config = PERF_COUNT_SW_CPU_CLOCK,
137                 .exclude_kernel = 1,
138         };
139         struct perf_cpu_map *cpus;
140         struct perf_cpu cpu;
141         int fd;
142
143         cpus = perf_cpu_map__new(NULL);
144         if (!cpus)
145                 return false;
146
147         cpu = perf_cpu_map__cpu(cpus, 0);
148         perf_cpu_map__put(cpus);
149
150         fd = sys_perf_event_open(&attr, -1, cpu.cpu, -1, 0);
151         if (fd < 0)
152                 return false;
153         close(fd);
154
155         return true;
156 }
157
158 /*
159  * Architectures are expected to know if AUX area sampling is supported by the
160  * hardware. Here we check for kernel support.
161  */
162 bool perf_can_aux_sample(void)
163 {
164         struct perf_event_attr attr = {
165                 .size = sizeof(struct perf_event_attr),
166                 .exclude_kernel = 1,
167                 /*
168                  * Non-zero value causes the kernel to calculate the effective
169                  * attribute size up to that byte.
170                  */
171                 .aux_sample_size = 1,
172         };
173         int fd;
174
175         fd = sys_perf_event_open(&attr, -1, 0, -1, 0);
176         /*
177          * If the kernel attribute is big enough to contain aux_sample_size
178          * then we assume that it is supported. We are relying on the kernel to
179          * validate the attribute size before anything else that could be wrong.
180          */
181         if (fd < 0 && errno == E2BIG)
182                 return false;
183         if (fd >= 0)
184                 close(fd);
185
186         return true;
187 }
188
189 bool perf_can_record_build_id(void)
190 {
191         return perf_probe_api(perf_probe_build_id);
192 }
193
194 bool perf_can_record_cgroup(void)
195 {
196         return perf_probe_api(perf_probe_cgroup);
197 }