diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-06-11 13:08:07 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-06-19 12:06:20 -0400 |
commit | 0b668bc9a74ce1bd3b8c5fd93e8d85ed955e11fe (patch) | |
tree | 771dbfc4676c223d19c27127f1ad8518e3b9a8b0 | |
parent | 27f18617b01dbbc928e9bd3731d1766222fb7e0d (diff) |
perf tools: Reconstruct hw cache event with modifiers from perf_event_attr
[root@sandy ~]# perf record -a -e dTLB-load-misses:u usleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.486 MB perf.data (~21216 samples) ]
Before:
[root@sandy ~]# perf evlist
dTLB-load-misses
[root@sandy ~]#
After:
[root@sandy ~]# perf evlist
dTLB-load-misses:u
[root@sandy ~]#
Ditto for other tools.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-7x1b0e6jthkr93lfjzsuakk5@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/util/evsel.c | 104 | ||||
-rw-r--r-- | tools/perf/util/evsel.h | 16 | ||||
-rw-r--r-- | tools/perf/util/parse-events.c | 115 |
3 files changed, 134 insertions, 101 deletions
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index dab893804a14..47f1fe2feab8 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -128,6 +128,105 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size) | |||
128 | return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); | 128 | return r + perf_evsel__add_modifiers(evsel, bf + r, size - r); |
129 | } | 129 | } |
130 | 130 | ||
131 | const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX] | ||
132 | [PERF_EVSEL__MAX_ALIASES] = { | ||
133 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, | ||
134 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, | ||
135 | { "LLC", "L2", }, | ||
136 | { "dTLB", "d-tlb", "Data-TLB", }, | ||
137 | { "iTLB", "i-tlb", "Instruction-TLB", }, | ||
138 | { "branch", "branches", "bpu", "btb", "bpc", }, | ||
139 | { "node", }, | ||
140 | }; | ||
141 | |||
142 | const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX] | ||
143 | [PERF_EVSEL__MAX_ALIASES] = { | ||
144 | { "load", "loads", "read", }, | ||
145 | { "store", "stores", "write", }, | ||
146 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, | ||
147 | }; | ||
148 | |||
149 | const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] | ||
150 | [PERF_EVSEL__MAX_ALIASES] = { | ||
151 | { "refs", "Reference", "ops", "access", }, | ||
152 | { "misses", "miss", }, | ||
153 | }; | ||
154 | |||
155 | #define C(x) PERF_COUNT_HW_CACHE_##x | ||
156 | #define CACHE_READ (1 << C(OP_READ)) | ||
157 | #define CACHE_WRITE (1 << C(OP_WRITE)) | ||
158 | #define CACHE_PREFETCH (1 << C(OP_PREFETCH)) | ||
159 | #define COP(x) (1 << x) | ||
160 | |||
161 | /* | ||
162 | * cache operartion stat | ||
163 | * L1I : Read and prefetch only | ||
164 | * ITLB and BPU : Read-only | ||
165 | */ | ||
166 | static unsigned long perf_evsel__hw_cache_stat[C(MAX)] = { | ||
167 | [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
168 | [C(L1I)] = (CACHE_READ | CACHE_PREFETCH), | ||
169 | [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
170 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
171 | [C(ITLB)] = (CACHE_READ), | ||
172 | [C(BPU)] = (CACHE_READ), | ||
173 | [C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
174 | }; | ||
175 | |||
176 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op) | ||
177 | { | ||
178 | if (perf_evsel__hw_cache_stat[type] & COP(op)) | ||
179 | return true; /* valid */ | ||
180 | else | ||
181 | return false; /* invalid */ | ||
182 | } | ||
183 | |||
184 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, | ||
185 | char *bf, size_t size) | ||
186 | { | ||
187 | if (result) { | ||
188 | return scnprintf(bf, size, "%s-%s-%s", perf_evsel__hw_cache[type][0], | ||
189 | perf_evsel__hw_cache_op[op][0], | ||
190 | perf_evsel__hw_cache_result[result][0]); | ||
191 | } | ||
192 | |||
193 | return scnprintf(bf, size, "%s-%s", perf_evsel__hw_cache[type][0], | ||
194 | perf_evsel__hw_cache_op[op][1]); | ||
195 | } | ||
196 | |||
197 | int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size) | ||
198 | { | ||
199 | u8 op, result, type = (config >> 0) & 0xff; | ||
200 | const char *err = "unknown-ext-hardware-cache-type"; | ||
201 | |||
202 | if (type > PERF_COUNT_HW_CACHE_MAX) | ||
203 | goto out_err; | ||
204 | |||
205 | op = (config >> 8) & 0xff; | ||
206 | err = "unknown-ext-hardware-cache-op"; | ||
207 | if (op > PERF_COUNT_HW_CACHE_OP_MAX) | ||
208 | goto out_err; | ||
209 | |||
210 | result = (config >> 16) & 0xff; | ||
211 | err = "unknown-ext-hardware-cache-result"; | ||
212 | if (result > PERF_COUNT_HW_CACHE_RESULT_MAX) | ||
213 | goto out_err; | ||
214 | |||
215 | err = "invalid-cache"; | ||
216 | if (!perf_evsel__is_cache_op_valid(type, op)) | ||
217 | goto out_err; | ||
218 | |||
219 | return __perf_evsel__hw_cache_type_op_res_name(type, op, result, bf, size); | ||
220 | out_err: | ||
221 | return scnprintf(bf, size, "%s", err); | ||
222 | } | ||
223 | |||
224 | static int perf_evsel__hw_cache_name(struct perf_evsel *evsel, char *bf, size_t size) | ||
225 | { | ||
226 | int ret = __perf_evsel__hw_cache_name(evsel->attr.config, bf, size); | ||
227 | return ret + perf_evsel__add_modifiers(evsel, bf + ret, size - ret); | ||
228 | } | ||
229 | |||
131 | int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size) | 230 | int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size) |
132 | { | 231 | { |
133 | int ret; | 232 | int ret; |
@@ -140,6 +239,11 @@ int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size) | |||
140 | case PERF_TYPE_HARDWARE: | 239 | case PERF_TYPE_HARDWARE: |
141 | ret = perf_evsel__hw_name(evsel, bf, size); | 240 | ret = perf_evsel__hw_name(evsel, bf, size); |
142 | break; | 241 | break; |
242 | |||
243 | case PERF_TYPE_HW_CACHE: | ||
244 | ret = perf_evsel__hw_cache_name(evsel, bf, size); | ||
245 | break; | ||
246 | |||
143 | default: | 247 | default: |
144 | /* | 248 | /* |
145 | * FIXME | 249 | * FIXME |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 4ba8b564e6f4..5bf946a05a6b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -83,7 +83,21 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
83 | struct perf_record_opts *opts, | 83 | struct perf_record_opts *opts, |
84 | struct perf_evsel *first); | 84 | struct perf_evsel *first); |
85 | 85 | ||
86 | const char* __perf_evsel__hw_name(u64 config); | 86 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op); |
87 | |||
88 | #define PERF_EVSEL__MAX_ALIASES 8 | ||
89 | |||
90 | extern const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX] | ||
91 | [PERF_EVSEL__MAX_ALIASES]; | ||
92 | extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX] | ||
93 | [PERF_EVSEL__MAX_ALIASES]; | ||
94 | const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] | ||
95 | [PERF_EVSEL__MAX_ALIASES]; | ||
96 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, | ||
97 | char *bf, size_t size); | ||
98 | int __perf_evsel__hw_cache_name(u64 config, char *bf, size_t size); | ||
99 | |||
100 | const char *__perf_evsel__hw_name(u64 config); | ||
87 | int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size); | 101 | int perf_evsel__name(struct perf_evsel *evsel, char *bf, size_t size); |
88 | 102 | ||
89 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 103 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 05dbc8b3c767..c8f8cf4a6920 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -74,51 +74,6 @@ static const char *sw_event_names[PERF_COUNT_SW_MAX] = { | |||
74 | "emulation-faults", | 74 | "emulation-faults", |
75 | }; | 75 | }; |
76 | 76 | ||
77 | #define MAX_ALIASES 8 | ||
78 | |||
79 | static const char *hw_cache[PERF_COUNT_HW_CACHE_MAX][MAX_ALIASES] = { | ||
80 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, | ||
81 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, | ||
82 | { "LLC", "L2", }, | ||
83 | { "dTLB", "d-tlb", "Data-TLB", }, | ||
84 | { "iTLB", "i-tlb", "Instruction-TLB", }, | ||
85 | { "branch", "branches", "bpu", "btb", "bpc", }, | ||
86 | { "node", }, | ||
87 | }; | ||
88 | |||
89 | static const char *hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][MAX_ALIASES] = { | ||
90 | { "load", "loads", "read", }, | ||
91 | { "store", "stores", "write", }, | ||
92 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, | ||
93 | }; | ||
94 | |||
95 | static const char *hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] | ||
96 | [MAX_ALIASES] = { | ||
97 | { "refs", "Reference", "ops", "access", }, | ||
98 | { "misses", "miss", }, | ||
99 | }; | ||
100 | |||
101 | #define C(x) PERF_COUNT_HW_CACHE_##x | ||
102 | #define CACHE_READ (1 << C(OP_READ)) | ||
103 | #define CACHE_WRITE (1 << C(OP_WRITE)) | ||
104 | #define CACHE_PREFETCH (1 << C(OP_PREFETCH)) | ||
105 | #define COP(x) (1 << x) | ||
106 | |||
107 | /* | ||
108 | * cache operartion stat | ||
109 | * L1I : Read and prefetch only | ||
110 | * ITLB and BPU : Read-only | ||
111 | */ | ||
112 | static unsigned long hw_cache_stat[C(MAX)] = { | ||
113 | [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
114 | [C(L1I)] = (CACHE_READ | CACHE_PREFETCH), | ||
115 | [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
116 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
117 | [C(ITLB)] = (CACHE_READ), | ||
118 | [C(BPU)] = (CACHE_READ), | ||
119 | [C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
120 | }; | ||
121 | |||
122 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ | 77 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ |
123 | while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ | 78 | while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ |
124 | if (sys_dirent.d_type == DT_DIR && \ | 79 | if (sys_dirent.d_type == DT_DIR && \ |
@@ -236,30 +191,6 @@ static const char *tracepoint_id_to_name(u64 config) | |||
236 | return buf; | 191 | return buf; |
237 | } | 192 | } |
238 | 193 | ||
239 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) | ||
240 | { | ||
241 | if (hw_cache_stat[cache_type] & COP(cache_op)) | ||
242 | return 1; /* valid */ | ||
243 | else | ||
244 | return 0; /* invalid */ | ||
245 | } | ||
246 | |||
247 | static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) | ||
248 | { | ||
249 | static char name[50]; | ||
250 | |||
251 | if (cache_result) { | ||
252 | sprintf(name, "%s-%s-%s", hw_cache[cache_type][0], | ||
253 | hw_cache_op[cache_op][0], | ||
254 | hw_cache_result[cache_result][0]); | ||
255 | } else { | ||
256 | sprintf(name, "%s-%s", hw_cache[cache_type][0], | ||
257 | hw_cache_op[cache_op][1]); | ||
258 | } | ||
259 | |||
260 | return name; | ||
261 | } | ||
262 | |||
263 | const char *event_type(int type) | 194 | const char *event_type(int type) |
264 | { | 195 | { |
265 | switch (type) { | 196 | switch (type) { |
@@ -287,7 +218,7 @@ const char *event_name(struct perf_evsel *evsel) | |||
287 | u64 config = evsel->attr.config; | 218 | u64 config = evsel->attr.config; |
288 | int type = evsel->attr.type; | 219 | int type = evsel->attr.type; |
289 | 220 | ||
290 | if (type == PERF_TYPE_RAW || type == PERF_TYPE_HARDWARE) { | 221 | if (type == PERF_TYPE_RAW || type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE) { |
291 | /* | 222 | /* |
292 | * XXX minimal fix, see comment on perf_evsen__name, this static buffer | 223 | * XXX minimal fix, see comment on perf_evsen__name, this static buffer |
293 | * will go away together with event_name in the next devel cycle. | 224 | * will go away together with event_name in the next devel cycle. |
@@ -316,26 +247,9 @@ const char *__event_name(int type, u64 config) | |||
316 | case PERF_TYPE_HARDWARE: | 247 | case PERF_TYPE_HARDWARE: |
317 | return __perf_evsel__hw_name(config); | 248 | return __perf_evsel__hw_name(config); |
318 | 249 | ||
319 | case PERF_TYPE_HW_CACHE: { | 250 | case PERF_TYPE_HW_CACHE: |
320 | u8 cache_type, cache_op, cache_result; | 251 | __perf_evsel__hw_cache_name(config, buf, sizeof(buf)); |
321 | 252 | return buf; | |
322 | cache_type = (config >> 0) & 0xff; | ||
323 | if (cache_type > PERF_COUNT_HW_CACHE_MAX) | ||
324 | return "unknown-ext-hardware-cache-type"; | ||
325 | |||
326 | cache_op = (config >> 8) & 0xff; | ||
327 | if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX) | ||
328 | return "unknown-ext-hardware-cache-op"; | ||
329 | |||
330 | cache_result = (config >> 16) & 0xff; | ||
331 | if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX) | ||
332 | return "unknown-ext-hardware-cache-result"; | ||
333 | |||
334 | if (!is_cache_op_valid(cache_type, cache_op)) | ||
335 | return "invalid-cache"; | ||
336 | |||
337 | return event_cache_name(cache_type, cache_op, cache_result); | ||
338 | } | ||
339 | 253 | ||
340 | case PERF_TYPE_SOFTWARE: | 254 | case PERF_TYPE_SOFTWARE: |
341 | if (config < PERF_COUNT_SW_MAX && sw_event_names[config]) | 255 | if (config < PERF_COUNT_SW_MAX && sw_event_names[config]) |
@@ -379,13 +293,13 @@ static int add_event(struct list_head **_list, int *idx, | |||
379 | return 0; | 293 | return 0; |
380 | } | 294 | } |
381 | 295 | ||
382 | static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size) | 296 | static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) |
383 | { | 297 | { |
384 | int i, j; | 298 | int i, j; |
385 | int n, longest = -1; | 299 | int n, longest = -1; |
386 | 300 | ||
387 | for (i = 0; i < size; i++) { | 301 | for (i = 0; i < size; i++) { |
388 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { | 302 | for (j = 0; j < PERF_EVSEL__MAX_ALIASES && names[i][j]; j++) { |
389 | n = strlen(names[i][j]); | 303 | n = strlen(names[i][j]); |
390 | if (n > longest && !strncasecmp(str, names[i][j], n)) | 304 | if (n > longest && !strncasecmp(str, names[i][j], n)) |
391 | longest = n; | 305 | longest = n; |
@@ -410,7 +324,7 @@ int parse_events_add_cache(struct list_head **list, int *idx, | |||
410 | * No fallback - if we cannot get a clear cache type | 324 | * No fallback - if we cannot get a clear cache type |
411 | * then bail out: | 325 | * then bail out: |
412 | */ | 326 | */ |
413 | cache_type = parse_aliases(type, hw_cache, | 327 | cache_type = parse_aliases(type, perf_evsel__hw_cache, |
414 | PERF_COUNT_HW_CACHE_MAX); | 328 | PERF_COUNT_HW_CACHE_MAX); |
415 | if (cache_type == -1) | 329 | if (cache_type == -1) |
416 | return -EINVAL; | 330 | return -EINVAL; |
@@ -423,18 +337,18 @@ int parse_events_add_cache(struct list_head **list, int *idx, | |||
423 | snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str); | 337 | snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str); |
424 | 338 | ||
425 | if (cache_op == -1) { | 339 | if (cache_op == -1) { |
426 | cache_op = parse_aliases(str, hw_cache_op, | 340 | cache_op = parse_aliases(str, perf_evsel__hw_cache_op, |
427 | PERF_COUNT_HW_CACHE_OP_MAX); | 341 | PERF_COUNT_HW_CACHE_OP_MAX); |
428 | if (cache_op >= 0) { | 342 | if (cache_op >= 0) { |
429 | if (!is_cache_op_valid(cache_type, cache_op)) | 343 | if (!perf_evsel__is_cache_op_valid(cache_type, cache_op)) |
430 | return -EINVAL; | 344 | return -EINVAL; |
431 | continue; | 345 | continue; |
432 | } | 346 | } |
433 | } | 347 | } |
434 | 348 | ||
435 | if (cache_result == -1) { | 349 | if (cache_result == -1) { |
436 | cache_result = parse_aliases(str, hw_cache_result, | 350 | cache_result = parse_aliases(str, perf_evsel__hw_cache_result, |
437 | PERF_COUNT_HW_CACHE_RESULT_MAX); | 351 | PERF_COUNT_HW_CACHE_RESULT_MAX); |
438 | if (cache_result >= 0) | 352 | if (cache_result >= 0) |
439 | continue; | 353 | continue; |
440 | } | 354 | } |
@@ -970,16 +884,17 @@ void print_events_type(u8 type) | |||
970 | int print_hwcache_events(const char *event_glob) | 884 | int print_hwcache_events(const char *event_glob) |
971 | { | 885 | { |
972 | unsigned int type, op, i, printed = 0; | 886 | unsigned int type, op, i, printed = 0; |
887 | char name[64]; | ||
973 | 888 | ||
974 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | 889 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { |
975 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | 890 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { |
976 | /* skip invalid cache type */ | 891 | /* skip invalid cache type */ |
977 | if (!is_cache_op_valid(type, op)) | 892 | if (!perf_evsel__is_cache_op_valid(type, op)) |
978 | continue; | 893 | continue; |
979 | 894 | ||
980 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 895 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
981 | char *name = event_cache_name(type, op, i); | 896 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, |
982 | 897 | name, sizeof(name)); | |
983 | if (event_glob != NULL && !strglobmatch(name, event_glob)) | 898 | if (event_glob != NULL && !strglobmatch(name, event_glob)) |
984 | continue; | 899 | continue; |
985 | 900 | ||