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 | 147 | |
270bbbe8 | 148 | #define MAX_EVENT_LENGTH 512 |
f6bdafef | 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) { | |
1281a49b | 428 | if (!strncmp(flags, "record", strlen(flags))) { |
bcd3279f | 429 | attr->sample_type |= PERF_SAMPLE_RAW; |
1281a49b LZ |
430 | attr->sample_type |= PERF_SAMPLE_TIME; |
431 | attr->sample_type |= PERF_SAMPLE_CPU; | |
432 | } | |
bcd3279f FW |
433 | } |
434 | ||
435 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | |
436 | sys_name, evt_name); | |
437 | ||
438 | fd = open(evt_path, O_RDONLY); | |
439 | if (fd < 0) | |
440 | return EVT_FAILED; | |
441 | ||
442 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | |
443 | close(fd); | |
444 | return EVT_FAILED; | |
445 | } | |
446 | ||
447 | close(fd); | |
448 | id = atoll(id_buf); | |
449 | attr->config = id; | |
450 | attr->type = PERF_TYPE_TRACEPOINT; | |
451 | *strp = evt_name + evt_length; | |
452 | ||
453 | return EVT_HANDLED; | |
8326f44d IM |
454 | } |
455 | ||
bcd3279f FW |
456 | /* sys + ':' + event + ':' + flags*/ |
457 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | |
458 | static enum event_result | |
459 | parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |
460 | { | |
461 | char evt_path[MAXPATHLEN]; | |
462 | struct dirent *evt_ent; | |
463 | DIR *evt_dir; | |
464 | ||
465 | snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name); | |
466 | evt_dir = opendir(evt_path); | |
467 | ||
468 | if (!evt_dir) { | |
469 | perror("Can't open event dir"); | |
470 | return EVT_FAILED; | |
471 | } | |
472 | ||
473 | while ((evt_ent = readdir(evt_dir))) { | |
474 | char event_opt[MAX_EVOPT_LEN + 1]; | |
475 | int len; | |
476 | unsigned int rem = MAX_EVOPT_LEN; | |
477 | ||
478 | if (!strcmp(evt_ent->d_name, ".") | |
479 | || !strcmp(evt_ent->d_name, "..") | |
480 | || !strcmp(evt_ent->d_name, "enable") | |
481 | || !strcmp(evt_ent->d_name, "filter")) | |
482 | continue; | |
483 | ||
484 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, | |
485 | evt_ent->d_name); | |
486 | if (len < 0) | |
487 | return EVT_FAILED; | |
488 | ||
489 | rem -= len; | |
490 | if (flags) { | |
491 | if (rem < strlen(flags) + 1) | |
492 | return EVT_FAILED; | |
493 | ||
494 | strcat(event_opt, ":"); | |
495 | strcat(event_opt, flags); | |
496 | } | |
497 | ||
498 | if (parse_events(NULL, event_opt, 0)) | |
499 | return EVT_FAILED; | |
500 | } | |
501 | ||
502 | return EVT_HANDLED_ALL; | |
503 | } | |
504 | ||
505 | ||
506 | static enum event_result parse_tracepoint_event(const char **strp, | |
f6bdafef JB |
507 | struct perf_counter_attr *attr) |
508 | { | |
509 | const char *evt_name; | |
3a9f131f | 510 | char *flags; |
f6bdafef | 511 | char sys_name[MAX_EVENT_LENGTH]; |
f6bdafef | 512 | unsigned int sys_length, evt_length; |
f6bdafef | 513 | |
5beeded1 | 514 | if (valid_debugfs_mount(debugfs_path)) |
f6bdafef JB |
515 | return 0; |
516 | ||
517 | evt_name = strchr(*strp, ':'); | |
518 | if (!evt_name) | |
bcd3279f | 519 | return EVT_FAILED; |
f6bdafef JB |
520 | |
521 | sys_length = evt_name - *strp; | |
522 | if (sys_length >= MAX_EVENT_LENGTH) | |
523 | return 0; | |
524 | ||
525 | strncpy(sys_name, *strp, sys_length); | |
526 | sys_name[sys_length] = '\0'; | |
527 | evt_name = evt_name + 1; | |
3a9f131f FW |
528 | |
529 | flags = strchr(evt_name, ':'); | |
530 | if (flags) { | |
1fc35b29 IM |
531 | /* split it out: */ |
532 | evt_name = strndup(evt_name, flags - evt_name); | |
3a9f131f | 533 | flags++; |
3a9f131f FW |
534 | } |
535 | ||
f6bdafef JB |
536 | evt_length = strlen(evt_name); |
537 | if (evt_length >= MAX_EVENT_LENGTH) | |
bcd3279f | 538 | return EVT_FAILED; |
f6bdafef | 539 | |
bcd3279f FW |
540 | if (!strcmp(evt_name, "*")) { |
541 | *strp = evt_name + evt_length; | |
542 | return parse_subsystem_tracepoint_event(sys_name, flags); | |
543 | } else | |
544 | return parse_single_tracepoint_event(sys_name, evt_name, | |
545 | evt_length, flags, | |
546 | attr, strp); | |
f6bdafef JB |
547 | } |
548 | ||
74d5b588 JSR |
549 | static int check_events(const char *str, unsigned int i) |
550 | { | |
61c45981 | 551 | int n; |
74d5b588 | 552 | |
61c45981 PM |
553 | n = strlen(event_symbols[i].symbol); |
554 | if (!strncmp(str, event_symbols[i].symbol, n)) | |
555 | return n; | |
556 | ||
557 | n = strlen(event_symbols[i].alias); | |
558 | if (n) | |
559 | if (!strncmp(str, event_symbols[i].alias, n)) | |
560 | return n; | |
74d5b588 JSR |
561 | return 0; |
562 | } | |
563 | ||
bcd3279f | 564 | static enum event_result |
61c45981 | 565 | parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) |
8ad8db37 | 566 | { |
61c45981 | 567 | const char *str = *strp; |
8ad8db37 | 568 | unsigned int i; |
61c45981 | 569 | int n; |
8ad8db37 | 570 | |
61c45981 PM |
571 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { |
572 | n = check_events(str, i); | |
573 | if (n > 0) { | |
574 | attr->type = event_symbols[i].type; | |
575 | attr->config = event_symbols[i].config; | |
576 | *strp = str + n; | |
bcd3279f | 577 | return EVT_HANDLED; |
61c45981 PM |
578 | } |
579 | } | |
bcd3279f | 580 | return EVT_FAILED; |
61c45981 PM |
581 | } |
582 | ||
bcd3279f FW |
583 | static enum event_result |
584 | parse_raw_event(const char **strp, struct perf_counter_attr *attr) | |
61c45981 PM |
585 | { |
586 | const char *str = *strp; | |
587 | u64 config; | |
588 | int n; | |
a21ca2ca | 589 | |
61c45981 | 590 | if (*str != 'r') |
bcd3279f | 591 | return EVT_FAILED; |
61c45981 PM |
592 | n = hex2u64(str + 1, &config); |
593 | if (n > 0) { | |
594 | *strp = str + n + 1; | |
595 | attr->type = PERF_TYPE_RAW; | |
596 | attr->config = config; | |
bcd3279f | 597 | return EVT_HANDLED; |
a21ca2ca | 598 | } |
bcd3279f | 599 | return EVT_FAILED; |
61c45981 | 600 | } |
8ad8db37 | 601 | |
bcd3279f | 602 | static enum event_result |
61c45981 PM |
603 | parse_numeric_event(const char **strp, struct perf_counter_attr *attr) |
604 | { | |
605 | const char *str = *strp; | |
606 | char *endp; | |
607 | unsigned long type; | |
608 | u64 config; | |
609 | ||
610 | type = strtoul(str, &endp, 0); | |
611 | if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { | |
612 | str = endp + 1; | |
613 | config = strtoul(str, &endp, 0); | |
614 | if (endp > str) { | |
615 | attr->type = type; | |
616 | attr->config = config; | |
617 | *strp = endp; | |
bcd3279f | 618 | return EVT_HANDLED; |
a0055ae2 | 619 | } |
61c45981 | 620 | } |
bcd3279f | 621 | return EVT_FAILED; |
61c45981 PM |
622 | } |
623 | ||
bcd3279f | 624 | static enum event_result |
61c45981 PM |
625 | parse_event_modifier(const char **strp, struct perf_counter_attr *attr) |
626 | { | |
627 | const char *str = *strp; | |
628 | int eu = 1, ek = 1, eh = 1; | |
a21ca2ca | 629 | |
61c45981 | 630 | if (*str++ != ':') |
a21ca2ca | 631 | return 0; |
61c45981 PM |
632 | while (*str) { |
633 | if (*str == 'u') | |
634 | eu = 0; | |
635 | else if (*str == 'k') | |
636 | ek = 0; | |
637 | else if (*str == 'h') | |
638 | eh = 0; | |
639 | else | |
640 | break; | |
641 | ++str; | |
5242519b | 642 | } |
61c45981 PM |
643 | if (str >= *strp + 2) { |
644 | *strp = str; | |
645 | attr->exclude_user = eu; | |
646 | attr->exclude_kernel = ek; | |
647 | attr->exclude_hv = eh; | |
648 | return 1; | |
649 | } | |
650 | return 0; | |
651 | } | |
8ad8db37 | 652 | |
61c45981 PM |
653 | /* |
654 | * Each event can have multiple symbolic names. | |
655 | * Symbolic names are (almost) exactly matched. | |
656 | */ | |
bcd3279f FW |
657 | static enum event_result |
658 | parse_event_symbols(const char **str, struct perf_counter_attr *attr) | |
61c45981 | 659 | { |
bcd3279f FW |
660 | enum event_result ret; |
661 | ||
662 | ret = parse_tracepoint_event(str, attr); | |
663 | if (ret != EVT_FAILED) | |
664 | goto modifier; | |
665 | ||
666 | ret = parse_raw_event(str, attr); | |
667 | if (ret != EVT_FAILED) | |
668 | goto modifier; | |
a21ca2ca | 669 | |
bcd3279f FW |
670 | ret = parse_numeric_event(str, attr); |
671 | if (ret != EVT_FAILED) | |
672 | goto modifier; | |
673 | ||
674 | ret = parse_symbolic_event(str, attr); | |
675 | if (ret != EVT_FAILED) | |
676 | goto modifier; | |
677 | ||
678 | ret = parse_generic_hw_event(str, attr); | |
679 | if (ret != EVT_FAILED) | |
680 | goto modifier; | |
681 | ||
682 | return EVT_FAILED; | |
683 | ||
684 | modifier: | |
61c45981 | 685 | parse_event_modifier(str, attr); |
8ad8db37 | 686 | |
bcd3279f | 687 | return ret; |
8ad8db37 IM |
688 | } |
689 | ||
f37a291c | 690 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
8ad8db37 | 691 | { |
a21ca2ca | 692 | struct perf_counter_attr attr; |
bcd3279f | 693 | enum event_result ret; |
8ad8db37 | 694 | |
61c45981 PM |
695 | for (;;) { |
696 | if (nr_counters == MAX_COUNTERS) | |
697 | return -1; | |
698 | ||
699 | memset(&attr, 0, sizeof(attr)); | |
bcd3279f FW |
700 | ret = parse_event_symbols(&str, &attr); |
701 | if (ret == EVT_FAILED) | |
61c45981 | 702 | return -1; |
8ad8db37 | 703 | |
61c45981 PM |
704 | if (!(*str == 0 || *str == ',' || isspace(*str))) |
705 | return -1; | |
8ad8db37 | 706 | |
bcd3279f FW |
707 | if (ret != EVT_HANDLED_ALL) { |
708 | attrs[nr_counters] = attr; | |
709 | nr_counters++; | |
710 | } | |
8ad8db37 | 711 | |
61c45981 PM |
712 | if (*str == 0) |
713 | break; | |
714 | if (*str == ',') | |
715 | ++str; | |
716 | while (isspace(*str)) | |
717 | ++str; | |
8ad8db37 IM |
718 | } |
719 | ||
720 | return 0; | |
721 | } | |
722 | ||
86847b62 TG |
723 | static const char * const event_type_descriptors[] = { |
724 | "", | |
725 | "Hardware event", | |
726 | "Software event", | |
727 | "Tracepoint event", | |
728 | "Hardware cache event", | |
729 | }; | |
730 | ||
f6bdafef JB |
731 | /* |
732 | * Print the events from <debugfs_mount_point>/tracing/events | |
733 | */ | |
734 | ||
735 | static void print_tracepoint_events(void) | |
736 | { | |
737 | DIR *sys_dir, *evt_dir; | |
738 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | |
6b58e7f1 | 739 | int sys_dir_fd; |
f6bdafef JB |
740 | char evt_path[MAXPATHLEN]; |
741 | ||
5beeded1 | 742 | if (valid_debugfs_mount(debugfs_path)) |
f6bdafef JB |
743 | return; |
744 | ||
5beeded1 | 745 | sys_dir = opendir(debugfs_path); |
f6bdafef JB |
746 | if (!sys_dir) |
747 | goto cleanup; | |
6b58e7f1 UD |
748 | sys_dir_fd = dirfd(sys_dir); |
749 | ||
750 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { | |
751 | int dfd = openat(sys_dir_fd, sys_dirent.d_name, | |
752 | O_RDONLY|O_DIRECTORY), evt_dir_fd; | |
753 | if (dfd == -1) | |
754 | continue; | |
755 | evt_dir = fdopendir(dfd); | |
756 | if (!evt_dir) { | |
757 | close(dfd); | |
758 | continue; | |
759 | } | |
760 | evt_dir_fd = dirfd(evt_dir); | |
761 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | |
f6bdafef JB |
762 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
763 | sys_dirent.d_name, evt_dirent.d_name); | |
48c2e17f | 764 | fprintf(stderr, " %-42s [%s]\n", evt_path, |
f6bdafef JB |
765 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); |
766 | } | |
767 | closedir(evt_dir); | |
768 | } | |
769 | ||
770 | cleanup: | |
771 | closedir(sys_dir); | |
772 | } | |
773 | ||
8ad8db37 | 774 | /* |
86847b62 | 775 | * Print the help text for the event symbols: |
8ad8db37 | 776 | */ |
86847b62 | 777 | void print_events(void) |
8ad8db37 | 778 | { |
86847b62 | 779 | struct event_symbol *syms = event_symbols; |
73c24cb8 | 780 | unsigned int i, type, op, prev_type = -1; |
74d5b588 | 781 | char name[40]; |
8ad8db37 | 782 | |
86847b62 TG |
783 | fprintf(stderr, "\n"); |
784 | fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); | |
8ad8db37 | 785 | |
86847b62 TG |
786 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
787 | type = syms->type + 1; | |
23cdb5d5 | 788 | if (type >= ARRAY_SIZE(event_type_descriptors)) |
86847b62 | 789 | type = 0; |
8ad8db37 | 790 | |
86847b62 TG |
791 | if (type != prev_type) |
792 | fprintf(stderr, "\n"); | |
8ad8db37 | 793 | |
74d5b588 JSR |
794 | if (strlen(syms->alias)) |
795 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | |
796 | else | |
797 | strcpy(name, syms->symbol); | |
48c2e17f | 798 | fprintf(stderr, " %-42s [%s]\n", name, |
86847b62 | 799 | event_type_descriptors[type]); |
8ad8db37 | 800 | |
86847b62 | 801 | prev_type = type; |
8ad8db37 IM |
802 | } |
803 | ||
73c24cb8 JSR |
804 | fprintf(stderr, "\n"); |
805 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | |
806 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | |
807 | /* skip invalid cache type */ | |
808 | if (!is_cache_op_valid(type, op)) | |
809 | continue; | |
810 | ||
811 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | |
48c2e17f | 812 | fprintf(stderr, " %-42s [%s]\n", |
73c24cb8 JSR |
813 | event_cache_name(type, op, i), |
814 | event_type_descriptors[4]); | |
815 | } | |
816 | } | |
817 | } | |
818 | ||
86847b62 | 819 | fprintf(stderr, "\n"); |
48c2e17f | 820 | fprintf(stderr, " %-42s [raw hardware event descriptor]\n", |
86847b62 TG |
821 | "rNNN"); |
822 | fprintf(stderr, "\n"); | |
823 | ||
f6bdafef JB |
824 | print_tracepoint_events(); |
825 | ||
86847b62 | 826 | exit(129); |
8ad8db37 | 827 | } |