perf metricgroup: Support multiple events for metricgroup
Some uncore metrics don't work as expected. For example, on cascadelakex: root@lkp-csl-2sp2:~# perf stat -M UNC_M_PMM_BANDWIDTH.TOTAL -a -- sleep 1 Performance counter stats for 'system wide': 1841092 unc_m_pmm_rpq_inserts 3680816 unc_m_pmm_wpq_inserts 1.001775055 seconds time elapsed root@lkp-csl-2sp2:~# perf stat -M UNC_M_PMM_READ_LATENCY -a -- sleep 1 Performance counter stats for 'system wide': 860649746 unc_m_pmm_rpq_occupancy.all 1840557 unc_m_pmm_rpq_inserts 12790627455 unc_m_clockticks 1.001773348 seconds time elapsed No metrics 'UNC_M_PMM_BANDWIDTH.TOTAL' or 'UNC_M_PMM_READ_LATENCY' are reported. The issue is, the case of an alias expanding to mulitple events is not supported, typically the uncore events. (see comments in find_evsel_group()). For UNC_M_PMM_BANDWIDTH.TOTAL in above example, the expanded event group is '{unc_m_pmm_rpq_inserts,unc_m_pmm_wpq_inserts}:W', but the actual events passed to find_evsel_group are: unc_m_pmm_rpq_inserts unc_m_pmm_rpq_inserts unc_m_pmm_rpq_inserts unc_m_pmm_rpq_inserts unc_m_pmm_rpq_inserts unc_m_pmm_rpq_inserts unc_m_pmm_wpq_inserts unc_m_pmm_wpq_inserts unc_m_pmm_wpq_inserts unc_m_pmm_wpq_inserts unc_m_pmm_wpq_inserts unc_m_pmm_wpq_inserts For this multiple events case, it's not supported well. This patch introduces a new field 'metric_leader' in struct evsel. The first event is considered as a metric leader. For the rest of same events, they point to the first event via it's metric_leader field in struct evsel. This design is for adding the counting results of all same events to the first event in group (the metric_leader). With this patch, root@lkp-csl-2sp2:~# perf stat -M UNC_M_PMM_BANDWIDTH.TOTAL -a -- sleep 1 Performance counter stats for 'system wide': 1842108 unc_m_pmm_rpq_inserts # 337.2 MB/sec UNC_M_PMM_BANDWIDTH.TOTAL 3682209 unc_m_pmm_wpq_inserts 1.001819706 seconds time elapsed root@lkp-csl-2sp2:~# perf stat -M UNC_M_PMM_READ_LATENCY -a -- sleep 1 Performance counter stats for 'system wide': 861970685 unc_m_pmm_rpq_occupancy.all # 219.4 ns UNC_M_PMM_READ_LATENCY 1842772 unc_m_pmm_rpq_inserts 12790196356 unc_m_clockticks 1.001749103 seconds time elapsed Now we can see the correct metrics 'UNC_M_PMM_BANDWIDTH.TOTAL' and 'UNC_M_PMM_READ_LATENCY'. Signed-off-by: Jin Yao <yao.jin@linux.intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Kan Liang <kan.liang@linux.intel.com> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lore.kernel.org/lkml/20190828055932.8269-5-yao.jin@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
287f2649f7
commit
f01642e491
3 changed files with 68 additions and 44 deletions
|
@ -168,6 +168,7 @@ struct evsel {
|
||||||
const char * metric_expr;
|
const char * metric_expr;
|
||||||
const char * metric_name;
|
const char * metric_name;
|
||||||
struct evsel **metric_events;
|
struct evsel **metric_events;
|
||||||
|
struct evsel *metric_leader;
|
||||||
bool collect_stat;
|
bool collect_stat;
|
||||||
bool weak_group;
|
bool weak_group;
|
||||||
bool percore;
|
bool percore;
|
||||||
|
|
|
@ -90,59 +90,63 @@ struct egroup {
|
||||||
const char *metric_unit;
|
const char *metric_unit;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool record_evsel(int *ind, struct evsel **start,
|
|
||||||
int idnum,
|
|
||||||
struct evsel **metric_events,
|
|
||||||
struct evsel *ev)
|
|
||||||
{
|
|
||||||
metric_events[*ind] = ev;
|
|
||||||
if (*ind == 0)
|
|
||||||
*start = ev;
|
|
||||||
if (++*ind == idnum) {
|
|
||||||
metric_events[*ind] = NULL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct evsel *find_evsel_group(struct evlist *perf_evlist,
|
static struct evsel *find_evsel_group(struct evlist *perf_evlist,
|
||||||
const char **ids,
|
const char **ids,
|
||||||
int idnum,
|
int idnum,
|
||||||
struct evsel **metric_events)
|
struct evsel **metric_events)
|
||||||
{
|
{
|
||||||
struct evsel *ev, *start = NULL;
|
struct evsel *ev;
|
||||||
int ind = 0;
|
int i = 0;
|
||||||
|
bool leader_found;
|
||||||
|
|
||||||
evlist__for_each_entry (perf_evlist, ev) {
|
evlist__for_each_entry (perf_evlist, ev) {
|
||||||
if (ev->collect_stat)
|
if (!strcmp(ev->name, ids[i])) {
|
||||||
continue;
|
if (!metric_events[i])
|
||||||
if (!strcmp(ev->name, ids[ind])) {
|
metric_events[i] = ev;
|
||||||
if (record_evsel(&ind, &start, idnum,
|
|
||||||
metric_events, ev))
|
|
||||||
return start;
|
|
||||||
} else {
|
} else {
|
||||||
/*
|
if (++i == idnum) {
|
||||||
* We saw some other event that is not
|
/* Discard the whole match and start again */
|
||||||
* in our list of events. Discard
|
i = 0;
|
||||||
* the whole match and start again.
|
memset(metric_events, 0,
|
||||||
*/
|
sizeof(struct evsel *) * idnum);
|
||||||
ind = 0;
|
continue;
|
||||||
start = NULL;
|
}
|
||||||
if (!strcmp(ev->name, ids[ind])) {
|
|
||||||
if (record_evsel(&ind, &start, idnum,
|
if (!strcmp(ev->name, ids[i]))
|
||||||
metric_events, ev))
|
metric_events[i] = ev;
|
||||||
return start;
|
else {
|
||||||
|
/* Discard the whole match and start again */
|
||||||
|
i = 0;
|
||||||
|
memset(metric_events, 0,
|
||||||
|
sizeof(struct evsel *) * idnum);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* This can happen when an alias expands to multiple
|
if (i != idnum - 1) {
|
||||||
* events, like for uncore events.
|
/* Not whole match */
|
||||||
* We don't support this case for now.
|
|
||||||
*/
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metric_events[idnum] = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < idnum; i++) {
|
||||||
|
leader_found = false;
|
||||||
|
evlist__for_each_entry(perf_evlist, ev) {
|
||||||
|
if (!leader_found && (ev == metric_events[i]))
|
||||||
|
leader_found = true;
|
||||||
|
|
||||||
|
if (leader_found &&
|
||||||
|
!strcmp(ev->name, metric_events[i]->name)) {
|
||||||
|
ev->metric_leader = metric_events[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metric_events[0];
|
||||||
|
}
|
||||||
|
|
||||||
static int metricgroup__setup_events(struct list_head *groups,
|
static int metricgroup__setup_events(struct list_head *groups,
|
||||||
struct evlist *perf_evlist,
|
struct evlist *perf_evlist,
|
||||||
struct rblist *metric_events_list)
|
struct rblist *metric_events_list)
|
||||||
|
|
|
@ -31,6 +31,8 @@ struct saved_value {
|
||||||
int cpu;
|
int cpu;
|
||||||
struct runtime_stat *stat;
|
struct runtime_stat *stat;
|
||||||
struct stats stats;
|
struct stats stats;
|
||||||
|
u64 metric_total;
|
||||||
|
int metric_other;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int saved_value_cmp(struct rb_node *rb_node, const void *entry)
|
static int saved_value_cmp(struct rb_node *rb_node, const void *entry)
|
||||||
|
@ -212,6 +214,7 @@ void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
|
||||||
{
|
{
|
||||||
int ctx = evsel_context(counter);
|
int ctx = evsel_context(counter);
|
||||||
u64 count_ns = count;
|
u64 count_ns = count;
|
||||||
|
struct saved_value *v;
|
||||||
|
|
||||||
count *= counter->scale;
|
count *= counter->scale;
|
||||||
|
|
||||||
|
@ -266,9 +269,15 @@ void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
|
||||||
update_runtime_stat(st, STAT_APERF, ctx, cpu, count);
|
update_runtime_stat(st, STAT_APERF, ctx, cpu, count);
|
||||||
|
|
||||||
if (counter->collect_stat) {
|
if (counter->collect_stat) {
|
||||||
struct saved_value *v = saved_value_lookup(counter, cpu, true,
|
v = saved_value_lookup(counter, cpu, true, STAT_NONE, 0, st);
|
||||||
STAT_NONE, 0, st);
|
|
||||||
update_stats(&v->stats, count);
|
update_stats(&v->stats, count);
|
||||||
|
if (counter->metric_leader)
|
||||||
|
v->metric_total += count;
|
||||||
|
} else if (counter->metric_leader) {
|
||||||
|
v = saved_value_lookup(counter->metric_leader,
|
||||||
|
cpu, true, STAT_NONE, 0, st);
|
||||||
|
v->metric_total += count;
|
||||||
|
v->metric_other++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -729,10 +738,10 @@ static void generic_metric(struct perf_stat_config *config,
|
||||||
char *n, *pn;
|
char *n, *pn;
|
||||||
|
|
||||||
expr__ctx_init(&pctx);
|
expr__ctx_init(&pctx);
|
||||||
expr__add_id(&pctx, name, avg);
|
|
||||||
for (i = 0; metric_events[i]; i++) {
|
for (i = 0; metric_events[i]; i++) {
|
||||||
struct saved_value *v;
|
struct saved_value *v;
|
||||||
struct stats *stats;
|
struct stats *stats;
|
||||||
|
u64 metric_total = 0;
|
||||||
|
|
||||||
if (!strcmp(metric_events[i]->name, "duration_time")) {
|
if (!strcmp(metric_events[i]->name, "duration_time")) {
|
||||||
stats = &walltime_nsecs_stats;
|
stats = &walltime_nsecs_stats;
|
||||||
|
@ -744,6 +753,9 @@ static void generic_metric(struct perf_stat_config *config,
|
||||||
break;
|
break;
|
||||||
stats = &v->stats;
|
stats = &v->stats;
|
||||||
scale = 1.0;
|
scale = 1.0;
|
||||||
|
|
||||||
|
if (v->metric_other)
|
||||||
|
metric_total = v->metric_total;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = strdup(metric_events[i]->name);
|
n = strdup(metric_events[i]->name);
|
||||||
|
@ -757,8 +769,15 @@ static void generic_metric(struct perf_stat_config *config,
|
||||||
pn = strchr(n, ' ');
|
pn = strchr(n, ' ');
|
||||||
if (pn)
|
if (pn)
|
||||||
*pn = 0;
|
*pn = 0;
|
||||||
|
|
||||||
|
if (metric_total)
|
||||||
|
expr__add_id(&pctx, n, metric_total);
|
||||||
|
else
|
||||||
expr__add_id(&pctx, n, avg_stats(stats)*scale);
|
expr__add_id(&pctx, n, avg_stats(stats)*scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr__add_id(&pctx, name, avg);
|
||||||
|
|
||||||
if (!metric_events[i]) {
|
if (!metric_events[i]) {
|
||||||
const char *p = metric_expr;
|
const char *p = metric_expr;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue