diff options
author | Jiri Olsa <jolsa@redhat.com> | 2012-03-15 15:09:15 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-03-16 13:20:21 -0400 |
commit | 89812fc81f8d62d70433a8ff63d26819f372e8ec (patch) | |
tree | 8d2c6ad6eee1200f5107fa8063a002f415887ba3 /tools/perf/util/parse-events.c | |
parent | 641cc938815dfd09f8fa1ec72deb814f0938ac33 (diff) |
perf tools: Add parser generator for events parsing
Changing event parsing to use flex/bison parse generator.
The event syntax stays as it was.
grammar description:
events: events ',' event | event
event: event_def PE_MODIFIER_EVENT | event_def
event_def: event_legacy_symbol sep_dc |
event_legacy_cache sep_dc |
event_legacy_breakpoint sep_dc |
event_legacy_tracepoint sep_dc |
event_legacy_numeric sep_dc |
event_legacy_raw sep_dc
event_legacy_symbol: PE_NAME_SYM
event_legacy_cache: PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT |
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT |
PE_NAME_CACHE_TYPE
event_legacy_raw: PE_SEP_RAW PE_VALUE
event_legacy_numeric: PE_VALUE ':' PE_VALUE
event_legacy_breakpoint: PE_SEP_BP ':' PE_VALUE ':' PE_MODIFIER_BP
event_breakpoint_type: PE_MODIFIER_BPTYPE | empty
PE_NAME_SYM: cpu-cycles|cycles |
stalled-cycles-frontend|idle-cycles-frontend |
stalled-cycles-backend|idle-cycles-backend |
instructions |
cache-references |
cache-misses |
branch-instructions|branches |
branch-misses |
bus-cycles |
cpu-clock |
task-clock |
page-faults|faults |
minor-faults |
major-faults |
context-switches|cs |
cpu-migrations|migrations |
alignment-faults |
emulation-faults
PE_NAME_CACHE_TYPE: L1-dcache|l1-d|l1d|L1-data |
L1-icache|l1-i|l1i|L1-instruction |
LLC|L2 |
dTLB|d-tlb|Data-TLB |
iTLB|i-tlb|Instruction-TLB |
branch|branches|bpu|btb|bpc |
node
PE_NAME_CACHE_OP_RESULT: load|loads|read |
store|stores|write |
prefetch|prefetches |
speculative-read|speculative-load |
refs|Reference|ops|access |
misses|miss
PE_MODIFIER_EVENT: [ukhp]{0,5}
PE_MODIFIER_BP: [rwx]
PE_SEP_BP: 'mem'
PE_SEP_RAW: 'r'
sep_dc: ':' |
Added flex/bison files for event grammar parsing. The generated
parser is part of the patch. Added makefile rule 'event-parser'
to generate the parser code out of the bison/flex sources.
Acked-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/n/tip-u4pfig5waq3ll2bfcdex8fgi@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util/parse-events.c')
-rw-r--r-- | tools/perf/util/parse-events.c | 486 |
1 files changed, 148 insertions, 338 deletions
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b029296d20d9..6e50b914cad4 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -11,6 +11,9 @@ | |||
11 | #include "cache.h" | 11 | #include "cache.h" |
12 | #include "header.h" | 12 | #include "header.h" |
13 | #include "debugfs.h" | 13 | #include "debugfs.h" |
14 | #include "parse-events-flex.h" | ||
15 | |||
16 | #define MAX_NAME_LEN 100 | ||
14 | 17 | ||
15 | struct event_symbol { | 18 | struct event_symbol { |
16 | u8 type; | 19 | u8 type; |
@@ -19,11 +22,7 @@ struct event_symbol { | |||
19 | const char *alias; | 22 | const char *alias; |
20 | }; | 23 | }; |
21 | 24 | ||
22 | enum event_result { | 25 | int parse_events_parse(struct list_head *list, int *idx); |
23 | EVT_FAILED, | ||
24 | EVT_HANDLED, | ||
25 | EVT_HANDLED_ALL | ||
26 | }; | ||
27 | 26 | ||
28 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x | 27 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
29 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | 28 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x |
@@ -354,7 +353,24 @@ const char *__event_name(int type, u64 config) | |||
354 | return "unknown"; | 353 | return "unknown"; |
355 | } | 354 | } |
356 | 355 | ||
357 | static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size) | 356 | static int add_event(struct list_head *list, int *idx, |
357 | struct perf_event_attr *attr, char *name) | ||
358 | { | ||
359 | struct perf_evsel *evsel; | ||
360 | |||
361 | event_attr_init(attr); | ||
362 | |||
363 | evsel = perf_evsel__new(attr, (*idx)++); | ||
364 | if (!evsel) | ||
365 | return -ENOMEM; | ||
366 | |||
367 | list_add_tail(&evsel->node, list); | ||
368 | |||
369 | evsel->name = strdup(name); | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size) | ||
358 | { | 374 | { |
359 | int i, j; | 375 | int i, j; |
360 | int n, longest = -1; | 376 | int n, longest = -1; |
@@ -362,58 +378,57 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int | |||
362 | for (i = 0; i < size; i++) { | 378 | for (i = 0; i < size; i++) { |
363 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { | 379 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { |
364 | n = strlen(names[i][j]); | 380 | n = strlen(names[i][j]); |
365 | if (n > longest && !strncasecmp(*str, names[i][j], n)) | 381 | if (n > longest && !strncasecmp(str, names[i][j], n)) |
366 | longest = n; | 382 | longest = n; |
367 | } | 383 | } |
368 | if (longest > 0) { | 384 | if (longest > 0) |
369 | *str += longest; | ||
370 | return i; | 385 | return i; |
371 | } | ||
372 | } | 386 | } |
373 | 387 | ||
374 | return -1; | 388 | return -1; |
375 | } | 389 | } |
376 | 390 | ||
377 | static enum event_result | 391 | int parse_events_add_cache(struct list_head *list, int *idx, |
378 | parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | 392 | char *type, char *op_result1, char *op_result2) |
379 | { | 393 | { |
380 | const char *s = *str; | 394 | struct perf_event_attr attr; |
395 | char name[MAX_NAME_LEN]; | ||
381 | int cache_type = -1, cache_op = -1, cache_result = -1; | 396 | int cache_type = -1, cache_op = -1, cache_result = -1; |
397 | char *op_result[2] = { op_result1, op_result2 }; | ||
398 | int i, n; | ||
382 | 399 | ||
383 | cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX); | ||
384 | /* | 400 | /* |
385 | * No fallback - if we cannot get a clear cache type | 401 | * No fallback - if we cannot get a clear cache type |
386 | * then bail out: | 402 | * then bail out: |
387 | */ | 403 | */ |
404 | cache_type = parse_aliases(type, hw_cache, | ||
405 | PERF_COUNT_HW_CACHE_MAX); | ||
388 | if (cache_type == -1) | 406 | if (cache_type == -1) |
389 | return EVT_FAILED; | 407 | return -EINVAL; |
408 | |||
409 | n = snprintf(name, MAX_NAME_LEN, "%s", type); | ||
390 | 410 | ||
391 | while ((cache_op == -1 || cache_result == -1) && *s == '-') { | 411 | for (i = 0; (i < 2) && (op_result[i]); i++) { |
392 | ++s; | 412 | char *str = op_result[i]; |
413 | |||
414 | snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str); | ||
393 | 415 | ||
394 | if (cache_op == -1) { | 416 | if (cache_op == -1) { |
395 | cache_op = parse_aliases(&s, hw_cache_op, | 417 | cache_op = parse_aliases(str, hw_cache_op, |
396 | PERF_COUNT_HW_CACHE_OP_MAX); | 418 | PERF_COUNT_HW_CACHE_OP_MAX); |
397 | if (cache_op >= 0) { | 419 | if (cache_op >= 0) { |
398 | if (!is_cache_op_valid(cache_type, cache_op)) | 420 | if (!is_cache_op_valid(cache_type, cache_op)) |
399 | return EVT_FAILED; | 421 | return -EINVAL; |
400 | continue; | 422 | continue; |
401 | } | 423 | } |
402 | } | 424 | } |
403 | 425 | ||
404 | if (cache_result == -1) { | 426 | if (cache_result == -1) { |
405 | cache_result = parse_aliases(&s, hw_cache_result, | 427 | cache_result = parse_aliases(str, hw_cache_result, |
406 | PERF_COUNT_HW_CACHE_RESULT_MAX); | 428 | PERF_COUNT_HW_CACHE_RESULT_MAX); |
407 | if (cache_result >= 0) | 429 | if (cache_result >= 0) |
408 | continue; | 430 | continue; |
409 | } | 431 | } |
410 | |||
411 | /* | ||
412 | * Can't parse this as a cache op or result, so back up | ||
413 | * to the '-'. | ||
414 | */ | ||
415 | --s; | ||
416 | break; | ||
417 | } | 432 | } |
418 | 433 | ||
419 | /* | 434 | /* |
@@ -428,20 +443,17 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | |||
428 | if (cache_result == -1) | 443 | if (cache_result == -1) |
429 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; | 444 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; |
430 | 445 | ||
431 | attr->config = cache_type | (cache_op << 8) | (cache_result << 16); | 446 | memset(&attr, 0, sizeof(attr)); |
432 | attr->type = PERF_TYPE_HW_CACHE; | 447 | attr.config = cache_type | (cache_op << 8) | (cache_result << 16); |
433 | 448 | attr.type = PERF_TYPE_HW_CACHE; | |
434 | *str = s; | 449 | return add_event(list, idx, &attr, name); |
435 | return EVT_HANDLED; | ||
436 | } | 450 | } |
437 | 451 | ||
438 | static enum event_result | 452 | static int add_tracepoint(struct list_head *list, int *idx, |
439 | parse_single_tracepoint_event(char *sys_name, | 453 | char *sys_name, char *evt_name) |
440 | const char *evt_name, | ||
441 | unsigned int evt_length, | ||
442 | struct perf_event_attr *attr, | ||
443 | const char **strp) | ||
444 | { | 454 | { |
455 | struct perf_event_attr attr; | ||
456 | char name[MAX_NAME_LEN]; | ||
445 | char evt_path[MAXPATHLEN]; | 457 | char evt_path[MAXPATHLEN]; |
446 | char id_buf[4]; | 458 | char id_buf[4]; |
447 | u64 id; | 459 | u64 id; |
@@ -452,130 +464,80 @@ parse_single_tracepoint_event(char *sys_name, | |||
452 | 464 | ||
453 | fd = open(evt_path, O_RDONLY); | 465 | fd = open(evt_path, O_RDONLY); |
454 | if (fd < 0) | 466 | if (fd < 0) |
455 | return EVT_FAILED; | 467 | return -1; |
456 | 468 | ||
457 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | 469 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { |
458 | close(fd); | 470 | close(fd); |
459 | return EVT_FAILED; | 471 | return -1; |
460 | } | 472 | } |
461 | 473 | ||
462 | close(fd); | 474 | close(fd); |
463 | id = atoll(id_buf); | 475 | id = atoll(id_buf); |
464 | attr->config = id; | ||
465 | attr->type = PERF_TYPE_TRACEPOINT; | ||
466 | *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */ | ||
467 | 476 | ||
468 | attr->sample_type |= PERF_SAMPLE_RAW; | 477 | memset(&attr, 0, sizeof(attr)); |
469 | attr->sample_type |= PERF_SAMPLE_TIME; | 478 | attr.config = id; |
470 | attr->sample_type |= PERF_SAMPLE_CPU; | 479 | attr.type = PERF_TYPE_TRACEPOINT; |
480 | attr.sample_type |= PERF_SAMPLE_RAW; | ||
481 | attr.sample_type |= PERF_SAMPLE_TIME; | ||
482 | attr.sample_type |= PERF_SAMPLE_CPU; | ||
483 | attr.sample_period = 1; | ||
471 | 484 | ||
472 | attr->sample_period = 1; | 485 | snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name); |
473 | 486 | return add_event(list, idx, &attr, name); | |
474 | |||
475 | return EVT_HANDLED; | ||
476 | } | 487 | } |
477 | 488 | ||
478 | /* sys + ':' + event + ':' + flags*/ | 489 | static int add_tracepoint_multi(struct list_head *list, int *idx, |
479 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 490 | char *sys_name, char *evt_name) |
480 | static enum event_result | ||
481 | parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name, | ||
482 | const char *evt_exp, char *flags) | ||
483 | { | 491 | { |
484 | char evt_path[MAXPATHLEN]; | 492 | char evt_path[MAXPATHLEN]; |
485 | struct dirent *evt_ent; | 493 | struct dirent *evt_ent; |
486 | DIR *evt_dir; | 494 | DIR *evt_dir; |
495 | int ret = 0; | ||
487 | 496 | ||
488 | snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); | 497 | snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); |
489 | evt_dir = opendir(evt_path); | 498 | evt_dir = opendir(evt_path); |
490 | |||
491 | if (!evt_dir) { | 499 | if (!evt_dir) { |
492 | perror("Can't open event dir"); | 500 | perror("Can't open event dir"); |
493 | return EVT_FAILED; | 501 | return -1; |
494 | } | 502 | } |
495 | 503 | ||
496 | while ((evt_ent = readdir(evt_dir))) { | 504 | while (!ret && (evt_ent = readdir(evt_dir))) { |
497 | char event_opt[MAX_EVOPT_LEN + 1]; | ||
498 | int len; | ||
499 | |||
500 | if (!strcmp(evt_ent->d_name, ".") | 505 | if (!strcmp(evt_ent->d_name, ".") |
501 | || !strcmp(evt_ent->d_name, "..") | 506 | || !strcmp(evt_ent->d_name, "..") |
502 | || !strcmp(evt_ent->d_name, "enable") | 507 | || !strcmp(evt_ent->d_name, "enable") |
503 | || !strcmp(evt_ent->d_name, "filter")) | 508 | || !strcmp(evt_ent->d_name, "filter")) |
504 | continue; | 509 | continue; |
505 | 510 | ||
506 | if (!strglobmatch(evt_ent->d_name, evt_exp)) | 511 | if (!strglobmatch(evt_ent->d_name, evt_name)) |
507 | continue; | 512 | continue; |
508 | 513 | ||
509 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, | 514 | ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name); |
510 | evt_ent->d_name, flags ? ":" : "", | ||
511 | flags ?: ""); | ||
512 | if (len < 0) | ||
513 | return EVT_FAILED; | ||
514 | |||
515 | if (parse_events(evlist, event_opt, 0)) | ||
516 | return EVT_FAILED; | ||
517 | } | 515 | } |
518 | 516 | ||
519 | return EVT_HANDLED_ALL; | 517 | return ret; |
520 | } | 518 | } |
521 | 519 | ||
522 | static enum event_result | 520 | int parse_events_add_tracepoint(struct list_head *list, int *idx, |
523 | parse_tracepoint_event(struct perf_evlist *evlist, const char **strp, | 521 | char *sys, char *event) |
524 | struct perf_event_attr *attr) | ||
525 | { | 522 | { |
526 | const char *evt_name; | 523 | int ret; |
527 | char *flags = NULL, *comma_loc; | ||
528 | char sys_name[MAX_EVENT_LENGTH]; | ||
529 | unsigned int sys_length, evt_length; | ||
530 | |||
531 | if (debugfs_valid_mountpoint(tracing_events_path)) | ||
532 | return 0; | ||
533 | 524 | ||
534 | evt_name = strchr(*strp, ':'); | 525 | ret = debugfs_valid_mountpoint(tracing_events_path); |
535 | if (!evt_name) | 526 | if (ret) |
536 | return EVT_FAILED; | 527 | return ret; |
537 | 528 | ||
538 | sys_length = evt_name - *strp; | 529 | return strpbrk(event, "*?") ? |
539 | if (sys_length >= MAX_EVENT_LENGTH) | 530 | add_tracepoint_multi(list, idx, sys, event) : |
540 | return 0; | 531 | add_tracepoint(list, idx, sys, event); |
541 | |||
542 | strncpy(sys_name, *strp, sys_length); | ||
543 | sys_name[sys_length] = '\0'; | ||
544 | evt_name = evt_name + 1; | ||
545 | |||
546 | comma_loc = strchr(evt_name, ','); | ||
547 | if (comma_loc) { | ||
548 | /* take the event name up to the comma */ | ||
549 | evt_name = strndup(evt_name, comma_loc - evt_name); | ||
550 | } | ||
551 | flags = strchr(evt_name, ':'); | ||
552 | if (flags) { | ||
553 | /* split it out: */ | ||
554 | evt_name = strndup(evt_name, flags - evt_name); | ||
555 | flags++; | ||
556 | } | ||
557 | |||
558 | evt_length = strlen(evt_name); | ||
559 | if (evt_length >= MAX_EVENT_LENGTH) | ||
560 | return EVT_FAILED; | ||
561 | if (strpbrk(evt_name, "*?")) { | ||
562 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | ||
563 | return parse_multiple_tracepoint_event(evlist, sys_name, | ||
564 | evt_name, flags); | ||
565 | } else { | ||
566 | return parse_single_tracepoint_event(sys_name, evt_name, | ||
567 | evt_length, attr, strp); | ||
568 | } | ||
569 | } | 532 | } |
570 | 533 | ||
571 | static enum event_result | 534 | static int |
572 | parse_breakpoint_type(const char *type, const char **strp, | 535 | parse_breakpoint_type(const char *type, struct perf_event_attr *attr) |
573 | struct perf_event_attr *attr) | ||
574 | { | 536 | { |
575 | int i; | 537 | int i; |
576 | 538 | ||
577 | for (i = 0; i < 3; i++) { | 539 | for (i = 0; i < 3; i++) { |
578 | if (!type[i]) | 540 | if (!type || !type[i]) |
579 | break; | 541 | break; |
580 | 542 | ||
581 | switch (type[i]) { | 543 | switch (type[i]) { |
@@ -589,164 +551,65 @@ parse_breakpoint_type(const char *type, const char **strp, | |||
589 | attr->bp_type |= HW_BREAKPOINT_X; | 551 | attr->bp_type |= HW_BREAKPOINT_X; |
590 | break; | 552 | break; |
591 | default: | 553 | default: |
592 | return EVT_FAILED; | 554 | return -EINVAL; |
593 | } | 555 | } |
594 | } | 556 | } |
557 | |||
595 | if (!attr->bp_type) /* Default */ | 558 | if (!attr->bp_type) /* Default */ |
596 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | 559 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; |
597 | 560 | ||
598 | *strp = type + i; | 561 | return 0; |
599 | |||
600 | return EVT_HANDLED; | ||
601 | } | 562 | } |
602 | 563 | ||
603 | static enum event_result | 564 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
604 | parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) | 565 | void *ptr, char *type) |
605 | { | 566 | { |
606 | const char *target; | 567 | struct perf_event_attr attr; |
607 | const char *type; | 568 | char name[MAX_NAME_LEN]; |
608 | char *endaddr; | ||
609 | u64 addr; | ||
610 | enum event_result err; | ||
611 | |||
612 | target = strchr(*strp, ':'); | ||
613 | if (!target) | ||
614 | return EVT_FAILED; | ||
615 | |||
616 | if (strncmp(*strp, "mem", target - *strp) != 0) | ||
617 | return EVT_FAILED; | ||
618 | |||
619 | target++; | ||
620 | |||
621 | addr = strtoull(target, &endaddr, 0); | ||
622 | if (target == endaddr) | ||
623 | return EVT_FAILED; | ||
624 | |||
625 | attr->bp_addr = addr; | ||
626 | *strp = endaddr; | ||
627 | 569 | ||
628 | type = strchr(target, ':'); | 570 | memset(&attr, 0, sizeof(attr)); |
571 | attr.bp_addr = (u64) ptr; | ||
629 | 572 | ||
630 | /* If no type is defined, just rw as default */ | 573 | if (parse_breakpoint_type(type, &attr)) |
631 | if (!type) { | 574 | return -EINVAL; |
632 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
633 | } else { | ||
634 | err = parse_breakpoint_type(++type, strp, attr); | ||
635 | if (err == EVT_FAILED) | ||
636 | return EVT_FAILED; | ||
637 | } | ||
638 | 575 | ||
639 | /* | 576 | /* |
640 | * We should find a nice way to override the access length | 577 | * We should find a nice way to override the access length |
641 | * Provide some defaults for now | 578 | * Provide some defaults for now |
642 | */ | 579 | */ |
643 | if (attr->bp_type == HW_BREAKPOINT_X) | 580 | if (attr.bp_type == HW_BREAKPOINT_X) |
644 | attr->bp_len = sizeof(long); | 581 | attr.bp_len = sizeof(long); |
645 | else | 582 | else |
646 | attr->bp_len = HW_BREAKPOINT_LEN_4; | 583 | attr.bp_len = HW_BREAKPOINT_LEN_4; |
647 | |||
648 | attr->type = PERF_TYPE_BREAKPOINT; | ||
649 | |||
650 | return EVT_HANDLED; | ||
651 | } | ||
652 | |||
653 | static int check_events(const char *str, unsigned int i) | ||
654 | { | ||
655 | int n; | ||
656 | |||
657 | n = strlen(event_symbols[i].symbol); | ||
658 | if (!strncasecmp(str, event_symbols[i].symbol, n)) | ||
659 | return n; | ||
660 | 584 | ||
661 | n = strlen(event_symbols[i].alias); | 585 | attr.type = PERF_TYPE_BREAKPOINT; |
662 | if (n) { | ||
663 | if (!strncasecmp(str, event_symbols[i].alias, n)) | ||
664 | return n; | ||
665 | } | ||
666 | 586 | ||
667 | return 0; | 587 | snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw"); |
588 | return add_event(list, idx, &attr, name); | ||
668 | } | 589 | } |
669 | 590 | ||
670 | static enum event_result | 591 | int |
671 | parse_symbolic_event(const char **strp, struct perf_event_attr *attr) | 592 | parse_events_add_numeric(struct list_head *list, int *idx, |
593 | unsigned long type, unsigned long config) | ||
672 | { | 594 | { |
673 | const char *str = *strp; | 595 | struct perf_event_attr attr; |
674 | unsigned int i; | ||
675 | int n; | ||
676 | |||
677 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | ||
678 | n = check_events(str, i); | ||
679 | if (n > 0) { | ||
680 | attr->type = event_symbols[i].type; | ||
681 | attr->config = event_symbols[i].config; | ||
682 | *strp = str + n; | ||
683 | return EVT_HANDLED; | ||
684 | } | ||
685 | } | ||
686 | return EVT_FAILED; | ||
687 | } | ||
688 | 596 | ||
689 | static enum event_result | 597 | memset(&attr, 0, sizeof(attr)); |
690 | parse_raw_event(const char **strp, struct perf_event_attr *attr) | 598 | attr.type = type; |
691 | { | 599 | attr.config = config; |
692 | const char *str = *strp; | 600 | return add_event(list, idx, &attr, |
693 | u64 config; | 601 | (char *) __event_name(type, config)); |
694 | int n; | ||
695 | |||
696 | if (*str != 'r') | ||
697 | return EVT_FAILED; | ||
698 | n = hex2u64(str + 1, &config); | ||
699 | if (n > 0) { | ||
700 | const char *end = str + n + 1; | ||
701 | if (*end != '\0' && *end != ',' && *end != ':') | ||
702 | return EVT_FAILED; | ||
703 | |||
704 | *strp = end; | ||
705 | attr->type = PERF_TYPE_RAW; | ||
706 | attr->config = config; | ||
707 | return EVT_HANDLED; | ||
708 | } | ||
709 | return EVT_FAILED; | ||
710 | } | 602 | } |
711 | 603 | ||
712 | static enum event_result | 604 | int parse_events_modifier(struct list_head *list, char *str) |
713 | parse_numeric_event(const char **strp, struct perf_event_attr *attr) | ||
714 | { | 605 | { |
715 | const char *str = *strp; | 606 | struct perf_evsel *evsel; |
716 | char *endp; | ||
717 | unsigned long type; | ||
718 | u64 config; | ||
719 | |||
720 | type = strtoul(str, &endp, 0); | ||
721 | if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { | ||
722 | str = endp + 1; | ||
723 | config = strtoul(str, &endp, 0); | ||
724 | if (endp > str) { | ||
725 | attr->type = type; | ||
726 | attr->config = config; | ||
727 | *strp = endp; | ||
728 | return EVT_HANDLED; | ||
729 | } | ||
730 | } | ||
731 | return EVT_FAILED; | ||
732 | } | ||
733 | |||
734 | static int | ||
735 | parse_event_modifier(const char **strp, struct perf_event_attr *attr) | ||
736 | { | ||
737 | const char *str = *strp; | ||
738 | int exclude = 0, exclude_GH = 0; | 607 | int exclude = 0, exclude_GH = 0; |
739 | int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0; | 608 | int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0; |
740 | 609 | ||
741 | if (!*str) | 610 | if (str == NULL) |
742 | return 0; | 611 | return 0; |
743 | 612 | ||
744 | if (*str == ',') | ||
745 | return 0; | ||
746 | |||
747 | if (*str++ != ':') | ||
748 | return -1; | ||
749 | |||
750 | while (*str) { | 613 | while (*str) { |
751 | if (*str == 'u') { | 614 | if (*str == 'u') { |
752 | if (!exclude) | 615 | if (!exclude) |
@@ -775,111 +638,60 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
775 | 638 | ||
776 | ++str; | 639 | ++str; |
777 | } | 640 | } |
778 | if (str < *strp + 2) | ||
779 | return -1; | ||
780 | 641 | ||
781 | *strp = str; | 642 | /* |
643 | * precise ip: | ||
644 | * | ||
645 | * 0 - SAMPLE_IP can have arbitrary skid | ||
646 | * 1 - SAMPLE_IP must have constant skid | ||
647 | * 2 - SAMPLE_IP requested to have 0 skid | ||
648 | * 3 - SAMPLE_IP must have 0 skid | ||
649 | * | ||
650 | * See also PERF_RECORD_MISC_EXACT_IP | ||
651 | */ | ||
652 | if (precise > 3) | ||
653 | return -EINVAL; | ||
782 | 654 | ||
783 | attr->exclude_user = eu; | 655 | list_for_each_entry(evsel, list, node) { |
784 | attr->exclude_kernel = ek; | 656 | evsel->attr.exclude_user = eu; |
785 | attr->exclude_hv = eh; | 657 | evsel->attr.exclude_kernel = ek; |
786 | attr->precise_ip = precise; | 658 | evsel->attr.exclude_hv = eh; |
787 | attr->exclude_host = eH; | 659 | evsel->attr.precise_ip = precise; |
788 | attr->exclude_guest = eG; | 660 | evsel->attr.exclude_host = eH; |
661 | evsel->attr.exclude_guest = eG; | ||
662 | } | ||
789 | 663 | ||
790 | return 0; | 664 | return 0; |
791 | } | 665 | } |
792 | 666 | ||
793 | /* | 667 | int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) |
794 | * Each event can have multiple symbolic names. | ||
795 | * Symbolic names are (almost) exactly matched. | ||
796 | */ | ||
797 | static enum event_result | ||
798 | parse_event_symbols(struct perf_evlist *evlist, const char **str, | ||
799 | struct perf_event_attr *attr) | ||
800 | { | 668 | { |
801 | enum event_result ret; | 669 | struct perf_evsel *evsel, *h; |
802 | 670 | LIST_HEAD(list); | |
803 | ret = parse_tracepoint_event(evlist, str, attr); | 671 | YY_BUFFER_STATE buffer; |
804 | if (ret != EVT_FAILED) | 672 | int ret, idx = evlist->nr_entries; |
805 | goto modifier; | ||
806 | 673 | ||
807 | ret = parse_raw_event(str, attr); | 674 | buffer = parse_events__scan_string(str); |
808 | if (ret != EVT_FAILED) | ||
809 | goto modifier; | ||
810 | 675 | ||
811 | ret = parse_numeric_event(str, attr); | 676 | ret = parse_events_parse(&list, &idx); |
812 | if (ret != EVT_FAILED) | ||
813 | goto modifier; | ||
814 | 677 | ||
815 | ret = parse_symbolic_event(str, attr); | 678 | parse_events__flush_buffer(buffer); |
816 | if (ret != EVT_FAILED) | 679 | parse_events__delete_buffer(buffer); |
817 | goto modifier; | ||
818 | 680 | ||
819 | ret = parse_generic_hw_event(str, attr); | 681 | if (!ret) { |
820 | if (ret != EVT_FAILED) | 682 | int entries = idx - evlist->nr_entries; |
821 | goto modifier; | 683 | perf_evlist__splice_list_tail(evlist, &list, entries); |
684 | return 0; | ||
685 | } | ||
822 | 686 | ||
823 | ret = parse_breakpoint_event(str, attr); | 687 | list_for_each_entry_safe(evsel, h, &list, node) |
824 | if (ret != EVT_FAILED) | 688 | perf_evsel__delete(evsel); |
825 | goto modifier; | ||
826 | 689 | ||
827 | fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); | 690 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); |
828 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | 691 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); |
829 | return EVT_FAILED; | ||
830 | |||
831 | modifier: | ||
832 | if (parse_event_modifier(str, attr) < 0) { | ||
833 | fprintf(stderr, "invalid event modifier: '%s'\n", *str); | ||
834 | fprintf(stderr, "Run 'perf list' for a list of valid events and modifiers\n"); | ||
835 | |||
836 | return EVT_FAILED; | ||
837 | } | ||
838 | |||
839 | return ret; | 692 | return ret; |
840 | } | 693 | } |
841 | 694 | ||
842 | int parse_events(struct perf_evlist *evlist , const char *str, int unset __used) | ||
843 | { | ||
844 | struct perf_event_attr attr; | ||
845 | enum event_result ret; | ||
846 | const char *ostr; | ||
847 | |||
848 | for (;;) { | ||
849 | ostr = str; | ||
850 | memset(&attr, 0, sizeof(attr)); | ||
851 | event_attr_init(&attr); | ||
852 | ret = parse_event_symbols(evlist, &str, &attr); | ||
853 | if (ret == EVT_FAILED) | ||
854 | return -1; | ||
855 | |||
856 | if (!(*str == 0 || *str == ',' || isspace(*str))) | ||
857 | return -1; | ||
858 | |||
859 | if (ret != EVT_HANDLED_ALL) { | ||
860 | struct perf_evsel *evsel; | ||
861 | evsel = perf_evsel__new(&attr, evlist->nr_entries); | ||
862 | if (evsel == NULL) | ||
863 | return -1; | ||
864 | perf_evlist__add(evlist, evsel); | ||
865 | |||
866 | evsel->name = calloc(str - ostr + 1, 1); | ||
867 | if (!evsel->name) | ||
868 | return -1; | ||
869 | strncpy(evsel->name, ostr, str - ostr); | ||
870 | } | ||
871 | |||
872 | if (*str == 0) | ||
873 | break; | ||
874 | if (*str == ',') | ||
875 | ++str; | ||
876 | while (isspace(*str)) | ||
877 | ++str; | ||
878 | } | ||
879 | |||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | int parse_events_option(const struct option *opt, const char *str, | 695 | int parse_events_option(const struct option *opt, const char *str, |
884 | int unset __used) | 696 | int unset __used) |
885 | { | 697 | { |
@@ -1052,8 +864,6 @@ int print_hwcache_events(const char *event_glob) | |||
1052 | return printed; | 864 | return printed; |
1053 | } | 865 | } |
1054 | 866 | ||
1055 | #define MAX_NAME_LEN 100 | ||
1056 | |||
1057 | /* | 867 | /* |
1058 | * Print the help text for the event symbols: | 868 | * Print the help text for the event symbols: |
1059 | */ | 869 | */ |