lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <a9fd23b4-70bb-e21f-d9fc-37562147b721@linux.intel.com>
Date:   Thu, 27 Apr 2023 16:06:45 -0400
From:   "Liang, Kan" <kan.liang@...ux.intel.com>
To:     Ian Rogers <irogers@...gle.com>,
        Arnaldo Carvalho de Melo <acme@...nel.org>,
        Ahmad Yasin <ahmad.yasin@...el.com>,
        Peter Zijlstra <peterz@...radead.org>,
        Ingo Molnar <mingo@...hat.com>,
        Stephane Eranian <eranian@...gle.com>,
        Andi Kleen <ak@...ux.intel.com>,
        Perry Taylor <perry.taylor@...el.com>,
        Samantha Alt <samantha.alt@...el.com>,
        Caleb Biggers <caleb.biggers@...el.com>,
        Weilin Wang <weilin.wang@...el.com>,
        Edward Baker <edward.baker@...el.com>,
        Mark Rutland <mark.rutland@....com>,
        Alexander Shishkin <alexander.shishkin@...ux.intel.com>,
        Jiri Olsa <jolsa@...nel.org>,
        Namhyung Kim <namhyung@...nel.org>,
        Adrian Hunter <adrian.hunter@...el.com>,
        Florian Fischer <florian.fischer@...q.space>,
        Rob Herring <robh@...nel.org>,
        Zhengjun Xing <zhengjun.xing@...ux.intel.com>,
        John Garry <john.g.garry@...cle.com>,
        Kajol Jain <kjain@...ux.ibm.com>,
        Sumanth Korikkar <sumanthk@...ux.ibm.com>,
        Thomas Richter <tmricht@...ux.ibm.com>,
        Tiezhu Yang <yangtiezhu@...ngson.cn>,
        Ravi Bangoria <ravi.bangoria@....com>,
        Leo Yan <leo.yan@...aro.org>,
        Yang Jihong <yangjihong1@...wei.com>,
        James Clark <james.clark@....com>,
        Suzuki Poulouse <suzuki.poulose@....com>,
        Kang Minchul <tegongkang@...il.com>,
        Athira Rajeev <atrajeev@...ux.vnet.ibm.com>,
        linux-perf-users@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH v1 15/40] perf parse-events: Avoid scanning PMUs before
 parsing



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@...gle.com>


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

Tested-by: Kan Liang <kan.liang@...ux.intel.com>

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;
>  

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ