summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChun-Tse Shao <ctshao@google.com>2026-02-04 02:06:22 +0300
committerArnaldo Carvalho de Melo <acme@redhat.com>2026-02-07 00:18:32 +0300
commitbb5a920b9099127915706fdd23eb540c9a69c338 (patch)
tree0ca4708165d81cac59e1ba57943300da34eebe0b
parent446c595dc0dd1759e56a7d736752d65361e13753 (diff)
downloadlinux-bb5a920b9099127915706fdd23eb540c9a69c338.tar.xz
perf stat: Ensure metrics are displayed even with failed events
Currently, `perf stat` skips or hides metrics when the underlying hardware events cannot be counted (e.g., due to insufficient permissions or unsupported events). In `--metric-only` mode, this often results in missing columns or blank spaces, making the output difficult to parse. Modify the logic to ensure metrics are consistently displayed by propagating NAN (Not a Number) through the expression evaluator. Specifically: 1. Update `prepare_metric()` in stat-shadow.c to treat uncounted events (where `run == 0`) as NAN. This leverages the existing math in expr.y to propagate NAN through metric expressions. 2. Remove the early return in the display logic's `printout()` function that was previously skipping metrics in `--metric-only` mode for failed events. l 3. Simplify `perf_stat__skip_metric_event()` to no longer depend on event runtime. Tested: 1. `perf all metrics test` did not crash while paranoid is 2. 2. Multiple combinations with `CPUs_utilized` while paranoid is 2. $ ./perf stat -M CPUs_utilized -a -- sleep 1 Performance counter stats for 'system wide': <not supported> msec cpu-clock:u # nan CPUs CPUs_utilized 1,006,356,120 duration_time 1.004375550 seconds time elapsed $ ./perf stat -M CPUs_utilized -a -j -- sleep 1 {"counter-value" : "<not supported>", "unit" : "msec", "event" : "cpu-clock:u", "event-runtime" : 0, "pcnt-running" : 100.00, "metric-value" : "nan", "metric-unit" : "CPUs CPUs_utilized"} {"counter-value" : "1006642462.000000", "unit" : "", "event" : "duration_time", "event-runtime" : 1, "pcnt-running" : 100.00} $ ./perf stat -M CPUs_utilized -a --metric-only -- sleep 1 Performance counter stats for 'system wide': CPUs CPUs_utilized nan 1.004424652 seconds time elapsed $ ./perf stat -M CPUs_utilized -a --metric-only -j -- sleep 1 {"CPUs CPUs_utilized" : "none"} Reviewed-by: Ian Rogers <irogers@google.com> Signed-off-by: Chun-Tse Shao <ctshao@google.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: James Clark <james.clark@linaro.org> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Kan Liang <kan.liang@linux.intel.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Yang Li <yang.lee@linux.alibaba.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/util/stat-display.c59
-rw-r--r--tools/perf/util/stat-shadow.c8
-rw-r--r--tools/perf/util/stat.h2
3 files changed, 29 insertions, 40 deletions
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index 2ce0602974a1..dc2b66855f6c 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -820,12 +820,6 @@ static void printout(struct perf_stat_config *config, struct outstate *os,
}
if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
- if (config->metric_only) {
- pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL,
- /*unit=*/NULL, /*val=*/0);
- return;
- }
-
ok = false;
if (counter->supported) {
@@ -848,33 +842,32 @@ static void printout(struct perf_stat_config *config, struct outstate *os,
print_running(config, os, run, ena, /*before_metric=*/true);
}
- if (ok) {
- if (!config->metric_only && counter->default_metricgroup && !counter->default_show_events) {
- void *from = NULL;
-
- aggr_printout(config, os, 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, os, counter, noise, /*before_metric=*/true);
- print_running(config, os, run, ena, /*before_metric=*/true);
- from = perf_stat__print_shadow_stats_metricgroup(config, counter, aggr_idx,
- &num, from, &out);
- } while (from != NULL);
- } else {
- perf_stat__print_shadow_stats(config, counter, aggr_idx, &out);
- }
+ if (!config->metric_only && counter->default_metricgroup &&
+ !counter->default_show_events) {
+ void *from = NULL;
+
+ aggr_printout(config, os, 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, os, counter, noise,
+ /*before_metric=*/true);
+ print_running(config, os, run, ena,
+ /*before_metric=*/true);
+ from = perf_stat__print_shadow_stats_metricgroup(
+ config, counter, aggr_idx, &num, from, &out);
+ } while (from != NULL);
} else {
- pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL, /*unit=*/NULL, /*val=*/0);
+ perf_stat__print_shadow_stats(config, counter, aggr_idx, &out);
}
if (!config->metric_only) {
@@ -987,7 +980,7 @@ 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, ena, run))
+ if (perf_stat__skip_metric_event(counter))
return;
if (val == 0 && should_skip_zero_counter(config, counter, &id))
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
index 9c83f7d96caa..5d8d09e0e6ae 100644
--- a/tools/perf/util/stat-shadow.c
+++ b/tools/perf/util/stat-shadow.c
@@ -83,7 +83,7 @@ static int prepare_metric(struct perf_stat_config *config,
}
/* Time events are always on CPU0, the first aggregation index. */
aggr = &ps->aggr[is_tool_time ? tool_aggr_idx : aggr_idx];
- if (!aggr || !metric_events[i]->supported) {
+ if (!aggr || !metric_events[i]->supported || aggr->counts.run == 0) {
/*
* Not supported events will have a count of 0, which
* can be confusing in a metric. Explicitly set the
@@ -335,14 +335,10 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
* 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,
- u64 ena, u64 run)
+bool perf_stat__skip_metric_event(struct evsel *evsel)
{
if (!evsel->default_metricgroup)
return false;
- if (!ena || !run)
- return true;
-
return !metricgroup__lookup(&evsel->evlist->metric_events, evsel, false);
}
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index f986911c9296..4bced233d2fc 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -163,7 +163,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
struct evsel *evsel,
int aggr_idx,
struct perf_stat_output_ctx *out);
-bool perf_stat__skip_metric_event(struct evsel *evsel, u64 ena, u64 run);
+bool perf_stat__skip_metric_event(struct evsel *evsel);
void *perf_stat__print_shadow_stats_metricgroup(struct perf_stat_config *config,
struct evsel *evsel,
int aggr_idx,