From 5c8edd351d1f48f1534260220d5fd4568f423c17 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 16 Sep 2026 22:07:06 -0700 Subject: [PATCH] perf config: Add core.hybrid-merge to configure event merging Provide a core.hybrid-merge configuration option in .perfconfig to allow enabling hybrid event aggregation by default, avoiding the need to pass --hybrid-merge explicitly on every invocation. The config value is a default rather than an explicit request, so with --hierarchy it is ignored with a warning, while giving both --hierarchy and --hybrid-merge on the command line remains an error. For the same reason it is silently ignored when there are no events to merge, as would be the case on any machine with a single core PMU, whereas an explicit --hybrid-merge still warns. Record whether the option came from the command line in symbol_conf so both can tell the two apart. 'perf stat' has its own merging options for counting, the sampling core.hybrid-merge deliberately doesn't alter it and its --hybrid-merge must still be given explicitly. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.1-pro Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-config.txt | 10 ++++++++ tools/perf/builtin-report.c | 24 ++++++++++++++----- tools/perf/builtin-top.c | 30 +++++++++++++++++------- tools/perf/util/config.c | 10 ++++++++ tools/perf/util/symbol_conf.h | 2 ++ 5 files changed, 62 insertions(+), 14 deletions(-) diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index 9b223f892829..6cff6f26af8d 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt @@ -217,6 +217,16 @@ core.*:: Sets a timeout (in milliseconds) for parsing 'addr2line' output. The default timeout is 5s. + hybrid-merge:: + Merge matching events from all hybrid core PMUs into one display + by default. For example, if a wildcard expands to run on both p-cores and + e-cores, this aggregates them into a single view. This applies to + 'perf report' and 'perf top', it is ignored with '--hierarchy' and + doesn't alter 'perf stat' where '--hybrid-merge' must be given + explicitly. As a default it is silently ignored when there is + nothing to merge, such as on a machine with a single core PMU, + whereas an explicit '--hybrid-merge' warns. + tui.*, gtk.*:: Subcommands that can be configured here are 'top', 'report' and 'annotate'. These values are booleans, for example: diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index bda4836fc524..57225bc87731 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -1120,7 +1120,13 @@ static int __cmd_report(struct report *rep) if (evlist__can_merge_hybrid(session->evlist, env)) { evlist__merge_hybrid(session->evlist, env); evlist__merge_hists_hybrid(session->evlist, false); - } else { + } else if (symbol_conf.hybrid_merge_set) { + /* + * Only an explicit --hybrid-merge warns. A + * core.hybrid-merge default is silent, as most + * machines aren't hybrid and there is nothing the + * user needs to do about it. + */ ui__warning("--hybrid-merge: no events to merge across core PMUs\n"); } } @@ -1460,8 +1466,9 @@ int cmd_report(int argc, const char **argv) parse_branch_mode), OPT_BOOLEAN(0, "branch-history", &branch_call_mode, "add last branch records to call history"), - OPT_BOOLEAN(0, "hybrid-merge", &symbol_conf.hybrid_merge, - "merge the same event across hybrid core PMUs"), + OPT_BOOLEAN_SET(0, "hybrid-merge", &symbol_conf.hybrid_merge, + &symbol_conf.hybrid_merge_set, + "merge the same event across hybrid core PMUs"), OPT_STRING(0, "objdump", &objdump_path, "path", "objdump binary to use for disassembly and annotations"), OPT_STRING(0, "addr2line", &addr2line_path, "path", @@ -1562,9 +1569,14 @@ int cmd_report(int argc, const char **argv) } if (symbol_conf.report_hierarchy && symbol_conf.hybrid_merge) { - pr_err("Error: --hierarchy and --hybrid-merge are mutually exclusive.\n"); - ret = -EINVAL; - goto exit; + if (symbol_conf.hybrid_merge_set) { + pr_err("Error: --hierarchy and --hybrid-merge are mutually exclusive.\n"); + ret = -EINVAL; + goto exit; + } + /* A config file default shouldn't fail an explicit option. */ + pr_warning("core.hybrid-merge ignored: --hierarchy cannot display merged hybrid events\n"); + symbol_conf.hybrid_merge = false; } if (disassembler_style) { diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 3bb3337f1244..a3442d2d6edf 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1314,9 +1314,11 @@ static int __cmd_top(struct perf_top *top) } /* - * Use global stat_config that is zero meaning aggr_mode is AGGR_NONE - * and hybrid_merge is false. + * Use global stat_config that is zero meaning aggr_mode is AGGR_NONE. + * Merging affects the event names as merged events share a name, all + * other stat_config behavior is unwanted here. */ + stat_config.hybrid_merge = symbol_conf.hybrid_merge; evlist__uniquify_evsel_names(top->evlist, &stat_config); ret = perf_top__start_counters(top); if (ret) @@ -1340,7 +1342,13 @@ static int __cmd_top(struct perf_top *top) if (evlist__can_merge_hybrid(top->evlist, /*env=*/NULL)) { evlist__merge_hybrid(top->evlist, /*env=*/NULL); evlist__merge_hists_hybrid(top->evlist, false); - } else { + } else if (symbol_conf.hybrid_merge_set) { + /* + * Only an explicit --hybrid-merge warns. A + * core.hybrid-merge default is silent, as most + * machines aren't hybrid and there is nothing the + * user needs to do about it. + */ ui__warning("--hybrid-merge: no events to merge across core PMUs\n"); } } @@ -1500,8 +1508,9 @@ int cmd_top(int argc, const char **argv) OPT_CALLBACK('e', "event", &parse_events_option_args, "event", "event selector. use 'perf list' to list available events", parse_events_option), - OPT_BOOLEAN(0, "hybrid-merge", &symbol_conf.hybrid_merge, - "merge the same event across hybrid core PMUs"), + OPT_BOOLEAN_SET(0, "hybrid-merge", &symbol_conf.hybrid_merge, + &symbol_conf.hybrid_merge_set, + "merge the same event across hybrid core PMUs"), OPT_CALLBACK(0, "filter", &top.evlist, "filter", "event filter", parse_filter), OPT_U64('c', "count", &opts->user_interval, "event period to sample"), @@ -1691,9 +1700,14 @@ int cmd_top(int argc, const char **argv) usage_with_options(top_usage, options); if (symbol_conf.report_hierarchy && symbol_conf.hybrid_merge) { - pr_err("Error: --hierarchy and --hybrid-merge are mutually exclusive.\n"); - status = -EINVAL; - goto out_put_evlist; + if (symbol_conf.hybrid_merge_set) { + pr_err("Error: --hierarchy and --hybrid-merge are mutually exclusive.\n"); + status = -EINVAL; + goto out_put_evlist; + } + /* A config file default shouldn't fail an explicit option. */ + pr_warning("core.hybrid-merge ignored: --hierarchy cannot display merged hybrid events\n"); + symbol_conf.hybrid_merge = false; } if (disassembler_style) { diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index b2972c35c1ec..8fe43b032e9a 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -470,6 +470,16 @@ static int perf_default_core_config(const char *var, const char *value) if (!strcmp(var, "core.addr2line-disable-warn")) symbol_conf.addr2line_disable_warn = perf_config_bool(var, value); + if (!strcmp(var, "core.hybrid-merge")) { + /* + * Note, this is for sampling tools like perf report and top. + * perf stat has its own merging options and the + * stat_config.hybrid_merge of "perf stat --hybrid-merge" is + * deliberately not set here. + */ + symbol_conf.hybrid_merge = perf_config_bool(var, value); + } + /* Add other config variables here. */ return 0; } diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index 50317bdaf2b6..37d35f42dcc1 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h @@ -39,6 +39,8 @@ enum symbol__weight_mode { struct symbol_conf { bool hybrid_merge; + /* Was hybrid_merge given on the command line rather than in a config file? */ + bool hybrid_merge_set; bool nanosecs; unsigned short priv_size; bool try_vmlinux_path,