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