xref: /openbmc/linux/tools/perf/util/pmu.c (revision 7e035230)
1 
2 #include <linux/list.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <stdio.h>
7 #include <dirent.h>
8 #include "sysfs.h"
9 #include "util.h"
10 #include "pmu.h"
11 #include "parse-events.h"
12 
13 int perf_pmu_parse(struct list_head *list, char *name);
14 extern FILE *perf_pmu_in;
15 
16 static LIST_HEAD(pmus);
17 
18 /*
19  * Parse & process all the sysfs attributes located under
20  * the directory specified in 'dir' parameter.
21  */
22 static int pmu_format_parse(char *dir, struct list_head *head)
23 {
24 	struct dirent *evt_ent;
25 	DIR *format_dir;
26 	int ret = 0;
27 
28 	format_dir = opendir(dir);
29 	if (!format_dir)
30 		return -EINVAL;
31 
32 	while (!ret && (evt_ent = readdir(format_dir))) {
33 		char path[PATH_MAX];
34 		char *name = evt_ent->d_name;
35 		FILE *file;
36 
37 		if (!strcmp(name, ".") || !strcmp(name, ".."))
38 			continue;
39 
40 		snprintf(path, PATH_MAX, "%s/%s", dir, name);
41 
42 		ret = -EINVAL;
43 		file = fopen(path, "r");
44 		if (!file)
45 			break;
46 
47 		perf_pmu_in = file;
48 		ret = perf_pmu_parse(head, name);
49 		fclose(file);
50 	}
51 
52 	closedir(format_dir);
53 	return ret;
54 }
55 
56 /*
57  * Reading/parsing the default pmu format definition, which should be
58  * located at:
59  * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
60  */
61 static int pmu_format(char *name, struct list_head *format)
62 {
63 	struct stat st;
64 	char path[PATH_MAX];
65 	const char *sysfs;
66 
67 	sysfs = sysfs_find_mountpoint();
68 	if (!sysfs)
69 		return -1;
70 
71 	snprintf(path, PATH_MAX,
72 		 "%s/bus/event_source/devices/%s/format", sysfs, name);
73 
74 	if (stat(path, &st) < 0)
75 		return 0;	/* no error if format does not exist */
76 
77 	if (pmu_format_parse(path, format))
78 		return -1;
79 
80 	return 0;
81 }
82 
83 static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
84 {
85 	struct perf_pmu__alias *alias;
86 	char buf[256];
87 	int ret;
88 
89 	ret = fread(buf, 1, sizeof(buf), file);
90 	if (ret == 0)
91 		return -EINVAL;
92 	buf[ret] = 0;
93 
94 	alias = malloc(sizeof(*alias));
95 	if (!alias)
96 		return -ENOMEM;
97 
98 	INIT_LIST_HEAD(&alias->terms);
99 	ret = parse_events_terms(&alias->terms, buf);
100 	if (ret) {
101 		free(alias);
102 		return ret;
103 	}
104 
105 	alias->name = strdup(name);
106 	list_add_tail(&alias->list, list);
107 	return 0;
108 }
109 
110 /*
111  * Process all the sysfs attributes located under the directory
112  * specified in 'dir' parameter.
113  */
114 static int pmu_aliases_parse(char *dir, struct list_head *head)
115 {
116 	struct dirent *evt_ent;
117 	DIR *event_dir;
118 	int ret = 0;
119 
120 	event_dir = opendir(dir);
121 	if (!event_dir)
122 		return -EINVAL;
123 
124 	while (!ret && (evt_ent = readdir(event_dir))) {
125 		char path[PATH_MAX];
126 		char *name = evt_ent->d_name;
127 		FILE *file;
128 
129 		if (!strcmp(name, ".") || !strcmp(name, ".."))
130 			continue;
131 
132 		snprintf(path, PATH_MAX, "%s/%s", dir, name);
133 
134 		ret = -EINVAL;
135 		file = fopen(path, "r");
136 		if (!file)
137 			break;
138 		ret = perf_pmu__new_alias(head, name, file);
139 		fclose(file);
140 	}
141 
142 	closedir(event_dir);
143 	return ret;
144 }
145 
146 /*
147  * Reading the pmu event aliases definition, which should be located at:
148  * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
149  */
150 static int pmu_aliases(char *name, struct list_head *head)
151 {
152 	struct stat st;
153 	char path[PATH_MAX];
154 	const char *sysfs;
155 
156 	sysfs = sysfs_find_mountpoint();
157 	if (!sysfs)
158 		return -1;
159 
160 	snprintf(path, PATH_MAX,
161 		 "%s/bus/event_source/devices/%s/events", sysfs, name);
162 
163 	if (stat(path, &st) < 0)
164 		return -1;
165 
166 	if (pmu_aliases_parse(path, head))
167 		return -1;
168 
169 	return 0;
170 }
171 
172 static int pmu_alias_terms(struct perf_pmu__alias *alias,
173 			   struct list_head *terms)
174 {
175 	struct parse_events__term *term, *clone;
176 	LIST_HEAD(list);
177 	int ret;
178 
179 	list_for_each_entry(term, &alias->terms, list) {
180 		ret = parse_events__term_clone(&clone, term);
181 		if (ret) {
182 			parse_events__free_terms(&list);
183 			return ret;
184 		}
185 		list_add_tail(&clone->list, &list);
186 	}
187 	list_splice(&list, terms);
188 	return 0;
189 }
190 
191 /*
192  * Reading/parsing the default pmu type value, which should be
193  * located at:
194  * /sys/bus/event_source/devices/<dev>/type as sysfs attribute.
195  */
196 static int pmu_type(char *name, __u32 *type)
197 {
198 	struct stat st;
199 	char path[PATH_MAX];
200 	const char *sysfs;
201 	FILE *file;
202 	int ret = 0;
203 
204 	sysfs = sysfs_find_mountpoint();
205 	if (!sysfs)
206 		return -1;
207 
208 	snprintf(path, PATH_MAX,
209 		 "%s/bus/event_source/devices/%s/type", sysfs, name);
210 
211 	if (stat(path, &st) < 0)
212 		return -1;
213 
214 	file = fopen(path, "r");
215 	if (!file)
216 		return -EINVAL;
217 
218 	if (1 != fscanf(file, "%u", type))
219 		ret = -1;
220 
221 	fclose(file);
222 	return ret;
223 }
224 
225 static struct perf_pmu *pmu_lookup(char *name)
226 {
227 	struct perf_pmu *pmu;
228 	LIST_HEAD(format);
229 	LIST_HEAD(aliases);
230 	__u32 type;
231 
232 	/*
233 	 * The pmu data we store & need consists of the pmu
234 	 * type value and format definitions. Load both right
235 	 * now.
236 	 */
237 	if (pmu_format(name, &format))
238 		return NULL;
239 
240 	if (pmu_type(name, &type))
241 		return NULL;
242 
243 	pmu = zalloc(sizeof(*pmu));
244 	if (!pmu)
245 		return NULL;
246 
247 	pmu_aliases(name, &aliases);
248 
249 	INIT_LIST_HEAD(&pmu->format);
250 	INIT_LIST_HEAD(&pmu->aliases);
251 	list_splice(&format, &pmu->format);
252 	list_splice(&aliases, &pmu->aliases);
253 	pmu->name = strdup(name);
254 	pmu->type = type;
255 	list_add_tail(&pmu->list, &pmus);
256 	return pmu;
257 }
258 
259 static struct perf_pmu *pmu_find(char *name)
260 {
261 	struct perf_pmu *pmu;
262 
263 	list_for_each_entry(pmu, &pmus, list)
264 		if (!strcmp(pmu->name, name))
265 			return pmu;
266 
267 	return NULL;
268 }
269 
270 struct perf_pmu *perf_pmu__find(char *name)
271 {
272 	struct perf_pmu *pmu;
273 
274 	/*
275 	 * Once PMU is loaded it stays in the list,
276 	 * so we keep us from multiple reading/parsing
277 	 * the pmu format definitions.
278 	 */
279 	pmu = pmu_find(name);
280 	if (pmu)
281 		return pmu;
282 
283 	return pmu_lookup(name);
284 }
285 
286 static struct perf_pmu__format*
287 pmu_find_format(struct list_head *formats, char *name)
288 {
289 	struct perf_pmu__format *format;
290 
291 	list_for_each_entry(format, formats, list)
292 		if (!strcmp(format->name, name))
293 			return format;
294 
295 	return NULL;
296 }
297 
298 /*
299  * Returns value based on the format definition (format parameter)
300  * and unformated value (value parameter).
301  *
302  * TODO maybe optimize a little ;)
303  */
304 static __u64 pmu_format_value(unsigned long *format, __u64 value)
305 {
306 	unsigned long fbit, vbit;
307 	__u64 v = 0;
308 
309 	for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) {
310 
311 		if (!test_bit(fbit, format))
312 			continue;
313 
314 		if (!(value & (1llu << vbit++)))
315 			continue;
316 
317 		v |= (1llu << fbit);
318 	}
319 
320 	return v;
321 }
322 
323 /*
324  * Setup one of config[12] attr members based on the
325  * user input data - temr parameter.
326  */
327 static int pmu_config_term(struct list_head *formats,
328 			   struct perf_event_attr *attr,
329 			   struct parse_events__term *term)
330 {
331 	struct perf_pmu__format *format;
332 	__u64 *vp;
333 
334 	/*
335 	 * Support only for hardcoded and numnerial terms.
336 	 * Hardcoded terms should be already in, so nothing
337 	 * to be done for them.
338 	 */
339 	if (parse_events__is_hardcoded_term(term))
340 		return 0;
341 
342 	if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM)
343 		return -EINVAL;
344 
345 	format = pmu_find_format(formats, term->config);
346 	if (!format)
347 		return -EINVAL;
348 
349 	switch (format->value) {
350 	case PERF_PMU_FORMAT_VALUE_CONFIG:
351 		vp = &attr->config;
352 		break;
353 	case PERF_PMU_FORMAT_VALUE_CONFIG1:
354 		vp = &attr->config1;
355 		break;
356 	case PERF_PMU_FORMAT_VALUE_CONFIG2:
357 		vp = &attr->config2;
358 		break;
359 	default:
360 		return -EINVAL;
361 	}
362 
363 	/*
364 	 * XXX If we ever decide to go with string values for
365 	 * non-hardcoded terms, here's the place to translate
366 	 * them into value.
367 	 */
368 	*vp |= pmu_format_value(format->bits, term->val.num);
369 	return 0;
370 }
371 
372 static int pmu_config(struct list_head *formats, struct perf_event_attr *attr,
373 		      struct list_head *head_terms)
374 {
375 	struct parse_events__term *term;
376 
377 	list_for_each_entry(term, head_terms, list)
378 		if (pmu_config_term(formats, attr, term))
379 			return -EINVAL;
380 
381 	return 0;
382 }
383 
384 /*
385  * Configures event's 'attr' parameter based on the:
386  * 1) users input - specified in terms parameter
387  * 2) pmu format definitions - specified by pmu parameter
388  */
389 int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
390 		     struct list_head *head_terms)
391 {
392 	attr->type = pmu->type;
393 	return pmu_config(&pmu->format, attr, head_terms);
394 }
395 
396 static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu,
397 					      struct parse_events__term *term)
398 {
399 	struct perf_pmu__alias *alias;
400 	char *name;
401 
402 	if (parse_events__is_hardcoded_term(term))
403 		return NULL;
404 
405 	if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) {
406 		if (term->val.num != 1)
407 			return NULL;
408 		if (pmu_find_format(&pmu->format, term->config))
409 			return NULL;
410 		name = term->config;
411 	} else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
412 		if (strcasecmp(term->config, "event"))
413 			return NULL;
414 		name = term->val.str;
415 	} else {
416 		return NULL;
417 	}
418 
419 	list_for_each_entry(alias, &pmu->aliases, list) {
420 		if (!strcasecmp(alias->name, name))
421 			return alias;
422 	}
423 	return NULL;
424 }
425 
426 /*
427  * Find alias in the terms list and replace it with the terms
428  * defined for the alias
429  */
430 int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
431 {
432 	struct parse_events__term *term, *h;
433 	struct perf_pmu__alias *alias;
434 	int ret;
435 
436 	list_for_each_entry_safe(term, h, head_terms, list) {
437 		alias = pmu_find_alias(pmu, term);
438 		if (!alias)
439 			continue;
440 		ret = pmu_alias_terms(alias, &term->list);
441 		if (ret)
442 			return ret;
443 		list_del(&term->list);
444 		free(term);
445 	}
446 	return 0;
447 }
448 
449 int perf_pmu__new_format(struct list_head *list, char *name,
450 			 int config, unsigned long *bits)
451 {
452 	struct perf_pmu__format *format;
453 
454 	format = zalloc(sizeof(*format));
455 	if (!format)
456 		return -ENOMEM;
457 
458 	format->name = strdup(name);
459 	format->value = config;
460 	memcpy(format->bits, bits, sizeof(format->bits));
461 
462 	list_add_tail(&format->list, list);
463 	return 0;
464 }
465 
466 void perf_pmu__set_format(unsigned long *bits, long from, long to)
467 {
468 	long b;
469 
470 	if (!to)
471 		to = from;
472 
473 	memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS));
474 	for (b = from; b <= to; b++)
475 		set_bit(b, bits);
476 }
477 
478 /* Simulated format definitions. */
479 static struct test_format {
480 	const char *name;
481 	const char *value;
482 } test_formats[] = {
483 	{ "krava01", "config:0-1,62-63\n", },
484 	{ "krava02", "config:10-17\n", },
485 	{ "krava03", "config:5\n", },
486 	{ "krava11", "config1:0,2,4,6,8,20-28\n", },
487 	{ "krava12", "config1:63\n", },
488 	{ "krava13", "config1:45-47\n", },
489 	{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
490 	{ "krava22", "config2:8,18,48,58\n", },
491 	{ "krava23", "config2:28-29,38\n", },
492 };
493 
494 #define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
495 
496 /* Simulated users input. */
497 static struct parse_events__term test_terms[] = {
498 	{
499 		.config    = (char *) "krava01",
500 		.val.num   = 15,
501 		.type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
502 		.type_term = PARSE_EVENTS__TERM_TYPE_USER,
503 	},
504 	{
505 		.config    = (char *) "krava02",
506 		.val.num   = 170,
507 		.type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
508 		.type_term = PARSE_EVENTS__TERM_TYPE_USER,
509 	},
510 	{
511 		.config    = (char *) "krava03",
512 		.val.num   = 1,
513 		.type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
514 		.type_term = PARSE_EVENTS__TERM_TYPE_USER,
515 	},
516 	{
517 		.config    = (char *) "krava11",
518 		.val.num   = 27,
519 		.type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
520 		.type_term = PARSE_EVENTS__TERM_TYPE_USER,
521 	},
522 	{
523 		.config    = (char *) "krava12",
524 		.val.num   = 1,
525 		.type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
526 		.type_term = PARSE_EVENTS__TERM_TYPE_USER,
527 	},
528 	{
529 		.config    = (char *) "krava13",
530 		.val.num   = 2,
531 		.type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
532 		.type_term = PARSE_EVENTS__TERM_TYPE_USER,
533 	},
534 	{
535 		.config    = (char *) "krava21",
536 		.val.num   = 119,
537 		.type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
538 		.type_term = PARSE_EVENTS__TERM_TYPE_USER,
539 	},
540 	{
541 		.config    = (char *) "krava22",
542 		.val.num   = 11,
543 		.type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
544 		.type_term = PARSE_EVENTS__TERM_TYPE_USER,
545 	},
546 	{
547 		.config    = (char *) "krava23",
548 		.val.num   = 2,
549 		.type_val  = PARSE_EVENTS__TERM_TYPE_NUM,
550 		.type_term = PARSE_EVENTS__TERM_TYPE_USER,
551 	},
552 };
553 #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
554 
555 /*
556  * Prepare format directory data, exported by kernel
557  * at /sys/bus/event_source/devices/<dev>/format.
558  */
559 static char *test_format_dir_get(void)
560 {
561 	static char dir[PATH_MAX];
562 	unsigned int i;
563 
564 	snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX");
565 	if (!mkdtemp(dir))
566 		return NULL;
567 
568 	for (i = 0; i < TEST_FORMATS_CNT; i++) {
569 		static char name[PATH_MAX];
570 		struct test_format *format = &test_formats[i];
571 		FILE *file;
572 
573 		snprintf(name, PATH_MAX, "%s/%s", dir, format->name);
574 
575 		file = fopen(name, "w");
576 		if (!file)
577 			return NULL;
578 
579 		if (1 != fwrite(format->value, strlen(format->value), 1, file))
580 			break;
581 
582 		fclose(file);
583 	}
584 
585 	return dir;
586 }
587 
588 /* Cleanup format directory. */
589 static int test_format_dir_put(char *dir)
590 {
591 	char buf[PATH_MAX];
592 	snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir);
593 	if (system(buf))
594 		return -1;
595 
596 	snprintf(buf, PATH_MAX, "rmdir %s\n", dir);
597 	return system(buf);
598 }
599 
600 static struct list_head *test_terms_list(void)
601 {
602 	static LIST_HEAD(terms);
603 	unsigned int i;
604 
605 	for (i = 0; i < TERMS_CNT; i++)
606 		list_add_tail(&test_terms[i].list, &terms);
607 
608 	return &terms;
609 }
610 
611 #undef TERMS_CNT
612 
613 int perf_pmu__test(void)
614 {
615 	char *format = test_format_dir_get();
616 	LIST_HEAD(formats);
617 	struct list_head *terms = test_terms_list();
618 	int ret;
619 
620 	if (!format)
621 		return -EINVAL;
622 
623 	do {
624 		struct perf_event_attr attr;
625 
626 		memset(&attr, 0, sizeof(attr));
627 
628 		ret = pmu_format_parse(format, &formats);
629 		if (ret)
630 			break;
631 
632 		ret = pmu_config(&formats, &attr, terms);
633 		if (ret)
634 			break;
635 
636 		ret = -EINVAL;
637 
638 		if (attr.config  != 0xc00000000002a823)
639 			break;
640 		if (attr.config1 != 0x8000400000000145)
641 			break;
642 		if (attr.config2 != 0x0400000020041d07)
643 			break;
644 
645 		ret = 0;
646 	} while (0);
647 
648 	test_format_dir_put(format);
649 	return ret;
650 }
651