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:
Jin Yao
2019-08-28 13:59:32 +08:00
committed by Arnaldo Carvalho de Melo
parent 287f2649f7
commit f01642e491
3 changed files with 68 additions and 44 deletions

View File

@@ -90,57 +90,61 @@ struct egroup {
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,
const char **ids,
int idnum,
struct evsel **metric_events)
{
struct evsel *ev, *start = NULL;
int ind = 0;
struct evsel *ev;
int i = 0;
bool leader_found;
evlist__for_each_entry (perf_evlist, ev) {
if (ev->collect_stat)
continue;
if (!strcmp(ev->name, ids[ind])) {
if (record_evsel(&ind, &start, idnum,
metric_events, ev))
return start;
if (!strcmp(ev->name, ids[i])) {
if (!metric_events[i])
metric_events[i] = ev;
} else {
/*
* We saw some other event that is not
* in our list of events. Discard
* the whole match and start again.
*/
ind = 0;
start = NULL;
if (!strcmp(ev->name, ids[ind])) {
if (record_evsel(&ind, &start, idnum,
metric_events, ev))
return start;
if (++i == idnum) {
/* Discard the whole match and start again */
i = 0;
memset(metric_events, 0,
sizeof(struct evsel *) * idnum);
continue;
}
if (!strcmp(ev->name, ids[i]))
metric_events[i] = ev;
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
* events, like for uncore events.
* We don't support this case for now.
*/
return NULL;
if (i != idnum - 1) {
/* Not whole match */
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,