1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Support for libpfm4 event encoding.
4 *
5 * Copyright 2020 Google LLC.
6 */
7 #include "util/cpumap.h"
8 #include "util/debug.h"
9 #include "util/event.h"
10 #include "util/evlist.h"
11 #include "util/evsel.h"
12 #include "util/parse-events.h"
13 #include "util/pmu.h"
14 #include "util/pfm.h"
15
16 #include <string.h>
17 #include <linux/kernel.h>
18 #include <perfmon/pfmlib_perf_event.h>
19
libpfm_initialize(void)20 static void libpfm_initialize(void)
21 {
22 int ret;
23
24 ret = pfm_initialize();
25 if (ret != PFM_SUCCESS) {
26 ui__warning("libpfm failed to initialize: %s\n",
27 pfm_strerror(ret));
28 }
29 }
30
parse_libpfm_events_option(const struct option * opt,const char * str,int unset __maybe_unused)31 int parse_libpfm_events_option(const struct option *opt, const char *str,
32 int unset __maybe_unused)
33 {
34 struct evlist *evlist = *(struct evlist **)opt->value;
35 struct perf_event_attr attr;
36 struct perf_pmu *pmu;
37 struct evsel *evsel, *grp_leader = NULL;
38 char *p, *q, *p_orig;
39 const char *sep;
40 int grp_evt = -1;
41 int ret;
42
43 libpfm_initialize();
44
45 p_orig = p = strdup(str);
46 if (!p)
47 return -1;
48 /*
49 * force loading of the PMU list
50 */
51 perf_pmu__scan(NULL);
52
53 for (q = p; strsep(&p, ",{}"); q = p) {
54 sep = p ? str + (p - p_orig - 1) : "";
55 if (*sep == '{') {
56 if (grp_evt > -1) {
57 ui__error(
58 "nested event groups not supported\n");
59 goto error;
60 }
61 grp_evt++;
62 }
63
64 /* no event */
65 if (*q == '\0') {
66 if (*sep == '}') {
67 if (grp_evt < 0) {
68 ui__error("cannot close a non-existing event group\n");
69 goto error;
70 }
71 grp_evt--;
72 }
73 continue;
74 }
75
76 memset(&attr, 0, sizeof(attr));
77 event_attr_init(&attr);
78
79 ret = pfm_get_perf_event_encoding(q, PFM_PLM0|PFM_PLM3,
80 &attr, NULL, NULL);
81
82 if (ret != PFM_SUCCESS) {
83 ui__error("failed to parse event %s : %s\n", str,
84 pfm_strerror(ret));
85 goto error;
86 }
87
88 pmu = perf_pmu__find_by_type((unsigned int)attr.type);
89 evsel = parse_events__add_event(evlist->core.nr_entries,
90 &attr, q, pmu);
91 if (evsel == NULL)
92 goto error;
93
94 evsel->is_libpfm_event = true;
95
96 evlist__add(evlist, evsel);
97
98 if (grp_evt == 0)
99 grp_leader = evsel;
100
101 if (grp_evt > -1) {
102 evsel__set_leader(evsel, grp_leader);
103 grp_leader->core.nr_members++;
104 grp_evt++;
105 }
106
107 if (*sep == '}') {
108 if (grp_evt < 0) {
109 ui__error(
110 "cannot close a non-existing event group\n");
111 goto error;
112 }
113 evlist->core.nr_groups++;
114 grp_leader = NULL;
115 grp_evt = -1;
116 }
117 }
118 free(p_orig);
119 return 0;
120 error:
121 free(p_orig);
122 return -1;
123 }
124
125 static const char *srcs[PFM_ATTR_CTRL_MAX] = {
126 [PFM_ATTR_CTRL_UNKNOWN] = "???",
127 [PFM_ATTR_CTRL_PMU] = "PMU",
128 [PFM_ATTR_CTRL_PERF_EVENT] = "perf_event",
129 };
130
131 static void
print_attr_flags(pfm_event_attr_info_t * info)132 print_attr_flags(pfm_event_attr_info_t *info)
133 {
134 int n = 0;
135
136 if (info->is_dfl) {
137 printf("[default] ");
138 n++;
139 }
140
141 if (info->is_precise) {
142 printf("[precise] ");
143 n++;
144 }
145
146 if (!n)
147 printf("- ");
148 }
149
150 static void
print_libpfm_events_detailed(pfm_event_info_t * info,bool long_desc)151 print_libpfm_events_detailed(pfm_event_info_t *info, bool long_desc)
152 {
153 pfm_event_attr_info_t ainfo;
154 const char *src;
155 int j, ret;
156
157 ainfo.size = sizeof(ainfo);
158
159 printf(" %s\n", info->name);
160 printf(" [%s]\n", info->desc);
161 if (long_desc) {
162 if (info->equiv)
163 printf(" Equiv: %s\n", info->equiv);
164
165 printf(" Code : 0x%"PRIx64"\n", info->code);
166 }
167 pfm_for_each_event_attr(j, info) {
168 ret = pfm_get_event_attr_info(info->idx, j,
169 PFM_OS_PERF_EVENT_EXT, &ainfo);
170 if (ret != PFM_SUCCESS)
171 continue;
172
173 if (ainfo.type == PFM_ATTR_UMASK) {
174 printf(" %s:%s\n", info->name, ainfo.name);
175 printf(" [%s]\n", ainfo.desc);
176 }
177
178 if (!long_desc)
179 continue;
180
181 if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX)
182 ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN;
183
184 src = srcs[ainfo.ctrl];
185 switch (ainfo.type) {
186 case PFM_ATTR_UMASK:
187 printf(" Umask : 0x%02"PRIx64" : %s: ",
188 ainfo.code, src);
189 print_attr_flags(&ainfo);
190 putchar('\n');
191 break;
192 case PFM_ATTR_MOD_BOOL:
193 printf(" Modif : %s: [%s] : %s (boolean)\n", src,
194 ainfo.name, ainfo.desc);
195 break;
196 case PFM_ATTR_MOD_INTEGER:
197 printf(" Modif : %s: [%s] : %s (integer)\n", src,
198 ainfo.name, ainfo.desc);
199 break;
200 case PFM_ATTR_NONE:
201 case PFM_ATTR_RAW_UMASK:
202 case PFM_ATTR_MAX:
203 default:
204 printf(" Attr : %s: [%s] : %s\n", src,
205 ainfo.name, ainfo.desc);
206 }
207 }
208 }
209
210 /*
211 * list all pmu::event:umask, pmu::event
212 * printed events may not be all valid combinations of umask for an event
213 */
214 static void
print_libpfm_events_raw(pfm_pmu_info_t * pinfo,pfm_event_info_t * info)215 print_libpfm_events_raw(pfm_pmu_info_t *pinfo, pfm_event_info_t *info)
216 {
217 pfm_event_attr_info_t ainfo;
218 int j, ret;
219 bool has_umask = false;
220
221 ainfo.size = sizeof(ainfo);
222
223 pfm_for_each_event_attr(j, info) {
224 ret = pfm_get_event_attr_info(info->idx, j,
225 PFM_OS_PERF_EVENT_EXT, &ainfo);
226 if (ret != PFM_SUCCESS)
227 continue;
228
229 if (ainfo.type != PFM_ATTR_UMASK)
230 continue;
231
232 printf("%s::%s:%s\n", pinfo->name, info->name, ainfo.name);
233 has_umask = true;
234 }
235 if (!has_umask)
236 printf("%s::%s\n", pinfo->name, info->name);
237 }
238
print_libpfm_events(bool name_only,bool long_desc)239 void print_libpfm_events(bool name_only, bool long_desc)
240 {
241 pfm_event_info_t info;
242 pfm_pmu_info_t pinfo;
243 int i, p, ret;
244
245 libpfm_initialize();
246
247 /* initialize to zero to indicate ABI version */
248 info.size = sizeof(info);
249 pinfo.size = sizeof(pinfo);
250
251 if (!name_only)
252 puts("\nList of pre-defined events (to be used in --pfm-events):\n");
253
254 pfm_for_all_pmus(p) {
255 bool printed_pmu = false;
256
257 ret = pfm_get_pmu_info(p, &pinfo);
258 if (ret != PFM_SUCCESS)
259 continue;
260
261 /* only print events that are supported by host HW */
262 if (!pinfo.is_present)
263 continue;
264
265 /* handled by perf directly */
266 if (pinfo.pmu == PFM_PMU_PERF_EVENT)
267 continue;
268
269 for (i = pinfo.first_event; i != -1;
270 i = pfm_get_event_next(i)) {
271
272 ret = pfm_get_event_info(i, PFM_OS_PERF_EVENT_EXT,
273 &info);
274 if (ret != PFM_SUCCESS)
275 continue;
276
277 if (!name_only && !printed_pmu) {
278 printf("%s:\n", pinfo.name);
279 printed_pmu = true;
280 }
281
282 if (!name_only)
283 print_libpfm_events_detailed(&info, long_desc);
284 else
285 print_libpfm_events_raw(&pinfo, &info);
286 }
287 if (!name_only && printed_pmu)
288 putchar('\n');
289 }
290 }
291