diff options
author | Zheng Yan <zheng.z.yan@intel.com> | 2012-06-15 02:31:41 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-06-18 06:13:26 -0400 |
commit | a6146d5040cce560f700221158d77dd335eed332 (patch) | |
tree | a5361219b1f8ae962ea54b997f4a83eb9cf15e7a | |
parent | 90e2b22dee908c13df256140a0d6527e3e8ea3f4 (diff) |
perf/tool: Add PMU event alias support
Add support to specify alias term within the event description.
The definition of pmu event alias is located at:
${sysfs_mount}/bus/event_source/devices/${pmu}/events/
Each file in the 'events' directory defines a event alias. Its contents
are like:
config=1,config1=2
Using pmu event alias, an event can be now specified like:
uncore/CLOCKTICKS/ or uncore/event=CLOCKTICKS/
Signed-off-by: Zheng Yan <zheng.z.yan@intel.com>
[ Cleaned it up. ]
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1339741902-8449-13-git-send-email-zheng.z.yan@intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | tools/perf/util/parse-events.c | 10 | ||||
-rw-r--r-- | tools/perf/util/parse-events.h | 2 | ||||
-rw-r--r-- | tools/perf/util/pmu.c | 166 | ||||
-rw-r--r-- | tools/perf/util/pmu.h | 11 |
4 files changed, 188 insertions, 1 deletions
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index d002170adb3f..3339424cc421 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -701,6 +701,9 @@ int parse_events_add_pmu(struct list_head **list, int *idx, | |||
701 | 701 | ||
702 | memset(&attr, 0, sizeof(attr)); | 702 | memset(&attr, 0, sizeof(attr)); |
703 | 703 | ||
704 | if (perf_pmu__check_alias(pmu, head_config)) | ||
705 | return -EINVAL; | ||
706 | |||
704 | /* | 707 | /* |
705 | * Configure hardcoded terms first, no need to check | 708 | * Configure hardcoded terms first, no need to check |
706 | * return value when called with fail == 0 ;) | 709 | * return value when called with fail == 0 ;) |
@@ -1143,6 +1146,13 @@ int parse_events__term_str(struct parse_events__term **term, | |||
1143 | config, str, 0); | 1146 | config, str, 0); |
1144 | } | 1147 | } |
1145 | 1148 | ||
1149 | int parse_events__term_clone(struct parse_events__term **new, | ||
1150 | struct parse_events__term *term) | ||
1151 | { | ||
1152 | return new_term(new, term->type_val, term->type_term, term->config, | ||
1153 | term->val.str, term->val.num); | ||
1154 | } | ||
1155 | |||
1146 | void parse_events__free_terms(struct list_head *terms) | 1156 | void parse_events__free_terms(struct list_head *terms) |
1147 | { | 1157 | { |
1148 | struct parse_events__term *term, *h; | 1158 | struct parse_events__term *term, *h; |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 9896edadbe62..a2c71684f6a4 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -78,6 +78,8 @@ int parse_events__term_num(struct parse_events__term **_term, | |||
78 | int type_term, char *config, long num); | 78 | int type_term, char *config, long num); |
79 | int parse_events__term_str(struct parse_events__term **_term, | 79 | int parse_events__term_str(struct parse_events__term **_term, |
80 | int type_term, char *config, char *str); | 80 | int type_term, char *config, char *str); |
81 | int parse_events__term_clone(struct parse_events__term **new, | ||
82 | struct parse_events__term *term); | ||
81 | void parse_events__free_terms(struct list_head *terms); | 83 | void parse_events__free_terms(struct list_head *terms); |
82 | int parse_events_modifier(struct list_head *list, char *str); | 84 | int parse_events_modifier(struct list_head *list, char *str); |
83 | int parse_events_add_tracepoint(struct list_head **list, int *idx, | 85 | int parse_events_add_tracepoint(struct list_head **list, int *idx, |
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 | { |
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 68c0db965e1f..535f2c5258ab 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -19,17 +19,26 @@ struct perf_pmu__format { | |||
19 | struct list_head list; | 19 | struct list_head list; |
20 | }; | 20 | }; |
21 | 21 | ||
22 | struct perf_pmu__alias { | ||
23 | char *name; | ||
24 | struct list_head terms; | ||
25 | struct list_head list; | ||
26 | }; | ||
27 | |||
22 | struct perf_pmu { | 28 | struct perf_pmu { |
23 | char *name; | 29 | char *name; |
24 | __u32 type; | 30 | __u32 type; |
25 | struct list_head format; | 31 | struct list_head format; |
32 | struct list_head aliases; | ||
26 | struct list_head list; | 33 | struct list_head list; |
27 | }; | 34 | }; |
28 | 35 | ||
29 | struct perf_pmu *perf_pmu__find(char *name); | 36 | struct perf_pmu *perf_pmu__find(char *name); |
30 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 37 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
31 | struct list_head *head_terms); | 38 | struct list_head *head_terms); |
32 | 39 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); | |
40 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | ||
41 | struct list_head *head_terms); | ||
33 | int perf_pmu_wrap(void); | 42 | int perf_pmu_wrap(void); |
34 | void perf_pmu_error(struct list_head *list, char *name, char const *msg); | 43 | void perf_pmu_error(struct list_head *list, char *name, char const *msg); |
35 | 44 | ||