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