diff options
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/evsel.c | 2 | ||||
-rw-r--r-- | tools/perf/util/evsel.h | 3 | ||||
-rw-r--r-- | tools/perf/util/parse-events.c | 28 | ||||
-rw-r--r-- | tools/perf/util/pmu.c | 138 | ||||
-rw-r--r-- | tools/perf/util/pmu.h | 3 |
5 files changed, 160 insertions, 14 deletions
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 46dd4c2a41ce..dad64926170f 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -162,6 +162,8 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
162 | evsel->idx = idx; | 162 | evsel->idx = idx; |
163 | evsel->attr = *attr; | 163 | evsel->attr = *attr; |
164 | evsel->leader = evsel; | 164 | evsel->leader = evsel; |
165 | evsel->unit = ""; | ||
166 | evsel->scale = 1.0; | ||
165 | INIT_LIST_HEAD(&evsel->node); | 167 | INIT_LIST_HEAD(&evsel->node); |
166 | hists__init(&evsel->hists); | 168 | hists__init(&evsel->hists); |
167 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); | 169 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 1ea7c92e6e33..8120eeb86ac1 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -68,6 +68,8 @@ struct perf_evsel { | |||
68 | u32 ids; | 68 | u32 ids; |
69 | struct hists hists; | 69 | struct hists hists; |
70 | char *name; | 70 | char *name; |
71 | double scale; | ||
72 | const char *unit; | ||
71 | struct event_format *tp_format; | 73 | struct event_format *tp_format; |
72 | union { | 74 | union { |
73 | void *priv; | 75 | void *priv; |
@@ -138,6 +140,7 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX]; | |||
138 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, | 140 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, |
139 | char *bf, size_t size); | 141 | char *bf, size_t size); |
140 | const char *perf_evsel__name(struct perf_evsel *evsel); | 142 | const char *perf_evsel__name(struct perf_evsel *evsel); |
143 | |||
141 | const char *perf_evsel__group_name(struct perf_evsel *evsel); | 144 | const char *perf_evsel__group_name(struct perf_evsel *evsel); |
142 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); | 145 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); |
143 | 146 | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6de6f89c2a61..969cb8f0d88d 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -269,9 +269,10 @@ const char *event_type(int type) | |||
269 | 269 | ||
270 | 270 | ||
271 | 271 | ||
272 | static int __add_event(struct list_head *list, int *idx, | 272 | static struct perf_evsel * |
273 | struct perf_event_attr *attr, | 273 | __add_event(struct list_head *list, int *idx, |
274 | char *name, struct cpu_map *cpus) | 274 | struct perf_event_attr *attr, |
275 | char *name, struct cpu_map *cpus) | ||
275 | { | 276 | { |
276 | struct perf_evsel *evsel; | 277 | struct perf_evsel *evsel; |
277 | 278 | ||
@@ -279,19 +280,19 @@ static int __add_event(struct list_head *list, int *idx, | |||
279 | 280 | ||
280 | evsel = perf_evsel__new_idx(attr, (*idx)++); | 281 | evsel = perf_evsel__new_idx(attr, (*idx)++); |
281 | if (!evsel) | 282 | if (!evsel) |
282 | return -ENOMEM; | 283 | return NULL; |
283 | 284 | ||
284 | evsel->cpus = cpus; | 285 | evsel->cpus = cpus; |
285 | if (name) | 286 | if (name) |
286 | evsel->name = strdup(name); | 287 | evsel->name = strdup(name); |
287 | list_add_tail(&evsel->node, list); | 288 | list_add_tail(&evsel->node, list); |
288 | return 0; | 289 | return evsel; |
289 | } | 290 | } |
290 | 291 | ||
291 | static int add_event(struct list_head *list, int *idx, | 292 | static int add_event(struct list_head *list, int *idx, |
292 | struct perf_event_attr *attr, char *name) | 293 | struct perf_event_attr *attr, char *name) |
293 | { | 294 | { |
294 | return __add_event(list, idx, attr, name, NULL); | 295 | return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM; |
295 | } | 296 | } |
296 | 297 | ||
297 | static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) | 298 | static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) |
@@ -633,6 +634,9 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
633 | { | 634 | { |
634 | struct perf_event_attr attr; | 635 | struct perf_event_attr attr; |
635 | struct perf_pmu *pmu; | 636 | struct perf_pmu *pmu; |
637 | struct perf_evsel *evsel; | ||
638 | char *unit; | ||
639 | double scale; | ||
636 | 640 | ||
637 | pmu = perf_pmu__find(name); | 641 | pmu = perf_pmu__find(name); |
638 | if (!pmu) | 642 | if (!pmu) |
@@ -640,7 +644,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
640 | 644 | ||
641 | memset(&attr, 0, sizeof(attr)); | 645 | memset(&attr, 0, sizeof(attr)); |
642 | 646 | ||
643 | if (perf_pmu__check_alias(pmu, head_config)) | 647 | if (perf_pmu__check_alias(pmu, head_config, &unit, &scale)) |
644 | return -EINVAL; | 648 | return -EINVAL; |
645 | 649 | ||
646 | /* | 650 | /* |
@@ -652,8 +656,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
652 | if (perf_pmu__config(pmu, &attr, head_config)) | 656 | if (perf_pmu__config(pmu, &attr, head_config)) |
653 | return -EINVAL; | 657 | return -EINVAL; |
654 | 658 | ||
655 | return __add_event(list, idx, &attr, pmu_event_name(head_config), | 659 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), |
656 | pmu->cpus); | 660 | pmu->cpus); |
661 | if (evsel) { | ||
662 | evsel->unit = unit; | ||
663 | evsel->scale = scale; | ||
664 | } | ||
665 | |||
666 | return evsel ? 0 : -ENOMEM; | ||
657 | } | 667 | } |
658 | 668 | ||
659 | int parse_events__modifier_group(struct list_head *list, | 669 | int parse_events__modifier_group(struct list_head *list, |
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 | } |
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 1179b26f244a..9183380e2038 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -28,7 +28,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |||
28 | int perf_pmu__config_terms(struct list_head *formats, | 28 | int perf_pmu__config_terms(struct list_head *formats, |
29 | struct perf_event_attr *attr, | 29 | struct perf_event_attr *attr, |
30 | struct list_head *head_terms); | 30 | struct list_head *head_terms); |
31 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); | 31 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
32 | char **unit, double *scale); | ||
32 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | 33 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, |
33 | struct list_head *head_terms); | 34 | struct list_head *head_terms); |
34 | int perf_pmu_wrap(void); | 35 | int perf_pmu_wrap(void); |