diff options
Diffstat (limited to 'tools/perf/util/pmu.c')
-rw-r--r-- | tools/perf/util/pmu.c | 138 |
1 files changed, 134 insertions, 4 deletions
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index c232d8dd410b..56fc10a5e288 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -1,19 +1,23 @@ | |||
1 | #include <linux/list.h> | 1 | #include <linux/list.h> |
2 | #include <sys/types.h> | 2 | #include <sys/types.h> |
3 | #include <sys/stat.h> | ||
4 | #include <unistd.h> | 3 | #include <unistd.h> |
5 | #include <stdio.h> | 4 | #include <stdio.h> |
6 | #include <dirent.h> | 5 | #include <dirent.h> |
7 | #include "fs.h" | 6 | #include "fs.h" |
7 | #include <locale.h> | ||
8 | #include "util.h" | 8 | #include "util.h" |
9 | #include "pmu.h" | 9 | #include "pmu.h" |
10 | #include "parse-events.h" | 10 | #include "parse-events.h" |
11 | #include "cpumap.h" | 11 | #include "cpumap.h" |
12 | 12 | ||
13 | #define UNIT_MAX_LEN 31 /* max length for event unit name */ | ||
14 | |||
13 | struct perf_pmu_alias { | 15 | struct perf_pmu_alias { |
14 | char *name; | 16 | char *name; |
15 | struct list_head terms; | 17 | struct list_head terms; |
16 | struct list_head list; | 18 | struct list_head list; |
19 | char unit[UNIT_MAX_LEN+1]; | ||
20 | double scale; | ||
17 | }; | 21 | }; |
18 | 22 | ||
19 | struct perf_pmu_format { | 23 | struct perf_pmu_format { |
@@ -94,7 +98,80 @@ static int pmu_format(const char *name, struct list_head *format) | |||
94 | return 0; | 98 | return 0; |
95 | } | 99 | } |
96 | 100 | ||
97 | static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) | 101 | static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) |
102 | { | ||
103 | struct stat st; | ||
104 | ssize_t sret; | ||
105 | char scale[128]; | ||
106 | int fd, ret = -1; | ||
107 | char path[PATH_MAX]; | ||
108 | char *lc; | ||
109 | |||
110 | snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); | ||
111 | |||
112 | fd = open(path, O_RDONLY); | ||
113 | if (fd == -1) | ||
114 | return -1; | ||
115 | |||
116 | if (fstat(fd, &st) < 0) | ||
117 | goto error; | ||
118 | |||
119 | sret = read(fd, scale, sizeof(scale)-1); | ||
120 | if (sret < 0) | ||
121 | goto error; | ||
122 | |||
123 | scale[sret] = '\0'; | ||
124 | /* | ||
125 | * save current locale | ||
126 | */ | ||
127 | lc = setlocale(LC_NUMERIC, NULL); | ||
128 | |||
129 | /* | ||
130 | * force to C locale to ensure kernel | ||
131 | * scale string is converted correctly. | ||
132 | * kernel uses default C locale. | ||
133 | */ | ||
134 | setlocale(LC_NUMERIC, "C"); | ||
135 | |||
136 | alias->scale = strtod(scale, NULL); | ||
137 | |||
138 | /* restore locale */ | ||
139 | setlocale(LC_NUMERIC, lc); | ||
140 | |||
141 | ret = 0; | ||
142 | error: | ||
143 | close(fd); | ||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name) | ||
148 | { | ||
149 | char path[PATH_MAX]; | ||
150 | ssize_t sret; | ||
151 | int fd; | ||
152 | |||
153 | snprintf(path, PATH_MAX, "%s/%s.unit", dir, name); | ||
154 | |||
155 | fd = open(path, O_RDONLY); | ||
156 | if (fd == -1) | ||
157 | return -1; | ||
158 | |||
159 | sret = read(fd, alias->unit, UNIT_MAX_LEN); | ||
160 | if (sret < 0) | ||
161 | goto error; | ||
162 | |||
163 | close(fd); | ||
164 | |||
165 | alias->unit[sret] = '\0'; | ||
166 | |||
167 | return 0; | ||
168 | error: | ||
169 | close(fd); | ||
170 | alias->unit[0] = '\0'; | ||
171 | return -1; | ||
172 | } | ||
173 | |||
174 | static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file) | ||
98 | { | 175 | { |
99 | struct perf_pmu_alias *alias; | 176 | struct perf_pmu_alias *alias; |
100 | char buf[256]; | 177 | char buf[256]; |
@@ -110,6 +187,9 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) | |||
110 | return -ENOMEM; | 187 | return -ENOMEM; |
111 | 188 | ||
112 | INIT_LIST_HEAD(&alias->terms); | 189 | INIT_LIST_HEAD(&alias->terms); |
190 | alias->scale = 1.0; | ||
191 | alias->unit[0] = '\0'; | ||
192 | |||
113 | ret = parse_events_terms(&alias->terms, buf); | 193 | ret = parse_events_terms(&alias->terms, buf); |
114 | if (ret) { | 194 | if (ret) { |
115 | free(alias); | 195 | free(alias); |
@@ -117,7 +197,14 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) | |||
117 | } | 197 | } |
118 | 198 | ||
119 | alias->name = strdup(name); | 199 | alias->name = strdup(name); |
200 | /* | ||
201 | * load unit name and scale if available | ||
202 | */ | ||
203 | perf_pmu__parse_unit(alias, dir, name); | ||
204 | perf_pmu__parse_scale(alias, dir, name); | ||
205 | |||
120 | list_add_tail(&alias->list, list); | 206 | list_add_tail(&alias->list, list); |
207 | |||
121 | return 0; | 208 | return 0; |
122 | } | 209 | } |
123 | 210 | ||
@@ -129,6 +216,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
129 | { | 216 | { |
130 | struct dirent *evt_ent; | 217 | struct dirent *evt_ent; |
131 | DIR *event_dir; | 218 | DIR *event_dir; |
219 | size_t len; | ||
132 | int ret = 0; | 220 | int ret = 0; |
133 | 221 | ||
134 | event_dir = opendir(dir); | 222 | event_dir = opendir(dir); |
@@ -143,13 +231,24 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
143 | if (!strcmp(name, ".") || !strcmp(name, "..")) | 231 | if (!strcmp(name, ".") || !strcmp(name, "..")) |
144 | continue; | 232 | continue; |
145 | 233 | ||
234 | /* | ||
235 | * skip .unit and .scale info files | ||
236 | * parsed in perf_pmu__new_alias() | ||
237 | */ | ||
238 | len = strlen(name); | ||
239 | if (len > 5 && !strcmp(name + len - 5, ".unit")) | ||
240 | continue; | ||
241 | if (len > 6 && !strcmp(name + len - 6, ".scale")) | ||
242 | continue; | ||
243 | |||
146 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | 244 | snprintf(path, PATH_MAX, "%s/%s", dir, name); |
147 | 245 | ||
148 | ret = -EINVAL; | 246 | ret = -EINVAL; |
149 | file = fopen(path, "r"); | 247 | file = fopen(path, "r"); |
150 | if (!file) | 248 | if (!file) |
151 | break; | 249 | break; |
152 | ret = perf_pmu__new_alias(head, name, file); | 250 | |
251 | ret = perf_pmu__new_alias(head, dir, name, file); | ||
153 | fclose(file); | 252 | fclose(file); |
154 | } | 253 | } |
155 | 254 | ||
@@ -508,16 +607,42 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, | |||
508 | return NULL; | 607 | return NULL; |
509 | } | 608 | } |
510 | 609 | ||
610 | |||
611 | static int check_unit_scale(struct perf_pmu_alias *alias, | ||
612 | char **unit, double *scale) | ||
613 | { | ||
614 | /* | ||
615 | * Only one term in event definition can | ||
616 | * define unit and scale, fail if there's | ||
617 | * more than one. | ||
618 | */ | ||
619 | if ((*unit && alias->unit) || | ||
620 | (*scale && alias->scale)) | ||
621 | return -EINVAL; | ||
622 | |||
623 | if (alias->unit) | ||
624 | *unit = alias->unit; | ||
625 | |||
626 | if (alias->scale) | ||
627 | *scale = alias->scale; | ||
628 | |||
629 | return 0; | ||
630 | } | ||
631 | |||
511 | /* | 632 | /* |
512 | * Find alias in the terms list and replace it with the terms | 633 | * Find alias in the terms list and replace it with the terms |
513 | * defined for the alias | 634 | * defined for the alias |
514 | */ | 635 | */ |
515 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) | 636 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
637 | char **unit, double *scale) | ||
516 | { | 638 | { |
517 | struct parse_events_term *term, *h; | 639 | struct parse_events_term *term, *h; |
518 | struct perf_pmu_alias *alias; | 640 | struct perf_pmu_alias *alias; |
519 | int ret; | 641 | int ret; |
520 | 642 | ||
643 | *unit = NULL; | ||
644 | *scale = 0; | ||
645 | |||
521 | list_for_each_entry_safe(term, h, head_terms, list) { | 646 | list_for_each_entry_safe(term, h, head_terms, list) { |
522 | alias = pmu_find_alias(pmu, term); | 647 | alias = pmu_find_alias(pmu, term); |
523 | if (!alias) | 648 | if (!alias) |
@@ -525,6 +650,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) | |||
525 | ret = pmu_alias_terms(alias, &term->list); | 650 | ret = pmu_alias_terms(alias, &term->list); |
526 | if (ret) | 651 | if (ret) |
527 | return ret; | 652 | return ret; |
653 | |||
654 | ret = check_unit_scale(alias, unit, scale); | ||
655 | if (ret) | ||
656 | return ret; | ||
657 | |||
528 | list_del(&term->list); | 658 | list_del(&term->list); |
529 | free(term); | 659 | free(term); |
530 | } | 660 | } |