diff options
Diffstat (limited to 'tools/perf/util/parse-events.c')
-rw-r--r-- | tools/perf/util/parse-events.c | 214 |
1 files changed, 158 insertions, 56 deletions
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 8cfb48cbbea0..05d0c5c2030c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -1,4 +1,4 @@ | |||
1 | 1 | #include "../../../include/linux/hw_breakpoint.h" | |
2 | #include "util.h" | 2 | #include "util.h" |
3 | #include "../perf.h" | 3 | #include "../perf.h" |
4 | #include "parse-options.h" | 4 | #include "parse-options.h" |
@@ -7,10 +7,12 @@ | |||
7 | #include "string.h" | 7 | #include "string.h" |
8 | #include "cache.h" | 8 | #include "cache.h" |
9 | #include "header.h" | 9 | #include "header.h" |
10 | #include "debugfs.h" | ||
10 | 11 | ||
11 | int nr_counters; | 12 | int nr_counters; |
12 | 13 | ||
13 | struct perf_event_attr attrs[MAX_COUNTERS]; | 14 | struct perf_event_attr attrs[MAX_COUNTERS]; |
15 | char *filters[MAX_COUNTERS]; | ||
14 | 16 | ||
15 | struct event_symbol { | 17 | struct event_symbol { |
16 | u8 type; | 18 | u8 type; |
@@ -46,6 +48,8 @@ static struct event_symbol event_symbols[] = { | |||
46 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, | 48 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, |
47 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, | 49 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, |
48 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, | 50 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, |
51 | { CSW(ALIGNMENT_FAULTS), "alignment-faults", "" }, | ||
52 | { CSW(EMULATION_FAULTS), "emulation-faults", "" }, | ||
49 | }; | 53 | }; |
50 | 54 | ||
51 | #define __PERF_EVENT_FIELD(config, name) \ | 55 | #define __PERF_EVENT_FIELD(config, name) \ |
@@ -74,6 +78,8 @@ static const char *sw_event_names[] = { | |||
74 | "CPU-migrations", | 78 | "CPU-migrations", |
75 | "minor-faults", | 79 | "minor-faults", |
76 | "major-faults", | 80 | "major-faults", |
81 | "alignment-faults", | ||
82 | "emulation-faults", | ||
77 | }; | 83 | }; |
78 | 84 | ||
79 | #define MAX_ALIASES 8 | 85 | #define MAX_ALIASES 8 |
@@ -148,16 +154,6 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) | |||
148 | 154 | ||
149 | #define MAX_EVENT_LENGTH 512 | 155 | #define MAX_EVENT_LENGTH 512 |
150 | 156 | ||
151 | int valid_debugfs_mount(const char *debugfs) | ||
152 | { | ||
153 | struct statfs st_fs; | ||
154 | |||
155 | if (statfs(debugfs, &st_fs) < 0) | ||
156 | return -ENOENT; | ||
157 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
158 | return -ENOENT; | ||
159 | return 0; | ||
160 | } | ||
161 | 157 | ||
162 | struct tracepoint_path *tracepoint_id_to_path(u64 config) | 158 | struct tracepoint_path *tracepoint_id_to_path(u64 config) |
163 | { | 159 | { |
@@ -170,7 +166,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
170 | char evt_path[MAXPATHLEN]; | 166 | char evt_path[MAXPATHLEN]; |
171 | char dir_path[MAXPATHLEN]; | 167 | char dir_path[MAXPATHLEN]; |
172 | 168 | ||
173 | if (valid_debugfs_mount(debugfs_path)) | 169 | if (debugfs_valid_mountpoint(debugfs_path)) |
174 | return NULL; | 170 | return NULL; |
175 | 171 | ||
176 | sys_dir = opendir(debugfs_path); | 172 | sys_dir = opendir(debugfs_path); |
@@ -201,7 +197,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
201 | if (id == config) { | 197 | if (id == config) { |
202 | closedir(evt_dir); | 198 | closedir(evt_dir); |
203 | closedir(sys_dir); | 199 | closedir(sys_dir); |
204 | path = calloc(1, sizeof(path)); | 200 | path = zalloc(sizeof(*path)); |
205 | path->system = malloc(MAX_EVENT_LENGTH); | 201 | path->system = malloc(MAX_EVENT_LENGTH); |
206 | if (!path->system) { | 202 | if (!path->system) { |
207 | free(path); | 203 | free(path); |
@@ -454,7 +450,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
454 | /* sys + ':' + event + ':' + flags*/ | 450 | /* sys + ':' + event + ':' + flags*/ |
455 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 451 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
456 | static enum event_result | 452 | static enum event_result |
457 | parse_subsystem_tracepoint_event(char *sys_name, char *flags) | 453 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, |
454 | char *flags) | ||
458 | { | 455 | { |
459 | char evt_path[MAXPATHLEN]; | 456 | char evt_path[MAXPATHLEN]; |
460 | struct dirent *evt_ent; | 457 | struct dirent *evt_ent; |
@@ -471,7 +468,6 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
471 | while ((evt_ent = readdir(evt_dir))) { | 468 | while ((evt_ent = readdir(evt_dir))) { |
472 | char event_opt[MAX_EVOPT_LEN + 1]; | 469 | char event_opt[MAX_EVOPT_LEN + 1]; |
473 | int len; | 470 | int len; |
474 | unsigned int rem = MAX_EVOPT_LEN; | ||
475 | 471 | ||
476 | if (!strcmp(evt_ent->d_name, ".") | 472 | if (!strcmp(evt_ent->d_name, ".") |
477 | || !strcmp(evt_ent->d_name, "..") | 473 | || !strcmp(evt_ent->d_name, "..") |
@@ -479,20 +475,15 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
479 | || !strcmp(evt_ent->d_name, "filter")) | 475 | || !strcmp(evt_ent->d_name, "filter")) |
480 | continue; | 476 | continue; |
481 | 477 | ||
482 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, | 478 | if (!strglobmatch(evt_ent->d_name, evt_exp)) |
483 | evt_ent->d_name); | 479 | continue; |
480 | |||
481 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, | ||
482 | evt_ent->d_name, flags ? ":" : "", | ||
483 | flags ?: ""); | ||
484 | if (len < 0) | 484 | if (len < 0) |
485 | return EVT_FAILED; | 485 | return EVT_FAILED; |
486 | 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)) | 487 | if (parse_events(NULL, event_opt, 0)) |
497 | return EVT_FAILED; | 488 | return EVT_FAILED; |
498 | } | 489 | } |
@@ -509,7 +500,7 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
509 | char sys_name[MAX_EVENT_LENGTH]; | 500 | char sys_name[MAX_EVENT_LENGTH]; |
510 | unsigned int sys_length, evt_length; | 501 | unsigned int sys_length, evt_length; |
511 | 502 | ||
512 | if (valid_debugfs_mount(debugfs_path)) | 503 | if (debugfs_valid_mountpoint(debugfs_path)) |
513 | return 0; | 504 | return 0; |
514 | 505 | ||
515 | evt_name = strchr(*strp, ':'); | 506 | evt_name = strchr(*strp, ':'); |
@@ -535,15 +526,91 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
535 | if (evt_length >= MAX_EVENT_LENGTH) | 526 | if (evt_length >= MAX_EVENT_LENGTH) |
536 | return EVT_FAILED; | 527 | return EVT_FAILED; |
537 | 528 | ||
538 | if (!strcmp(evt_name, "*")) { | 529 | if (strpbrk(evt_name, "*?")) { |
539 | *strp = evt_name + evt_length; | 530 | *strp = evt_name + evt_length; |
540 | return parse_subsystem_tracepoint_event(sys_name, flags); | 531 | return parse_multiple_tracepoint_event(sys_name, evt_name, |
532 | flags); | ||
541 | } else | 533 | } else |
542 | return parse_single_tracepoint_event(sys_name, evt_name, | 534 | return parse_single_tracepoint_event(sys_name, evt_name, |
543 | evt_length, flags, | 535 | evt_length, flags, |
544 | attr, strp); | 536 | attr, strp); |
545 | } | 537 | } |
546 | 538 | ||
539 | static enum event_result | ||
540 | parse_breakpoint_type(const char *type, const char **strp, | ||
541 | struct perf_event_attr *attr) | ||
542 | { | ||
543 | int i; | ||
544 | |||
545 | for (i = 0; i < 3; i++) { | ||
546 | if (!type[i]) | ||
547 | break; | ||
548 | |||
549 | switch (type[i]) { | ||
550 | case 'r': | ||
551 | attr->bp_type |= HW_BREAKPOINT_R; | ||
552 | break; | ||
553 | case 'w': | ||
554 | attr->bp_type |= HW_BREAKPOINT_W; | ||
555 | break; | ||
556 | case 'x': | ||
557 | attr->bp_type |= HW_BREAKPOINT_X; | ||
558 | break; | ||
559 | default: | ||
560 | return EVT_FAILED; | ||
561 | } | ||
562 | } | ||
563 | if (!attr->bp_type) /* Default */ | ||
564 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
565 | |||
566 | *strp = type + i; | ||
567 | |||
568 | return EVT_HANDLED; | ||
569 | } | ||
570 | |||
571 | static enum event_result | ||
572 | parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) | ||
573 | { | ||
574 | const char *target; | ||
575 | const char *type; | ||
576 | char *endaddr; | ||
577 | u64 addr; | ||
578 | enum event_result err; | ||
579 | |||
580 | target = strchr(*strp, ':'); | ||
581 | if (!target) | ||
582 | return EVT_FAILED; | ||
583 | |||
584 | if (strncmp(*strp, "mem", target - *strp) != 0) | ||
585 | return EVT_FAILED; | ||
586 | |||
587 | target++; | ||
588 | |||
589 | addr = strtoull(target, &endaddr, 0); | ||
590 | if (target == endaddr) | ||
591 | return EVT_FAILED; | ||
592 | |||
593 | attr->bp_addr = addr; | ||
594 | *strp = endaddr; | ||
595 | |||
596 | type = strchr(target, ':'); | ||
597 | |||
598 | /* If no type is defined, just rw as default */ | ||
599 | if (!type) { | ||
600 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
601 | } else { | ||
602 | err = parse_breakpoint_type(++type, strp, attr); | ||
603 | if (err == EVT_FAILED) | ||
604 | return EVT_FAILED; | ||
605 | } | ||
606 | |||
607 | /* We should find a nice way to override the access type */ | ||
608 | attr->bp_len = HW_BREAKPOINT_LEN_4; | ||
609 | attr->type = PERF_TYPE_BREAKPOINT; | ||
610 | |||
611 | return EVT_HANDLED; | ||
612 | } | ||
613 | |||
547 | static int check_events(const char *str, unsigned int i) | 614 | static int check_events(const char *str, unsigned int i) |
548 | { | 615 | { |
549 | int n; | 616 | int n; |
@@ -677,6 +744,12 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr) | |||
677 | if (ret != EVT_FAILED) | 744 | if (ret != EVT_FAILED) |
678 | goto modifier; | 745 | goto modifier; |
679 | 746 | ||
747 | ret = parse_breakpoint_event(str, attr); | ||
748 | if (ret != EVT_FAILED) | ||
749 | goto modifier; | ||
750 | |||
751 | fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); | ||
752 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
680 | return EVT_FAILED; | 753 | return EVT_FAILED; |
681 | 754 | ||
682 | modifier: | 755 | modifier: |
@@ -685,11 +758,11 @@ modifier: | |||
685 | return ret; | 758 | return ret; |
686 | } | 759 | } |
687 | 760 | ||
688 | static void store_event_type(const char *orgname) | 761 | static int store_event_type(const char *orgname) |
689 | { | 762 | { |
690 | char filename[PATH_MAX], *c; | 763 | char filename[PATH_MAX], *c; |
691 | FILE *file; | 764 | FILE *file; |
692 | int id; | 765 | int id, n; |
693 | 766 | ||
694 | sprintf(filename, "%s/", debugfs_path); | 767 | sprintf(filename, "%s/", debugfs_path); |
695 | strncat(filename, orgname, strlen(orgname)); | 768 | strncat(filename, orgname, strlen(orgname)); |
@@ -701,21 +774,24 @@ static void store_event_type(const char *orgname) | |||
701 | 774 | ||
702 | file = fopen(filename, "r"); | 775 | file = fopen(filename, "r"); |
703 | if (!file) | 776 | if (!file) |
704 | return; | 777 | return 0; |
705 | if (fscanf(file, "%i", &id) < 1) | 778 | n = fscanf(file, "%i", &id); |
706 | die("cannot store event ID"); | ||
707 | fclose(file); | 779 | fclose(file); |
708 | perf_header__push_event(id, orgname); | 780 | if (n < 1) { |
781 | pr_err("cannot store event ID\n"); | ||
782 | return -EINVAL; | ||
783 | } | ||
784 | return perf_header__push_event(id, orgname); | ||
709 | } | 785 | } |
710 | 786 | ||
711 | |||
712 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 787 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
713 | { | 788 | { |
714 | struct perf_event_attr attr; | 789 | struct perf_event_attr attr; |
715 | enum event_result ret; | 790 | enum event_result ret; |
716 | 791 | ||
717 | if (strchr(str, ':')) | 792 | if (strchr(str, ':')) |
718 | store_event_type(str); | 793 | if (store_event_type(str) < 0) |
794 | return -1; | ||
719 | 795 | ||
720 | for (;;) { | 796 | for (;;) { |
721 | if (nr_counters == MAX_COUNTERS) | 797 | if (nr_counters == MAX_COUNTERS) |
@@ -745,12 +821,35 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
745 | return 0; | 821 | return 0; |
746 | } | 822 | } |
747 | 823 | ||
824 | int parse_filter(const struct option *opt __used, const char *str, | ||
825 | int unset __used) | ||
826 | { | ||
827 | int i = nr_counters - 1; | ||
828 | int len = strlen(str); | ||
829 | |||
830 | if (i < 0 || attrs[i].type != PERF_TYPE_TRACEPOINT) { | ||
831 | fprintf(stderr, | ||
832 | "-F option should follow a -e tracepoint option\n"); | ||
833 | return -1; | ||
834 | } | ||
835 | |||
836 | filters[i] = malloc(len + 1); | ||
837 | if (!filters[i]) { | ||
838 | fprintf(stderr, "not enough memory to hold filter string\n"); | ||
839 | return -1; | ||
840 | } | ||
841 | strcpy(filters[i], str); | ||
842 | |||
843 | return 0; | ||
844 | } | ||
845 | |||
748 | static const char * const event_type_descriptors[] = { | 846 | static const char * const event_type_descriptors[] = { |
749 | "", | ||
750 | "Hardware event", | 847 | "Hardware event", |
751 | "Software event", | 848 | "Software event", |
752 | "Tracepoint event", | 849 | "Tracepoint event", |
753 | "Hardware cache event", | 850 | "Hardware cache event", |
851 | "Raw hardware event descriptor", | ||
852 | "Hardware breakpoint", | ||
754 | }; | 853 | }; |
755 | 854 | ||
756 | /* | 855 | /* |
@@ -764,7 +863,7 @@ static void print_tracepoint_events(void) | |||
764 | char evt_path[MAXPATHLEN]; | 863 | char evt_path[MAXPATHLEN]; |
765 | char dir_path[MAXPATHLEN]; | 864 | char dir_path[MAXPATHLEN]; |
766 | 865 | ||
767 | if (valid_debugfs_mount(debugfs_path)) | 866 | if (debugfs_valid_mountpoint(debugfs_path)) |
768 | return; | 867 | return; |
769 | 868 | ||
770 | sys_dir = opendir(debugfs_path); | 869 | sys_dir = opendir(debugfs_path); |
@@ -782,8 +881,8 @@ static void print_tracepoint_events(void) | |||
782 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | 881 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { |
783 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 882 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
784 | sys_dirent.d_name, evt_dirent.d_name); | 883 | sys_dirent.d_name, evt_dirent.d_name); |
785 | fprintf(stderr, " %-42s [%s]\n", evt_path, | 884 | printf(" %-42s [%s]\n", evt_path, |
786 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | 885 | event_type_descriptors[PERF_TYPE_TRACEPOINT]); |
787 | } | 886 | } |
788 | closedir(evt_dir); | 887 | closedir(evt_dir); |
789 | } | 888 | } |
@@ -799,28 +898,26 @@ void print_events(void) | |||
799 | unsigned int i, type, op, prev_type = -1; | 898 | unsigned int i, type, op, prev_type = -1; |
800 | char name[40]; | 899 | char name[40]; |
801 | 900 | ||
802 | fprintf(stderr, "\n"); | 901 | printf("\n"); |
803 | fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); | 902 | printf("List of pre-defined events (to be used in -e):\n"); |
804 | 903 | ||
805 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 904 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
806 | type = syms->type + 1; | 905 | type = syms->type; |
807 | if (type >= ARRAY_SIZE(event_type_descriptors)) | ||
808 | type = 0; | ||
809 | 906 | ||
810 | if (type != prev_type) | 907 | if (type != prev_type) |
811 | fprintf(stderr, "\n"); | 908 | printf("\n"); |
812 | 909 | ||
813 | if (strlen(syms->alias)) | 910 | if (strlen(syms->alias)) |
814 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | 911 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); |
815 | else | 912 | else |
816 | strcpy(name, syms->symbol); | 913 | strcpy(name, syms->symbol); |
817 | fprintf(stderr, " %-42s [%s]\n", name, | 914 | printf(" %-42s [%s]\n", name, |
818 | event_type_descriptors[type]); | 915 | event_type_descriptors[type]); |
819 | 916 | ||
820 | prev_type = type; | 917 | prev_type = type; |
821 | } | 918 | } |
822 | 919 | ||
823 | fprintf(stderr, "\n"); | 920 | printf("\n"); |
824 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | 921 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { |
825 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | 922 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { |
826 | /* skip invalid cache type */ | 923 | /* skip invalid cache type */ |
@@ -828,17 +925,22 @@ void print_events(void) | |||
828 | continue; | 925 | continue; |
829 | 926 | ||
830 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 927 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
831 | fprintf(stderr, " %-42s [%s]\n", | 928 | printf(" %-42s [%s]\n", |
832 | event_cache_name(type, op, i), | 929 | event_cache_name(type, op, i), |
833 | event_type_descriptors[4]); | 930 | event_type_descriptors[PERF_TYPE_HW_CACHE]); |
834 | } | 931 | } |
835 | } | 932 | } |
836 | } | 933 | } |
837 | 934 | ||
838 | fprintf(stderr, "\n"); | 935 | printf("\n"); |
839 | fprintf(stderr, " %-42s [raw hardware event descriptor]\n", | 936 | printf(" %-42s [%s]\n", |
840 | "rNNN"); | 937 | "rNNN", event_type_descriptors[PERF_TYPE_RAW]); |
841 | fprintf(stderr, "\n"); | 938 | printf("\n"); |
939 | |||
940 | printf(" %-42s [%s]\n", | ||
941 | "mem:<addr>[:access]", | ||
942 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | ||
943 | printf("\n"); | ||
842 | 944 | ||
843 | print_tracepoint_events(); | 945 | print_tracepoint_events(); |
844 | 946 | ||