diff options
Diffstat (limited to 'tools/perf/util/stat-display.c')
-rw-r--r-- | tools/perf/util/stat-display.c | 202 |
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); |