diff options
Diffstat (limited to 'tools/perf/util/pmu.c')
-rw-r--r-- | tools/perf/util/pmu.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index a119a5371699..74d0948ec368 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -80,6 +80,114 @@ static int pmu_format(char *name, struct list_head *format) | |||
80 | return 0; | 80 | return 0; |
81 | } | 81 | } |
82 | 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 | |||
83 | /* | 191 | /* |
84 | * Reading/parsing the default pmu type value, which should be | 192 | * Reading/parsing the default pmu type value, which should be |
85 | * located at: | 193 | * located at: |
@@ -118,6 +226,7 @@ static struct perf_pmu *pmu_lookup(char *name) | |||
118 | { | 226 | { |
119 | struct perf_pmu *pmu; | 227 | struct perf_pmu *pmu; |
120 | LIST_HEAD(format); | 228 | LIST_HEAD(format); |
229 | LIST_HEAD(aliases); | ||
121 | __u32 type; | 230 | __u32 type; |
122 | 231 | ||
123 | /* | 232 | /* |
@@ -135,8 +244,12 @@ static struct perf_pmu *pmu_lookup(char *name) | |||
135 | if (!pmu) | 244 | if (!pmu) |
136 | return NULL; | 245 | return NULL; |
137 | 246 | ||
247 | pmu_aliases(name, &aliases); | ||
248 | |||
138 | INIT_LIST_HEAD(&pmu->format); | 249 | INIT_LIST_HEAD(&pmu->format); |
250 | INIT_LIST_HEAD(&pmu->aliases); | ||
139 | list_splice(&format, &pmu->format); | 251 | list_splice(&format, &pmu->format); |
252 | list_splice(&aliases, &pmu->aliases); | ||
140 | pmu->name = strdup(name); | 253 | pmu->name = strdup(name); |
141 | pmu->type = type; | 254 | pmu->type = type; |
142 | return pmu; | 255 | return pmu; |
@@ -279,6 +392,59 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |||
279 | return pmu_config(&pmu->format, attr, head_terms); | 392 | return pmu_config(&pmu->format, attr, head_terms); |
280 | } | 393 | } |
281 | 394 | ||
395 | static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, | ||
396 | struct parse_events__term *term) | ||
397 | { | ||
398 | struct perf_pmu__alias *alias; | ||
399 | char *name; | ||
400 | |||
401 | if (parse_events__is_hardcoded_term(term)) | ||
402 | return NULL; | ||
403 | |||
404 | if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { | ||
405 | if (term->val.num != 1) | ||
406 | return NULL; | ||
407 | if (pmu_find_format(&pmu->format, term->config)) | ||
408 | return NULL; | ||
409 | name = term->config; | ||
410 | } else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { | ||
411 | if (strcasecmp(term->config, "event")) | ||
412 | return NULL; | ||
413 | name = term->val.str; | ||
414 | } else { | ||
415 | return NULL; | ||
416 | } | ||
417 | |||
418 | list_for_each_entry(alias, &pmu->aliases, list) { | ||
419 | if (!strcasecmp(alias->name, name)) | ||
420 | return alias; | ||
421 | } | ||
422 | return NULL; | ||
423 | } | ||
424 | |||
425 | /* | ||
426 | * Find alias in the terms list and replace it with the terms | ||
427 | * defined for the alias | ||
428 | */ | ||
429 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) | ||
430 | { | ||
431 | struct parse_events__term *term, *h; | ||
432 | struct perf_pmu__alias *alias; | ||
433 | int ret; | ||
434 | |||
435 | list_for_each_entry_safe(term, h, head_terms, list) { | ||
436 | alias = pmu_find_alias(pmu, term); | ||
437 | if (!alias) | ||
438 | continue; | ||
439 | ret = pmu_alias_terms(alias, &term->list); | ||
440 | if (ret) | ||
441 | return ret; | ||
442 | list_del(&term->list); | ||
443 | free(term); | ||
444 | } | ||
445 | return 0; | ||
446 | } | ||
447 | |||
282 | int perf_pmu__new_format(struct list_head *list, char *name, | 448 | int perf_pmu__new_format(struct list_head *list, char *name, |
283 | int config, unsigned long *bits) | 449 | int config, unsigned long *bits) |
284 | { | 450 | { |