Commit | Line | Data |
---|---|---|
8ad8db37 | 1 | |
8ad8db37 | 2 | #include "util.h" |
6b58e7f1 | 3 | #include "../perf.h" |
8ad8db37 IM |
4 | #include "parse-options.h" |
5 | #include "parse-events.h" | |
6 | #include "exec_cmd.h" | |
a0055ae2 | 7 | #include "string.h" |
5beeded1 | 8 | #include "cache.h" |
8ad8db37 | 9 | |
a21ca2ca | 10 | int nr_counters; |
8ad8db37 | 11 | |
a21ca2ca | 12 | struct perf_counter_attr attrs[MAX_COUNTERS]; |
8ad8db37 IM |
13 | |
14 | struct event_symbol { | |
83a0944f IM |
15 | u8 type; |
16 | u64 config; | |
17 | const char *symbol; | |
18 | const char *alias; | |
8ad8db37 IM |
19 | }; |
20 | ||
bcd3279f FW |
21 | enum event_result { |
22 | EVT_FAILED, | |
23 | EVT_HANDLED, | |
24 | EVT_HANDLED_ALL | |
25 | }; | |
26 | ||
5beeded1 JB |
27 | char debugfs_path[MAXPATHLEN]; |
28 | ||
51e26842 JSR |
29 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
30 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | |
a21ca2ca | 31 | |
8ad8db37 | 32 | static struct event_symbol event_symbols[] = { |
74d5b588 JSR |
33 | { CHW(CPU_CYCLES), "cpu-cycles", "cycles" }, |
34 | { CHW(INSTRUCTIONS), "instructions", "" }, | |
35 | { CHW(CACHE_REFERENCES), "cache-references", "" }, | |
36 | { CHW(CACHE_MISSES), "cache-misses", "" }, | |
37 | { CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" }, | |
38 | { CHW(BRANCH_MISSES), "branch-misses", "" }, | |
39 | { CHW(BUS_CYCLES), "bus-cycles", "" }, | |
40 | ||
41 | { CSW(CPU_CLOCK), "cpu-clock", "" }, | |
42 | { CSW(TASK_CLOCK), "task-clock", "" }, | |
c0c22dbf | 43 | { CSW(PAGE_FAULTS), "page-faults", "faults" }, |
74d5b588 JSR |
44 | { CSW(PAGE_FAULTS_MIN), "minor-faults", "" }, |
45 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, | |
46 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, | |
47 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, | |
8ad8db37 IM |
48 | }; |
49 | ||
5242519b IM |
50 | #define __PERF_COUNTER_FIELD(config, name) \ |
51 | ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT) | |
52 | ||
53 | #define PERF_COUNTER_RAW(config) __PERF_COUNTER_FIELD(config, RAW) | |
54 | #define PERF_COUNTER_CONFIG(config) __PERF_COUNTER_FIELD(config, CONFIG) | |
55 | #define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) | |
56 | #define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) | |
57 | ||
83a0944f | 58 | static const char *hw_event_names[] = { |
8faf3b54 | 59 | "cycles", |
5242519b | 60 | "instructions", |
8faf3b54 IM |
61 | "cache-references", |
62 | "cache-misses", | |
5242519b | 63 | "branches", |
8faf3b54 IM |
64 | "branch-misses", |
65 | "bus-cycles", | |
5242519b IM |
66 | }; |
67 | ||
83a0944f | 68 | static const char *sw_event_names[] = { |
44175b6f IM |
69 | "cpu-clock-msecs", |
70 | "task-clock-msecs", | |
8faf3b54 IM |
71 | "page-faults", |
72 | "context-switches", | |
73 | "CPU-migrations", | |
74 | "minor-faults", | |
75 | "major-faults", | |
5242519b IM |
76 | }; |
77 | ||
8326f44d IM |
78 | #define MAX_ALIASES 8 |
79 | ||
83a0944f | 80 | static const char *hw_cache[][MAX_ALIASES] = { |
9590b7ba AB |
81 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, |
82 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, | |
e5c59547 JSR |
83 | { "LLC", "L2" }, |
84 | { "dTLB", "d-tlb", "Data-TLB", }, | |
85 | { "iTLB", "i-tlb", "Instruction-TLB", }, | |
86 | { "branch", "branches", "bpu", "btb", "bpc", }, | |
8326f44d IM |
87 | }; |
88 | ||
83a0944f | 89 | static const char *hw_cache_op[][MAX_ALIASES] = { |
e5c59547 JSR |
90 | { "load", "loads", "read", }, |
91 | { "store", "stores", "write", }, | |
92 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, | |
8326f44d IM |
93 | }; |
94 | ||
83a0944f | 95 | static const char *hw_cache_result[][MAX_ALIASES] = { |
e5c59547 JSR |
96 | { "refs", "Reference", "ops", "access", }, |
97 | { "misses", "miss", }, | |
8326f44d IM |
98 | }; |
99 | ||
06813f6c JSR |
100 | #define C(x) PERF_COUNT_HW_CACHE_##x |
101 | #define CACHE_READ (1 << C(OP_READ)) | |
102 | #define CACHE_WRITE (1 << C(OP_WRITE)) | |
103 | #define CACHE_PREFETCH (1 << C(OP_PREFETCH)) | |
104 | #define COP(x) (1 << x) | |
105 | ||
106 | /* | |
107 | * cache operartion stat | |
108 | * L1I : Read and prefetch only | |
109 | * ITLB and BPU : Read-only | |
110 | */ | |
111 | static unsigned long hw_cache_stat[C(MAX)] = { | |
112 | [C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | |
113 | [C(L1I)] = (CACHE_READ | CACHE_PREFETCH), | |
114 | [C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | |
115 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | |
116 | [C(ITLB)] = (CACHE_READ), | |
117 | [C(BPU)] = (CACHE_READ), | |
118 | }; | |
119 | ||
6b58e7f1 | 120 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ |
f6bdafef | 121 | while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ |
6b58e7f1 | 122 | if (sys_dirent.d_type == DT_DIR && \ |
f6bdafef JB |
123 | (strcmp(sys_dirent.d_name, ".")) && \ |
124 | (strcmp(sys_dirent.d_name, ".."))) | |
125 | ||
ae07b63f PZ |
126 | static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) |
127 | { | |
128 | char evt_path[MAXPATHLEN]; | |
129 | int fd; | |
130 | ||
131 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | |
132 | sys_dir->d_name, evt_dir->d_name); | |
133 | fd = open(evt_path, O_RDONLY); | |
134 | if (fd < 0) | |
135 | return -EINVAL; | |
136 | close(fd); | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
6b58e7f1 | 141 | #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \ |
f6bdafef | 142 | while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ |
6b58e7f1 | 143 | if (evt_dirent.d_type == DT_DIR && \ |
f6bdafef | 144 | (strcmp(evt_dirent.d_name, ".")) && \ |
ae07b63f PZ |
145 | (strcmp(evt_dirent.d_name, "..")) && \ |
146 | (!tp_event_has_id(&sys_dirent, &evt_dirent))) | |
f6bdafef JB |
147 | |
148 | #define MAX_EVENT_LENGTH 30 | |
149 | ||
5beeded1 | 150 | int valid_debugfs_mount(const char *debugfs) |
f6bdafef JB |
151 | { |
152 | struct statfs st_fs; | |
153 | ||
5beeded1 | 154 | if (statfs(debugfs, &st_fs) < 0) |
f6bdafef JB |
155 | return -ENOENT; |
156 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | |
157 | return -ENOENT; | |
158 | return 0; | |
159 | } | |
160 | ||
1ef2ed10 | 161 | struct tracepoint_path *tracepoint_id_to_path(u64 config) |
f6bdafef | 162 | { |
1ef2ed10 | 163 | struct tracepoint_path *path = NULL; |
f6bdafef JB |
164 | DIR *sys_dir, *evt_dir; |
165 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | |
f6bdafef | 166 | char id_buf[4]; |
6b58e7f1 | 167 | int sys_dir_fd, fd; |
f6bdafef JB |
168 | u64 id; |
169 | char evt_path[MAXPATHLEN]; | |
170 | ||
5beeded1 | 171 | if (valid_debugfs_mount(debugfs_path)) |
1ef2ed10 | 172 | return NULL; |
f6bdafef | 173 | |
5beeded1 | 174 | sys_dir = opendir(debugfs_path); |
f6bdafef JB |
175 | if (!sys_dir) |
176 | goto cleanup; | |
6b58e7f1 UD |
177 | sys_dir_fd = dirfd(sys_dir); |
178 | ||
179 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { | |
180 | int dfd = openat(sys_dir_fd, sys_dirent.d_name, | |
181 | O_RDONLY|O_DIRECTORY), evt_dir_fd; | |
182 | if (dfd == -1) | |
183 | continue; | |
184 | evt_dir = fdopendir(dfd); | |
185 | if (!evt_dir) { | |
186 | close(dfd); | |
187 | continue; | |
188 | } | |
189 | evt_dir_fd = dirfd(evt_dir); | |
190 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | |
191 | snprintf(evt_path, MAXPATHLEN, "%s/id", | |
f6bdafef | 192 | evt_dirent.d_name); |
6b58e7f1 | 193 | fd = openat(evt_dir_fd, evt_path, O_RDONLY); |
f6bdafef JB |
194 | if (fd < 0) |
195 | continue; | |
196 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | |
197 | close(fd); | |
198 | continue; | |
199 | } | |
200 | close(fd); | |
201 | id = atoll(id_buf); | |
202 | if (id == config) { | |
203 | closedir(evt_dir); | |
204 | closedir(sys_dir); | |
1ef2ed10 FW |
205 | path = calloc(1, sizeof(path)); |
206 | path->system = malloc(MAX_EVENT_LENGTH); | |
207 | if (!path->system) { | |
208 | free(path); | |
209 | return NULL; | |
210 | } | |
211 | path->name = malloc(MAX_EVENT_LENGTH); | |
212 | if (!path->name) { | |
213 | free(path->system); | |
214 | free(path); | |
215 | return NULL; | |
216 | } | |
217 | strncpy(path->system, sys_dirent.d_name, | |
218 | MAX_EVENT_LENGTH); | |
219 | strncpy(path->name, evt_dirent.d_name, | |
220 | MAX_EVENT_LENGTH); | |
221 | return path; | |
f6bdafef JB |
222 | } |
223 | } | |
224 | closedir(evt_dir); | |
225 | } | |
226 | ||
227 | cleanup: | |
228 | closedir(sys_dir); | |
1ef2ed10 FW |
229 | return NULL; |
230 | } | |
231 | ||
232 | #define TP_PATH_LEN (MAX_EVENT_LENGTH * 2 + 1) | |
233 | static const char *tracepoint_id_to_name(u64 config) | |
234 | { | |
235 | static char buf[TP_PATH_LEN]; | |
236 | struct tracepoint_path *path; | |
237 | ||
238 | path = tracepoint_id_to_path(config); | |
239 | if (path) { | |
240 | snprintf(buf, TP_PATH_LEN, "%s:%s", path->system, path->name); | |
241 | free(path->name); | |
242 | free(path->system); | |
243 | free(path); | |
244 | } else | |
245 | snprintf(buf, TP_PATH_LEN, "%s:%s", "unknown", "unknown"); | |
246 | ||
247 | return buf; | |
f6bdafef JB |
248 | } |
249 | ||
06813f6c JSR |
250 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) |
251 | { | |
252 | if (hw_cache_stat[cache_type] & COP(cache_op)) | |
253 | return 1; /* valid */ | |
254 | else | |
255 | return 0; /* invalid */ | |
256 | } | |
257 | ||
e5c59547 JSR |
258 | static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) |
259 | { | |
260 | static char name[50]; | |
261 | ||
262 | if (cache_result) { | |
263 | sprintf(name, "%s-%s-%s", hw_cache[cache_type][0], | |
264 | hw_cache_op[cache_op][0], | |
265 | hw_cache_result[cache_result][0]); | |
266 | } else { | |
267 | sprintf(name, "%s-%s", hw_cache[cache_type][0], | |
268 | hw_cache_op[cache_op][1]); | |
269 | } | |
270 | ||
271 | return name; | |
272 | } | |
273 | ||
83a0944f | 274 | const char *event_name(int counter) |
5242519b | 275 | { |
9cffa8d5 | 276 | u64 config = attrs[counter].config; |
a21ca2ca | 277 | int type = attrs[counter].type; |
8f18aec5 PZ |
278 | |
279 | return __event_name(type, config); | |
280 | } | |
281 | ||
83a0944f | 282 | const char *__event_name(int type, u64 config) |
8f18aec5 | 283 | { |
5242519b IM |
284 | static char buf[32]; |
285 | ||
8f18aec5 | 286 | if (type == PERF_TYPE_RAW) { |
a21ca2ca | 287 | sprintf(buf, "raw 0x%llx", config); |
5242519b IM |
288 | return buf; |
289 | } | |
290 | ||
291 | switch (type) { | |
292 | case PERF_TYPE_HARDWARE: | |
f4dbfa8f | 293 | if (config < PERF_COUNT_HW_MAX) |
a21ca2ca | 294 | return hw_event_names[config]; |
5242519b IM |
295 | return "unknown-hardware"; |
296 | ||
8326f44d | 297 | case PERF_TYPE_HW_CACHE: { |
9cffa8d5 | 298 | u8 cache_type, cache_op, cache_result; |
8326f44d IM |
299 | |
300 | cache_type = (config >> 0) & 0xff; | |
301 | if (cache_type > PERF_COUNT_HW_CACHE_MAX) | |
302 | return "unknown-ext-hardware-cache-type"; | |
303 | ||
304 | cache_op = (config >> 8) & 0xff; | |
8faf3b54 IM |
305 | if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX) |
306 | return "unknown-ext-hardware-cache-op"; | |
8326f44d IM |
307 | |
308 | cache_result = (config >> 16) & 0xff; | |
8faf3b54 IM |
309 | if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX) |
310 | return "unknown-ext-hardware-cache-result"; | |
8326f44d | 311 | |
06813f6c JSR |
312 | if (!is_cache_op_valid(cache_type, cache_op)) |
313 | return "invalid-cache"; | |
8326f44d | 314 | |
e5c59547 | 315 | return event_cache_name(cache_type, cache_op, cache_result); |
8326f44d IM |
316 | } |
317 | ||
5242519b | 318 | case PERF_TYPE_SOFTWARE: |
f4dbfa8f | 319 | if (config < PERF_COUNT_SW_MAX) |
a21ca2ca | 320 | return sw_event_names[config]; |
5242519b IM |
321 | return "unknown-software"; |
322 | ||
f6bdafef JB |
323 | case PERF_TYPE_TRACEPOINT: |
324 | return tracepoint_id_to_name(config); | |
325 | ||
5242519b IM |
326 | default: |
327 | break; | |
328 | } | |
329 | ||
330 | return "unknown"; | |
331 | } | |
332 | ||
83a0944f | 333 | static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size) |
8326f44d IM |
334 | { |
335 | int i, j; | |
61c45981 | 336 | int n, longest = -1; |
8326f44d IM |
337 | |
338 | for (i = 0; i < size; i++) { | |
61c45981 PM |
339 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { |
340 | n = strlen(names[i][j]); | |
341 | if (n > longest && !strncasecmp(*str, names[i][j], n)) | |
342 | longest = n; | |
343 | } | |
344 | if (longest > 0) { | |
345 | *str += longest; | |
346 | return i; | |
8326f44d IM |
347 | } |
348 | } | |
349 | ||
8953645f | 350 | return -1; |
8326f44d IM |
351 | } |
352 | ||
bcd3279f | 353 | static enum event_result |
61c45981 | 354 | parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) |
8326f44d | 355 | { |
61c45981 PM |
356 | const char *s = *str; |
357 | int cache_type = -1, cache_op = -1, cache_result = -1; | |
8326f44d | 358 | |
61c45981 | 359 | cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX); |
8326f44d IM |
360 | /* |
361 | * No fallback - if we cannot get a clear cache type | |
362 | * then bail out: | |
363 | */ | |
364 | if (cache_type == -1) | |
bcd3279f | 365 | return EVT_FAILED; |
61c45981 PM |
366 | |
367 | while ((cache_op == -1 || cache_result == -1) && *s == '-') { | |
368 | ++s; | |
369 | ||
370 | if (cache_op == -1) { | |
371 | cache_op = parse_aliases(&s, hw_cache_op, | |
372 | PERF_COUNT_HW_CACHE_OP_MAX); | |
373 | if (cache_op >= 0) { | |
374 | if (!is_cache_op_valid(cache_type, cache_op)) | |
375 | return 0; | |
376 | continue; | |
377 | } | |
378 | } | |
379 | ||
380 | if (cache_result == -1) { | |
381 | cache_result = parse_aliases(&s, hw_cache_result, | |
382 | PERF_COUNT_HW_CACHE_RESULT_MAX); | |
383 | if (cache_result >= 0) | |
384 | continue; | |
385 | } | |
386 | ||
387 | /* | |
388 | * Can't parse this as a cache op or result, so back up | |
389 | * to the '-'. | |
390 | */ | |
391 | --s; | |
392 | break; | |
393 | } | |
8326f44d | 394 | |
8326f44d IM |
395 | /* |
396 | * Fall back to reads: | |
397 | */ | |
8953645f IM |
398 | if (cache_op == -1) |
399 | cache_op = PERF_COUNT_HW_CACHE_OP_READ; | |
8326f44d | 400 | |
8326f44d IM |
401 | /* |
402 | * Fall back to accesses: | |
403 | */ | |
404 | if (cache_result == -1) | |
405 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; | |
406 | ||
407 | attr->config = cache_type | (cache_op << 8) | (cache_result << 16); | |
408 | attr->type = PERF_TYPE_HW_CACHE; | |
409 | ||
61c45981 | 410 | *str = s; |
bcd3279f FW |
411 | return EVT_HANDLED; |
412 | } | |
413 | ||
414 | static enum event_result | |
415 | parse_single_tracepoint_event(char *sys_name, | |
416 | const char *evt_name, | |
417 | unsigned int evt_length, | |
418 | char *flags, | |
419 | struct perf_counter_attr *attr, | |
420 | const char **strp) | |
421 | { | |
422 | char evt_path[MAXPATHLEN]; | |
423 | char id_buf[4]; | |
424 | u64 id; | |
425 | int fd; | |
426 | ||
427 | if (flags) { | |
428 | if (!strncmp(flags, "record", strlen(flags))) | |
429 | attr->sample_type |= PERF_SAMPLE_RAW; | |
430 | } | |
431 | ||
432 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | |
433 | sys_name, evt_name); | |
434 | ||
435 | fd = open(evt_path, O_RDONLY); | |
436 | if (fd < 0) | |
437 | return EVT_FAILED; | |
438 | ||
439 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | |
440 | close(fd); | |
441 | return EVT_FAILED; | |
442 | } | |
443 | ||
444 | close(fd); | |
445 | id = atoll(id_buf); | |
446 | attr->config = id; | |
447 | attr->type = PERF_TYPE_TRACEPOINT; | |
448 | *strp = evt_name + evt_length; | |
449 | ||
450 | return EVT_HANDLED; | |
8326f44d IM |
451 | } |
452 | ||
bcd3279f FW |
453 | /* sys + ':' + event + ':' + flags*/ |
454 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | |
455 | static enum event_result | |
456 | parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |
457 | { | |
458 | char evt_path[MAXPATHLEN]; | |
459 | struct dirent *evt_ent; | |
460 | DIR *evt_dir; | |
461 | ||
462 | snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name); | |
463 | evt_dir = opendir(evt_path); | |
464 | ||
465 | if (!evt_dir) { | |
466 | perror("Can't open event dir"); | |
467 | return EVT_FAILED; | |
468 | } | |
469 | ||
470 | while ((evt_ent = readdir(evt_dir))) { | |
471 | char event_opt[MAX_EVOPT_LEN + 1]; | |
472 | int len; | |
473 | unsigned int rem = MAX_EVOPT_LEN; | |
474 | ||
475 | if (!strcmp(evt_ent->d_name, ".") | |
476 | || !strcmp(evt_ent->d_name, "..") | |
477 | || !strcmp(evt_ent->d_name, "enable") | |
478 | || !strcmp(evt_ent->d_name, "filter")) | |
479 | continue; | |
480 | ||
481 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, | |
482 | evt_ent->d_name); | |
483 | if (len < 0) | |
484 | return EVT_FAILED; | |
485 | ||
486 | rem -= len; | |
487 | if (flags) { | |
488 | if (rem < strlen(flags) + 1) | |
489 | return EVT_FAILED; | |
490 | ||
491 | strcat(event_opt, ":"); | |
492 | strcat(event_opt, flags); | |
493 | } | |
494 | ||
495 | if (parse_events(NULL, event_opt, 0)) | |
496 | return EVT_FAILED; | |
497 | } | |
498 | ||
499 | return EVT_HANDLED_ALL; | |
500 | } | |
501 | ||
502 | ||
503 | static enum event_result parse_tracepoint_event(const char **strp, | |
f6bdafef JB |
504 | struct perf_counter_attr *attr) |
505 | { | |
506 | const char *evt_name; | |
3a9f131f | 507 | char *flags; |
f6bdafef | 508 | char sys_name[MAX_EVENT_LENGTH]; |
f6bdafef | 509 | unsigned int sys_length, evt_length; |
f6bdafef | 510 | |
5beeded1 | 511 | if (valid_debugfs_mount(debugfs_path)) |
f6bdafef JB |
512 | return 0; |
513 | ||
514 | evt_name = strchr(*strp, ':'); | |
515 | if (!evt_name) | |
bcd3279f | 516 | return EVT_FAILED; |
f6bdafef JB |
517 | |
518 | sys_length = evt_name - *strp; | |
519 | if (sys_length >= MAX_EVENT_LENGTH) | |
520 | return 0; | |
521 | ||
522 | strncpy(sys_name, *strp, sys_length); | |
523 | sys_name[sys_length] = '\0'; | |
524 | evt_name = evt_name + 1; | |
3a9f131f FW |
525 | |
526 | flags = strchr(evt_name, ':'); | |
527 | if (flags) { | |
528 | *flags = '\0'; | |
529 | flags++; | |
3a9f131f FW |
530 | } |
531 | ||
f6bdafef JB |
532 | evt_length = strlen(evt_name); |
533 | if (evt_length >= MAX_EVENT_LENGTH) | |
bcd3279f | 534 | return EVT_FAILED; |
f6bdafef | 535 | |
bcd3279f FW |
536 | if (!strcmp(evt_name, "*")) { |
537 | *strp = evt_name + evt_length; | |
538 | return parse_subsystem_tracepoint_event(sys_name, flags); | |
539 | } else | |
540 | return parse_single_tracepoint_event(sys_name, evt_name, | |
541 | evt_length, flags, | |
542 | attr, strp); | |
f6bdafef JB |
543 | } |
544 | ||
74d5b588 JSR |
545 | static int check_events(const char *str, unsigned int i) |
546 | { | |
61c45981 | 547 | int n; |
74d5b588 | 548 | |
61c45981 PM |
549 | n = strlen(event_symbols[i].symbol); |
550 | if (!strncmp(str, event_symbols[i].symbol, n)) | |
551 | return n; | |
552 | ||
553 | n = strlen(event_symbols[i].alias); | |
554 | if (n) | |
555 | if (!strncmp(str, event_symbols[i].alias, n)) | |
556 | return n; | |
74d5b588 JSR |
557 | return 0; |
558 | } | |
559 | ||
bcd3279f | 560 | static enum event_result |
61c45981 | 561 | parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) |
8ad8db37 | 562 | { |
61c45981 | 563 | const char *str = *strp; |
8ad8db37 | 564 | unsigned int i; |
61c45981 | 565 | int n; |
8ad8db37 | 566 | |
61c45981 PM |
567 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { |
568 | n = check_events(str, i); | |
569 | if (n > 0) { | |
570 | attr->type = event_symbols[i].type; | |
571 | attr->config = event_symbols[i].config; | |
572 | *strp = str + n; | |
bcd3279f | 573 | return EVT_HANDLED; |
61c45981 PM |
574 | } |
575 | } | |
bcd3279f | 576 | return EVT_FAILED; |
61c45981 PM |
577 | } |
578 | ||
bcd3279f FW |
579 | static enum event_result |
580 | parse_raw_event(const char **strp, struct perf_counter_attr *attr) | |
61c45981 PM |
581 | { |
582 | const char *str = *strp; | |
583 | u64 config; | |
584 | int n; | |
a21ca2ca | 585 | |
61c45981 | 586 | if (*str != 'r') |
bcd3279f | 587 | return EVT_FAILED; |
61c45981 PM |
588 | n = hex2u64(str + 1, &config); |
589 | if (n > 0) { | |
590 | *strp = str + n + 1; | |
591 | attr->type = PERF_TYPE_RAW; | |
592 | attr->config = config; | |
bcd3279f | 593 | return EVT_HANDLED; |
a21ca2ca | 594 | } |
bcd3279f | 595 | return EVT_FAILED; |
61c45981 | 596 | } |
8ad8db37 | 597 | |
bcd3279f | 598 | static enum event_result |
61c45981 PM |
599 | parse_numeric_event(const char **strp, struct perf_counter_attr *attr) |
600 | { | |
601 | const char *str = *strp; | |
602 | char *endp; | |
603 | unsigned long type; | |
604 | u64 config; | |
605 | ||
606 | type = strtoul(str, &endp, 0); | |
607 | if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { | |
608 | str = endp + 1; | |
609 | config = strtoul(str, &endp, 0); | |
610 | if (endp > str) { | |
611 | attr->type = type; | |
612 | attr->config = config; | |
613 | *strp = endp; | |
bcd3279f | 614 | return EVT_HANDLED; |
a0055ae2 | 615 | } |
61c45981 | 616 | } |
bcd3279f | 617 | return EVT_FAILED; |
61c45981 PM |
618 | } |
619 | ||
bcd3279f | 620 | static enum event_result |
61c45981 PM |
621 | parse_event_modifier(const char **strp, struct perf_counter_attr *attr) |
622 | { | |
623 | const char *str = *strp; | |
624 | int eu = 1, ek = 1, eh = 1; | |
a21ca2ca | 625 | |
61c45981 | 626 | if (*str++ != ':') |
a21ca2ca | 627 | return 0; |
61c45981 PM |
628 | while (*str) { |
629 | if (*str == 'u') | |
630 | eu = 0; | |
631 | else if (*str == 'k') | |
632 | ek = 0; | |
633 | else if (*str == 'h') | |
634 | eh = 0; | |
635 | else | |
636 | break; | |
637 | ++str; | |
5242519b | 638 | } |
61c45981 PM |
639 | if (str >= *strp + 2) { |
640 | *strp = str; | |
641 | attr->exclude_user = eu; | |
642 | attr->exclude_kernel = ek; | |
643 | attr->exclude_hv = eh; | |
644 | return 1; | |
645 | } | |
646 | return 0; | |
647 | } | |
8ad8db37 | 648 | |
61c45981 PM |
649 | /* |
650 | * Each event can have multiple symbolic names. | |
651 | * Symbolic names are (almost) exactly matched. | |
652 | */ | |
bcd3279f FW |
653 | static enum event_result |
654 | parse_event_symbols(const char **str, struct perf_counter_attr *attr) | |
61c45981 | 655 | { |
bcd3279f FW |
656 | enum event_result ret; |
657 | ||
658 | ret = parse_tracepoint_event(str, attr); | |
659 | if (ret != EVT_FAILED) | |
660 | goto modifier; | |
661 | ||
662 | ret = parse_raw_event(str, attr); | |
663 | if (ret != EVT_FAILED) | |
664 | goto modifier; | |
a21ca2ca | 665 | |
bcd3279f FW |
666 | ret = parse_numeric_event(str, attr); |
667 | if (ret != EVT_FAILED) | |
668 | goto modifier; | |
669 | ||
670 | ret = parse_symbolic_event(str, attr); | |
671 | if (ret != EVT_FAILED) | |
672 | goto modifier; | |
673 | ||
674 | ret = parse_generic_hw_event(str, attr); | |
675 | if (ret != EVT_FAILED) | |
676 | goto modifier; | |
677 | ||
678 | return EVT_FAILED; | |
679 | ||
680 | modifier: | |
61c45981 | 681 | parse_event_modifier(str, attr); |
8ad8db37 | 682 | |
bcd3279f | 683 | return ret; |
8ad8db37 IM |
684 | } |
685 | ||
f37a291c | 686 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
8ad8db37 | 687 | { |
a21ca2ca | 688 | struct perf_counter_attr attr; |
bcd3279f | 689 | enum event_result ret; |
8ad8db37 | 690 | |
61c45981 PM |
691 | for (;;) { |
692 | if (nr_counters == MAX_COUNTERS) | |
693 | return -1; | |
694 | ||
695 | memset(&attr, 0, sizeof(attr)); | |
bcd3279f FW |
696 | ret = parse_event_symbols(&str, &attr); |
697 | if (ret == EVT_FAILED) | |
61c45981 | 698 | return -1; |
8ad8db37 | 699 | |
61c45981 PM |
700 | if (!(*str == 0 || *str == ',' || isspace(*str))) |
701 | return -1; | |
8ad8db37 | 702 | |
bcd3279f FW |
703 | if (ret != EVT_HANDLED_ALL) { |
704 | attrs[nr_counters] = attr; | |
705 | nr_counters++; | |
706 | } | |
8ad8db37 | 707 | |
61c45981 PM |
708 | if (*str == 0) |
709 | break; | |
710 | if (*str == ',') | |
711 | ++str; | |
712 | while (isspace(*str)) | |
713 | ++str; | |
8ad8db37 IM |
714 | } |
715 | ||
716 | return 0; | |
717 | } | |
718 | ||
86847b62 TG |
719 | static const char * const event_type_descriptors[] = { |
720 | "", | |
721 | "Hardware event", | |
722 | "Software event", | |
723 | "Tracepoint event", | |
724 | "Hardware cache event", | |
725 | }; | |
726 | ||
f6bdafef JB |
727 | /* |
728 | * Print the events from <debugfs_mount_point>/tracing/events | |
729 | */ | |
730 | ||
731 | static void print_tracepoint_events(void) | |
732 | { | |
733 | DIR *sys_dir, *evt_dir; | |
734 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | |
6b58e7f1 | 735 | int sys_dir_fd; |
f6bdafef JB |
736 | char evt_path[MAXPATHLEN]; |
737 | ||
5beeded1 | 738 | if (valid_debugfs_mount(debugfs_path)) |
f6bdafef JB |
739 | return; |
740 | ||
5beeded1 | 741 | sys_dir = opendir(debugfs_path); |
f6bdafef JB |
742 | if (!sys_dir) |
743 | goto cleanup; | |
6b58e7f1 UD |
744 | sys_dir_fd = dirfd(sys_dir); |
745 | ||
746 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { | |
747 | int dfd = openat(sys_dir_fd, sys_dirent.d_name, | |
748 | O_RDONLY|O_DIRECTORY), evt_dir_fd; | |
749 | if (dfd == -1) | |
750 | continue; | |
751 | evt_dir = fdopendir(dfd); | |
752 | if (!evt_dir) { | |
753 | close(dfd); | |
754 | continue; | |
755 | } | |
756 | evt_dir_fd = dirfd(evt_dir); | |
757 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | |
f6bdafef JB |
758 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
759 | sys_dirent.d_name, evt_dirent.d_name); | |
48c2e17f | 760 | fprintf(stderr, " %-42s [%s]\n", evt_path, |
f6bdafef JB |
761 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); |
762 | } | |
763 | closedir(evt_dir); | |
764 | } | |
765 | ||
766 | cleanup: | |
767 | closedir(sys_dir); | |
768 | } | |
769 | ||
8ad8db37 | 770 | /* |
86847b62 | 771 | * Print the help text for the event symbols: |
8ad8db37 | 772 | */ |
86847b62 | 773 | void print_events(void) |
8ad8db37 | 774 | { |
86847b62 | 775 | struct event_symbol *syms = event_symbols; |
73c24cb8 | 776 | unsigned int i, type, op, prev_type = -1; |
74d5b588 | 777 | char name[40]; |
8ad8db37 | 778 | |
86847b62 TG |
779 | fprintf(stderr, "\n"); |
780 | fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); | |
8ad8db37 | 781 | |
86847b62 TG |
782 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
783 | type = syms->type + 1; | |
23cdb5d5 | 784 | if (type >= ARRAY_SIZE(event_type_descriptors)) |
86847b62 | 785 | type = 0; |
8ad8db37 | 786 | |
86847b62 TG |
787 | if (type != prev_type) |
788 | fprintf(stderr, "\n"); | |
8ad8db37 | 789 | |
74d5b588 JSR |
790 | if (strlen(syms->alias)) |
791 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | |
792 | else | |
793 | strcpy(name, syms->symbol); | |
48c2e17f | 794 | fprintf(stderr, " %-42s [%s]\n", name, |
86847b62 | 795 | event_type_descriptors[type]); |
8ad8db37 | 796 | |
86847b62 | 797 | prev_type = type; |
8ad8db37 IM |
798 | } |
799 | ||
73c24cb8 JSR |
800 | fprintf(stderr, "\n"); |
801 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | |
802 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | |
803 | /* skip invalid cache type */ | |
804 | if (!is_cache_op_valid(type, op)) | |
805 | continue; | |
806 | ||
807 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | |
48c2e17f | 808 | fprintf(stderr, " %-42s [%s]\n", |
73c24cb8 JSR |
809 | event_cache_name(type, op, i), |
810 | event_type_descriptors[4]); | |
811 | } | |
812 | } | |
813 | } | |
814 | ||
86847b62 | 815 | fprintf(stderr, "\n"); |
48c2e17f | 816 | fprintf(stderr, " %-42s [raw hardware event descriptor]\n", |
86847b62 TG |
817 | "rNNN"); |
818 | fprintf(stderr, "\n"); | |
819 | ||
f6bdafef JB |
820 | print_tracepoint_events(); |
821 | ||
86847b62 | 822 | exit(129); |
8ad8db37 | 823 | } |