Re: [PATCH v1 15/40] perf parse-events: Avoid scanning PMUs before parsing

From: Liang, Kan
Date: Thu Apr 27 2023 - 16:07:01 EST




On 2023-04-26 3:00 a.m., Ian Rogers wrote:
> The event parser needs to handle two special cases:
> 1) legacy events like L1-dcache-load-miss. These event names don't
> appear in json or sysfs, and lookup tables are used for the config
> value.
> 2) raw events where 'r0xead' is the same as 'read' unless the PMU has
> an event called 'read' in which case the event has priority.
>
> The previous parser to handle these cases would scan all PMUs for
> components of event names. These components would then be used to
> classify in the lexer whether the token should be part of a legacy
> event, a raw event or an event. The grammar would handle legacy event
> tokens or recombining the tokens back into a regular event name. The
> code wasn't PMU specific and had issues around events like AMD's
> branch-brs that would fail to parse as it expects brs to be a suffix
> on a legacy event style name:
>
> $ perf stat -e branch-brs true
> event syntax error: 'branch-brs'
> \___ parser error
>
> This change removes processing all PMUs by using the lexer in the form
> of a regular expression matcher. The lexer will return the token for
> the longest matched sequence of characters, and in the event of a tie
> the first. The legacy events are a fixed number of regular
> expressions, and by matching these before a name token its possible to
> generate an accurate legacy event token with everything else matching
> as a name. Because of the lexer change the handling of hyphens in the
> grammar can be removed as hyphens just become a part of the name.
>
> To handle raw events and terms the parser is changed to defer trying
> to evaluate whether something is a raw event until the PMU is known in
> the grammar. Once the PMU is known, the events of the PMU can be
> scanned for the 'read' style problem. A new term type is added for
> these raw terms, used to enable deferring the evaluation.
>
> While this change is large, it has stats of:
> 170 insertions(+), 436 deletions(-)
> the bulk of the change is deleting the old approach. It isn't possible
> to break apart the code added due to the dependencies on how the parts
> of the parsing work.
>
> Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>


Run the test on Cascade Lake and Alder Lake. It looks good.

Tested-by: Kan Liang <kan.liang@xxxxxxxxxxxxxxx>

Thanks,
Kan

> ---
> tools/perf/tests/parse-events.c | 24 +--
> tools/perf/tests/pmu-events.c | 9 -
> tools/perf/util/parse-events.c | 329 ++++++++++----------------------
> tools/perf/util/parse-events.h | 16 +-
> tools/perf/util/parse-events.l | 85 +--------
> tools/perf/util/parse-events.y | 143 +++++---------
> 6 files changed, 170 insertions(+), 436 deletions(-)
>
> diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
> index 177464793aa8..6eadb8a47dbf 100644
> --- a/tools/perf/tests/parse-events.c
> +++ b/tools/perf/tests/parse-events.c
> @@ -664,11 +664,11 @@ static int test__checkterms_simple(struct list_head *terms)
> */
> term = list_entry(term->list.next, struct parse_events_term, list);
> TEST_ASSERT_VAL("wrong type term",
> - term->type_term == PARSE_EVENTS__TERM_TYPE_USER);
> + term->type_term == PARSE_EVENTS__TERM_TYPE_RAW);
> TEST_ASSERT_VAL("wrong type val",
> - term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
> - TEST_ASSERT_VAL("wrong val", term->val.num == 1);
> - TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "read"));
> + term->type_val == PARSE_EVENTS__TERM_TYPE_STR);
> + TEST_ASSERT_VAL("wrong val", !strcmp(term->val.str, "read"));
> + TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "raw"));
>
> /*
> * r0xead
> @@ -678,11 +678,11 @@ static int test__checkterms_simple(struct list_head *terms)
> */
> term = list_entry(term->list.next, struct parse_events_term, list);
> TEST_ASSERT_VAL("wrong type term",
> - term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG);
> + term->type_term == PARSE_EVENTS__TERM_TYPE_RAW);
> TEST_ASSERT_VAL("wrong type val",
> - term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
> - TEST_ASSERT_VAL("wrong val", term->val.num == 0xead);
> - TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "config"));
> + term->type_val == PARSE_EVENTS__TERM_TYPE_STR);
> + TEST_ASSERT_VAL("wrong val", !strcmp(term->val.str, "r0xead"));
> + TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "raw"));
> return TEST_OK;
> }
>
> @@ -2090,7 +2090,6 @@ static int test_event_fake_pmu(const char *str)
> return -ENOMEM;
>
> parse_events_error__init(&err);
> - perf_pmu__test_parse_init();
> ret = __parse_events(evlist, str, &err, &perf_pmu__fake, /*warn_if_reordered=*/true);
> if (ret) {
> pr_debug("failed to parse event '%s', err %d, str '%s'\n",
> @@ -2144,13 +2143,6 @@ static int test_term(const struct terms_test *t)
>
> INIT_LIST_HEAD(&terms);
>
> - /*
> - * The perf_pmu__test_parse_init prepares perf_pmu_events_list
> - * which gets freed in parse_events_terms.
> - */
> - if (perf_pmu__test_parse_init())
> - return -1;
> -
> ret = parse_events_terms(&terms, t->str);
> if (ret) {
> pr_debug("failed to parse terms '%s', err %d\n",
> diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
> index 1dff863b9711..a2cde61b1c77 100644
> --- a/tools/perf/tests/pmu-events.c
> +++ b/tools/perf/tests/pmu-events.c
> @@ -776,15 +776,6 @@ static int check_parse_id(const char *id, struct parse_events_error *error,
> for (cur = strchr(dup, '@') ; cur; cur = strchr(++cur, '@'))
> *cur = '/';
>
> - if (fake_pmu) {
> - /*
> - * Every call to __parse_events will try to initialize the PMU
> - * state from sysfs and then clean it up at the end. Reset the
> - * PMU events to the test state so that we don't pick up
> - * erroneous prefixes and suffixes.
> - */
> - perf_pmu__test_parse_init();
> - }
> ret = __parse_events(evlist, dup, error, fake_pmu, /*warn_if_reordered=*/true);
> free(dup);
>
> diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
> index 4ba01577618e..e416e653cf74 100644
> --- a/tools/perf/util/parse-events.c
> +++ b/tools/perf/util/parse-events.c
> @@ -34,11 +34,6 @@
>
> #define MAX_NAME_LEN 100
>
> -struct perf_pmu_event_symbol {
> - char *symbol;
> - enum perf_pmu_event_symbol_type type;
> -};
> -
> #ifdef PARSER_DEBUG
> extern int parse_events_debug;
> #endif
> @@ -49,15 +44,6 @@ static int parse_events__with_hybrid_pmu(struct parse_events_state *parse_state,
> const char *str, char *pmu_name,
> struct list_head *list);
>
> -static struct perf_pmu_event_symbol *perf_pmu_events_list;
> -/*
> - * The variable indicates the number of supported pmu event symbols.
> - * 0 means not initialized and ready to init
> - * -1 means failed to init, don't try anymore
> - * >0 is the number of supported pmu event symbols
> - */
> -static int perf_pmu_events_list_num;
> -
> struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
> [PERF_COUNT_HW_CPU_CYCLES] = {
> .symbol = "cpu-cycles",
> @@ -236,6 +222,57 @@ static char *get_config_name(struct list_head *head_terms)
> return get_config_str(head_terms, PARSE_EVENTS__TERM_TYPE_NAME);
> }
>
> +/**
> + * fix_raw - For each raw term see if there is an event (aka alias) in pmu that
> + * matches the raw's string value. If the string value matches an
> + * event then change the term to be an event, if not then change it to
> + * be a config term. For example, "read" may be an event of the PMU or
> + * a raw hex encoding of 0xead. The fix-up is done late so the PMU of
> + * the event can be determined and we don't need to scan all PMUs
> + * ahead-of-time.
> + * @config_terms: the list of terms that may contain a raw term.
> + * @pmu: the PMU to scan for events from.
> + */
> +static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu)
> +{
> + struct parse_events_term *term;
> +
> + list_for_each_entry(term, config_terms, list) {
> + struct perf_pmu_alias *alias;
> + bool matched = false;
> +
> + if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW)
> + continue;
> +
> + list_for_each_entry(alias, &pmu->aliases, list) {
> + if (!strcmp(alias->name, term->val.str)) {
> + free(term->config);
> + term->config = term->val.str;
> + term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
> + term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
> + term->val.num = 1;
> + term->no_value = true;
> + matched = true;
> + break;
> + }
> + }
> + if (!matched) {
> + u64 num;
> +
> + free(term->config);
> + term->config=strdup("config");
> + errno = 0;
> + num = strtoull(term->val.str + 1, NULL, 16);
> + assert(errno == 0);
> + free(term->val.str);
> + term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
> + term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG;
> + term->val.num = num;
> + term->no_value = false;
> + }
> + }
> +}
> +
> static struct evsel *
> __add_event(struct list_head *list, int *idx,
> struct perf_event_attr *attr,
> @@ -328,18 +365,27 @@ static int add_event_tool(struct list_head *list, int *idx,
> return 0;
> }
>
> -static int parse_aliases(char *str, const char *const names[][EVSEL__MAX_ALIASES], int size)
> +/**
> + * parse_aliases - search names for entries beginning or equalling str ignoring
> + * case. If mutliple entries in names match str then the longest
> + * is chosen.
> + * @str: The needle to look for.
> + * @names: The haystack to search.
> + * @size: The size of the haystack.
> + * @longest: Out argument giving the length of the matching entry.
> + */
> +static int parse_aliases(const char *str, const char *const names[][EVSEL__MAX_ALIASES], int size,
> + int *longest)
> {
> - int i, j;
> - int n, longest = -1;
> + *longest = -1;
> + for (int i = 0; i < size; i++) {
> + for (int j = 0; j < EVSEL__MAX_ALIASES && names[i][j]; j++) {
> + int n = strlen(names[i][j]);
>
> - for (i = 0; i < size; i++) {
> - for (j = 0; j < EVSEL__MAX_ALIASES && names[i][j]; j++) {
> - n = strlen(names[i][j]);
> - if (n > longest && !strncasecmp(str, names[i][j], n))
> - longest = n;
> + if (n > *longest && !strncasecmp(str, names[i][j], n))
> + *longest = n;
> }
> - if (longest > 0)
> + if (*longest > 0)
> return i;
> }
>
> @@ -357,52 +403,58 @@ static int config_attr(struct perf_event_attr *attr,
> struct parse_events_error *err,
> config_term_func_t config_term);
>
> -int parse_events_add_cache(struct list_head *list, int *idx,
> - char *type, char *op_result1, char *op_result2,
> +int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
> struct parse_events_error *err,
> struct list_head *head_config,
> struct parse_events_state *parse_state)
> {
> struct perf_event_attr attr;
> LIST_HEAD(config_terms);
> - char name[MAX_NAME_LEN];
> const char *config_name, *metric_id;
> int cache_type = -1, cache_op = -1, cache_result = -1;
> - char *op_result[2] = { op_result1, op_result2 };
> - int i, n, ret;
> + int ret, len;
> + const char *name_end = &name[strlen(name) + 1];
> bool hybrid;
> + const char *str = name;
>
> /*
> - * No fallback - if we cannot get a clear cache type
> - * then bail out:
> + * Search str for the legacy cache event name composed of 1, 2 or 3
> + * hyphen separated sections. The first section is the cache type while
> + * the others are the optional op and optional result. To make life hard
> + * the names in the table also contain hyphens and the longest name
> + * should always be selected.
> */
> - cache_type = parse_aliases(type, evsel__hw_cache, PERF_COUNT_HW_CACHE_MAX);
> + cache_type = parse_aliases(str, evsel__hw_cache, PERF_COUNT_HW_CACHE_MAX, &len);
> if (cache_type == -1)
> return -EINVAL;
> + str += len + 1;
>
> config_name = get_config_name(head_config);
> - n = snprintf(name, MAX_NAME_LEN, "%s", type);
> -
> - for (i = 0; (i < 2) && (op_result[i]); i++) {
> - char *str = op_result[i];
> -
> - n += snprintf(name + n, MAX_NAME_LEN - n, "-%s", str);
> -
> - if (cache_op == -1) {
> + if (str < name_end) {
> + cache_op = parse_aliases(str, evsel__hw_cache_op,
> + PERF_COUNT_HW_CACHE_OP_MAX, &len);
> + if (cache_op >= 0) {
> + if (!evsel__is_cache_op_valid(cache_type, cache_op))
> + return -EINVAL;
> + str += len + 1;
> + } else {
> + cache_result = parse_aliases(str, evsel__hw_cache_result,
> + PERF_COUNT_HW_CACHE_RESULT_MAX, &len);
> + if (cache_result >= 0)
> + str += len + 1;
> + }
> + }
> + if (str < name_end) {
> + if (cache_op < 0) {
> cache_op = parse_aliases(str, evsel__hw_cache_op,
> - PERF_COUNT_HW_CACHE_OP_MAX);
> + PERF_COUNT_HW_CACHE_OP_MAX, &len);
> if (cache_op >= 0) {
> if (!evsel__is_cache_op_valid(cache_type, cache_op))
> return -EINVAL;
> - continue;
> }
> - }
> -
> - if (cache_result == -1) {
> + } else if (cache_result < 0) {
> cache_result = parse_aliases(str, evsel__hw_cache_result,
> - PERF_COUNT_HW_CACHE_RESULT_MAX);
> - if (cache_result >= 0)
> - continue;
> + PERF_COUNT_HW_CACHE_RESULT_MAX, &len);
> }
> }
>
> @@ -968,6 +1020,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {
> [PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output",
> [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size",
> [PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id",
> + [PARSE_EVENTS__TERM_TYPE_RAW] = "raw",
> };
>
> static bool config_term_shrinked;
> @@ -1089,6 +1142,9 @@ do { \
> case PARSE_EVENTS__TERM_TYPE_METRIC_ID:
> CHECK_TYPE_VAL(STR);
> break;
> + case PARSE_EVENTS__TERM_TYPE_RAW:
> + CHECK_TYPE_VAL(STR);
> + break;
> case PARSE_EVENTS__TERM_TYPE_MAX_STACK:
> CHECK_TYPE_VAL(NUM);
> break;
> @@ -1485,6 +1541,8 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
> parse_events_error__handle(err, 0, err_str, NULL);
> return -EINVAL;
> }
> + if (head_config)
> + fix_raw(head_config, pmu);
>
> if (pmu->default_config) {
> memcpy(&attr, pmu->default_config,
> @@ -1875,180 +1933,6 @@ int parse_events_name(struct list_head *list, const char *name)
> return 0;
> }
>
> -static int
> -comp_pmu(const void *p1, const void *p2)
> -{
> - struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1;
> - struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2;
> -
> - return strcasecmp(pmu1->symbol, pmu2->symbol);
> -}
> -
> -static void perf_pmu__parse_cleanup(void)
> -{
> - if (perf_pmu_events_list_num > 0) {
> - struct perf_pmu_event_symbol *p;
> - int i;
> -
> - for (i = 0; i < perf_pmu_events_list_num; i++) {
> - p = perf_pmu_events_list + i;
> - zfree(&p->symbol);
> - }
> - zfree(&perf_pmu_events_list);
> - perf_pmu_events_list_num = 0;
> - }
> -}
> -
> -#define SET_SYMBOL(str, stype) \
> -do { \
> - p->symbol = str; \
> - if (!p->symbol) \
> - goto err; \
> - p->type = stype; \
> -} while (0)
> -
> -/*
> - * Read the pmu events list from sysfs
> - * Save it into perf_pmu_events_list
> - */
> -static void perf_pmu__parse_init(void)
> -{
> -
> - struct perf_pmu *pmu = NULL;
> - struct perf_pmu_alias *alias;
> - int len = 0;
> -
> - pmu = NULL;
> - while ((pmu = perf_pmu__scan(pmu)) != NULL) {
> - list_for_each_entry(alias, &pmu->aliases, list) {
> - char *tmp = strchr(alias->name, '-');
> -
> - if (tmp) {
> - char *tmp2 = NULL;
> -
> - tmp2 = strchr(tmp + 1, '-');
> - len++;
> - if (tmp2)
> - len++;
> - }
> -
> - len++;
> - }
> - }
> -
> - if (len == 0) {
> - perf_pmu_events_list_num = -1;
> - return;
> - }
> - perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len);
> - if (!perf_pmu_events_list)
> - return;
> - perf_pmu_events_list_num = len;
> -
> - len = 0;
> - pmu = NULL;
> - while ((pmu = perf_pmu__scan(pmu)) != NULL) {
> - list_for_each_entry(alias, &pmu->aliases, list) {
> - struct perf_pmu_event_symbol *p = perf_pmu_events_list + len;
> - char *tmp = strchr(alias->name, '-');
> - char *tmp2 = NULL;
> -
> - if (tmp)
> - tmp2 = strchr(tmp + 1, '-');
> - if (tmp2) {
> - SET_SYMBOL(strndup(alias->name, tmp - alias->name),
> - PMU_EVENT_SYMBOL_PREFIX);
> - p++;
> - tmp++;
> - SET_SYMBOL(strndup(tmp, tmp2 - tmp), PMU_EVENT_SYMBOL_SUFFIX);
> - p++;
> - SET_SYMBOL(strdup(++tmp2), PMU_EVENT_SYMBOL_SUFFIX2);
> - len += 3;
> - } else if (tmp) {
> - SET_SYMBOL(strndup(alias->name, tmp - alias->name),
> - PMU_EVENT_SYMBOL_PREFIX);
> - p++;
> - SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX);
> - len += 2;
> - } else {
> - SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL);
> - len++;
> - }
> - }
> - }
> - qsort(perf_pmu_events_list, len,
> - sizeof(struct perf_pmu_event_symbol), comp_pmu);
> -
> - return;
> -err:
> - perf_pmu__parse_cleanup();
> -}
> -
> -/*
> - * This function injects special term in
> - * perf_pmu_events_list so the test code
> - * can check on this functionality.
> - */
> -int perf_pmu__test_parse_init(void)
> -{
> - struct perf_pmu_event_symbol *list, *tmp, symbols[] = {
> - {(char *)"read", PMU_EVENT_SYMBOL},
> - {(char *)"event", PMU_EVENT_SYMBOL_PREFIX},
> - {(char *)"two", PMU_EVENT_SYMBOL_SUFFIX},
> - {(char *)"hyphen", PMU_EVENT_SYMBOL_SUFFIX},
> - {(char *)"hyph", PMU_EVENT_SYMBOL_SUFFIX2},
> - };
> - unsigned long i, j;
> -
> - tmp = list = malloc(sizeof(*list) * ARRAY_SIZE(symbols));
> - if (!list)
> - return -ENOMEM;
> -
> - for (i = 0; i < ARRAY_SIZE(symbols); i++, tmp++) {
> - tmp->type = symbols[i].type;
> - tmp->symbol = strdup(symbols[i].symbol);
> - if (!tmp->symbol)
> - goto err_free;
> - }
> -
> - perf_pmu_events_list = list;
> - perf_pmu_events_list_num = ARRAY_SIZE(symbols);
> -
> - qsort(perf_pmu_events_list, ARRAY_SIZE(symbols),
> - sizeof(struct perf_pmu_event_symbol), comp_pmu);
> - return 0;
> -
> -err_free:
> - for (j = 0, tmp = list; j < i; j++, tmp++)
> - zfree(&tmp->symbol);
> - free(list);
> - return -ENOMEM;
> -}
> -
> -enum perf_pmu_event_symbol_type
> -perf_pmu__parse_check(const char *name)
> -{
> - struct perf_pmu_event_symbol p, *r;
> -
> - /* scan kernel pmu events from sysfs if needed */
> - if (perf_pmu_events_list_num == 0)
> - perf_pmu__parse_init();
> - /*
> - * name "cpu" could be prefix of cpu-cycles or cpu// events.
> - * cpu-cycles has been handled by hardcode.
> - * So it must be cpu// events, not kernel pmu event.
> - */
> - if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu"))
> - return PMU_EVENT_SYMBOL_ERR;
> -
> - p.symbol = strdup(name);
> - r = bsearch(&p, perf_pmu_events_list,
> - (size_t) perf_pmu_events_list_num,
> - sizeof(struct perf_pmu_event_symbol), comp_pmu);
> - zfree(&p.symbol);
> - return r ? r->type : PMU_EVENT_SYMBOL_ERR;
> -}
> -
> static int parse_events__scanner(const char *str,
> struct parse_events_state *parse_state)
> {
> @@ -2086,7 +1970,6 @@ int parse_events_terms(struct list_head *terms, const char *str)
> int ret;
>
> ret = parse_events__scanner(str, &parse_state);
> - perf_pmu__parse_cleanup();
>
> if (!ret) {
> list_splice(parse_state.terms, terms);
> @@ -2111,7 +1994,6 @@ static int parse_events__with_hybrid_pmu(struct parse_events_state *parse_state,
> int ret;
>
> ret = parse_events__scanner(str, &ps);
> - perf_pmu__parse_cleanup();
>
> if (!ret) {
> if (!list_empty(&ps.list)) {
> @@ -2267,7 +2149,6 @@ int __parse_events(struct evlist *evlist, const char *str,
> int ret;
>
> ret = parse_events__scanner(str, &parse_state);
> - perf_pmu__parse_cleanup();
>
> if (!ret && list_empty(&parse_state.list)) {
> WARN_ONCE(true, "WARNING: event parser found nothing\n");
> diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
> index 86ad4438a2aa..f638542c8638 100644
> --- a/tools/perf/util/parse-events.h
> +++ b/tools/perf/util/parse-events.h
> @@ -41,14 +41,6 @@ int parse_events_terms(struct list_head *terms, const char *str);
> int parse_filter(const struct option *opt, const char *str, int unset);
> int exclude_perf(const struct option *opt, const char *arg, int unset);
>
> -enum perf_pmu_event_symbol_type {
> - PMU_EVENT_SYMBOL_ERR, /* not a PMU EVENT */
> - PMU_EVENT_SYMBOL, /* normal style PMU event */
> - PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */
> - PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */
> - PMU_EVENT_SYMBOL_SUFFIX2, /* suffix of pre-suf2 style event */
> -};
> -
> enum {
> PARSE_EVENTS__TERM_TYPE_NUM,
> PARSE_EVENTS__TERM_TYPE_STR,
> @@ -78,6 +70,7 @@ enum {
> PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT,
> PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE,
> PARSE_EVENTS__TERM_TYPE_METRIC_ID,
> + PARSE_EVENTS__TERM_TYPE_RAW,
> __PARSE_EVENTS__TERM_TYPE_NR,
> };
>
> @@ -174,8 +167,7 @@ int parse_events_add_numeric(struct parse_events_state *parse_state,
> int parse_events_add_tool(struct parse_events_state *parse_state,
> struct list_head *list,
> int tool_event);
> -int parse_events_add_cache(struct list_head *list, int *idx,
> - char *type, char *op_result1, char *op_result2,
> +int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
> struct parse_events_error *error,
> struct list_head *head_config,
> struct parse_events_state *parse_state);
> @@ -198,8 +190,6 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
> int parse_events_copy_term_list(struct list_head *old,
> struct list_head **new);
>
> -enum perf_pmu_event_symbol_type
> -perf_pmu__parse_check(const char *name);
> void parse_events__set_leader(char *name, struct list_head *list);
> void parse_events_update_lists(struct list_head *list_event,
> struct list_head *list_all);
> @@ -241,8 +231,6 @@ static inline bool is_sdt_event(char *str __maybe_unused)
> }
> #endif /* HAVE_LIBELF_SUPPORT */
>
> -int perf_pmu__test_parse_init(void);
> -
> struct evsel *parse_events__add_event_hybrid(struct list_head *list, int *idx,
> struct perf_event_attr *attr,
> const char *name,
> diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
> index 51fe0a9fb3de..4b35c099189a 100644
> --- a/tools/perf/util/parse-events.l
> +++ b/tools/perf/util/parse-events.l
> @@ -63,17 +63,6 @@ static int str(yyscan_t scanner, int token)
> return token;
> }
>
> -static int raw(yyscan_t scanner)
> -{
> - YYSTYPE *yylval = parse_events_get_lval(scanner);
> - char *text = parse_events_get_text(scanner);
> -
> - if (perf_pmu__parse_check(text) == PMU_EVENT_SYMBOL)
> - return str(scanner, PE_NAME);
> -
> - return __value(yylval, text + 1, 16, PE_RAW);
> -}
> -
> static bool isbpf_suffix(char *text)
> {
> int len = strlen(text);
> @@ -131,35 +120,6 @@ do { \
> yyless(0); \
> } while (0)
>
> -static int pmu_str_check(yyscan_t scanner, struct parse_events_state *parse_state)
> -{
> - YYSTYPE *yylval = parse_events_get_lval(scanner);
> - char *text = parse_events_get_text(scanner);
> -
> - yylval->str = strdup(text);
> -
> - /*
> - * If we're not testing then parse check determines the PMU event type
> - * which if it isn't a PMU returns PE_NAME. When testing the result of
> - * parse check can't be trusted so we return PE_PMU_EVENT_FAKE unless
> - * an '!' is present in which case the text can't be a PMU name.
> - */
> - switch (perf_pmu__parse_check(text)) {
> - case PMU_EVENT_SYMBOL_PREFIX:
> - return PE_PMU_EVENT_PRE;
> - case PMU_EVENT_SYMBOL_SUFFIX:
> - return PE_PMU_EVENT_SUF;
> - case PMU_EVENT_SYMBOL_SUFFIX2:
> - return PE_PMU_EVENT_SUF2;
> - case PMU_EVENT_SYMBOL:
> - return parse_state->fake_pmu
> - ? PE_PMU_EVENT_FAKE : PE_KERNEL_PMU_EVENT;
> - default:
> - return parse_state->fake_pmu && !strchr(text,'!')
> - ? PE_PMU_EVENT_FAKE : PE_NAME;
> - }
> -}
> -
> static int sym(yyscan_t scanner, int type, int config)
> {
> YYSTYPE *yylval = parse_events_get_lval(scanner);
> @@ -211,13 +171,15 @@ bpf_source [^,{}]+\.c[a-zA-Z0-9._]*
> num_dec [0-9]+
> num_hex 0x[a-fA-F0-9]+
> num_raw_hex [a-fA-F0-9]+
> -name [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]!]*
> +name [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]!\-]*
> name_tag [\'][a-zA-Z_*?\[\]][a-zA-Z0-9_*?\-,\.\[\]:=]*[\']
> name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]*
> drv_cfg_term [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)?
> /* If you add a modifier you need to update check_modifier() */
> modifier_event [ukhpPGHSDIWeb]+
> modifier_bp [rwx]{1,3}
> +lc_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)
> +lc_op_result (load|loads|read|store|stores|write|prefetch|prefetches|speculative-read|speculative-load|refs|Reference|ops|access|misses|miss)
>
> %%
>
> @@ -303,8 +265,8 @@ percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); }
> aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); }
> aux-sample-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); }
> metric-id { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_METRIC_ID); }
> -r{num_raw_hex} { return raw(yyscanner); }
> -r0x{num_raw_hex} { return raw(yyscanner); }
> +r{num_raw_hex} { return str(yyscanner, PE_RAW); }
> +r0x{num_raw_hex} { return str(yyscanner, PE_RAW); }
> , { return ','; }
> "/" { BEGIN(INITIAL); return '/'; }
> {name_minus} { return str(yyscanner, PE_NAME); }
> @@ -359,47 +321,20 @@ system_time { return tool(yyscanner, PERF_TOOL_SYSTEM_TIME); }
> bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); }
> cgroup-switches { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CGROUP_SWITCHES); }
>
> - /*
> - * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately.
> - * Because the prefix cycles is mixed up with cpu-cycles.
> - * loads and stores are mixed up with cache event
> - */
> -cycles-ct |
> -cycles-t |
> -mem-loads |
> -mem-loads-aux |
> -mem-stores |
> -topdown-[a-z-]+ |
> -tx-capacity-[a-z-]+ |
> -el-capacity-[a-z-]+ { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
> -
> -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 { return str(yyscanner, PE_NAME_CACHE_TYPE); }
> -
> -load|loads|read |
> -store|stores|write |
> -prefetch|prefetches |
> -speculative-read|speculative-load |
> -refs|Reference|ops|access |
> -misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); }
> -
> +{lc_type} { return str(yyscanner, PE_LEGACY_CACHE); }
> +{lc_type}-{lc_op_result} { return str(yyscanner, PE_LEGACY_CACHE); }
> +{lc_type}-{lc_op_result}-{lc_op_result} { return str(yyscanner, PE_LEGACY_CACHE); }
> mem: { BEGIN(mem); return PE_PREFIX_MEM; }
> -r{num_raw_hex} { return raw(yyscanner); }
> +r{num_raw_hex} { return str(yyscanner, PE_RAW); }
> {num_dec} { return value(yyscanner, 10); }
> {num_hex} { return value(yyscanner, 16); }
>
> {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
> {bpf_object} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_OBJECT); }
> {bpf_source} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_SOURCE); }
> -{name} { return pmu_str_check(yyscanner, _parse_state); }
> +{name} { return str(yyscanner, PE_NAME); }
> {name_tag} { return str(yyscanner, PE_NAME); }
> "/" { BEGIN(config); return '/'; }
> -- { return '-'; }
> , { BEGIN(event); return ','; }
> : { return ':'; }
> "{" { BEGIN(event); return '{'; }
> diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
> index 4488443e506e..e7072b5601c5 100644
> --- a/tools/perf/util/parse-events.y
> +++ b/tools/perf/util/parse-events.y
> @@ -8,6 +8,7 @@
>
> #define YYDEBUG 1
>
> +#include <errno.h>
> #include <fnmatch.h>
> #include <stdio.h>
> #include <linux/compiler.h>
> @@ -52,36 +53,35 @@ static void free_list_evsel(struct list_head* list_evsel)
> %}
>
> %token PE_START_EVENTS PE_START_TERMS
> -%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
> +%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_TERM
> %token PE_VALUE_SYM_TOOL
> %token PE_EVENT_NAME
> -%token PE_NAME
> +%token PE_RAW PE_NAME
> %token PE_BPF_OBJECT PE_BPF_SOURCE
> %token PE_MODIFIER_EVENT PE_MODIFIER_BP
> -%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
> +%token PE_LEGACY_CACHE
> %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
> %token PE_ERROR
> -%token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_PMU_EVENT_SUF2 PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
> +%token PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
> %token PE_ARRAY_ALL PE_ARRAY_RANGE
> %token PE_DRV_CFG_TERM
> %type <num> PE_VALUE
> %type <num> PE_VALUE_SYM_HW
> %type <num> PE_VALUE_SYM_SW
> %type <num> PE_VALUE_SYM_TOOL
> -%type <num> PE_RAW
> %type <num> PE_TERM
> %type <num> value_sym
> +%type <str> PE_RAW
> %type <str> PE_NAME
> %type <str> PE_BPF_OBJECT
> %type <str> PE_BPF_SOURCE
> -%type <str> PE_NAME_CACHE_TYPE
> -%type <str> PE_NAME_CACHE_OP_RESULT
> +%type <str> PE_LEGACY_CACHE
> %type <str> PE_MODIFIER_EVENT
> %type <str> PE_MODIFIER_BP
> %type <str> PE_EVENT_NAME
> -%type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_PMU_EVENT_SUF2 PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
> +%type <str> PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
> %type <str> PE_DRV_CFG_TERM
> -%type <str> event_pmu_name
> +%type <str> name_or_raw
> %destructor { free ($$); } <str>
> %type <term> event_term
> %destructor { parse_events_term__delete ($$); } <term>
> @@ -273,11 +273,8 @@ event_def: event_pmu |
> event_legacy_raw sep_dc |
> event_bpf_file
>
> -event_pmu_name:
> -PE_NAME | PE_PMU_EVENT_PRE
> -
> event_pmu:
> -event_pmu_name opt_pmu_config
> +PE_NAME opt_pmu_config
> {
> struct parse_events_state *parse_state = _parse_state;
> struct parse_events_error *error = parse_state->error;
> @@ -303,10 +300,12 @@ event_pmu_name opt_pmu_config
> list = alloc_list();
> if (!list)
> CLEANUP_YYABORT;
> + /* Attempt to add to list assuming $1 is a PMU name. */
> if (parse_events_add_pmu(_parse_state, list, $1, $2, /*auto_merge_stats=*/false)) {
> struct perf_pmu *pmu = NULL;
> int ok = 0;
>
> + /* Failure to add, try wildcard expansion of $1 as a PMU name. */
> if (asprintf(&pattern, "%s*", $1) < 0)
> CLEANUP_YYABORT;
>
> @@ -329,6 +328,12 @@ event_pmu_name opt_pmu_config
> }
> }
>
> + if (!ok) {
> + /* Failure to add, assume $1 is an event name. */
> + zfree(&list);
> + ok = !parse_events_multi_pmu_add(_parse_state, $1, $2, &list);
> + $2 = NULL;
> + }
> if (!ok)
> CLEANUP_YYABORT;
> }
> @@ -352,41 +357,27 @@ PE_KERNEL_PMU_EVENT sep_dc
> $$ = list;
> }
> |
> -PE_KERNEL_PMU_EVENT opt_pmu_config
> +PE_NAME sep_dc
> {
> struct list_head *list;
> int err;
>
> - /* frees $2 */
> - err = parse_events_multi_pmu_add(_parse_state, $1, $2, &list);
> + err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list);
> free($1);
> if (err < 0)
> YYABORT;
> $$ = list;
> }
> |
> -PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF '-' PE_PMU_EVENT_SUF2 sep_dc
> -{
> - struct list_head *list;
> - char pmu_name[128];
> - snprintf(pmu_name, sizeof(pmu_name), "%s-%s-%s", $1, $3, $5);
> - free($1);
> - free($3);
> - free($5);
> - if (parse_events_multi_pmu_add(_parse_state, pmu_name, NULL, &list) < 0)
> - YYABORT;
> - $$ = list;
> -}
> -|
> -PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc
> +PE_KERNEL_PMU_EVENT opt_pmu_config
> {
> struct list_head *list;
> - char pmu_name[128];
> + int err;
>
> - snprintf(pmu_name, sizeof(pmu_name), "%s-%s", $1, $3);
> + /* frees $2 */
> + err = parse_events_multi_pmu_add(_parse_state, $1, $2, &list);
> free($1);
> - free($3);
> - if (parse_events_multi_pmu_add(_parse_state, pmu_name, NULL, &list) < 0)
> + if (err < 0)
> YYABORT;
> $$ = list;
> }
> @@ -476,7 +467,7 @@ PE_VALUE_SYM_TOOL sep_slash_slash_dc
> }
>
> event_legacy_cache:
> -PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT opt_event_config
> +PE_LEGACY_CACHE opt_event_config
> {
> struct parse_events_state *parse_state = _parse_state;
> struct parse_events_error *error = parse_state->error;
> @@ -485,51 +476,8 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT opt_e
>
> list = alloc_list();
> ABORT_ON(!list);
> - err = parse_events_add_cache(list, &parse_state->idx, $1, $3, $5, error, $6,
> - parse_state);
> - parse_events_terms__delete($6);
> - free($1);
> - free($3);
> - free($5);
> - if (err) {
> - free_list_evsel(list);
> - YYABORT;
> - }
> - $$ = list;
> -}
> -|
> -PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT opt_event_config
> -{
> - struct parse_events_state *parse_state = _parse_state;
> - struct parse_events_error *error = parse_state->error;
> - struct list_head *list;
> - int err;
> + err = parse_events_add_cache(list, &parse_state->idx, $1, error, $2, parse_state);
>
> - list = alloc_list();
> - ABORT_ON(!list);
> - err = parse_events_add_cache(list, &parse_state->idx, $1, $3, NULL, error, $4,
> - parse_state);
> - parse_events_terms__delete($4);
> - free($1);
> - free($3);
> - if (err) {
> - free_list_evsel(list);
> - YYABORT;
> - }
> - $$ = list;
> -}
> -|
> -PE_NAME_CACHE_TYPE opt_event_config
> -{
> - struct parse_events_state *parse_state = _parse_state;
> - struct parse_events_error *error = parse_state->error;
> - struct list_head *list;
> - int err;
> -
> - list = alloc_list();
> - ABORT_ON(!list);
> - err = parse_events_add_cache(list, &parse_state->idx, $1, NULL, NULL, error, $2,
> - parse_state);
> parse_events_terms__delete($2);
> free($1);
> if (err) {
> @@ -633,17 +581,6 @@ tracepoint_name opt_event_config
> }
>
> tracepoint_name:
> -PE_NAME '-' PE_NAME ':' PE_NAME
> -{
> - struct tracepoint_name tracepoint;
> -
> - ABORT_ON(asprintf(&tracepoint.sys, "%s-%s", $1, $3) < 0);
> - tracepoint.event = $5;
> - free($1);
> - free($3);
> - $$ = tracepoint;
> -}
> -|
> PE_NAME ':' PE_NAME
> {
> struct tracepoint_name tracepoint = {$1, $3};
> @@ -673,10 +610,15 @@ PE_RAW opt_event_config
> {
> struct list_head *list;
> int err;
> + u64 num;
>
> list = alloc_list();
> ABORT_ON(!list);
> - err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, $1, $2);
> + errno = 0;
> + num = strtoull($1 + 1, NULL, 16);
> + ABORT_ON(errno);
> + free($1);
> + err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, num, $2);
> parse_events_terms__delete($2);
> if (err) {
> free(list);
> @@ -781,17 +723,22 @@ event_term
> $$ = head;
> }
>
> +name_or_raw: PE_RAW | PE_NAME
> +
> event_term:
> PE_RAW
> {
> struct parse_events_term *term;
>
> - ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_CONFIG,
> - NULL, $1, false, &@1, NULL));
> + if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_RAW,
> + strdup("raw"), $1, &@1, &@1)) {
> + free($1);
> + YYABORT;
> + }
> $$ = term;
> }
> |
> -PE_NAME '=' PE_NAME
> +name_or_raw '=' PE_NAME
> {
> struct parse_events_term *term;
>
> @@ -804,7 +751,7 @@ PE_NAME '=' PE_NAME
> $$ = term;
> }
> |
> -PE_NAME '=' PE_VALUE
> +name_or_raw '=' PE_VALUE
> {
> struct parse_events_term *term;
>
> @@ -816,7 +763,7 @@ PE_NAME '=' PE_VALUE
> $$ = term;
> }
> |
> -PE_NAME '=' PE_VALUE_SYM_HW
> +name_or_raw '=' PE_VALUE_SYM_HW
> {
> struct parse_events_term *term;
> int config = $3 & 255;
> @@ -876,7 +823,7 @@ PE_TERM
> $$ = term;
> }
> |
> -PE_NAME array '=' PE_NAME
> +name_or_raw array '=' PE_NAME
> {
> struct parse_events_term *term;
>
> @@ -891,7 +838,7 @@ PE_NAME array '=' PE_NAME
> $$ = term;
> }
> |
> -PE_NAME array '=' PE_VALUE
> +name_or_raw array '=' PE_VALUE
> {
> struct parse_events_term *term;
>