summaryrefslogtreecommitdiff
path: root/tools/perf/util/stat-display.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/stat-display.c')
-rw-r--r--tools/perf/util/stat-display.c202
1 files changed, 127 insertions, 75 deletions
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index a5d72f4a515c..e852ac0d9847 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -115,14 +115,29 @@ static void print_running_csv(struct perf_stat_config *config, u64 run, u64 ena)
config->csv_sep, run, config->csv_sep, enabled_percent);
}
struct outstate {
- FILE *fh;
+ /* Std mode: insert a newline before the next metric */
bool newline;
+ /* JSON mode: track need for comma for a previous field or not */
bool first;
- const char *prefix;
- int nfields;
- int aggr_nr;
+ /* Num CSV separators remaining to pad out when not all fields are printed */
+ int csv_col_pad;
+
+ /*
+ * The following don't track state across fields, but are here as a shortcut to
+ * pass data to the print functions. The alternative would be to update the
+ * function signatures of the entire print stack to pass them through.
+ */
+ /* Place to output to */
+ FILE * const fh;
+ /* Lines are timestamped in --interval-print mode */
+ char timestamp[64];
+ /* Num items aggregated in current line. See struct perf_stat_aggr.nr */
+ int aggr_nr;
+ /* Core/socket/die etc ID for the current line */
struct aggr_cpu_id id;
+ /* Event for current line */
struct evsel *evsel;
+ /* Cgroup for current line */
struct cgroup *cgrp;
};
@@ -419,8 +434,8 @@ 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);
+ if (config->interval)
+ fputs(os->timestamp, os->fh);
aggr_printout(config, os, os->evsel, os->id, os->aggr_nr);
}
@@ -472,7 +487,7 @@ static void new_line_csv(struct perf_stat_config *config, void *ctx)
int i;
__new_line_std_csv(config, os);
- for (i = 0; i < os->nfields; i++)
+ for (i = 0; i < os->csv_col_pad; i++)
fputs(config->csv_sep, os->fh);
}
@@ -523,8 +538,8 @@ static void new_line_json(struct perf_stat_config *config, void *ctx)
fputs("\n{", os->fh);
os->first = true;
- if (os->prefix)
- json_out(os, "%s", os->prefix);
+ if (config->interval)
+ json_out(os, "%s", os->timestamp);
aggr_printout(config, os, os->evsel, os->id, os->aggr_nr);
}
@@ -549,12 +564,12 @@ static void print_metricgroup_header_csv(struct perf_stat_config *config,
if (!metricgroup_name) {
/* Leave space for running and enabling */
- for (i = 0; i < os->nfields - 2; i++)
+ for (i = 0; i < os->csv_col_pad - 2; i++)
fputs(config->csv_sep, os->fh);
return;
}
- for (i = 0; i < os->nfields; i++)
+ for (i = 0; i < os->csv_col_pad; i++)
fputs(config->csv_sep, os->fh);
fprintf(config->output, "%s", metricgroup_name);
new_line_csv(config, ctx);
@@ -673,11 +688,6 @@ static void print_metric_only_json(struct perf_stat_config *config __maybe_unuse
json_out(os, "\"%s\" : \"%s\"", unit, vals);
}
-static void new_line_metric(struct perf_stat_config *config __maybe_unused,
- void *ctx __maybe_unused)
-{
-}
-
static void print_metric_header(struct perf_stat_config *config,
void *ctx,
enum metric_threshold_classify thresh __maybe_unused,
@@ -839,22 +849,23 @@ static void printout(struct perf_stat_config *config, struct outstate *os,
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;
+ nl = config->metric_only ? NULL : new_line_csv;
pmh = print_metricgroup_header_csv;
- os->nfields = 4 + (counter->cgrp ? 1 : 0);
+ os->csv_col_pad = 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;
+ nl = config->metric_only ? NULL : 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;
+ nl = config->metric_only ? NULL : new_line_std;
pmh = print_metricgroup_header_std;
}
if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
if (config->metric_only) {
- pm(config, os, METRIC_THRESHOLD_UNKNOWN, "", "", 0);
+ pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL,
+ /*unit=*/NULL, /*val=*/0);
return;
}
@@ -909,7 +920,7 @@ static void printout(struct perf_stat_config *config, struct outstate *os,
perf_stat__print_shadow_stats(config, counter, uval, aggr_idx,
&out, &config->metric_events);
} else {
- pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL, /*unit=*/"", /*val=*/0);
+ pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL, /*unit=*/NULL, /*val=*/0);
}
if (!config->metric_only) {
@@ -918,12 +929,16 @@ static void printout(struct perf_stat_config *config, struct outstate *os,
}
}
-static void uniquify_event_name(struct evsel *counter)
+static void evsel__uniquify_counter(struct evsel *counter)
{
const char *name, *pmu_name;
char *new_name, *config;
int ret;
+ /* No uniquification necessary. */
+ if (!counter->needs_uniquify)
+ return;
+
/* The evsel was already uniquified. */
if (counter->uniquified_name)
return;
@@ -931,19 +946,6 @@ static void uniquify_event_name(struct evsel *counter)
/* Avoid checking to uniquify twice. */
counter->uniquified_name = true;
- /* The evsel has a "name=" config term or is from libpfm. */
- if (counter->use_config_name || counter->is_libpfm_event)
- return;
-
- /* Legacy no PMU event, don't uniquify. */
- if (!counter->pmu ||
- (counter->pmu->type < PERF_TYPE_MAX && counter->pmu->type != PERF_TYPE_RAW))
- return;
-
- /* A sysfs or json event replacing a legacy event, don't uniquify. */
- if (counter->pmu->is_core && counter->alternate_hw_config != PERF_COUNT_HW_MAX)
- return;
-
name = evsel__name(counter);
pmu_name = counter->pmu->name;
/* Already prefixed by the PMU name. */
@@ -982,17 +984,6 @@ static void uniquify_event_name(struct evsel *counter)
}
}
-static bool hybrid_uniquify(struct evsel *evsel, struct perf_stat_config *config)
-{
- return evsel__is_hybrid(evsel) && !config->hybrid_merge;
-}
-
-static void uniquify_counter(struct perf_stat_config *config, struct evsel *counter)
-{
- if (config->aggr_mode == AGGR_NONE || hybrid_uniquify(counter, config))
- uniquify_event_name(counter);
-}
-
/**
* should_skip_zero_count() - Check if the event should print 0 values.
* @config: The perf stat configuration (including aggregation mode).
@@ -1078,7 +1069,7 @@ static void print_counter_aggrdata(struct perf_stat_config *config,
if (counter->merged_stat)
return;
- uniquify_counter(config, counter);
+ evsel__uniquify_counter(counter);
val = aggr->counts.val;
ena = aggr->counts.ena;
@@ -1095,13 +1086,13 @@ static void print_counter_aggrdata(struct perf_stat_config *config,
os->first = true;
fputc('{', output);
}
- if (os->prefix) {
+ if (config->interval) {
if (config->json_output)
- json_out(os, "%s", os->prefix);
+ json_out(os, "%s", os->timestamp);
else
- fprintf(output, "%s", os->prefix);
+ fprintf(output, "%s", os->timestamp);
} else if (config->summary && config->csv_output &&
- !config->no_csv_summary && !config->interval)
+ !config->no_csv_summary)
fprintf(output, "%s%s", "summary", config->csv_sep);
}
@@ -1128,11 +1119,11 @@ static void print_metric_begin(struct perf_stat_config *config,
if (config->json_output)
fputc('{', config->output);
- if (os->prefix) {
+ if (config->interval) {
if (config->json_output)
- json_out(os, "%s", os->prefix);
+ json_out(os, "%s", os->timestamp);
else
- fprintf(config->output, "%s", os->prefix);
+ fprintf(config->output, "%s", os->timestamp);
}
evsel = evlist__first(evlist);
id = config->aggr_map->map[aggr_idx];
@@ -1318,7 +1309,7 @@ static void print_metric_headers(struct perf_stat_config *config,
struct perf_stat_output_ctx out = {
.ctx = &os,
.print_metric = print_metric_header,
- .new_line = new_line_metric,
+ .new_line = NULL,
.force_header = true,
};
@@ -1353,20 +1344,20 @@ static void print_metric_headers(struct perf_stat_config *config,
fputc('\n', config->output);
}
-static void prepare_interval(struct perf_stat_config *config,
- char *prefix, size_t len, struct timespec *ts)
+static void prepare_timestamp(struct perf_stat_config *config,
+ struct outstate *os, struct timespec *ts)
{
if (config->iostat_run)
return;
if (config->json_output)
- scnprintf(prefix, len, "\"interval\" : %lu.%09lu",
+ scnprintf(os->timestamp, sizeof(os->timestamp), "\"interval\" : %lu.%09lu",
(unsigned long) ts->tv_sec, ts->tv_nsec);
else if (config->csv_output)
- scnprintf(prefix, len, "%lu.%09lu%s",
+ scnprintf(os->timestamp, sizeof(os->timestamp), "%lu.%09lu%s",
(unsigned long) ts->tv_sec, ts->tv_nsec, config->csv_sep);
else
- scnprintf(prefix, len, "%6lu.%09lu ",
+ scnprintf(os->timestamp, sizeof(os->timestamp), "%6lu.%09lu ",
(unsigned long) ts->tv_sec, ts->tv_nsec);
}
@@ -1659,7 +1650,8 @@ static void print_cgroup_counter(struct perf_stat_config *config, struct evlist
print_metric_end(config, os);
}
-static void disable_uniquify(struct evlist *evlist)
+/* Should uniquify be disabled for the evlist? */
+static bool evlist__disable_uniquify(const struct evlist *evlist)
{
struct evsel *counter;
struct perf_pmu *last_pmu = NULL;
@@ -1668,20 +1660,84 @@ static void disable_uniquify(struct evlist *evlist)
evlist__for_each_entry(evlist, counter) {
/* If PMUs vary then uniquify can be useful. */
if (!first && counter->pmu != last_pmu)
- return;
+ return false;
first = false;
if (counter->pmu) {
/* Allow uniquify for uncore PMUs. */
if (!counter->pmu->is_core)
- return;
+ return false;
/* Keep hybrid event names uniquified for clarity. */
if (perf_pmus__num_core_pmus() > 1)
- return;
+ return false;
+ }
+ }
+ return true;
+}
+
+static void evsel__set_needs_uniquify(struct evsel *counter, const struct perf_stat_config *config)
+{
+ struct evsel *evsel;
+
+ if (counter->merged_stat) {
+ /* Counter won't be shown. */
+ return;
+ }
+
+ if (counter->use_config_name || counter->is_libpfm_event) {
+ /* Original name will be used. */
+ return;
+ }
+
+ if (!config->hybrid_merge && evsel__is_hybrid(counter)) {
+ /* Unique hybrid counters necessary. */
+ counter->needs_uniquify = true;
+ return;
+ }
+
+ if (counter->core.attr.type < PERF_TYPE_MAX && counter->core.attr.type != PERF_TYPE_RAW) {
+ /* Legacy event, don't uniquify. */
+ return;
+ }
+
+ if (counter->pmu && counter->pmu->is_core &&
+ counter->alternate_hw_config != PERF_COUNT_HW_MAX) {
+ /* A sysfs or json event replacing a legacy event, don't uniquify. */
+ return;
+ }
+
+ if (config->aggr_mode == AGGR_NONE) {
+ /* Always unique with no aggregation. */
+ counter->needs_uniquify = true;
+ return;
+ }
+
+ /*
+ * Do other non-merged events in the evlist have the same name? If so
+ * uniquify is necessary.
+ */
+ evlist__for_each_entry(counter->evlist, evsel) {
+ if (evsel == counter || evsel->merged_stat)
+ continue;
+
+ if (evsel__name_is(counter, evsel__name(evsel))) {
+ counter->needs_uniquify = true;
+ return;
}
}
- evlist__for_each_entry_continue(evlist, counter) {
- counter->uniquified_name = true;
+}
+
+static void evlist__set_needs_uniquify(struct evlist *evlist, const struct perf_stat_config *config)
+{
+ struct evsel *counter;
+
+ if (evlist__disable_uniquify(evlist)) {
+ evlist__for_each_entry(evlist, counter)
+ counter->uniquified_name = true;
+ return;
}
+
+ evlist__for_each_entry(evlist, counter)
+ evsel__set_needs_uniquify(counter, config);
}
void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config,
@@ -1689,23 +1745,19 @@ void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *conf
int argc, const char **argv)
{
bool metric_only = config->metric_only;
- int interval = config->interval;
struct evsel *counter;
- char buf[64];
struct outstate os = {
.fh = config->output,
.first = true,
};
- disable_uniquify(evlist);
+ evlist__set_needs_uniquify(evlist, config);
if (config->iostat_run)
evlist->selected = evlist__first(evlist);
- if (interval) {
- os.prefix = buf;
- prepare_interval(config, buf, sizeof(buf), ts);
- }
+ if (config->interval)
+ prepare_timestamp(config, &os, ts);
print_header(config, _target, evlist, argc, argv);
@@ -1724,7 +1776,7 @@ void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *conf
case AGGR_THREAD:
case AGGR_GLOBAL:
if (config->iostat_run) {
- iostat_print_counters(evlist, config, ts, buf,
+ iostat_print_counters(evlist, config, ts, os.timestamp,
(iostat_print_counter_t)print_counter, &os);
} else if (config->cgroup_list) {
print_cgroup_counter(config, evlist, &os);