[PATCH v3 12/27] perf parse-events: Support no alias assigned event inside hybrid PMU

Jin Yao
2021-03-29T07:03:15Z

On hybrid platform, similar to hardware event, user may want
to enable other events only on one PMU. So following syntax
should be supported:

cpu_core/<event>/
cpu_atom/<event>/

But the syntax doesn't work for some events, such as cache
event.

Before:

# perf stat -e cpu_core/LLC-loads/ -a -- sleep 1
event syntax error: 'cpu_core/LLC-loads/'
\___ unknown term 'LLC-loads' for pmu 'cpu_core'

Cache events are much more complex than hardware events, so
we can't create aliases for them. We use another solution.
For example, if we use "cpu_core/LLC-loads/", in parse_events_add_pmu(),
term->config is "LLC-loads".

We create a new "parse_events_state" with the pmu_name and use
parse_events__scanner to scan the term->config ("LLC-loads" in
this example). The parse_events_add_cache() would be called during
parsing. The parse_state->pmu_name is used to identify the pmu
where the event should be enabled on.

After:

# ./perf stat -e cpu_core/LLC-loads/ -a -- sleep 1

Performance counter stats for 'system wide':

24,593 cpu_core/LLC-loads/

1.003911601 seconds time elapsed

Signed-off-by: Jin Yao <yao.jin@linux.intel.com>
---
v3:
- Rename the patch:
'perf parse-events: Support hardware events inside PMU' -->
'perf parse-events: Support no alias assigned event inside hybrid PMU'

- Major code is moved to parse-events-hybrid.c.
- Refine the code.

tools/perf/util/parse-events-hybrid.c | 18 +++++-
tools/perf/util/parse-events-hybrid.h | 3 +-
tools/perf/util/parse-events.c | 80 +++++++++++++++++++++++++--
tools/perf/util/parse-events.h | 4 +-
tools/perf/util/parse-events.y | 9 ++-
tools/perf/util/pmu.c | 4 +-
tools/perf/util/pmu.h | 2 +-
7 files changed, 108 insertions(+), 12 deletions(-)

diff --git a/tools/perf/util/parse-events-hybrid.c b/tools/perf/util/parse-events-hybrid.c
index 8a630cbab8f3..5bf176b55573 100644
--- a/tools/perf/util/parse-events-hybrid.c
+++ b/tools/perf/util/parse-events-hybrid.c
@@ -64,6 +64,11 @@ static int add_hw_hybrid(struct parse_events_state *parse_state,
int ret;

perf_pmu__for_each_hybrid_pmu(pmu) {
+ if (parse_state->pmu_name &&
+ strcmp(parse_state->pmu_name, pmu->name)) {
+ continue;
+ }
+
ret = create_event_hybrid(PERF_TYPE_HARDWARE_PMU,
&parse_state->idx, list, attr, name,
config_terms, pmu);
@@ -100,6 +105,11 @@ static int add_raw_hybrid(struct parse_events_state *parse_state,
int ret;

perf_pmu__for_each_hybrid_pmu(pmu) {
+ if (parse_state->pmu_name &&
+ strcmp(parse_state->pmu_name, pmu->name)) {
+ continue;
+ }
+
ret = create_raw_event_hybrid(&parse_state->idx, list, attr,
name, config_terms, pmu);
if (ret)
@@ -137,7 +147,8 @@ int parse_events__add_numeric_hybrid(struct parse_events_state *parse_state,
int parse_events__add_cache_hybrid(struct list_head *list, int *idx,
struct perf_event_attr *attr, char *name,
struct list_head *config_terms,
- bool *hybrid)
+ bool *hybrid,
+ struct parse_events_state *parse_state)
{
struct perf_pmu *pmu;
int ret;
@@ -148,6 +159,11 @@ int parse_events__add_cache_hybrid(struct list_head *list, int *idx,

*hybrid = true;
perf_pmu__for_each_hybrid_pmu(pmu) {
+ if (parse_state->pmu_name &&
+ strcmp(parse_state->pmu_name, pmu->name)) {
+ continue;
+ }
+
ret = create_event_hybrid(PERF_TYPE_HW_CACHE_PMU, idx, list,
attr, name, config_terms, pmu);
if (ret)
diff --git a/tools/perf/util/parse-events-hybrid.h b/tools/perf/util/parse-events-hybrid.h
index 9ad33cd0cef4..f33bd67aa851 100644
--- a/tools/perf/util/parse-events-hybrid.h
+++ b/tools/perf/util/parse-events-hybrid.h
@@ -17,6 +17,7 @@ int parse_events__add_numeric_hybrid(struct parse_events_state *parse_state,
int parse_events__add_cache_hybrid(struct list_head *list, int *idx,
struct perf_event_attr *attr, char *name,
struct list_head *config_terms,
- bool *hybrid);
+ bool *hybrid,
+ struct parse_events_state *parse_state);

#endif /* __PERF_PARSE_EVENTS_HYBRID_H */
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 3692fa3c964a..020c785be742 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -38,6 +38,7 @@
#include "util/event.h"
#include "util/pfm.h"
#include "util/parse-events-hybrid.h"
+#include "util/pmu-hybrid.h"
#include "perf.h"

#define MAX_NAME_LEN 100
@@ -48,6 +49,9 @@ extern int parse_events_debug;
int parse_events_parse(void *parse_state, void *scanner);
static int get_config_terms(struct list_head *head_config,
struct list_head *head_terms __maybe_unused);
+static int parse_events__with_hybrid_pmu(struct parse_events_state *parse_state,
+ const char *str, char *pmu_name,
+ bool *found, struct list_head *list);

static struct perf_pmu_event_symbol *perf_pmu_events_list;
/*
@@ -450,7 +454,8 @@ static int config_attr(struct perf_event_attr *attr,
int parse_events_add_cache(struct list_head *list, int *idx,
char *type, char *op_result1, char *op_result2,
struct parse_events_error *err,
- struct list_head *head_config)
+ struct list_head *head_config,
+ struct parse_events_state *parse_state)
{
struct perf_event_attr attr;
LIST_HEAD(config_terms);
@@ -521,7 +526,7 @@ int parse_events_add_cache(struct list_head *list, int *idx,

i = parse_events__add_cache_hybrid(list, idx, &attr,
config_name ? : name, &config_terms,
- &hybrid);
+ &hybrid, parse_state);
if (hybrid)
return i;

@@ -1481,7 +1486,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
struct perf_pmu *pmu;
struct evsel *evsel;
struct parse_events_error *err = parse_state->error;
- bool use_uncore_alias;
+ bool use_uncore_alias, found = false;
LIST_HEAD(config_terms);

if (verbose > 1) {
@@ -1530,8 +1535,28 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
}
}

- if (!parse_state->fake_pmu && perf_pmu__check_alias(pmu, head_config, &info))
+ if (!parse_state->fake_pmu &&
+ perf_pmu__check_alias(pmu, head_config, &info, &found)) {
return -EINVAL;
+ }
+
+ if (!parse_state->fake_pmu && head_config && !found &&
+ perf_pmu__is_hybrid(name)) {
+ struct parse_events_term *term;
+ int ret;
+
+ list_for_each_entry(term, head_config, list) {
+ if (!term->config)
+ continue;
+
+ ret = parse_events__with_hybrid_pmu(parse_state,
+ term->config,
+ name, &found,
+ list);
+ if (found)
+ return ret;
+ }
+ }

if (verbose > 1) {
fprintf(stderr, "After aliases, add event pmu '%s' with '",
@@ -1605,6 +1630,15 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
struct perf_pmu *pmu = NULL;
int ok = 0;

+ if (parse_state->pmu_name) {
+ list = malloc(sizeof(struct list_head));
+ if (!list)
+ return -1;
+ INIT_LIST_HEAD(list);
+ *listp = list;
+ return 0;
+ }
+
*listp = NULL;
/* Add it for all PMUs that support the alias */
list = malloc(sizeof(struct list_head));
@@ -2176,6 +2210,44 @@ int parse_events_terms(struct list_head *terms, const char *str)
return ret;
}

+static int list_entries_nr(struct list_head *list)
+{
+ struct list_head *pos;
+ int n = 0;
+
+ list_for_each(pos, list)
+ n++;
+
+ return n;
+}
+
+static int parse_events__with_hybrid_pmu(struct parse_events_state *parse_state,
+ const char *str, char *pmu_name,
+ bool *found, struct list_head *list)
+{
+ struct parse_events_state ps = {
+ .list = LIST_HEAD_INIT(ps.list),
+ .stoken = PE_START_EVENTS,
+ .pmu_name = pmu_name,
+ .idx = parse_state->idx,
+ };
+ int ret;
+
+ *found = false;
+ ret = parse_events__scanner(str, &ps);
+ perf_pmu__parse_cleanup();
+
+ if (!ret) {
+ if (!list_empty(&ps.list)) {
+ *found = true;
+ list_splice(&ps.list, list);
+ parse_state->idx = list_entries_nr(list);
+ }
+ }
+
+ return ret;
+}
+
int __parse_events(struct evlist *evlist, const char *str,
struct parse_events_error *err, struct perf_pmu *fake_pmu)
{
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index c4f2f96304ce..f9d8e8e41c38 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -138,6 +138,7 @@ struct parse_events_state {
struct list_head *terms;
int stoken;
struct perf_pmu *fake_pmu;
+ char *pmu_name;
};

void parse_events__handle_error(struct parse_events_error *err, int idx,
@@ -188,7 +189,8 @@ int parse_events_add_tool(struct parse_events_state *parse_state,
int parse_events_add_cache(struct list_head *list, int *idx,
char *type, char *op_result1, char *op_result2,
struct parse_events_error *error,
- struct list_head *head_config);
+ struct list_head *head_config,
+ struct parse_events_state *parse_state);
int parse_events_add_breakpoint(struct list_head *list, int *idx,
u64 addr, char *type, u64 len);
int parse_events_add_pmu(struct parse_events_state *parse_state,
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index d57ac86ce7ca..aba12a4d488e 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -454,7 +454,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);
+ 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);
@@ -475,7 +476,8 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT opt_event_config

list = alloc_list();
ABORT_ON(!list);
- err = parse_events_add_cache(list, &parse_state->idx, $1, $3, NULL, error, $4);
+ 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);
@@ -495,7 +497,8 @@ PE_NAME_CACHE_TYPE opt_event_config

list = alloc_list();
ABORT_ON(!list);
- err = parse_events_add_cache(list, &parse_state->idx, $1, NULL, NULL, error, $2);
+ 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) {
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 72e5ae5e868e..7167ae88e0db 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1412,7 +1412,7 @@ static int check_info_data(struct perf_pmu_alias *alias,
* defined for the alias
*/
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
- struct perf_pmu_info *info)
+ struct perf_pmu_info *info, bool *found)
{
struct parse_events_term *term, *h;
struct perf_pmu_alias *alias;
@@ -1430,6 +1430,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
info->metric_expr = NULL;
info->metric_name = NULL;

+ *found = false;
list_for_each_entry_safe(term, h, head_terms, list) {
alias = pmu_find_alias(pmu, term);
if (!alias)
@@ -1438,6 +1439,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
if (ret)
return ret;

+ *found = true;
ret = check_info_data(alias, info);
if (ret)
return ret;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 5ed2abab7fe0..381a2e94fbba 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -91,7 +91,7 @@ int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats,
__u64 perf_pmu__format_bits(struct list_head *formats, const char *name);
int perf_pmu__format_type(struct list_head *formats, const char *name);
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
- struct perf_pmu_info *info);
+ struct perf_pmu_info *info, bool *found);
struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
struct list_head *head_terms);
void perf_pmu_error(struct list_head *list, char *name, char const *msg);
--
2.17.1