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