aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/pmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/pmu.c')
-rw-r--r--tools/perf/util/pmu.c138
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
13struct perf_pmu_alias { 15struct 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
19struct perf_pmu_format { 23struct 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
97static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) 101static 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;
142error:
143 close(fd);
144 return ret;
145}
146
147static 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;
168error:
169 close(fd);
170 alias->unit[0] = '\0';
171 return -1;
172}
173
174static 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
611static 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 */
515int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) 636int 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 }