Skip to content

Commit

Permalink
perf stat: New metricgroup output for the default mode
Browse files Browse the repository at this point in the history
In the default mode, the current output of the metricgroup include both
events and metrics, which is not necessary and just makes the output
hard to read. Since different ARCHs (even different generations in the
same ARCH) may use different events. The output also vary on different
platforms.

For a metricgroup, only outputting the value of each metric is good
enough.

Add a new field default_metricgroup in evsel to indicate an event of the
default metricgroup. For those events, printout() should print the
metricgroup name rather than each event.

Add perf_stat__skip_metric_event() to skip the evsel in the Default
metricgroup, if it's not running or not the metric event.

Add print_metricgroup_header_t to pass the functions which print the
display name of each metricgroup in the Default metricgroup. Support all
three output methods.

Factor out perf_stat__print_shadow_stats_metricgroup() to print out each
metrics.

On SPR:

Before:

 ./perf_old stat sleep 1

 Performance counter stats for 'sleep 1':

              0.54 msec task-clock:u                     #    0.001 CPUs utilized
                 0      context-switches:u               #    0.000 /sec
                 0      cpu-migrations:u                 #    0.000 /sec
                68      page-faults:u                    #  125.445 K/sec
           540,970      cycles:u                         #    0.998 GHz
           556,325      instructions:u                   #    1.03  insn per cycle
           123,602      branches:u                       #  228.018 M/sec
             6,889      branch-misses:u                  #    5.57% of all branches
         3,245,820      TOPDOWN.SLOTS:u                  #     18.4 %  tma_backend_bound
                                                  #     17.2 %  tma_retiring
                                                  #     23.1 %  tma_bad_speculation
                                                  #     41.4 %  tma_frontend_bound
           564,859      topdown-retiring:u
         1,370,999      topdown-fe-bound:u
           603,271      topdown-be-bound:u
           744,874      topdown-bad-spec:u
            12,661      INT_MISC.UOP_DROPPING:u          #   23.357 M/sec

       1.001798215 seconds time elapsed

       0.000193000 seconds user
       0.001700000 seconds sys

After:

$ ./perf stat sleep 1

 Performance counter stats for 'sleep 1':

              0.51 msec task-clock:u                     #    0.001 CPUs utilized
                 0      context-switches:u               #    0.000 /sec
                 0      cpu-migrations:u                 #    0.000 /sec
                68      page-faults:u                    #  132.683 K/sec
           545,228      cycles:u                         #    1.064 GHz
           555,509      instructions:u                   #    1.02  insn per cycle
           123,574      branches:u                       #  241.120 M/sec
             6,957      branch-misses:u                  #    5.63% of all branches
                        TopdownL1                 #     17.5 %  tma_backend_bound
                                                  #     22.6 %  tma_bad_speculation
                                                  #     42.7 %  tma_frontend_bound
                                                  #     17.1 %  tma_retiring
                        TopdownL2                 #     21.8 %  tma_branch_mispredicts
                                                  #     11.5 %  tma_core_bound
                                                  #     13.4 %  tma_fetch_bandwidth
                                                  #     29.3 %  tma_fetch_latency
                                                  #      2.7 %  tma_heavy_operations
                                                  #     14.5 %  tma_light_operations
                                                  #      0.8 %  tma_machine_clears
                                                  #      6.1 %  tma_memory_bound

       1.001712086 seconds time elapsed

       0.000151000 seconds user
       0.001618000 seconds sys

Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ahmad Yasin <ahmad.yasin@intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: https://lore.kernel.org/r/20230616031420.3751973-3-kan.liang@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
  • Loading branch information
Kan Liang authored and Arnaldo Carvalho de Melo committed Jun 16, 2023
1 parent 1c0e479 commit 6a80d79
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 22 deletions.
1 change: 1 addition & 0 deletions tools/perf/builtin-stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -2172,6 +2172,7 @@ static int add_default_attributes(void)

evlist__for_each_entry(metric_evlist, metric_evsel) {
metric_evsel->skippable = true;
metric_evsel->default_metricgroup = true;
}
evlist__splice_list_tail(evsel_list, &metric_evlist->core.entries);
evlist__delete(metric_evlist);
Expand Down
1 change: 1 addition & 0 deletions tools/perf/util/evsel.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ struct evsel {
bool reset_group;
bool errored;
bool needs_auxtrace_mmap;
bool default_metricgroup; /* A member of the Default metricgroup */
struct hashmap *per_pkg_mask;
int err;
struct {
Expand Down
108 changes: 98 additions & 10 deletions tools/perf/util/stat-display.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#define CNTR_NOT_SUPPORTED "<not supported>"
#define CNTR_NOT_COUNTED "<not counted>"

#define MGROUP_LEN 50
#define METRIC_LEN 38
#define EVNAME_LEN 32
#define COUNTS_LEN 18
Expand Down Expand Up @@ -364,16 +365,27 @@ static void new_line_std(struct perf_stat_config *config __maybe_unused,
os->newline = true;
}

static void do_new_line_std(struct perf_stat_config *config,
struct outstate *os)
static inline void __new_line_std_csv(struct perf_stat_config *config,
struct outstate *os)
{
fputc('\n', os->fh);
if (os->prefix)
fputs(os->prefix, os->fh);
aggr_printout(config, os->evsel, os->id, os->aggr_nr);
}

static inline void __new_line_std(struct outstate *os)
{
fprintf(os->fh, " ");
}

static void do_new_line_std(struct perf_stat_config *config,
struct outstate *os)
{
__new_line_std_csv(config, os);
if (config->aggr_mode == AGGR_NONE)
fprintf(os->fh, " ");
fprintf(os->fh, " ");
__new_line_std(os);
}

static void print_metric_std(struct perf_stat_config *config,
Expand Down Expand Up @@ -408,10 +420,7 @@ static void new_line_csv(struct perf_stat_config *config, void *ctx)
struct outstate *os = ctx;
int i;

fputc('\n', os->fh);
if (os->prefix)
fprintf(os->fh, "%s", os->prefix);
aggr_printout(config, os->evsel, os->id, os->aggr_nr);
__new_line_std_csv(config, os);
for (i = 0; i < os->nfields; i++)
fputs(config->csv_sep, os->fh);
}
Expand Down Expand Up @@ -462,6 +471,54 @@ static void new_line_json(struct perf_stat_config *config, void *ctx)
aggr_printout(config, os->evsel, os->id, os->aggr_nr);
}

static void print_metricgroup_header_json(struct perf_stat_config *config,
void *ctx,
const char *metricgroup_name)
{
if (!metricgroup_name)
return;

fprintf(config->output, "\"metricgroup\" : \"%s\"}", metricgroup_name);
new_line_json(config, ctx);
}

static void print_metricgroup_header_csv(struct perf_stat_config *config,
void *ctx,
const char *metricgroup_name)
{
struct outstate *os = ctx;
int i;

if (!metricgroup_name) {
/* Leave space for running and enabling */
for (i = 0; i < os->nfields - 2; i++)
fputs(config->csv_sep, os->fh);
return;
}

for (i = 0; i < os->nfields; i++)
fputs(config->csv_sep, os->fh);
fprintf(config->output, "%s", metricgroup_name);
new_line_csv(config, ctx);
}

static void print_metricgroup_header_std(struct perf_stat_config *config,
void *ctx,
const char *metricgroup_name)
{
struct outstate *os = ctx;
int n;

if (!metricgroup_name) {
__new_line_std(os);
return;
}

n = fprintf(config->output, " %*s", EVNAME_LEN, metricgroup_name);

fprintf(config->output, "%*s", MGROUP_LEN - n - 1, "");
}

/* Filter out some columns that don't work well in metrics only mode */

static bool valid_only_metric(const char *unit)
Expand Down Expand Up @@ -713,19 +770,23 @@ static void printout(struct perf_stat_config *config, struct outstate *os,
struct perf_stat_output_ctx out;
print_metric_t pm;
new_line_t nl;
print_metricgroup_header_t pmh;
bool ok = true;
struct evsel *counter = os->evsel;

if (config->csv_output) {
pm = config->metric_only ? print_metric_only_csv : print_metric_csv;
nl = config->metric_only ? new_line_metric : new_line_csv;
pmh = print_metricgroup_header_csv;
os->nfields = 4 + (counter->cgrp ? 1 : 0);
} else if (config->json_output) {
pm = config->metric_only ? print_metric_only_json : print_metric_json;
nl = config->metric_only ? new_line_metric : new_line_json;
pmh = print_metricgroup_header_json;
} else {
pm = config->metric_only ? print_metric_only : print_metric_std;
nl = config->metric_only ? new_line_metric : new_line_std;
pmh = print_metricgroup_header_std;
}

if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
Expand All @@ -747,19 +808,43 @@ static void printout(struct perf_stat_config *config, struct outstate *os,

out.print_metric = pm;
out.new_line = nl;
out.print_metricgroup_header = pmh;
out.ctx = os;
out.force_header = false;

if (!config->metric_only) {
if (!config->metric_only && !counter->default_metricgroup) {
abs_printout(config, os->id, os->aggr_nr, counter, uval, ok);

print_noise(config, counter, noise, /*before_metric=*/true);
print_running(config, run, ena, /*before_metric=*/true);
}

if (ok) {
perf_stat__print_shadow_stats(config, counter, uval, aggr_idx,
&out, &config->metric_events);
if (!config->metric_only && counter->default_metricgroup) {
void *from = NULL;

aggr_printout(config, os->evsel, os->id, os->aggr_nr);
/* Print out all the metricgroup with the same metric event. */
do {
int num = 0;

/* Print out the new line for the next new metricgroup. */
if (from) {
if (config->json_output)
new_line_json(config, (void *)os);
else
__new_line_std_csv(config, os);
}

print_noise(config, counter, noise, /*before_metric=*/true);
print_running(config, run, ena, /*before_metric=*/true);
from = perf_stat__print_shadow_stats_metricgroup(config, counter, aggr_idx,
&num, from, &out,
&config->metric_events);
} while (from != NULL);
} else
perf_stat__print_shadow_stats(config, counter, uval, aggr_idx,
&out, &config->metric_events);
} else {
pm(config, os, /*color=*/NULL, /*format=*/NULL, /*unit=*/"", /*val=*/0);
}
Expand Down Expand Up @@ -889,6 +974,9 @@ static void print_counter_aggrdata(struct perf_stat_config *config,
ena = aggr->counts.ena;
run = aggr->counts.run;

if (perf_stat__skip_metric_event(counter, &config->metric_events, ena, run))
return;

if (val == 0 && should_skip_zero_counter(config, counter, &id))
return;

Expand Down
131 changes: 119 additions & 12 deletions tools/perf/util/stat-shadow.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,106 @@ double test_generic_metric(struct metric_expr *mexp, int aggr_idx)
return ratio;
}

static void perf_stat__print_metricgroup_header(struct perf_stat_config *config,
struct evsel *evsel,
void *ctxp,
const char *name,
struct perf_stat_output_ctx *out)
{
bool need_full_name = perf_pmus__num_core_pmus() > 1;
static const char *last_name;
static const char *last_pmu;
char full_name[64];

/*
* A metricgroup may have several metric events,
* e.g.,TopdownL1 on e-core of ADL.
* The name has been output by the first metric
* event. Only align with other metics from
* different metric events.
*/
if (last_name && !strcmp(last_name, name)) {
if (!need_full_name || !strcmp(last_pmu, evsel->pmu_name)) {
out->print_metricgroup_header(config, ctxp, NULL);
return;
}
}

if (need_full_name)
scnprintf(full_name, sizeof(full_name), "%s (%s)", name, evsel->pmu_name);
else
scnprintf(full_name, sizeof(full_name), "%s", name);

out->print_metricgroup_header(config, ctxp, full_name);

last_name = name;
last_pmu = evsel->pmu_name;
}

/**
* perf_stat__print_shadow_stats_metricgroup - Print out metrics associated with the evsel
* For the non-default, all metrics associated
* with the evsel are printed.
* For the default mode, only the metrics from
* the same metricgroup and the name of the
* metricgroup are printed. To print the metrics
* from the next metricgroup (if available),
* invoke the function with correspoinding
* metric_expr.
*/
void *perf_stat__print_shadow_stats_metricgroup(struct perf_stat_config *config,
struct evsel *evsel,
int aggr_idx,
int *num,
void *from,
struct perf_stat_output_ctx *out,
struct rblist *metric_events)
{
struct metric_event *me;
struct metric_expr *mexp = from;
void *ctxp = out->ctx;
bool header_printed = false;
const char *name = NULL;

me = metricgroup__lookup(metric_events, evsel, false);
if (me == NULL)
return NULL;

if (!mexp)
mexp = list_first_entry(&me->head, typeof(*mexp), nd);

list_for_each_entry_from(mexp, &me->head, nd) {
/* Print the display name of the Default metricgroup */
if (!config->metric_only && me->is_default) {
if (!name)
name = mexp->default_metricgroup_name;
/*
* Two or more metricgroup may share the same metric
* event, e.g., TopdownL1 and TopdownL2 on SPR.
* Return and print the prefix, e.g., noise, running
* for the next metricgroup.
*/
if (strcmp(name, mexp->default_metricgroup_name))
return (void *)mexp;
/* Only print the name of the metricgroup once */
if (!header_printed) {
header_printed = true;
perf_stat__print_metricgroup_header(config, evsel, ctxp,
name, out);
}
}

if ((*num)++ > 0)
out->new_line(config, ctxp);
generic_metric(config, mexp->metric_expr, mexp->metric_threshold,
mexp->metric_events, mexp->metric_refs, evsel->name,
mexp->metric_name, mexp->metric_unit, mexp->runtime,
aggr_idx, out);
}

return NULL;
}

void perf_stat__print_shadow_stats(struct perf_stat_config *config,
struct evsel *evsel,
double avg, int aggr_idx,
Expand All @@ -565,7 +665,6 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
};
print_metric_t print_metric = out->print_metric;
void *ctxp = out->ctx;
struct metric_event *me;
int num = 1;

if (config->iostat_run) {
Expand All @@ -592,18 +691,26 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
}
}

if ((me = metricgroup__lookup(metric_events, evsel, false)) != NULL) {
struct metric_expr *mexp;
perf_stat__print_shadow_stats_metricgroup(config, evsel, aggr_idx,
&num, NULL, out, metric_events);

list_for_each_entry (mexp, &me->head, nd) {
if (num++ > 0)
out->new_line(config, ctxp);
generic_metric(config, mexp->metric_expr, mexp->metric_threshold,
mexp->metric_events, mexp->metric_refs, evsel->name,
mexp->metric_name, mexp->metric_unit, mexp->runtime,
aggr_idx, out);
}
}
if (num == 0)
print_metric(config, ctxp, NULL, NULL, NULL, 0);
}

/**
* perf_stat__skip_metric_event - Skip the evsel in the Default metricgroup,
* if it's not running or not the metric event.
*/
bool perf_stat__skip_metric_event(struct evsel *evsel,
struct rblist *metric_events,
u64 ena, u64 run)
{
if (!evsel->default_metricgroup)
return false;

if (!ena || !run)
return true;

return !metricgroup__lookup(metric_events, evsel, false);
}
15 changes: 15 additions & 0 deletions tools/perf/util/stat.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,16 @@ typedef void (*print_metric_t)(struct perf_stat_config *config,
const char *fmt, double val);
typedef void (*new_line_t)(struct perf_stat_config *config, void *ctx);

/* Used to print the display name of the Default metricgroup for now. */
typedef void (*print_metricgroup_header_t)(struct perf_stat_config *config,
void *ctx, const char *metricgroup_name);

void perf_stat__reset_shadow_stats(void);
struct perf_stat_output_ctx {
void *ctx;
print_metric_t print_metric;
new_line_t new_line;
print_metricgroup_header_t print_metricgroup_header;
bool force_header;
};

Expand All @@ -171,6 +176,16 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
double avg, int aggr_idx,
struct perf_stat_output_ctx *out,
struct rblist *metric_events);
bool perf_stat__skip_metric_event(struct evsel *evsel,
struct rblist *metric_events,
u64 ena, u64 run);
void *perf_stat__print_shadow_stats_metricgroup(struct perf_stat_config *config,
struct evsel *evsel,
int aggr_idx,
int *num,
void *from,
struct perf_stat_output_ctx *out,
struct rblist *metric_events);

int evlist__alloc_stats(struct perf_stat_config *config,
struct evlist *evlist, bool alloc_raw);
Expand Down

0 comments on commit 6a80d79

Please sign in to comment.