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