Commit dcb4e1022b40d886027500821a592dd8f8ccde8f
Committed by
Arnaldo Carvalho de Melo
1 parent
42f60c2d63
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
perf tools: Parse the pmu event prefix and suffix
There are two types of event formats for PMU events. E.g. el-abort OR cpu/el-abort/. However, the lexer mistakenly recognizes the simple style format as two events. The parse_events_pmu_check function uses bsearch to search the name in known pmu event list. It can tell the lexer that the name is a PE_NAME or a PMU event name prefix or a PMU event name suffix. All these information will be used for accurately parsing kernel PMU events. The pmu events list will be read from sysfs at runtime. Note: Currently, the patch only want to handle the PMU event name as "a-b" and "a". The only exception, "stalled-cycles-frontend" and "stalled-cycles-fronted", are already hardcoded in lexer. Signed-off-by: Kan Liang <kan.liang@intel.com> Acked-by: Jiri Olsa <jolsa@redhat.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Jiri Olsa <jolsa@redhat.com> Link: http://lkml.kernel.org/r/1412694532-23391-3-git-send-email-kan.liang@intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Showing 4 changed files with 141 additions and 10 deletions Side-by-side Diff
tools/perf/util/parse-events.c
... | ... | @@ -30,6 +30,15 @@ |
30 | 30 | #endif |
31 | 31 | int parse_events_parse(void *data, void *scanner); |
32 | 32 | |
33 | +static struct perf_pmu_event_symbol *perf_pmu_events_list; | |
34 | +/* | |
35 | + * The variable indicates the number of supported pmu event symbols. | |
36 | + * 0 means not initialized and ready to init | |
37 | + * -1 means failed to init, don't try anymore | |
38 | + * >0 is the number of supported pmu event symbols | |
39 | + */ | |
40 | +static int perf_pmu_events_list_num; | |
41 | + | |
33 | 42 | static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { |
34 | 43 | [PERF_COUNT_HW_CPU_CYCLES] = { |
35 | 44 | .symbol = "cpu-cycles", |
... | ... | @@ -863,6 +872,113 @@ |
863 | 872 | return 0; |
864 | 873 | } |
865 | 874 | |
875 | +static int | |
876 | +comp_pmu(const void *p1, const void *p2) | |
877 | +{ | |
878 | + struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; | |
879 | + struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; | |
880 | + | |
881 | + return strcmp(pmu1->symbol, pmu2->symbol); | |
882 | +} | |
883 | + | |
884 | +static void perf_pmu__parse_cleanup(void) | |
885 | +{ | |
886 | + if (perf_pmu_events_list_num > 0) { | |
887 | + struct perf_pmu_event_symbol *p; | |
888 | + int i; | |
889 | + | |
890 | + for (i = 0; i < perf_pmu_events_list_num; i++) { | |
891 | + p = perf_pmu_events_list + i; | |
892 | + free(p->symbol); | |
893 | + } | |
894 | + free(perf_pmu_events_list); | |
895 | + perf_pmu_events_list = NULL; | |
896 | + perf_pmu_events_list_num = 0; | |
897 | + } | |
898 | +} | |
899 | + | |
900 | +#define SET_SYMBOL(str, stype) \ | |
901 | +do { \ | |
902 | + p->symbol = str; \ | |
903 | + if (!p->symbol) \ | |
904 | + goto err; \ | |
905 | + p->type = stype; \ | |
906 | +} while (0) | |
907 | + | |
908 | +/* | |
909 | + * Read the pmu events list from sysfs | |
910 | + * Save it into perf_pmu_events_list | |
911 | + */ | |
912 | +static void perf_pmu__parse_init(void) | |
913 | +{ | |
914 | + | |
915 | + struct perf_pmu *pmu = NULL; | |
916 | + struct perf_pmu_alias *alias; | |
917 | + int len = 0; | |
918 | + | |
919 | + pmu = perf_pmu__find("cpu"); | |
920 | + if ((pmu == NULL) || list_empty(&pmu->aliases)) { | |
921 | + perf_pmu_events_list_num = -1; | |
922 | + return; | |
923 | + } | |
924 | + list_for_each_entry(alias, &pmu->aliases, list) { | |
925 | + if (strchr(alias->name, '-')) | |
926 | + len++; | |
927 | + len++; | |
928 | + } | |
929 | + perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len); | |
930 | + if (!perf_pmu_events_list) | |
931 | + return; | |
932 | + perf_pmu_events_list_num = len; | |
933 | + | |
934 | + len = 0; | |
935 | + list_for_each_entry(alias, &pmu->aliases, list) { | |
936 | + struct perf_pmu_event_symbol *p = perf_pmu_events_list + len; | |
937 | + char *tmp = strchr(alias->name, '-'); | |
938 | + | |
939 | + if (tmp != NULL) { | |
940 | + SET_SYMBOL(strndup(alias->name, tmp - alias->name), | |
941 | + PMU_EVENT_SYMBOL_PREFIX); | |
942 | + p++; | |
943 | + SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX); | |
944 | + len += 2; | |
945 | + } else { | |
946 | + SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL); | |
947 | + len++; | |
948 | + } | |
949 | + } | |
950 | + qsort(perf_pmu_events_list, len, | |
951 | + sizeof(struct perf_pmu_event_symbol), comp_pmu); | |
952 | + | |
953 | + return; | |
954 | +err: | |
955 | + perf_pmu__parse_cleanup(); | |
956 | +} | |
957 | + | |
958 | +enum perf_pmu_event_symbol_type | |
959 | +perf_pmu__parse_check(const char *name) | |
960 | +{ | |
961 | + struct perf_pmu_event_symbol p, *r; | |
962 | + | |
963 | + /* scan kernel pmu events from sysfs if needed */ | |
964 | + if (perf_pmu_events_list_num == 0) | |
965 | + perf_pmu__parse_init(); | |
966 | + /* | |
967 | + * name "cpu" could be prefix of cpu-cycles or cpu// events. | |
968 | + * cpu-cycles has been handled by hardcode. | |
969 | + * So it must be cpu// events, not kernel pmu event. | |
970 | + */ | |
971 | + if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu")) | |
972 | + return PMU_EVENT_SYMBOL_ERR; | |
973 | + | |
974 | + p.symbol = strdup(name); | |
975 | + r = bsearch(&p, perf_pmu_events_list, | |
976 | + (size_t) perf_pmu_events_list_num, | |
977 | + sizeof(struct perf_pmu_event_symbol), comp_pmu); | |
978 | + free(p.symbol); | |
979 | + return r ? r->type : PMU_EVENT_SYMBOL_ERR; | |
980 | +} | |
981 | + | |
866 | 982 | static int parse_events__scanner(const char *str, void *data, int start_token) |
867 | 983 | { |
868 | 984 | YY_BUFFER_STATE buffer; |
... | ... | @@ -917,6 +1033,7 @@ |
917 | 1033 | int ret; |
918 | 1034 | |
919 | 1035 | ret = parse_events__scanner(str, &data, PE_START_EVENTS); |
1036 | + perf_pmu__parse_cleanup(); | |
920 | 1037 | if (!ret) { |
921 | 1038 | int entries = data.idx - evlist->nr_entries; |
922 | 1039 | perf_evlist__splice_list_tail(evlist, &data.list, entries); |
tools/perf/util/parse-events.h
... | ... | @@ -35,6 +35,18 @@ |
35 | 35 | |
36 | 36 | #define EVENTS_HELP_MAX (128*1024) |
37 | 37 | |
38 | +enum perf_pmu_event_symbol_type { | |
39 | + PMU_EVENT_SYMBOL_ERR, /* not a PMU EVENT */ | |
40 | + PMU_EVENT_SYMBOL, /* normal style PMU event */ | |
41 | + PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */ | |
42 | + PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */ | |
43 | +}; | |
44 | + | |
45 | +struct perf_pmu_event_symbol { | |
46 | + char *symbol; | |
47 | + enum perf_pmu_event_symbol_type type; | |
48 | +}; | |
49 | + | |
38 | 50 | enum { |
39 | 51 | PARSE_EVENTS__TERM_TYPE_NUM, |
40 | 52 | PARSE_EVENTS__TERM_TYPE_STR, |
... | ... | @@ -95,6 +107,8 @@ |
95 | 107 | void *ptr, char *type); |
96 | 108 | int parse_events_add_pmu(struct list_head *list, int *idx, |
97 | 109 | char *pmu , struct list_head *head_config); |
110 | +enum perf_pmu_event_symbol_type | |
111 | +perf_pmu__parse_check(const char *name); | |
98 | 112 | void parse_events__set_leader(char *name, struct list_head *list); |
99 | 113 | void parse_events_update_lists(struct list_head *list_event, |
100 | 114 | struct list_head *list_all); |
tools/perf/util/pmu.c
... | ... | @@ -12,16 +12,6 @@ |
12 | 12 | #include "parse-events.h" |
13 | 13 | #include "cpumap.h" |
14 | 14 | |
15 | -#define UNIT_MAX_LEN 31 /* max length for event unit name */ | |
16 | - | |
17 | -struct perf_pmu_alias { | |
18 | - char *name; | |
19 | - struct list_head terms; /* HEAD struct parse_events_term -> list */ | |
20 | - struct list_head list; /* ELEM */ | |
21 | - char unit[UNIT_MAX_LEN+1]; | |
22 | - double scale; | |
23 | -}; | |
24 | - | |
25 | 15 | struct perf_pmu_format { |
26 | 16 | char *name; |
27 | 17 | int value; |
tools/perf/util/pmu.h
... | ... | @@ -30,6 +30,16 @@ |
30 | 30 | double scale; |
31 | 31 | }; |
32 | 32 | |
33 | +#define UNIT_MAX_LEN 31 /* max length for event unit name */ | |
34 | + | |
35 | +struct perf_pmu_alias { | |
36 | + char *name; | |
37 | + struct list_head terms; /* HEAD struct parse_events_term -> list */ | |
38 | + struct list_head list; /* ELEM */ | |
39 | + char unit[UNIT_MAX_LEN+1]; | |
40 | + double scale; | |
41 | +}; | |
42 | + | |
33 | 43 | struct perf_pmu *perf_pmu__find(const char *name); |
34 | 44 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
35 | 45 | struct list_head *head_terms); |