[PATCH v1 23/41] perf stat report: Display metrics for recorded events
From: Ian Rogers
Date: Tue Nov 03 2026 - 18:53:11 EST
perf stat report displays the recorded counts but not the metrics that
perf stat record displayed, as metrics aren't recorded in the perf.data
file. Inspect the recorded command line in struct perf_env and display
the metrics requested on the perf stat record command line (or the
default metrics when no events or metrics were explicitly specified),
for example:
$ perf stat record -M insn_per_cycle -a sleep 1
$ perf stat report
...
784,599,279 cpu_atom/cpu-cycles/ # 0.7 instructions insn_per_cycle
586,070,692 cpu_atom/instructions/
776,737,361 cpu_core/cpu-cycles/ # 1.3 instructions insn_per_cycle
1,011,699,468 cpu_core/instructions/
1.004467030 seconds time elapsed
Also add --all-metrics (and the stat.all-metrics config option) to
display all metrics whose events were recorded in the perf.data file,
as well as -M/--metrics and --metric-only options to perf stat report.
Set stat_config.system_wide from the recorded thread map as metrics
such as CPUs_utilized depend upon it.
Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/Documentation/perf-config.txt | 7 ++++
tools/perf/Documentation/perf-stat.txt | 5 +++
tools/perf/builtin-stat.c | 53 +++++++++++++++++++++---
tools/perf/util/config.c | 3 ++
tools/perf/util/stat.h | 1 +
5 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
index 6cff6f26af8d..906e59c7e9d5 100644
--- a/tools/perf/Documentation/perf-config.txt
+++ b/tools/perf/Documentation/perf-config.txt
@@ -713,6 +713,13 @@ stat.*::
(boolean) Change the default for "--big-num". To make
"--no-big-num" the default, set "stat.big-num=false".
+ stat.all-metrics::
+ (boolean) Change the default for "--all-metrics" in
+ "perf stat report" and "perf script". When true, all metrics
+ whose events were recorded in the data file are displayed rather
+ than only the metrics requested on the "perf stat record"
+ command line.
+
intel-pt.*::
intel-pt.cache-divisor::
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 8ea17da89976..7258ec280770 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -426,6 +426,11 @@ information at the boundary of the level 3 cache in the system.
--per-core::
Aggregate counts per physical processor for system-wide mode measurements.
+--all-metrics::
+Display all metrics that match the recorded events in the perf data file,
+rather than only the metrics requested on the 'perf stat record' command line.
+Default setting can be changed with "perf config stat.all-metrics=true".
+
-M::
--metrics::
Print metrics or metricgroups specified in a comma separated list.
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index f81192cec481..12004244af0e 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -2232,6 +2232,28 @@ static int process_stat_round_event(const struct perf_tool *tool __maybe_unused,
struct perf_env *env = perf_session__env(session);
const char **argv = env->cmdline_argv;
int argc = env->nr_cmdline;
+ static bool metrics_added;
+
+ if (!metrics_added) {
+ struct perf_thread_map *threads = evlist__core(session->evlist)->threads;
+ bool system_wide = !threads || perf_thread_map__pid(threads, /*idx=*/0) == -1;
+
+ /* Metrics may use system_wide, such as for #target_cpu. */
+ stat_config.system_wide = system_wide;
+ metricgroup__add_recorded_metrics(session->evlist, metrics,
+ parse_events_option_args.pmu_filter,
+ parse_events_option_args.cputype_filter,
+ stat_config.metric_no_group,
+ stat_config.metric_no_merge,
+ stat_config.metric_no_threshold,
+ stat_config.all_metrics,
+ system_wide,
+ /*default_metricgroup=*/true,
+ force_metric_only ? NULL
+ : &stat_config.metric_only);
+ zfree(&metrics);
+ metrics_added = true;
+ }
process_counters();
@@ -2282,6 +2304,8 @@ int process_stat_config_event(const struct perf_tool *tool,
static int set_maps(struct perf_stat *st)
{
+ struct evsel *counter;
+
if (!st->cpus || !st->threads)
return 0;
@@ -2293,6 +2317,9 @@ static int set_maps(struct perf_stat *st)
if (evlist__alloc_stats(&stat_config, evsel_list, /*alloc_raw=*/true))
return -ENOMEM;
+ evlist__for_each_entry(evsel_list, counter)
+ counter->supported = false;
+
st->maps_allocated = true;
return 0;
}
@@ -2351,7 +2378,7 @@ static int __cmd_report(int argc, const char **argv)
{
struct perf_session *session;
struct opt_aggr_mode opt_mode = {};
- const struct option options[] = {
+ struct option options[] = {
OPT_STRING('i', "input", &input_name, "file", "input file name"),
OPT_BOOLEAN(0, "per-thread", &opt_mode.thread, "aggregate counts per thread"),
OPT_BOOLEAN(0, "per-socket", &opt_mode.socket,
@@ -2367,11 +2394,20 @@ static int __cmd_report(int argc, const char **argv)
OPT_BOOLEAN(0, "per-node", &opt_mode.node, "aggregate counts per numa node"),
OPT_BOOLEAN('A', "no-aggr", &opt_mode.no_aggr,
"disable aggregation across CPUs or PMUs"),
+ OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list",
+ "display specified metrics or metric groups (separated by ,)",
+ append_metric_groups),
+ OPT_BOOLEAN(0, "all-metrics", &stat_config.all_metrics,
+ "display all metrics matching recorded events"),
+ OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL,
+ "Only print computed metrics. No raw values",
+ enable_metric_only),
OPT_END()
};
struct stat st;
int ret;
+ set_option_flag(options, 'M', "metrics", PARSE_OPT_NONEG);
argc = parse_options(argc, argv, options, stat_report_usage, 0);
perf_stat.aggr_mode = opt_aggr_mode_to_aggr_mode(&opt_mode);
@@ -2398,8 +2434,10 @@ static int __cmd_report(int argc, const char **argv)
perf_stat.tool.stat_round = process_stat_round_event;
session = perf_session__new(&perf_stat.data, &perf_stat.tool);
- if (IS_ERR(session))
- return PTR_ERR(session);
+ if (IS_ERR(session)) {
+ ret = PTR_ERR(session);
+ goto out;
+ }
perf_stat.session = session;
stat_config.output = stderr;
@@ -2408,10 +2446,13 @@ static int __cmd_report(int argc, const char **argv)
ret = perf_session__process_events(session);
if (ret)
- return ret;
+ goto out;
perf_session__delete(session);
- return 0;
+out:
+ /* Freed when processing the first stat round, if there was one. */
+ zfree(&metrics);
+ return ret;
}
static void setup_system_wide(int forks)
@@ -2589,6 +2630,8 @@ int cmd_stat(int argc, const char **argv)
OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list",
"monitor specified metrics or metric groups (separated by ,)",
append_metric_groups),
+ OPT_BOOLEAN(0, "all-metrics", &stat_config.all_metrics,
+ "display all metrics matching recorded events (report only)"),
OPT_BOOLEAN_FLAG(0, "all-kernel", &stat_config.all_kernel,
"Configure all used events to run in kernel space.",
PARSE_OPT_EXCLUSIVE),
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 287401f159e6..d64d0db51d8e 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -514,6 +514,9 @@ static int perf_stat_config(const char *var, const char *value)
if (!strcmp(var, "stat.bpf-counter-events"))
evsel__bpf_counter_events = strdup(value);
+ if (!strcmp(var, "stat.all-metrics"))
+ stat_config.all_metrics = perf_config_bool(var, value);
+
/* Add other config variables here. */
return 0;
}
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index e3598037a6aa..a957479804c1 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -82,6 +82,7 @@ struct perf_stat_config {
bool metric_no_group;
bool metric_no_merge;
bool metric_no_threshold;
+ bool all_metrics;
bool hardware_aware_grouping;
bool stop_read_counter;
bool iostat_run;
--
2.56.0.385.gac671c819a-goog