perf parse-events: Avoid scanning PMUs before parsing

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@google.com>
Tested-by: Kan Liang <kan.liang@linux.intel.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ahmad Yasin <ahmad.yasin@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: Caleb Biggers <caleb.biggers@intel.com>
Cc: Edward Baker <edward.baker@intel.com>
Cc: Florian Fischer <florian.fischer@muhq.space>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Kajol Jain <kjain@linux.ibm.com>
Cc: Kang Minchul <tegongkang@gmail.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Perry Taylor <perry.taylor@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Rob Herring <robh@kernel.org>
Cc: Samantha Alt <samantha.alt@intel.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Sumanth Korikkar <sumanthk@linux.ibm.com>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Tiezhu Yang <yangtiezhu@loongson.cn>
Cc: Weilin Wang <weilin.wang@intel.com>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Cc: Yang Jihong <yangjihong1@huawei.com>
Link: https://lore.kernel.org/r/20230502223851.2234828-19-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Ian Rogers 2023-05-02 15:38:25 -07:00 committed by Arnaldo Carvalho de Melo
parent 442eeb7704
commit 70c90e4a6b
6 changed files with 175 additions and 441 deletions

View File

@ -676,11 +676,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
@ -690,11 +690,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;
}
@ -2104,7 +2104,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",
@ -2158,13 +2157,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",

View File

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

View File

@ -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,
@ -329,18 +366,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;
}
@ -358,52 +404,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);
}
}
@ -969,6 +1021,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;
@ -1090,6 +1143,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;
@ -1486,6 +1542,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,
@ -1870,180 +1928,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)
{
@ -2081,7 +1965,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);
@ -2106,7 +1989,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)) {
@ -2269,7 +2151,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");

View File

@ -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,

View File

@ -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 '{'; }

View File

@ -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,6 +357,18 @@ PE_KERNEL_PMU_EVENT sep_dc
$$ = list;
}
|
PE_NAME sep_dc
{
struct list_head *list;
int err;
err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list);
free($1);
if (err < 0)
YYABORT;
$$ = list;
}
|
PE_KERNEL_PMU_EVENT opt_pmu_config
{
struct list_head *list;
@ -365,32 +382,6 @@ PE_KERNEL_PMU_EVENT opt_pmu_config
$$ = 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
{
struct list_head *list;
char pmu_name[128];
snprintf(pmu_name, sizeof(pmu_name), "%s-%s", $1, $3);
free($1);
free($3);
if (parse_events_multi_pmu_add(_parse_state, pmu_name, NULL, &list) < 0)
YYABORT;
$$ = list;
}
|
PE_PMU_EVENT_FAKE sep_dc
{
struct list_head *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;