xref: /openbmc/linux/tools/perf/util/pmus.c (revision 15c57a80)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/list.h>
3 #include <linux/zalloc.h>
4 #include <subcmd/pager.h>
5 #include <sys/types.h>
6 #include <dirent.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include "debug.h"
10 #include "evsel.h"
11 #include "pmus.h"
12 #include "pmu.h"
13 #include "print-events.h"
14 
15 static LIST_HEAD(core_pmus);
16 static LIST_HEAD(other_pmus);
17 
18 void perf_pmus__destroy(void)
19 {
20 	struct perf_pmu *pmu, *tmp;
21 
22 	list_for_each_entry_safe(pmu, tmp, &core_pmus, list) {
23 		list_del(&pmu->list);
24 
25 		perf_pmu__delete(pmu);
26 	}
27 	list_for_each_entry_safe(pmu, tmp, &other_pmus, list) {
28 		list_del(&pmu->list);
29 
30 		perf_pmu__delete(pmu);
31 	}
32 }
33 
34 static struct perf_pmu *pmu_find(const char *name)
35 {
36 	struct perf_pmu *pmu;
37 
38 	list_for_each_entry(pmu, &core_pmus, list) {
39 		if (!strcmp(pmu->name, name) ||
40 		    (pmu->alias_name && !strcmp(pmu->alias_name, name)))
41 			return pmu;
42 	}
43 	list_for_each_entry(pmu, &other_pmus, list) {
44 		if (!strcmp(pmu->name, name) ||
45 		    (pmu->alias_name && !strcmp(pmu->alias_name, name)))
46 			return pmu;
47 	}
48 
49 	return NULL;
50 }
51 
52 struct perf_pmu *perf_pmus__find(const char *name)
53 {
54 	struct perf_pmu *pmu;
55 	int dirfd;
56 
57 	/*
58 	 * Once PMU is loaded it stays in the list,
59 	 * so we keep us from multiple reading/parsing
60 	 * the pmu format definitions.
61 	 */
62 	pmu = pmu_find(name);
63 	if (pmu)
64 		return pmu;
65 
66 	dirfd = perf_pmu__event_source_devices_fd();
67 	pmu = perf_pmu__lookup(is_pmu_core(name) ? &core_pmus : &other_pmus, dirfd, name);
68 	close(dirfd);
69 
70 	return pmu;
71 }
72 
73 static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
74 {
75 	struct perf_pmu *pmu;
76 
77 	/*
78 	 * Once PMU is loaded it stays in the list,
79 	 * so we keep us from multiple reading/parsing
80 	 * the pmu format definitions.
81 	 */
82 	pmu = pmu_find(name);
83 	if (pmu)
84 		return pmu;
85 
86 	return perf_pmu__lookup(is_pmu_core(name) ? &core_pmus : &other_pmus, dirfd, name);
87 }
88 
89 /* Add all pmus in sysfs to pmu list: */
90 static void pmu_read_sysfs(void)
91 {
92 	int fd;
93 	DIR *dir;
94 	struct dirent *dent;
95 
96 	fd = perf_pmu__event_source_devices_fd();
97 	if (fd < 0)
98 		return;
99 
100 	dir = fdopendir(fd);
101 	if (!dir)
102 		return;
103 
104 	while ((dent = readdir(dir))) {
105 		if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
106 			continue;
107 		/* add to static LIST_HEAD(core_pmus) or LIST_HEAD(other_pmus): */
108 		perf_pmu__find2(fd, dent->d_name);
109 	}
110 
111 	closedir(dir);
112 }
113 
114 struct perf_pmu *perf_pmus__find_by_type(unsigned int type)
115 {
116 	struct perf_pmu *pmu;
117 
118 	list_for_each_entry(pmu, &core_pmus, list) {
119 		if (pmu->type == type)
120 			return pmu;
121 	}
122 	list_for_each_entry(pmu, &other_pmus, list) {
123 		if (pmu->type == type)
124 			return pmu;
125 	}
126 	return NULL;
127 }
128 
129 /*
130  * pmu iterator: If pmu is NULL, we start at the begin, otherwise return the
131  * next pmu. Returns NULL on end.
132  */
133 struct perf_pmu *perf_pmus__scan(struct perf_pmu *pmu)
134 {
135 	bool use_core_pmus = !pmu || pmu->is_core;
136 
137 	if (!pmu) {
138 		pmu_read_sysfs();
139 		pmu = list_prepare_entry(pmu, &core_pmus, list);
140 	}
141 	if (use_core_pmus) {
142 		list_for_each_entry_continue(pmu, &core_pmus, list)
143 			return pmu;
144 
145 		pmu = NULL;
146 		pmu = list_prepare_entry(pmu, &other_pmus, list);
147 	}
148 	list_for_each_entry_continue(pmu, &other_pmus, list)
149 		return pmu;
150 	return NULL;
151 }
152 
153 const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str)
154 {
155 	struct perf_pmu *pmu = NULL;
156 
157 	while ((pmu = perf_pmus__scan(pmu)) != NULL) {
158 		if (!strcmp(pmu->name, str))
159 			return pmu;
160 		/* Ignore "uncore_" prefix. */
161 		if (!strncmp(pmu->name, "uncore_", 7)) {
162 			if (!strcmp(pmu->name + 7, str))
163 				return pmu;
164 		}
165 		/* Ignore "cpu_" prefix on Intel hybrid PMUs. */
166 		if (!strncmp(pmu->name, "cpu_", 4)) {
167 			if (!strcmp(pmu->name + 4, str))
168 				return pmu;
169 		}
170 	}
171 	return NULL;
172 }
173 
174 int perf_pmus__num_mem_pmus(void)
175 {
176 	struct perf_pmu *pmu = NULL;
177 	int count = 0;
178 
179 	while ((pmu = perf_pmus__scan(pmu)) != NULL) {
180 		if (perf_pmu__is_mem_pmu(pmu))
181 			count++;
182 	}
183 	return count;
184 }
185 
186 /** Struct for ordering events as output in perf list. */
187 struct sevent {
188 	/** PMU for event. */
189 	const struct perf_pmu *pmu;
190 	/**
191 	 * Optional event for name, desc, etc. If not present then this is a
192 	 * selectable PMU and the event name is shown as "//".
193 	 */
194 	const struct perf_pmu_alias *event;
195 	/** Is the PMU for the CPU? */
196 	bool is_cpu;
197 };
198 
199 static int cmp_sevent(const void *a, const void *b)
200 {
201 	const struct sevent *as = a;
202 	const struct sevent *bs = b;
203 	const char *a_pmu_name = NULL, *b_pmu_name = NULL;
204 	const char *a_name = "//", *a_desc = NULL, *a_topic = "";
205 	const char *b_name = "//", *b_desc = NULL, *b_topic = "";
206 	int ret;
207 
208 	if (as->event) {
209 		a_name = as->event->name;
210 		a_desc = as->event->desc;
211 		a_topic = as->event->topic ?: "";
212 		a_pmu_name = as->event->pmu_name;
213 	}
214 	if (bs->event) {
215 		b_name = bs->event->name;
216 		b_desc = bs->event->desc;
217 		b_topic = bs->event->topic ?: "";
218 		b_pmu_name = bs->event->pmu_name;
219 	}
220 	/* Put extra events last. */
221 	if (!!a_desc != !!b_desc)
222 		return !!a_desc - !!b_desc;
223 
224 	/* Order by topics. */
225 	ret = strcmp(a_topic, b_topic);
226 	if (ret)
227 		return ret;
228 
229 	/* Order CPU core events to be first */
230 	if (as->is_cpu != bs->is_cpu)
231 		return as->is_cpu ? -1 : 1;
232 
233 	/* Order by PMU name. */
234 	if (as->pmu != bs->pmu) {
235 		a_pmu_name = a_pmu_name ?: (as->pmu->name ?: "");
236 		b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: "");
237 		ret = strcmp(a_pmu_name, b_pmu_name);
238 		if (ret)
239 			return ret;
240 	}
241 
242 	/* Order by event name. */
243 	return strcmp(a_name, b_name);
244 }
245 
246 static bool pmu_alias_is_duplicate(struct sevent *alias_a,
247 				   struct sevent *alias_b)
248 {
249 	const char *a_pmu_name = NULL, *b_pmu_name = NULL;
250 	const char *a_name = "//", *b_name = "//";
251 
252 
253 	if (alias_a->event) {
254 		a_name = alias_a->event->name;
255 		a_pmu_name = alias_a->event->pmu_name;
256 	}
257 	if (alias_b->event) {
258 		b_name = alias_b->event->name;
259 		b_pmu_name = alias_b->event->pmu_name;
260 	}
261 
262 	/* Different names -> never duplicates */
263 	if (strcmp(a_name, b_name))
264 		return false;
265 
266 	/* Don't remove duplicates for different PMUs */
267 	a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: "");
268 	b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: "");
269 	return strcmp(a_pmu_name, b_pmu_name) == 0;
270 }
271 
272 static int sub_non_neg(int a, int b)
273 {
274 	if (b > a)
275 		return 0;
276 	return a - b;
277 }
278 
279 static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
280 			  const struct perf_pmu_alias *alias)
281 {
282 	struct parse_events_term *term;
283 	int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name);
284 
285 	list_for_each_entry(term, &alias->terms, list) {
286 		if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
287 			used += snprintf(buf + used, sub_non_neg(len, used),
288 					",%s=%s", term->config,
289 					term->val.str);
290 	}
291 
292 	if (sub_non_neg(len, used) > 0) {
293 		buf[used] = '/';
294 		used++;
295 	}
296 	if (sub_non_neg(len, used) > 0) {
297 		buf[used] = '\0';
298 		used++;
299 	} else
300 		buf[len - 1] = '\0';
301 
302 	return buf;
303 }
304 
305 void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
306 {
307 	struct perf_pmu *pmu;
308 	struct perf_pmu_alias *event;
309 	char buf[1024];
310 	int printed = 0;
311 	int len, j;
312 	struct sevent *aliases;
313 
314 	pmu = NULL;
315 	len = 0;
316 	while ((pmu = perf_pmus__scan(pmu)) != NULL) {
317 		list_for_each_entry(event, &pmu->aliases, list)
318 			len++;
319 		if (pmu->selectable)
320 			len++;
321 	}
322 	aliases = zalloc(sizeof(struct sevent) * len);
323 	if (!aliases) {
324 		pr_err("FATAL: not enough memory to print PMU events\n");
325 		return;
326 	}
327 	pmu = NULL;
328 	j = 0;
329 	while ((pmu = perf_pmus__scan(pmu)) != NULL) {
330 		bool is_cpu = pmu->is_core;
331 
332 		list_for_each_entry(event, &pmu->aliases, list) {
333 			aliases[j].event = event;
334 			aliases[j].pmu = pmu;
335 			aliases[j].is_cpu = is_cpu;
336 			j++;
337 		}
338 		if (pmu->selectable) {
339 			aliases[j].event = NULL;
340 			aliases[j].pmu = pmu;
341 			aliases[j].is_cpu = is_cpu;
342 			j++;
343 		}
344 	}
345 	len = j;
346 	qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
347 	for (j = 0; j < len; j++) {
348 		const char *name, *alias = NULL, *scale_unit = NULL,
349 			*desc = NULL, *long_desc = NULL,
350 			*encoding_desc = NULL, *topic = NULL,
351 			*pmu_name = NULL;
352 		bool deprecated = false;
353 		size_t buf_used;
354 
355 		/* Skip duplicates */
356 		if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1]))
357 			continue;
358 
359 		if (!aliases[j].event) {
360 			/* A selectable event. */
361 			pmu_name = aliases[j].pmu->name;
362 			buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1;
363 			name = buf;
364 		} else {
365 			if (aliases[j].event->desc) {
366 				name = aliases[j].event->name;
367 				buf_used = 0;
368 			} else {
369 				name = format_alias(buf, sizeof(buf), aliases[j].pmu,
370 						    aliases[j].event);
371 				if (aliases[j].is_cpu) {
372 					alias = name;
373 					name = aliases[j].event->name;
374 				}
375 				buf_used = strlen(buf) + 1;
376 			}
377 			pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: "");
378 			if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) {
379 				scale_unit = buf + buf_used;
380 				buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
381 						"%G%s", aliases[j].event->scale,
382 						aliases[j].event->unit) + 1;
383 			}
384 			desc = aliases[j].event->desc;
385 			long_desc = aliases[j].event->long_desc;
386 			topic = aliases[j].event->topic;
387 			encoding_desc = buf + buf_used;
388 			buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
389 					"%s/%s/", pmu_name, aliases[j].event->str) + 1;
390 			deprecated = aliases[j].event->deprecated;
391 		}
392 		print_cb->print_event(print_state,
393 				pmu_name,
394 				topic,
395 				name,
396 				alias,
397 				scale_unit,
398 				deprecated,
399 				"Kernel PMU event",
400 				desc,
401 				long_desc,
402 				encoding_desc);
403 	}
404 	if (printed && pager_in_use())
405 		printf("\n");
406 
407 	zfree(&aliases);
408 }
409 
410 bool perf_pmus__have_event(const char *pname, const char *name)
411 {
412 	struct perf_pmu *pmu = perf_pmus__find(pname);
413 
414 	return pmu && perf_pmu__have_event(pmu, name);
415 }
416 
417 bool perf_pmus__has_hybrid(void)
418 {
419 	static bool hybrid_scanned, has_hybrid;
420 
421 	if (!hybrid_scanned) {
422 		struct perf_pmu *pmu = NULL;
423 
424 		while ((pmu = perf_pmus__scan(pmu)) != NULL) {
425 			if (pmu->is_core && is_pmu_hybrid(pmu->name)) {
426 				has_hybrid = true;
427 				break;
428 			}
429 		}
430 		hybrid_scanned = true;
431 	}
432 	return has_hybrid;
433 }
434 
435 struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)
436 {
437 	struct perf_pmu *pmu = evsel->pmu;
438 
439 	if (!pmu) {
440 		pmu = perf_pmus__find_by_type(evsel->core.attr.type);
441 		((struct evsel *)evsel)->pmu = pmu;
442 	}
443 	return pmu;
444 }
445