From nobody Mon Jun 8 22:52:29 2026 Received: from mail-pg1-f201.google.com (mail-pg1-f201.google.com [209.85.215.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4A63D34AAE3 for ; Mon, 25 May 2026 23:19:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751156; cv=none; b=c0KXqizfCmVQtMG6Jy9Kj1ifvOn+RmzMrPravMGQV+9vOPhOdY1cWpWxaEmiip8wfQkTmBCFhneEfCdGJbQL1Z416Aw+vQfMHg6LacKSY1DKcnwMxIEtMPBryp7gLrOFGIJoCqjGWXlTlMWxg0uCV1/Wf7jKioPmcyg+iIPa12g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751156; c=relaxed/simple; bh=YW/PB6OhofSUgC0gwmp9VNw+PaDdq2oRNCE/6R5+QhI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=gWTqBIjiquQj8i4w8iI8g/OHRuckhY22lJ7hxXLmIZckSbTFwmUtiuYAhHgIJ4BbX53NETiI0wwknE8fij2eJj3N6kMuuJ///j3uoLpsg8Ul8urdFOZw+0MiTjAih2EkwYsJZj80mMi3xXhHtCCx1021U8F1e0AYp1WfG9A4IqI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=DEiIZQGw; arc=none smtp.client-ip=209.85.215.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="DEiIZQGw" Received: by mail-pg1-f201.google.com with SMTP id 41be03b00d2f7-c8514f8ed5dso2939855a12.3 for ; Mon, 25 May 2026 16:19:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751153; x=1780355953; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=zv+XX5u/+jo6hcOxSb6D7eKRFv4netVa4jcNjpv4Efk=; b=DEiIZQGwVcGXd/My3eHXFEVLjlU7J/i1ACHWWa/VkGcKsC0TaMffwnBwFAL7sVbE2t lQiEmk/G7OmhbkQZNkAc3OeHqdzSEiZW9SQi6ahWhHxRFMJv5nqr4qJjE7u4i8XP9gf4 WTPL0xiYWEMLtMUwCND2x8OpH7mb5GEz15RaPGk2wC+PPZa/Q6mgCCwWogu2ZaVCrys+ oIaV9xDCKhOyF9LtYKbqtIr21F2ca3eTiMttlWP6PH3nzH8FLxTOTZSe0W6wXV6vrFSg FiO+S8jaMQZfRqiMEGC//e5sv0RU0nv7tYwe+WysxEnRRAzlRgx7vP9PstBTHtoIazXf AYyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751153; x=1780355953; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=zv+XX5u/+jo6hcOxSb6D7eKRFv4netVa4jcNjpv4Efk=; b=RMVCjz0PTVdxXL8uw5IvEm7pNTxAJiYcOBIl+tyxGFu498waRfA0mYori1JwFBRZgN SHEHQqD9yWnO9LqQ7sggMfEaDs+cHg+/o5w9Ug86ZlL21drk4m0GEecwQarsFRMbuAnH NCP+n8tuAK6o4gNjO9zO2oX735/YGOVwDHdwUZM2KYGwNWDHkAUgrLBvdppFUef0n46i lPNZs8RzzapVqAOIU9HmzBs9MxWDDD3HW0NKwtYntSx4mttkooHb7s1hNKYkFTIf1Elq AsQu5mtUSQjoeYhaEwU0rCaXL4RfeXCBzBuG7ap95u6CHcUw7Yrmj6R5gTIM/JO0zPs4 3ysw== X-Forwarded-Encrypted: i=1; AFNElJ+EFsJV0poBMcaTmJO4EJ/j/eQCP1mtkAKrJ+Apg0j4VjlVI18BRC7xhFR6VV2tJshsscJmrT4xTIqHd8g=@vger.kernel.org X-Gm-Message-State: AOJu0YwaIbz7fbukRC0h+zJDkEG4mWhFQmEKmZ/ZKVTvqaXvQ6JGDZH6 malv12Z4Z+64O2dF7+stmbEK7GYxltxpZyWN/H22pivV6BApTc7P2HXa3gzfFLATtfUXxugQaGk gGFKkzq8T+Q== X-Received: from pfij18.prod.google.com ([2002:aa7:8012:0:b0:835:20c7:97df]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a00:4b4f:b0:829:8cfb:df45 with SMTP id d2e1a72fcca58-8415f323ebdmr15444446b3a.15.1779751152407; Mon, 25 May 2026 16:19:12 -0700 (PDT) Date: Mon, 25 May 2026 16:18:47 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-2-irogers@google.com> Subject: [RFC PATCH v2 01/14] perf stat: Introduce core generic print traversal engine and header stubs From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch introduces the initial infrastructure for decoupling the perf stat printing API. It declares the struct perf_stat_print_callbacks interface and the core traversal driver perf_stat__print_cb() inside the newly created util/stat-print.h and util/stat-print.c files. The generic traversal driver perf_stat__print_cb() drive traversing the event lists across all supported cpu aggregation modes (global, die, socket, cache, cluster, core, thread, none). It implements the clean display filtering checks (perf_stat__skip_metric_event(), hybrid wildcard merges) and the basic metrics allowlist filter (is_basic_shadow_metric()) to keep formatting callbacks decoupled. This also introduces two format-agnostic shared helpers to centralize aggregation prefix formatting: - perf_stat__get_aggr_key(): resolves JSON key names. - perf_stat__get_aggr_id_char(): formats unified aggregation identifiers. Adds empty format-specific stubs (perf_stat__print_std, _csv, _json) to ensure that the new print files link and compile cleanly under util/Buil= d, without affecting the legacy print path. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/builtin-stat.c | 261 ++++++++-------- tools/perf/util/Build | 4 + tools/perf/util/stat-display.c | 28 +- tools/perf/util/stat-print-csv.c | 13 + tools/perf/util/stat-print-json.c | 13 + tools/perf/util/stat-print-std.c | 13 + tools/perf/util/stat-print.c | 490 ++++++++++++++++++++++++++++++ tools/perf/util/stat-print.h | 133 ++++++++ tools/perf/util/stat.h | 2 + 9 files changed, 815 insertions(+), 142 deletions(-) create mode 100644 tools/perf/util/stat-print-csv.c create mode 100644 tools/perf/util/stat-print-json.c create mode 100644 tools/perf/util/stat-print-std.c create mode 100644 tools/perf/util/stat-print.c create mode 100644 tools/perf/util/stat-print.h diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 99d7db372b48..ef8f5da99c64 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -40,62 +40,64 @@ * Jaswinder Singh Rajput */ =20 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "asm/bug.h" #include "builtin.h" +#include "util/affinity.h" +#include "util/bpf_counter.h" #include "util/cgroup.h" -#include -#include "util/parse-events.h" -#include "util/pmus.h" -#include "util/pmu.h" -#include "util/tool_pmu.h" +#include "util/color.h" +#include "util/counts.h" +#include "util/cpumap.h" +#include "util/debug.h" #include "util/event.h" #include "util/evlist.h" #include "util/evsel.h" -#include "util/debug.h" -#include "util/color.h" -#include "util/stat.h" #include "util/header.h" -#include "util/cpumap.h" -#include "util/thread_map.h" -#include "util/counts.h" -#include "util/topdown.h" +#include "util/intel-tpebs.h" +#include "util/iostat.h" +#include "util/metricgroup.h" +#include "util/parse-events.h" +#include "util/pfm.h" +#include "util/pmu.h" +#include "util/pmus.h" #include "util/session.h" -#include "util/tool.h" +#include "util/stat-print.h" +#include "util/stat.h" #include "util/string2.h" -#include "util/metricgroup.h" #include "util/synthetic-events.h" #include "util/target.h" +#include "util/thread_map.h" #include "util/time-utils.h" +#include "util/tool.h" +#include "util/tool_pmu.h" #include "util/top.h" -#include "util/affinity.h" -#include "util/pfm.h" -#include "util/bpf_counter.h" -#include "util/iostat.h" +#include "util/topdown.h" #include "util/util.h" -#include "util/intel-tpebs.h" -#include "asm/bug.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include =20 #ifdef HAVE_BPF_SKEL #include "util/bpf_skel/bperf_cgroup.h" @@ -123,6 +125,7 @@ static struct target target; static volatile sig_atomic_t child_pid =3D -1; static int detailed_run =3D 0; static bool transaction_run; +static bool use_perf_stat_print; static bool topdown_run =3D false; static bool smi_cost =3D false; static bool smi_reset =3D false; @@ -1091,7 +1094,10 @@ static void print_counters(struct timespec *ts, int = argc, const char **argv) if (quiet) return; =20 - evlist__print_counters(evsel_list, &stat_config, &target, ts, argc, argv); + if (use_perf_stat_print) + perf_stat__print(evsel_list, &stat_config, &target, ts, argc, argv); + else + evlist__print_counters(evsel_list, &stat_config, &target, ts, argc, argv= ); } =20 static volatile sig_atomic_t signr =3D -1; @@ -2455,155 +2461,152 @@ int cmd_stat(int argc, const char **argv) bool affinity =3D true, affinity_set =3D false; struct option stat_options[] =3D { OPT_BOOLEAN('T', "transaction", &transaction_run, - "hardware transaction statistics"), + "hardware transaction statistics"), OPT_CALLBACK('e', "event", &parse_events_option_args, "event", - "event selector. use 'perf list' to list available events", - parse_events_option), - OPT_CALLBACK(0, "filter", &evsel_list, "filter", - "event filter", parse_filter), + "event selector. use 'perf list' to list available events", + parse_events_option), + OPT_CALLBACK(0, "filter", &evsel_list, "filter", "event filter", parse_f= ilter), OPT_BOOLEAN('i', "no-inherit", &stat_config.no_inherit, - "child tasks do not inherit counters"), - OPT_STRING('p', "pid", &target.pid, "pid", - "stat events on existing process id"), - OPT_STRING('t', "tid", &target.tid, "tid", - "stat events on existing thread id"), + "child tasks do not inherit counters"), + OPT_STRING('p', "pid", &target.pid, "pid", "stat events on existing proc= ess id"), + OPT_STRING('t', "tid", &target.tid, "tid", "stat events on existing thre= ad id"), #ifdef HAVE_BPF_SKEL OPT_STRING('b', "bpf-prog", &target.bpf_str, "bpf-prog-id", - "stat events on existing bpf program id"), - OPT_BOOLEAN(0, "bpf-counters", &target.use_bpf, - "use bpf program to count events"), + "stat events on existing bpf program id"), + OPT_BOOLEAN(0, "bpf-counters", &target.use_bpf, "use bpf program to coun= t events"), OPT_STRING(0, "bpf-attr-map", &target.attr_map, "attr-map-path", - "path to perf_event_attr map"), + "path to perf_event_attr map"), #endif OPT_BOOLEAN('a', "all-cpus", &target.system_wide, - "system-wide collection from all CPUs"), + "system-wide collection from all CPUs"), OPT_BOOLEAN(0, "scale", &stat_config.scale, - "Use --no-scale to disable counter scaling for multiplexing"), + "Use --no-scale to disable counter scaling for multiplexing"), OPT_INCR('v', "verbose", &verbose, - "be more verbose (show counter open errors, etc)"), + "be more verbose (show counter open errors, etc)"), OPT_INTEGER('r', "repeat", &stat_config.run_count, - "repeat command and print average + stddev (max: 100, forever: 0)"), + "repeat command and print average + stddev (max: 100, forever: 0)"), OPT_BOOLEAN(0, "table", &stat_config.walltime_run_table, - "display details about each run (only with -r option)"), + "display details about each run (only with -r option)"), OPT_BOOLEAN('n', "null", &stat_config.null_run, - "null run - dont start any counters"), - OPT_INCR('d', "detailed", &detailed_run, - "detailed run - start a lot of events"), - OPT_BOOLEAN('S', "sync", &sync_run, - "call sync() before starting a run"), + "null run - dont start any counters"), + OPT_INCR('d', "detailed", &detailed_run, "detailed run - start a lot of = events"), + OPT_BOOLEAN('S', "sync", &sync_run, "call sync() before starting a run"), OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, - "print large numbers with thousands\' separators", - stat__set_big_num), + "print large numbers with thousands\' separators", + stat__set_big_num), OPT_STRING('C', "cpu", &target.cpu_list, "cpu", - "list of cpus to monitor in system-wide"), + "list of cpus to monitor in system-wide"), OPT_BOOLEAN('A', "no-aggr", &opt_mode.no_aggr, - "disable aggregation across CPUs or PMUs"), + "disable aggregation across CPUs or PMUs"), OPT_BOOLEAN(0, "no-merge", &opt_mode.no_aggr, - "disable aggregation the same as -A or -no-aggr"), + "disable aggregation the same as -A or -no-aggr"), OPT_BOOLEAN(0, "hybrid-merge", &stat_config.hybrid_merge, - "Merge identical named hybrid events"), + "Merge identical named hybrid events"), OPT_STRING('x', "field-separator", &stat_config.csv_sep, "separator", - "print counts with custom separator"), + "print counts with custom separator"), OPT_BOOLEAN('j', "json-output", &stat_config.json_output, - "print counts in JSON format"), + "print counts in JSON format"), OPT_CALLBACK('G', "cgroup", &evsel_list, "name", - "monitor event in cgroup name only", parse_stat_cgroups), + "monitor event in cgroup name only", parse_stat_cgroups), OPT_STRING(0, "for-each-cgroup", &stat_config.cgroup_list, "name", - "expand events for each cgroup"), + "expand events for each cgroup"), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), - OPT_INTEGER(0, "log-fd", &output_fd, - "log output to fd, instead of stderr"), + OPT_INTEGER(0, "log-fd", &output_fd, "log output to fd, instead of stder= r"), OPT_STRING(0, "pre", &pre_cmd, "command", - "command to run prior to the measured command"), + "command to run prior to the measured command"), OPT_STRING(0, "post", &post_cmd, "command", - "command to run after to the measured command"), + "command to run after to the measured command"), OPT_UINTEGER('I', "interval-print", &stat_config.interval, - "print counts at regular interval in ms " - "(overhead is possible for values <=3D 100ms)"), + "print counts at regular interval in ms " + "(overhead is possible for values <=3D 100ms)"), OPT_INTEGER(0, "interval-count", &stat_config.times, - "print counts for fixed number of times"), + "print counts for fixed number of times"), OPT_BOOLEAN(0, "interval-clear", &stat_config.interval_clear, - "clear screen in between new interval"), - OPT_UINTEGER(0, "timeout", &stat_config.timeout, + "clear screen in between new interval"), + OPT_UINTEGER( + 0, "timeout", &stat_config.timeout, "stop workload and print counts after a timeout period in ms (>=3D 10ms= )"), OPT_BOOLEAN(0, "per-socket", &opt_mode.socket, - "aggregate counts per processor socket"), + "aggregate counts per processor socket"), OPT_BOOLEAN(0, "per-die", &opt_mode.die, "aggregate counts per processor= die"), OPT_BOOLEAN(0, "per-cluster", &opt_mode.cluster, - "aggregate counts per processor cluster"), - OPT_CALLBACK_OPTARG(0, "per-cache", &opt_mode.cache, &stat_config.aggr_l= evel, - "cache level", "aggregate count at this cache level (Default: LLC)", - parse_cache_level), + "aggregate counts per processor cluster"), + OPT_CALLBACK_OPTARG( + 0, "per-cache", &opt_mode.cache, &stat_config.aggr_level, "cache level", + "aggregate count at this cache level (Default: LLC)", parse_cache_level= ), OPT_BOOLEAN(0, "per-core", &opt_mode.core, - "aggregate counts per physical processor core"), + "aggregate counts per physical processor core"), OPT_BOOLEAN(0, "per-thread", &opt_mode.thread, "aggregate counts per thr= ead"), OPT_BOOLEAN(0, "per-node", &opt_mode.node, "aggregate counts per numa no= de"), - OPT_INTEGER('D', "delay", &target.initial_delay, + OPT_INTEGER( + 'D', "delay", &target.initial_delay, "ms to wait before starting measurement after program start (-1: start = with events disabled)"), OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL, - "Only print computed metrics. No raw values", enable_metric_only), + "Only print computed metrics. No raw values", + enable_metric_only), OPT_BOOLEAN(0, "metric-no-group", &stat_config.metric_no_group, - "don't group metric events, impacts multiplexing"), + "don't group metric events, impacts multiplexing"), OPT_BOOLEAN(0, "metric-no-merge", &stat_config.metric_no_merge, - "don't try to share events between metrics in a group"), + "don't try to share events between metrics in a group"), OPT_BOOLEAN(0, "metric-no-threshold", &stat_config.metric_no_threshold, - "disable adding events for the metric threshold calculation"), - OPT_BOOLEAN(0, "topdown", &topdown_run, - "measure top-down statistics"), + "disable adding events for the metric threshold calculation"), + OPT_BOOLEAN(0, "topdown", &topdown_run, "measure top-down statistics"), #ifdef HAVE_ARCH_X86_64_SUPPORT OPT_BOOLEAN(0, "record-tpebs", &tpebs_recording, - "enable recording for tpebs when retire_latency required"), + "enable recording for tpebs when retire_latency required"), OPT_CALLBACK(0, "tpebs-mode", &tpebs_mode, "tpebs-mode", - "Mode of TPEBS recording: mean, min or max", - parse_tpebs_mode), + "Mode of TPEBS recording: mean, min or max", parse_tpebs_mode), #endif OPT_UINTEGER(0, "td-level", &stat_config.topdown_level, - "Set the metrics level for the top-down statistics (0: max level)"), - OPT_BOOLEAN(0, "smi-cost", &smi_cost, - "measure SMI cost"), + "Set the metrics level for the top-down statistics (0: max level)"= ), + OPT_BOOLEAN(0, "smi-cost", &smi_cost, "measure SMI cost"), OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list", - "monitor specified metrics or metric groups (separated by ,)", - append_metric_groups), + "monitor specified metrics or metric groups (separated by ,)", + append_metric_groups), OPT_BOOLEAN_FLAG(0, "all-kernel", &stat_config.all_kernel, - "Configure all used events to run in kernel space.", - PARSE_OPT_EXCLUSIVE), + "Configure all used events to run in kernel space.", + PARSE_OPT_EXCLUSIVE), OPT_BOOLEAN_FLAG(0, "all-user", &stat_config.all_user, - "Configure all used events to run in user space.", - PARSE_OPT_EXCLUSIVE), + "Configure all used events to run in user space.", + PARSE_OPT_EXCLUSIVE), OPT_BOOLEAN(0, "percore-show-thread", &stat_config.percore_show_thread, - "Use with 'percore' event qualifier to show the event " - "counts of one hardware thread by sum up total hardware " - "threads of same physical core"), - OPT_BOOLEAN(0, "summary", &stat_config.summary, - "print summary for interval mode"), + "Use with 'percore' event qualifier to show the event " + "counts of one hardware thread by sum up total hardware " + "threads of same physical core"), + OPT_BOOLEAN(0, "new", &use_perf_stat_print, + "use new clean API code for display output"), + OPT_BOOLEAN(0, "summary", &stat_config.summary, "print summary for inter= val mode"), OPT_BOOLEAN(0, "no-csv-summary", &stat_config.no_csv_summary, - "don't print 'summary' for CSV summary output"), + "don't print 'summary' for CSV summary output"), OPT_BOOLEAN(0, "quiet", &quiet, - "don't print any output, messages or warnings (useful with record)"), + "don't print any output, messages or warnings (useful with record)"= ), OPT_BOOLEAN_SET(0, "affinity", &affinity, &affinity_set, - "enable (default) or disable affinity optimizations to reduce IPIs"), + "enable (default) or disable affinity optimizations to reduce IPIs"), OPT_CALLBACK(0, "cputype", &evsel_list, "hybrid cpu type", - "Only enable events on applying cpu with this type " - "for hybrid platform (e.g. core or atom)", - parse_cputype), - OPT_CALLBACK(0, "pmu-filter", &evsel_list, "pmu", + "Only enable events on applying cpu with this type " + "for hybrid platform (e.g. core or atom)", + parse_cputype), + OPT_CALLBACK( + 0, "pmu-filter", &evsel_list, "pmu", "Only enable events on applying pmu with specified " "for multiple pmus with same type(e.g. hisi_sicl2_cpa0 or hisi_sicl0_cp= a0)", parse_pmu_filter), #ifdef HAVE_LIBPFM OPT_CALLBACK(0, "pfm-events", &evsel_list, "event", - "libpfm4 event selector. use 'perf list' to list available events", - parse_libpfm_events_option), + "libpfm4 event selector. use 'perf list' to list available events", + parse_libpfm_events_option), #endif - OPT_CALLBACK(0, "control", &stat_config, "fd:ctl-fd[,ack-fd] or fifo:ctl= -fifo[,ack-fifo]", + OPT_CALLBACK( + 0, "control", &stat_config, + "fd:ctl-fd[,ack-fd] or fifo:ctl-fifo[,ack-fifo]", "Listen on ctl-fd descriptor for command to control measurement ('enabl= e': enable events, 'disable': disable events).\n" "\t\t\t Optionally send control command completion ('ack\\n') to ack-f= d descriptor.\n" "\t\t\t Alternatively, ctl-fifo / ack-fifo will be opened and used as = ctl-fd / ack-fd.", parse_control_option), OPT_CALLBACK_OPTARG(0, "iostat", &evsel_list, &stat_config, "default", - "measure I/O performance metrics provided by arch/platform", - iostat_parse), + "measure I/O performance metrics provided by arch/platform", + iostat_parse), OPT_END() }; const char * const stat_usage[] =3D { diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 4bbc78b1f741..b03099e820d4 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -113,6 +113,10 @@ perf-util-y +=3D counts.o perf-util-y +=3D stat.o perf-util-y +=3D stat-shadow.o perf-util-y +=3D stat-display.o +perf-util-y +=3D stat-print.o +perf-util-y +=3D stat-print-std.o +perf-util-y +=3D stat-print-csv.o +perf-util-y +=3D stat-print-json.o perf-util-y +=3D perf_api_probe.o perf-util-y +=3D record.o perf-util-y +=3D srcline.o diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index 2b69d238858c..e5aed8d629e6 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -37,7 +37,7 @@ #define PID_LEN 7 #define CPUS_LEN 4 =20 -static int aggr_header_lens[] =3D { +const int aggr_header_lens[] =3D { [AGGR_CORE] =3D 18, [AGGR_CACHE] =3D 22, [AGGR_CLUSTER] =3D 20, @@ -49,7 +49,7 @@ static int aggr_header_lens[] =3D { [AGGR_GLOBAL] =3D 0, }; =20 -static const char *aggr_header_csv[] =3D { +const char *aggr_header_csv[] =3D { [AGGR_CORE] =3D "core,ctrs,", [AGGR_CACHE] =3D "cache,ctrs,", [AGGR_CLUSTER] =3D "cluster,ctrs,", @@ -61,7 +61,7 @@ static const char *aggr_header_csv[] =3D { [AGGR_GLOBAL] =3D "" }; =20 -static const char *aggr_header_std[] =3D { +const char *aggr_header_std[] =3D { [AGGR_CORE] =3D "core", [AGGR_CACHE] =3D "cache", [AGGR_CLUSTER] =3D "cluster", @@ -580,13 +580,16 @@ static void print_metricgroup_header_std(struct perf_= stat_config *config, const char *metricgroup_name) { struct outstate *os =3D ctx; + int n; =20 if (!metricgroup_name) { __new_line_std(config, os); return; } =20 - fprintf(config->output, " %*s", config->metric_only_len, metricgroup_name= ); + n =3D fprintf(config->output, " %*s", EVNAME_LEN, metricgroup_name); + + fprintf(config->output, "%*s", MGROUP_LEN + config->unit_width + 2 - n, "= "); } =20 static void print_metric_only(struct perf_stat_config *config, @@ -596,20 +599,19 @@ static void print_metric_only(struct perf_stat_config= *config, struct outstate *os =3D ctx; FILE *out =3D os->fh; char str[1024]; - unsigned mlen; + unsigned mlen =3D config->metric_only_len; const char *color =3D metric_threshold_classify__color(thresh); - int olen; =20 - if (!unit) { - os->first =3D false; - return; - } + if (!unit) + unit =3D ""; + if (mlen < strlen(unit)) + mlen =3D strlen(unit) + 1; =20 - mlen =3D max_t(unsigned, strlen(unit), config->metric_only_len); + if (color) + mlen +=3D strlen(color) + sizeof(PERF_COLOR_RESET) - 1; =20 - olen =3D snprintf(str, sizeof(str), fmt ?: "", val); color_snprintf(str, sizeof(str), color ?: "", fmt ?: "", val); - fprintf(out, "%*s%s", max_t(int, mlen - olen, 1), "", str); + fprintf(out, "%*s ", mlen, str); os->first =3D false; } =20 diff --git a/tools/perf/util/stat-print-csv.c b/tools/perf/util/stat-print-= csv.c new file mode 100644 index 000000000000..e9d1e7c30c90 --- /dev/null +++ b/tools/perf/util/stat-print-csv.c @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "stat-print.h" +#include + +int perf_stat__print_csv(struct evlist *evlist __maybe_unused, + const struct perf_stat_config *config __maybe_unused, + const struct target *target __maybe_unused, + const struct timespec *ts __maybe_unused, + int argc __maybe_unused, + const char **argv __maybe_unused) +{ + return 0; +} diff --git a/tools/perf/util/stat-print-json.c b/tools/perf/util/stat-print= -json.c new file mode 100644 index 000000000000..72df7a94095d --- /dev/null +++ b/tools/perf/util/stat-print-json.c @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "stat-print.h" +#include + +int perf_stat__print_json(struct evlist *evlist __maybe_unused, + const struct perf_stat_config *config __maybe_unused, + const struct target *target __maybe_unused, + const struct timespec *ts __maybe_unused, + int argc __maybe_unused, + const char **argv __maybe_unused) +{ + return 0; +} diff --git a/tools/perf/util/stat-print-std.c b/tools/perf/util/stat-print-= std.c new file mode 100644 index 000000000000..83987e97c889 --- /dev/null +++ b/tools/perf/util/stat-print-std.c @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "stat-print.h" +#include + +int perf_stat__print_std(struct evlist *evlist __maybe_unused, + const struct perf_stat_config *config __maybe_unused, + const struct target *target __maybe_unused, + const struct timespec *ts __maybe_unused, + int argc __maybe_unused, + const char **argv __maybe_unused) +{ + return 0; +} diff --git a/tools/perf/util/stat-print.c b/tools/perf/util/stat-print.c new file mode 100644 index 000000000000..92ff9f1fe31c --- /dev/null +++ b/tools/perf/util/stat-print.c @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "stat-print.h" + +#include +#include +#include +#include +#include + +#include + +#include "cpumap.h" +#include "debug.h" +#include "evlist.h" +#include "evsel.h" +#include "expr.h" +#include "metricgroup.h" +#include "stat.h" +#include "thread_map.h" +#include "tool_pmu.h" + +/* + * Unified Aggregation Helpers (Shared by STD, CSV, JSON Formats) + */ + +const char *perf_stat__get_aggr_key(const struct perf_stat_config *config, + const struct evsel *evsel) +{ + switch (config->aggr_mode) { + case AGGR_CORE: + return "core"; + case AGGR_CACHE: + return "cache"; + case AGGR_CLUSTER: + return "cluster"; + case AGGR_DIE: + return "die"; + case AGGR_SOCKET: + return "socket"; + case AGGR_NODE: + return "node"; + case AGGR_NONE: + if (evsel->percore && !config->percore_show_thread) + return "core"; + return "cpu"; + case AGGR_THREAD: + return "thread"; + case AGGR_GLOBAL: + case AGGR_UNSET: + case AGGR_MAX: + default: + return ""; + } +} + +int perf_stat__get_aggr_id_char(const struct perf_stat_config *config, str= uct evsel *evsel, + struct aggr_cpu_id id, char *buf, size_t buf_size) +{ + switch (config->aggr_mode) { + case AGGR_CORE: + return scnprintf(buf, buf_size, "S%d-D%d-C%d", id.socket, id.die, id.cor= e); + case AGGR_CACHE: + return scnprintf(buf, buf_size, "S%d-D%d-L%d-ID%d", id.socket, id.die, i= d.cache_lvl, + id.cache); + case AGGR_CLUSTER: + return scnprintf(buf, buf_size, "S%d-D%d-CLS%d", id.socket, id.die, id.c= luster); + case AGGR_DIE: + return scnprintf(buf, buf_size, "S%d-D%d", id.socket, id.die); + case AGGR_SOCKET: + return scnprintf(buf, buf_size, "S%d", id.socket); + case AGGR_NODE: + return scnprintf(buf, buf_size, "N%d", id.node); + case AGGR_NONE: + if (evsel->percore && !config->percore_show_thread) { + return scnprintf(buf, buf_size, "S%d-D%d-C%d", id.socket, id.die, id.co= re); + } else if (id.cpu.cpu > -1) { + return scnprintf(buf, buf_size, "%d", id.cpu.cpu); + } + break; + case AGGR_THREAD: + return scnprintf(buf, buf_size, "%s-%d", + perf_thread_map__comm(evsel->core.threads, id.thread_idx), + perf_thread_map__pid(evsel->core.threads, id.thread_idx)); + case AGGR_GLOBAL: + case AGGR_UNSET: + case AGGR_MAX: + default: + break; + } + buf[0] =3D '\0'; + return -1; +} + +/* + * Traversal Driver and Calculation Code + */ + +/** + * tool_pmu__is_time_event - Check if event is a tool PMU time event. + * + * Copied from stat-shadow.c to make stat-print.c self-contained. + */ +static bool tool_pmu__is_time_event(const struct perf_stat_config *config, + const struct evsel *evsel, int *tool_aggr_idx) +{ + enum tool_pmu_event event =3D evsel__tool_event(evsel); + int aggr_idx; + + if (event !=3D TOOL_PMU__EVENT_DURATION_TIME && event !=3D TOOL_PMU__EVEN= T_USER_TIME && + event !=3D TOOL_PMU__EVENT_SYSTEM_TIME) + return false; + + if (config) { + cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { + if (config->aggr_map->map[aggr_idx].cpu.cpu =3D=3D 0) { + *tool_aggr_idx =3D aggr_idx; + return true; + } + } + pr_debug("Unexpected CPU0 missing in aggregation for tool event.\n"); + } + *tool_aggr_idx =3D 0; /* Assume the first aggregation index works. */ + return true; +} + +/** + * prepare_metric - Collect event values required for a metric. + * @config: Perf stat configuration. + * @mexp: The metric expression. + * @evsel: The associated event selector. + * @pctx: Expr parse context to add ID/values to. + * @aggr_idx: Aggregation index to read values from. + * + * Iterates over the events required for the metric expression, reads their + * counts for the given aggregation index, and adds them to the expression + * parser context. + * + * Copied and refactored from stat-shadow.c. + */ +static int prepare_metric(const struct perf_stat_config *config, const str= uct metric_expr *mexp, + struct evsel *evsel, struct expr_parse_ctx *pctx, int aggr_idx) +{ + struct evsel *const *metric_events =3D mexp->metric_events; + struct metric_ref *metric_refs =3D mexp->metric_refs; + int i; + + for (i =3D 0; metric_events[i]; i++) { + int source_count =3D 0, tool_aggr_idx; + bool is_tool_time =3D + tool_pmu__is_time_event(config, metric_events[i], &tool_aggr_idx); + struct perf_stat_evsel *ps =3D metric_events[i]->stats; + char *n; + double val; + + /* + * If there are multiple uncore PMUs and we're not reading the + * leader's stats, determine the stats for the appropriate + * uncore PMU. + */ + if (evsel && evsel->metric_leader && evsel->pmu !=3D evsel->metric_leade= r->pmu && + mexp->metric_events[i]->pmu =3D=3D evsel->metric_leader->pmu) { + struct evsel *pos; + + evlist__for_each_entry(evsel->evlist, pos) { + if (pos->pmu !=3D evsel->pmu) + continue; + if (pos->metric_leader !=3D mexp->metric_events[i]) + continue; + ps =3D pos->stats; + source_count =3D 1; + break; + } + } + /* Time events are always on CPU0, the first aggregation index. */ + if (!ps || !metric_events[i]->supported) { + val =3D NAN; + source_count =3D 0; + } else { + struct perf_stat_aggr *aggr =3D + &ps->aggr[is_tool_time ? tool_aggr_idx : aggr_idx]; + + if (aggr->counts.run =3D=3D 0) { + val =3D NAN; + source_count =3D 0; + } else { + val =3D aggr->counts.val; + if (is_tool_time) { + /* Convert time event nanoseconds to seconds. */ + val *=3D 1e-9; + } + if (!source_count) + source_count =3D evsel__source_count(metric_events[i]); + } + } + n =3D strdup(evsel__metric_id(metric_events[i])); + if (!n) + return -ENOMEM; + + expr__add_id_val_source_count(pctx, n, val, source_count); + } + + for (int j =3D 0; metric_refs && metric_refs[j].metric_name; j++) { + int ret =3D expr__add_ref(pctx, &metric_refs[j]); + + if (ret) + return ret; + } + + return i; +} + +/** + * calculate_and_print_metric - Compute and print a single metric. + * + * Parses the metric expression, computes the ratio, and calls the print_m= etric + * callback directly with clean parameters. + * Returns the return value of the print_metric callback (0 on success, or= error). + */ +static int calculate_and_print_metric(const struct perf_stat_config *confi= g, + const struct perf_stat_print_callbacks *cb, void *outer_ctx, + struct metric_expr *mexp, struct evsel *evsel, int aggr_idx) +{ + const char *metric_name =3D mexp->metric_name; + const char *metric_expr =3D mexp->metric_expr; + const char *metric_threshold =3D mexp->metric_threshold; + const char *metric_unit =3D mexp->metric_unit; + struct evsel *const *metric_events =3D mexp->metric_events; + int runtime =3D mexp->runtime; + struct expr_parse_ctx *pctx; + double ratio, scale, threshold; + int i; + enum metric_threshold_classify thresh =3D METRIC_THRESHOLD_UNKNOWN; + int ret =3D 0; + + if (!cb->print_metric) + return 0; + + pctx =3D expr__ctx_new(); + if (!pctx) + return -ENOMEM; + + if (config->user_requested_cpu_list) + pctx->sctx.user_requested_cpu_list =3D strdup(config->user_requested_cpu= _list); + pctx->sctx.runtime =3D runtime; + pctx->sctx.system_wide =3D config->system_wide; + i =3D prepare_metric(config, mexp, evsel, pctx, aggr_idx); + if (i < 0) { + expr__ctx_free(pctx); + return i; + } + if (!metric_events[i]) { + if (expr__parse(&ratio, pctx, metric_expr) =3D=3D 0) { + char *unit; + + if (metric_threshold && + expr__parse(&threshold, pctx, metric_threshold) =3D=3D 0 && + !isnan(threshold)) { + thresh =3D fpclassify(threshold) =3D=3D FP_ZERO ? METRIC_THRESHOLD_GOO= D : + METRIC_THRESHOLD_BAD; + } + + if (metric_unit && metric_name) { + if (perf_pmu__convert_scale(metric_unit, &unit, &scale) >=3D 0) { + ratio *=3D scale; + } + ret =3D cb->print_metric(outer_ctx, config, evsel, aggr_idx, + metric_name, unit, ratio, thresh); + } else { + ret =3D cb->print_metric(outer_ctx, config, evsel, aggr_idx, + metric_name ?: (evsel->name ?: ""), NULL, + ratio, thresh); + } + } + } + + expr__ctx_free(pctx); + return ret; +} + +/** + * perf_stat_print_metricgroup - Traverse metrics for an event. + * + * Returns 0 on success, or a negative error code on failure. + */ +static bool is_basic_shadow_metric(const char *name) +{ + static const char *const basic_metrics[] =3D { + "insn_per_cycle", "branch_miss_rate", "branch_frequency", + "cycles_frequency", "page_faults_per_second", "migrations_per_second", + "cs_per_second", "CPUs_utilized", + }; + for (size_t i =3D 0; i < ARRAY_SIZE(basic_metrics); i++) { + if (!strcmp(basic_metrics[i], name)) + return true; + } + return false; +} + +static int perf_stat_print_metricgroup(const struct perf_stat_config *conf= ig, + const struct perf_stat_print_callbacks *cb, void *outer_ctx, + struct evsel *evsel, int aggr_idx) +{ + struct metric_event *me; + struct metric_expr *mexp; + struct rblist *metric_events =3D &evsel->evlist->metric_events; + int ret; + + me =3D metricgroup__lookup(metric_events, evsel, false); + if (me =3D=3D NULL) + return 0; + + list_for_each_entry(mexp, &me->head, nd) { + if (!config->metric_only && + (!evsel->default_metricgroup || evsel->default_show_events)) { + if (!is_basic_shadow_metric(mexp->metric_name)) + continue; + } + + ret =3D calculate_and_print_metric(config, cb, outer_ctx, mexp, evsel, a= ggr_idx); + if (ret) + return ret; + } + return 0; +} + +/** + * perf_stat_print_metrics - Entry point for metric calculation & printing. + * + * Returns 0 on success, or a negative error code on failure. + */ +static int perf_stat_print_metrics(const struct perf_stat_config *config, + const struct perf_stat_print_callbacks *cb, void *outer_ctx, + struct evsel *evsel, int aggr_idx) +{ + if (config->iostat_run) { + /* IOSTAT metrics not supported yet in new API */ + return 0; + } + + return perf_stat_print_metricgroup(config, cb, outer_ctx, evsel, aggr_idx= ); +} + +int perf_stat__print_cb(struct evlist *evlist, const struct perf_stat_conf= ig *config, + const struct target *target __maybe_unused, + const struct timespec *ts __maybe_unused, int argc __maybe_unused, + const char **argv __maybe_unused, + const struct perf_stat_print_callbacks *cb, void *ctx) +{ + struct evsel *counter; + int aggr_idx; + int ret =3D 0; + + evlist__uniquify_evsel_names(evlist, config); + + if (cb->print_start) { + ret =3D cb->print_start(ctx, config); + if (ret) + return ret; + } + + switch (config->aggr_mode) { + case AGGR_GLOBAL: + case AGGR_NONE: + case AGGR_SOCKET: + case AGGR_DIE: + case AGGR_CLUSTER: + case AGGR_CACHE: + case AGGR_CORE: + case AGGR_THREAD: + case AGGR_NODE: + if (config->aggr_map) { + cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { + evlist__for_each_entry(evlist, counter) { + struct perf_stat_evsel *ps =3D counter->stats; + u64 val =3D 0, ena =3D 0, run =3D 0; + + if (ps && ps->aggr) { + val =3D ps->aggr[aggr_idx].counts.val; + ena =3D ps->aggr[aggr_idx].counts.ena; + run =3D ps->aggr[aggr_idx].counts.run; + } + + /* Skip already merged uncore/hybrid events */ + if (config->aggr_mode !=3D AGGR_NONE) { + if (evsel__is_hybrid(counter)) { + if (config->hybrid_merge && + counter->first_wildcard_match !=3D NULL) + continue; + } else { + if (counter->first_wildcard_match !=3D NULL) + continue; + } + } + + if (perf_stat__skip_metric_event(counter)) + continue; + + if (cb->print_event) { + double stdev_pct =3D 0.0; + if (ps && ps->res_stats.n > 1) { + stdev_pct =3D rel_stddev_stats( + stddev_stats(&ps->res_stats), val); + } + ret =3D cb->print_event(ctx, config, counter, + aggr_idx, val, ena, run, + stdev_pct); + if (ret) + goto out; + } + + ret =3D perf_stat_print_metrics(config, cb, ctx, counter, + aggr_idx); + if (ret) + goto out; + } + } + } else { + evlist__for_each_entry(evlist, counter) { + struct perf_stat_evsel *ps =3D counter->stats; + u64 val =3D 0, ena =3D 0, run =3D 0; + + if (ps && ps->aggr) { + val =3D ps->aggr[0].counts.val; + ena =3D ps->aggr[0].counts.ena; + run =3D ps->aggr[0].counts.run; + } + + /* Skip already merged uncore/hybrid events */ + if (config->aggr_mode !=3D AGGR_NONE) { + if (evsel__is_hybrid(counter)) { + if (config->hybrid_merge && + counter->first_wildcard_match !=3D NULL) + continue; + } else { + if (counter->first_wildcard_match !=3D NULL) + continue; + } + } + + if (perf_stat__skip_metric_event(counter)) + continue; + + if (cb->print_event) { + double stdev_pct =3D 0.0; + if (ps && ps->res_stats.n > 1) { + stdev_pct =3D rel_stddev_stats( + stddev_stats(&ps->res_stats), val); + } + ret =3D cb->print_event(ctx, config, counter, 0, val, ena, + run, stdev_pct); + if (ret) + goto out; + } + + ret =3D perf_stat_print_metrics(config, cb, ctx, counter, 0); + if (ret) + goto out; + } + } + break; + case AGGR_UNSET: + case AGGR_MAX: + default: + fprintf(config->output, "Aggregation mode %d not supported in new API ye= t\n", + config->aggr_mode); + break; + } + +out: + if (cb->print_end) { + int err =3D cb->print_end(ctx, config); + if (!ret) + ret =3D err; + } + + return ret; +} + +int perf_stat__print(struct evlist *evlist, const struct perf_stat_config = *config, + const struct target *target, const struct timespec *ts, int argc, + const char **argv) +{ + if (config->csv_output) { + return perf_stat__print_csv(evlist, config, target, ts, argc, argv); + } else if (config->json_output) { + return perf_stat__print_json(evlist, config, target, ts, argc, argv); + } else { + return perf_stat__print_std(evlist, config, target, ts, argc, argv); + } +} diff --git a/tools/perf/util/stat-print.h b/tools/perf/util/stat-print.h new file mode 100644 index 000000000000..a86414f32584 --- /dev/null +++ b/tools/perf/util/stat-print.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PERF_STAT_PRINT_H +#define __PERF_STAT_PRINT_H + +#include + +#include "stat.h" + +#define CNTR_NOT_SUPPORTED "" +#define CNTR_NOT_COUNTED "" + +struct evlist; +struct perf_stat_config; +struct target; +struct timespec; +struct evsel; +struct aggr_cpu_id; + +extern const int aggr_header_lens[]; +extern const char *aggr_header_csv[]; +extern const char *aggr_header_std[]; + +/** + * struct perf_stat_print_callbacks - Callbacks for rendering perf stat ou= tput. + * + * This structure defines the interface for different output formats (e.g., + * Standard, CSV, JSON) to render the collected performance counter statis= tics. + * The core display logic traverses the events and metrics and calls these + * callbacks in a streaming fashion, which build an in-memory DOM tree. The + * final rendering and output formatting is executed entirely in print_end. + */ +struct perf_stat_print_callbacks { + /** + * print_start - Called before any event or metric is printed. + * @ctx: Opaque context pointer passed to the print function. + * @config: Perf stat configuration. + */ + int (*print_start)(void *ctx, const struct perf_stat_config *config); + + /** + * print_end - Called after all events and metrics have been traversed. + * Executes the actual formatting and printing of the buffered tree. + * @ctx: Opaque context pointer. + * @config: Perf stat configuration. + */ + int (*print_end)(void *ctx, const struct perf_stat_config *config); + + /** + * print_event - Called to buffer an event (counter) value. + * @ctx: Opaque context pointer. + * @config: Perf stat configuration. + * @evsel: The event selector being printed (mutable for lazy initializat= ion). + * @aggr_idx: Aggregation index in evsel->stats. + * @val: Raw counter value. + * @ena: Enabled time for the counter (for multiplexing). + * @run: Running time for the counter (for multiplexing). + * @stdev_pct: Standard deviation percentage across multiple repeated run= s. + * + * Returns 0 on success, or a negative error code (e.g., -ENOMEM) on fail= ure. + */ + int (*print_event)(void *ctx, const struct perf_stat_config *config, stru= ct evsel *evsel, + int aggr_idx, u64 val, u64 ena, u64 run, double stdev_pct); + + /** + * print_metric - Called to buffer a metric value associated with an even= t. + * @ctx: Opaque context pointer. + * @config: Perf stat configuration. + * @evsel: The event selector associated with the metric (mutable). + * @aggr_idx: Aggregation index. + * @name: The display name of the metric. + * @unit: The unit of the metric (e.g., "%", "GHz", or NULL). + * @val: The calculated metric value. + * @thresh: Threshold classification (e.g., good, bad) for color coding. + * + * Returns 0 on success, or a negative error code (e.g., -ENOMEM) on fail= ure. + */ + int (*print_metric)(void *ctx, const struct perf_stat_config *config, str= uct evsel *evsel, + int aggr_idx, const char *name, const char *unit, double val, + enum metric_threshold_classify thresh); +}; + +/** + * perf_stat__get_aggr_key - Get the JSON key name for an aggregation mode. + */ +const char *perf_stat__get_aggr_key(const struct perf_stat_config *config, + const struct evsel *evsel); + +/** + * perf_stat__get_aggr_id_char - Get the unified aggregation ID string. + * + * Returns the formatted string size, or a negative error code on failure. + */ +int perf_stat__get_aggr_id_char(const struct perf_stat_config *config, str= uct evsel *evsel, + struct aggr_cpu_id id, char *buf, size_t buf_size); + +/** + * perf_stat__print_cb - Drive the traversal and call callbacks. + * + * Defined in stat-print.c. Called by format-specific entry points. + * Returns 0 on success, or a negative error code on failure. + */ +int perf_stat__print_cb(struct evlist *evlist, const struct perf_stat_conf= ig *config, + const struct target *target, const struct timespec *ts, int argc, + const char **argv, const struct perf_stat_print_callbacks *cb, void *ct= x); + +/** + * perf_stat__print - Entry point for the decoupled print API. + * + * Defined in stat-print.c. Dispatches to format-specific entry points. + * Returns 0 on success, or a negative error code on failure. + */ +int perf_stat__print(struct evlist *evlist, const struct perf_stat_config = *config, + const struct target *target, const struct timespec *ts, int argc, + const char **argv); + +/* + * Format-specific entry points, implemented in their respective files. + * All return 0 on success, or a negative error code on failure. + */ + +int perf_stat__print_std(struct evlist *evlist, const struct perf_stat_con= fig *config, + const struct target *target, const struct timespec *ts, int argc, + const char **argv); + +int perf_stat__print_csv(struct evlist *evlist, const struct perf_stat_con= fig *config, + const struct target *target, const struct timespec *ts, int argc, + const char **argv); + +int perf_stat__print_json(struct evlist *evlist, const struct perf_stat_co= nfig *config, + const struct target *target, const struct timespec *ts, int argc, + const char **argv); + +#endif /* __PERF_STAT_PRINT_H */ diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 4bced233d2fc..77873d51786c 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -106,6 +106,8 @@ struct perf_stat_config { bool ctl_fd_close; const char *cgroup_list; unsigned int topdown_level; + bool headers_printed; + bool metric_only_headers_printed; }; =20 extern struct perf_stat_config stat_config; --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 87EAC35F610 for ; Mon, 25 May 2026 23:19:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751157; cv=none; b=nC+++htVxTFjyH/l2I02+2pF1wADfgjj2szmm/j2S618B6ThcFaK68zM58Ef22Xi33AKThsmr2nBG37UojIUt34T8Toi5XndrFO5WAUtyPCZ4hq85/QEyQBmembDV7+ASNt+RM/tzx++K/zcDwn/TRHqcbpi5/Hg9WKEMtx/Y8Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751157; c=relaxed/simple; bh=P8aR2HU9nMFusBk8jj3mMOImyY5A2mXmm53jZwQlLAw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=PJqIHYkrzd4b8WvhNMHMsHJ4FymgzGan2zmxgEB1ef54/L63HAe4SyQTBLeXvmsqBpfh4TLZwHxv9J15kHMvshF2Jpz2t0WsDLuVD5V9l5JaxCJsZ6cXVuimxHjPox/JXcDGrEZw0I5OcVQeUbJJ3zLfc04mjHljS7cp+3+MeXs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=tiutuU2c; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="tiutuU2c" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2f5943ca81aso880160eec.0 for ; Mon, 25 May 2026 16:19:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751155; x=1780355955; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=XgmhaQgR4hd++AApPfkYKsldNRvCs5BxCpuV6v8uPUg=; b=tiutuU2couXTxossP2ZH3BMylgFWT06f60YIXKxInvUi5u3N8N1pDkwCNZLOW/VLDk 9GJeeKw0jKQlTVzHt+/mdFymx06OXNtcWXtuC55sywhx4WEdwY0AR5pqN/nNWp4TWPSb kbqKs0hG28CMA9fyFolv0GU7q4mcbOznz2Rp+SRhjUrmSNgmHABwrYy7p918S4JDylFP l6wuLtvS5ltdBXIdtMmGpQjIDz6yUKgtBHvxoUsznZkDOCP212u7jNdb9PMNj6Ub22pa oOL4p0SNP+rIaxJcUOJVzBpp8BZQVThMhVwhFgjzt7BioqcSAlMmfYYAIvRBnjdQB8Jt 6YsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751155; x=1780355955; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=XgmhaQgR4hd++AApPfkYKsldNRvCs5BxCpuV6v8uPUg=; b=pvJAac+JLlhA8k88tRYa0+IOtz2SIA85rgPxHBeIEtk1g3cRVOsq47sVIhzypRYdM0 64u0uH4Vbi6EXkkvp57fobQCHYoeQrFDcCQLP8gQpN+d3zQMHcXbU4Y2yX8H7KevqxS6 JH/6XU/90QON2rQ80PbUhHdA7oT0JTcbaNvFotVUNhjepRB4Hi04JmCvp9AfZ0eGutem k5PgpUWszn6AFL/PDer+iSkZMFWfhxPt5kT/EkxskQLIHPvUTxNmm+yWethKpLHjhPg/ luFM1VCw+8ZvN9oAh5EWwrVKlrLcePR7qMMuvH2mOze/hz7jO7Xbztzh1fYK7rhZj/Ha ++dw== X-Forwarded-Encrypted: i=1; AFNElJ82ZlbaB2fvxqP5YwU5kXbpep+ohdKCoAiU51VMHyr/3GmXPFOC349Jhblvl8UiCDFPv/hi6EbLBdYs8Es=@vger.kernel.org X-Gm-Message-State: AOJu0Yx5I0FC1XLD9a5YFam0PeINk6+2eaSrUXL89nqT2rJJnB1MXFxe LY6J0yPTALSujLcaMzeUYkAjmDoDcy3iQYn5HaS/DrRvUN2etaKln04EqqNNAFwTK7QTkllcV0w U3a0gV1Be/Q== X-Received: from dycmb2-n1.prod.google.com ([2002:a05:693c:20c2:10b0:2ee:9140:ff36]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:1901:b0:2f2:6dde:df53 with SMTP id 5a478bee46e88-30449156e46mr7526613eec.17.1779751154435; Mon, 25 May 2026 16:19:14 -0700 (PDT) Date: Mon, 25 May 2026 16:18:48 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-3-irogers@google.com> Subject: [RFC PATCH v2 02/14] perf stat: Implement standard console (STD) formatting callbacks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch implements standard console formatting callbacks inside util/stat-print-std.c, replacing the empty stubs introduced in Commit 1. Introduces the format-private `struct queued_event` and `struct queued_metr= ic` DOM nodes to buffer traversal streams, and fully encapsulates DOM state initialization and queue cleanups inside std_print_start() and std_print_en= d(). Utilizes the newly centralized unified aggregation helpers to resolve CPU a= nd thread prefixes cleanly, and incorporates full interval-mode timestamp printing support across all rows. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/util/stat-print-std.c | 776 ++++++++++++++++++++++++++++++- 1 file changed, 768 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/stat-print-std.c b/tools/perf/util/stat-print-= std.c index 83987e97c889..aa4a083bb85a 100644 --- a/tools/perf/util/stat-print-std.c +++ b/tools/perf/util/stat-print-std.c @@ -1,13 +1,773 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#include "stat-print.h" +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + #include +#include +#include + +#include "color.h" +#include "cpumap.h" +#include "debug.h" +#include "evlist.h" +#include "evsel.h" +#include "metricgroup.h" +#include "stat-print.h" +#include "stat.h" +#include "target.h" +#include "thread_map.h" +#include "tool_pmu.h" + +#define COUNTS_LEN 18 +#define EVNAME_LEN 32 +#define COMM_LEN 16 +#define PID_LEN 7 +#define MGROUP_LEN 50 +#define METRIC_LEN 38 + + + +/** + * struct queued_metric - In-memory record of a buffered metric. + * @list: Linked list node for queueing. + * @name: The display name of the metric. + * @unit: The metric's unit (e.g., "%", "GHz", or NULL). + * @val: The calculated ratio/metric value. + * @thresh: Threshold classification for color coding. + * @aggr_idx: Aggregation index in evsel stats. + */ +struct queued_metric { + struct list_head list; + char *name; + char *unit; + double val; + enum metric_threshold_classify thresh; + int aggr_idx; +}; + +/** + * struct queued_event - In-memory record of a buffered counter event. + * @list: Linked list node for queueing. + * @evsel: The associated performance event selector. + * @name: The uniquely formatted/resolved event name. + * @val: Raw aggregated counter value. + * @ena: Enabled time for multiplexing percentage. + * @run: Running time for multiplexing percentage. + * @stdev_pct: Standard deviation percentage across repeated runs. + * @aggr_idx: Aggregation index. + * @is_metricgroup: Whether this represents a unified metricgroup header. + * @metrics_list: Linked list head containing nested queued_metric structu= res. + */ +struct queued_event { + struct list_head list; + struct evsel *evsel; + char *name; + u64 val, ena, run; + double stdev_pct; + int aggr_idx; + bool is_metricgroup; + struct list_head metrics_list; +}; + +/** + * struct std_print_state - Print state context for Standard console outpu= t. + * @fp: File descriptor to output to. + * @timestamp: Formatted interval timestamp (optional). + * @events_list: Linked list head containing queued_event nodes. + * @current_event: Pointer to the currently active event being printed. + * Serves as a temporary bridge to associate streaming met= rics back to + * their parent event node during list buffering. This rel= ies on a + * strict temporal coupling in the traversal driver: the d= river always + * invokes print_metric() callbacks for a counter synchron= ously and + * immediately after its print_event() callback, prior to = advancing + * to the next event or aggregation node. This pointer is = completely + * private to standard printing, keeping the traversal dri= ver decoupled + * and preserving strict encapsulation. + * @target: target query parameters for header printout. + * @argc: Command argument count. + * @argv: Command argument values. + */ +struct std_print_state { + FILE *fp; + char timestamp[64]; + struct list_head events_list; + struct queued_event *current_event; + const struct target *target; + int argc; + const char **argv; +}; + +/** + * struct std_metric_only_print_state - Metric-only print state context fo= r Standard console output. + * @fp: File descriptor to output to. + * @queued_metrics: Linked list head containing queued_metric nodes. + * @timestamp: Formatted interval timestamp (optional). + * @target: target query parameters. + * @argc: Command argument count. + * @argv: Command argument values. + */ +struct std_metric_only_print_state { + FILE *fp; + struct list_head queued_metrics; + char timestamp[64]; + const struct target *target; + int argc; + const char **argv; + struct evlist *evlist; +}; + +/** + * print_aggr_id_std - Print the aggregation prefix for STD format. + * + * Uses the unified perf_stat__get_aggr_id_char helper to format the base + * aggregation string, and pads it dynamically using aggr_header_lens. + */ +static void print_aggr_id_std(const struct perf_stat_config *config, FILE = *output, + struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) +{ + char buf[128]; + + if (perf_stat__get_aggr_id_char(config, evsel, id, buf, sizeof(buf)) < 0) + return; + + if (config->aggr_mode =3D=3D AGGR_NONE) { + if (evsel->percore && !config->percore_show_thread) { + fprintf(output, "%-*s ", aggr_header_lens[AGGR_CORE], buf); + } else if (id.cpu.cpu > -1) { + /* For CPU none mode, prepend "CPU" during console print */ + char cpu_buf[160]; + snprintf(cpu_buf, sizeof(cpu_buf), "CPU%s", buf); + fprintf(output, "%-*s ", aggr_header_lens[AGGR_NONE], cpu_buf); + } + return; + } + + if (config->aggr_mode =3D=3D AGGR_THREAD) { + fprintf(output, "%-*s ", aggr_header_lens[AGGR_THREAD], buf); + return; + } + + /* Socket/Die/Node/Cache/Cluster modes print base ID and aggr count */ + fprintf(output, "%-s %*d ", buf, 4, aggr_nr); +} + +/** + * should_skip_zero_counter - Check if a zero-valued counter should be ski= pped. + * + * Implemented locally for standard console formatting. + */ +static bool should_skip_zero_counter(const struct perf_stat_config *config= , struct evsel *counter, + int aggr_idx) +{ + struct perf_cpu cpu; + unsigned int idx; + struct aggr_cpu_id id; + + if (verbose =3D=3D 0 && counter->skippable && !counter->supported) + return true; + + if (config->metric_only) + return false; + + if (config->aggr_mode =3D=3D AGGR_THREAD && config->system_wide) + return true; + + if (aggr_idx < 0 || !config->aggr_map || !config->aggr_get_id) + return false; + + id =3D config->aggr_map->map[aggr_idx]; + + if (evsel__is_tool(counter)) { + struct aggr_cpu_id own_id =3D config->aggr_get_id((struct perf_stat_conf= ig *)config, + (struct perf_cpu){ .cpu =3D 0 }); + + return !aggr_cpu_id__equal(&id, &own_id); + } + + perf_cpu_map__for_each_cpu(cpu, idx, counter->core.cpus) { + struct aggr_cpu_id own_id =3D + config->aggr_get_id((struct perf_stat_config *)config, cpu); + + if (aggr_cpu_id__equal(&id, &own_id)) + return false; + } + return true; +} + +/* + * Standard (STD) Output Callbacks - Normal Mode + */ + +static int std_print_start(void *ctx, const struct perf_stat_config *confi= g __maybe_unused) +{ + struct std_print_state *ps =3D ctx; =20 -int perf_stat__print_std(struct evlist *evlist __maybe_unused, - const struct perf_stat_config *config __maybe_unused, - const struct target *target __maybe_unused, - const struct timespec *ts __maybe_unused, - int argc __maybe_unused, - const char **argv __maybe_unused) + INIT_LIST_HEAD(&ps->events_list); + ps->current_event =3D NULL; + return 0; +} + +static int std_print_event(void *ctx, const struct perf_stat_config *confi= g, struct evsel *evsel, + int aggr_idx, u64 val, u64 ena, u64 run, double stdev_pct) { + struct std_print_state *ps =3D ctx; + struct queued_event *ev; + + /* Skip zero counters locally in STD callbacks if they qualify */ + if (val =3D=3D 0 && should_skip_zero_counter(config, evsel, aggr_idx)) { + ps->current_event =3D NULL; + return 0; + } + + ev =3D malloc(sizeof(*ev)); + if (!ev) + return -ENOMEM; + + ev->name =3D strdup(evsel__name(evsel)); + if (!ev->name) { + free(ev); + return -ENOMEM; + } + + ev->evsel =3D evsel; + ev->val =3D val; + ev->ena =3D ena; + ev->run =3D run; + ev->stdev_pct =3D stdev_pct; + ev->aggr_idx =3D aggr_idx; + INIT_LIST_HEAD(&ev->metrics_list); + + list_add_tail(&ev->list, &ps->events_list); + ps->current_event =3D ev; + return 0; } + +static int std_print_metric(void *ctx, const struct perf_stat_config *conf= ig __maybe_unused, + struct evsel *evsel __maybe_unused, int aggr_idx __maybe_unused, + const char *name, const char *unit, double val, + enum metric_threshold_classify thresh) +{ + struct std_print_state *ps =3D ctx; + struct queued_metric *b; + + if (!ps->current_event) + return 0; + + if (evsel !=3D ps->current_event->evsel) { + pr_err("decoupled print engine: temporal coupling violation: evsel misma= tch!\n"); + return -EINVAL; + } + + b =3D malloc(sizeof(*b)); + if (!b) + return -ENOMEM; + + b->name =3D strdup(name); + if (!b->name) { + free(b); + return -ENOMEM; + } + + if (unit && unit[0]) { + b->unit =3D strdup(unit); + if (!b->unit) { + free(b->name); + free(b); + return -ENOMEM; + } + } else { + b->unit =3D NULL; + } + + b->val =3D val; + b->thresh =3D thresh; + list_add_tail(&b->list, &ps->current_event->metrics_list); + + return 0; +} + +#define USEC_PER_SEC 1000000ULL +#define NSEC_PER_SEC 1000000000ULL + +static double timeval2double(struct timeval *t) +{ + return t->tv_sec + (double)t->tv_usec / USEC_PER_SEC; +} + +static void print_footer_std(const struct perf_stat_config *config) +{ + double avg =3D avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; + FILE *output =3D config->output; + + if (config->interval) + return; + + if (!config->null_run) + fprintf(output, "\n"); + + if (config->run_count =3D=3D 1) { + fprintf(output, " %17.9f seconds time elapsed", avg); + + if (config->ru_display) { + double ru_utime =3D + timeval2double((struct timeval *)&config->ru_data.ru_utime); + double ru_stime =3D + timeval2double((struct timeval *)&config->ru_data.ru_stime); + + fprintf(output, "\n\n"); + fprintf(output, " %17.9f seconds user\n", ru_utime); + fprintf(output, " %17.9f seconds sys\n", ru_stime); + } + } else { + double sd =3D stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; + fprintf(output, " %17.9f +- %-17.9f seconds time elapsed", avg, sd); + } + fprintf(output, "\n"); +} + +/** + * print_header_std - Print the header prefix matching old API. + * + * Copied and adapted from stat-display.c. + */ +static void print_header_std(const struct perf_stat_config *config, const = struct target *target, + int argc, const char **argv) +{ + FILE *output =3D config->output; + int i; + + fprintf(output, "\n"); + fprintf(output, " Performance counter stats for "); + if (target->bpf_str) + fprintf(output, "\'BPF program(s) %s", target->bpf_str); + else if (target->system_wide) + fprintf(output, "\'system wide"); + else if (target->cpu_list) + fprintf(output, "\'CPU(s) %s", target->cpu_list); + else if (!target__has_task(target)) { + fprintf(output, "\'%s", argv ? argv[0] : "pipe"); + for (i =3D 1; argv && (i < argc); i++) + fprintf(output, " %s", argv[i]); + } else if (target->pid) + fprintf(output, "process id \'%s", target->pid); + else + fprintf(output, "thread id \'%s", target->tid); + + fprintf(output, "\'"); + if (config->run_count > 1) + fprintf(output, " (%d runs)", config->run_count); + fprintf(output, ":\n\n"); +} + +static int std_print_end(void *ctx, const struct perf_stat_config *config) +{ + struct std_print_state *ps =3D ctx; + struct queued_event *ev, *tmp_ev; + struct queued_metric *met, *tmp_met; + FILE *out =3D ps->fp; + bool first; + const char *last_mg_name =3D NULL; + const struct perf_pmu *last_pmu =3D NULL; + int last_aggr_idx =3D -1; + + /* Print the formatted header prefix (only in non-interval mode) */ + if (!config->interval) + print_header_std(config, ps->target, ps->argc, ps->argv); + + list_for_each_entry_safe(ev, tmp_ev, &ps->events_list, list) { + struct evsel *evsel =3D ev->evsel; + double sc =3D evsel->scale; + const char *fmt; + const char *bad_count =3D evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT= _SUPPORTED; + struct metric_event *me =3D + metricgroup__lookup(&evsel->evlist->metric_events, evsel, false); + bool is_metricgroup =3D false; + bool skip_header =3D false; + char full_name[128] =3D ""; + + if (me && me->is_default && !evsel->default_show_events) { + struct metric_expr *mexp =3D + list_first_entry(&me->head, struct metric_expr, nd); + const char *mg_name =3D mexp->default_metricgroup_name; + bool need_full_name =3D perf_pmus__num_core_pmus() > 1; + + if (need_full_name && evsel->pmu) + scnprintf(full_name, sizeof(full_name), "%s (%s)", mg_name, + evsel->pmu->name); + else + scnprintf(full_name, sizeof(full_name), "%s", mg_name); + is_metricgroup =3D true; + + if (last_mg_name && !strcmp(last_mg_name, mg_name) && + last_pmu =3D=3D evsel->pmu && last_aggr_idx =3D=3D ev->aggr_idx) { + skip_header =3D true; + } + last_mg_name =3D mg_name; + last_pmu =3D evsel->pmu; + last_aggr_idx =3D ev->aggr_idx; + } + + /* Print interval timestamp if configured */ + if (config->interval && ps->timestamp[0] && !skip_header) + fprintf(out, "%s", ps->timestamp); + + /* 1. Print aggregation prefix first (if we don't skip header) */ + if (!skip_header && config->aggr_map && ev->aggr_idx >=3D 0) { + struct aggr_cpu_id id =3D config->aggr_map->map[ev->aggr_idx]; + int aggr_nr =3D 0; + if (evsel->stats && evsel->stats->aggr) { + aggr_nr =3D evsel->stats->aggr[ev->aggr_idx].nr; + } + print_aggr_id_std(config, out, evsel, id, aggr_nr); + } + + /* 2. Print event value (scaled) or spaces if metricgroup */ + if (is_metricgroup) { + if (!skip_header) { + int n =3D fprintf(out, " %*s", EVNAME_LEN, full_name); + fprintf(out, "%*s", MGROUP_LEN + config->unit_width + 2 - n, ""); + } + } else { + if (config->big_num) + fmt =3D floor(sc) !=3D sc ? "%'*.2f " : "%'*.0f "; + else + fmt =3D floor(sc) !=3D sc ? "%*.2f " : "%*.0f "; + + if (ev->run =3D=3D 0 || ev->ena =3D=3D 0) { + fprintf(out, "%*s ", COUNTS_LEN, bad_count); + } else { + double scaled =3D (double)ev->val; + double avg; + if (ev->ena < ev->run) { + scaled =3D (double)ev->val * ev->run / ev->ena; + } + avg =3D scaled * sc; + fprintf(out, fmt, COUNTS_LEN, avg); + } + + /* 3. Print unit */ + if (evsel->unit) { + fprintf(out, "%-*s ", config->unit_width, evsel->unit); + } else { + if (config->unit_width > 0) + fprintf(out, "%-*s ", config->unit_width, ""); + } + + /* 4. Print event name */ + fprintf(out, "%-*s", EVNAME_LEN, evsel__name(evsel)); + + /* If there are no metrics, print noise and multiplexing percentage */ + if (list_empty(&ev->metrics_list)) { + if (ev->stdev_pct) + fprintf(out, " ( +-%6.2f%% )", ev->stdev_pct); + if (ev->run !=3D ev->ena) + fprintf(out, " (%.2f%%)", 100.0 * ev->run / ev->ena); + } + } + + first =3D true; + list_for_each_entry_safe(met, tmp_met, &ev->metrics_list, list) { + const char *color =3D metric_threshold_classify__color(met->thresh); + char unit_name[128]; + const char *m_fmt =3D (met->unit && met->unit[0]) ? "%8.1f" : "%8.2f"; + + if (met->unit && met->unit[0]) { + snprintf(unit_name, sizeof(unit_name), "%s %s", met->unit, + met->name); + } else { + snprintf(unit_name, sizeof(unit_name), "%s", met->name); + } + + if (first) { + if (skip_header) { + if (config->interval && ps->timestamp[0]) + fprintf(out, "%s", ps->timestamp); + if (config->aggr_map && ev->aggr_idx >=3D 0) { + struct aggr_cpu_id id =3D + config->aggr_map->map[ev->aggr_idx]; + int aggr_nr =3D 0; + if (evsel->stats && evsel->stats->aggr) { + aggr_nr =3D + evsel->stats->aggr[ev->aggr_idx].nr; + } + print_aggr_id_std(config, out, evsel, id, aggr_nr); + } + fprintf(out, "%*s# ", + COUNTS_LEN + EVNAME_LEN + config->unit_width + 3, + ""); + } else { + fprintf(out, " # "); + } + first =3D false; + } else { + /* Align subsequent metric lines */ + fprintf(out, "\n"); + if (config->interval && ps->timestamp[0]) + fprintf(out, "%s", ps->timestamp); + if (config->aggr_map && ev->aggr_idx >=3D 0) { + struct aggr_cpu_id id =3D config->aggr_map->map[ev->aggr_idx]; + int aggr_nr =3D 0; + if (evsel->stats && evsel->stats->aggr) { + aggr_nr =3D evsel->stats->aggr[ev->aggr_idx].nr; + } + print_aggr_id_std(config, out, evsel, id, aggr_nr); + } + fprintf(out, "%*s# ", + COUNTS_LEN + EVNAME_LEN + config->unit_width + 3, ""); + } + + if (color && color[0]) { + color_fprintf(out, color, m_fmt, met->val); + } else { + fprintf(out, m_fmt, met->val); + } + /* Print the metric unit and name left-aligned padded to METRIC_LEN - n= - 1 =3D 26 */ + fprintf(out, " %-26s", unit_name); + + /* If this is the last metric in the list, print noise and multiplexing= percentage */ + if (list_is_last(&met->list, &ev->metrics_list)) { + if (ev->stdev_pct) + fprintf(out, " ( +-%6.2f%% )", ev->stdev_pct); + if (ev->run !=3D ev->ena) + fprintf(out, " (%.2f%%)", 100.0 * ev->run / ev->ena); + } + + list_del(&met->list); + free(met->name); + free(met->unit); + free(met); + } + fprintf(out, "\n"); + + list_del(&ev->list); + free(ev->name); + free(ev); + } + print_footer_std(config); + return 0; +} + +static const struct perf_stat_print_callbacks std_print_callbacks =3D { + .print_start =3D std_print_start, + .print_end =3D std_print_end, + .print_event =3D std_print_event, + .print_metric =3D std_print_metric, +}; + +/* + * Standard (STD) Output Callbacks - Metric-Only Mode + */ + +static int std_metric_only_print_start(void *ctx, + const struct perf_stat_config *config __maybe_unused) +{ + struct std_metric_only_print_state *ps =3D ctx; + INIT_LIST_HEAD(&ps->queued_metrics); + return 0; +} + +static int std_metric_only_print_metric(void *ctx, + const struct perf_stat_config *config __maybe_unused, + struct evsel *evsel __maybe_unused, int aggr_idx, + const char *name, const char *unit, double val, + enum metric_threshold_classify thresh) +{ + struct std_metric_only_print_state *ps =3D ctx; + struct queued_metric *b =3D malloc(sizeof(*b)); + + if (!b) + return -ENOMEM; + + b->name =3D strdup(name); + if (!b->name) { + free(b); + return -ENOMEM; + } + + if (unit && unit[0]) { + b->unit =3D strdup(unit); + if (!b->unit) { + free(b->name); + free(b); + return -ENOMEM; + } + } else { + b->unit =3D NULL; + } + + b->val =3D val; + b->thresh =3D thresh; + b->aggr_idx =3D aggr_idx; + list_add_tail(&b->list, &ps->queued_metrics); + + return 0; +} + +static int std_metric_only_print_end(void *ctx, const struct perf_stat_con= fig *config) +{ + struct std_metric_only_print_state *ps =3D ctx; + struct queued_metric *b, *tmp; + FILE *out =3D ps->fp; + int first_aggr =3D -1; + /* Initialize to -2 to distinguish from -1 (a valid index in AGGR_GLOBAL = mode) */ + int current_aggr =3D -2; + const char *color; + char *str; + int mlen; + int ret =3D 0; + int err; + + if (list_empty(&ps->queued_metrics)) + return 0; + + first_aggr =3D list_first_entry(&ps->queued_metrics, struct queued_metric= , list)->aggr_idx; + + if (!config->metric_only_headers_printed) { + /* Print the formatted header prefix */ + if (!config->interval) + print_header_std(config, ps->target, ps->argc, ps->argv); + + if (config->aggr_map && first_aggr >=3D 0) { + int len =3D aggr_header_lens[config->aggr_mode]; + + fprintf(out, "%*s", len + 1, ""); + } + + /* Print headers */ + list_for_each_entry(b, &ps->queued_metrics, list) { + if (b->aggr_idx =3D=3D first_aggr) { + char *header_name; + + if (b->unit && b->unit[0]) { + err =3D asprintf(&header_name, "%s %s", b->unit, b->name); + } else { + header_name =3D strdup(b->name); + err =3D header_name ? 0 : -1; + } + if (err < 0) { + ret =3D -ENOMEM; + goto cleanup; + } + fprintf(out, "%*s ", config->metric_only_len, header_name); + free(header_name); + } + } + fprintf(out, "\n\n"); + ((struct perf_stat_config *)config)->metric_only_headers_printed =3D tru= e; + } + + /* Print values */ + list_for_each_entry_safe(b, tmp, &ps->queued_metrics, list) { + if (b->aggr_idx !=3D current_aggr) { + if (current_aggr !=3D -2) + fprintf(out, "\n"); + current_aggr =3D b->aggr_idx; + if (config->interval && ps->timestamp[0]) + fprintf(out, "%s", ps->timestamp); + if (config->aggr_map && current_aggr >=3D 0) { + struct aggr_cpu_id id =3D config->aggr_map->map[current_aggr]; + struct evsel *mock_evsel =3D list_first_entry(&ps->evlist->core.entrie= s, struct evsel, core.node); + int aggr_nr =3D 0; + + if (mock_evsel->stats && mock_evsel->stats->aggr) + aggr_nr =3D mock_evsel->stats->aggr[current_aggr].nr; + + print_aggr_id_std(config, out, mock_evsel, id, aggr_nr); + } + } + color =3D metric_threshold_classify__color(b->thresh); + mlen =3D config->metric_only_len; + + if (color && color[0]) { + err =3D asprintf(&str, "%s%.1f%s", color, b->val, PERF_COLOR_RESET); + mlen +=3D strlen(color) + sizeof(PERF_COLOR_RESET) - 1; + } else { + err =3D asprintf(&str, "%.1f", b->val); + } + if (err < 0) { + ret =3D -ENOMEM; + goto cleanup; + } + fprintf(out, "%*s ", mlen, str); + free(str); + + list_del(&b->list); + free(b->name); + free(b->unit); + free(b); + } + print_footer_std(config); + return 0; + +cleanup: + list_for_each_entry_safe(b, tmp, &ps->queued_metrics, list) { + list_del(&b->list); + free(b->name); + free(b->unit); + free(b); + } + return ret; +} + +static const struct perf_stat_print_callbacks std_metric_only_print_callba= cks =3D { + .print_start =3D std_metric_only_print_start, + .print_end =3D std_metric_only_print_end, + .print_event =3D NULL, + .print_metric =3D std_metric_only_print_metric, +}; + +int perf_stat__print_std(struct evlist *evlist, const struct perf_stat_con= fig *config, + const struct target *target, const struct timespec *ts, int argc, + const char **argv) +{ + struct std_print_state ps =3D { + .fp =3D config->output, + .target =3D target, + .argc =3D argc, + .argv =3D argv, + }; + + if (config->metric_only) { + struct std_metric_only_print_state mops =3D { + .fp =3D config->output, + .target =3D target, + .argc =3D argc, + .argv =3D argv, + .evlist =3D evlist, + }; + if (config->interval && ts) { + scnprintf(mops.timestamp, sizeof(mops.timestamp), "%6lu.%09lu ", + (unsigned long)ts->tv_sec, ts->tv_nsec); + } else { + mops.timestamp[0] =3D '\0'; + } + return perf_stat__print_cb(evlist, config, target, ts, argc, argv, + &std_metric_only_print_callbacks, &mops); + } else { + if (config->interval && !config->headers_printed) { + FILE *output =3D config->output; + + if (config->aggr_mode =3D=3D AGGR_GLOBAL) { + fprintf(output, "#%*s %*s %*s events\n", 15 - 2, "time", 18, "counts",= config->unit_width, "unit"); + } else { + fprintf(output, "#%*s %-*s ctrs %*s %*s events\n", + 15 - 2, "time", + aggr_header_lens[config->aggr_mode], aggr_header_std[config->aggr_mod= e], + 18, "counts", config->unit_width, "unit"); + } + ((struct perf_stat_config *)config)->headers_printed =3D true; + } + if (config->interval && ts) { + scnprintf(ps.timestamp, sizeof(ps.timestamp), "%6lu.%09lu ", + (unsigned long)ts->tv_sec, ts->tv_nsec); + } else { + ps.timestamp[0] =3D '\0'; + } + return perf_stat__print_cb(evlist, config, target, ts, argc, argv, + &std_print_callbacks, &ps); + } +} --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6224C2AD2C for ; Mon, 25 May 2026 23:19:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751158; cv=none; b=PYje9NlJppPa/2NI4n+J9pco2d8ciCSjtpLA0Vk4Uu0SEAfDGZUNMawGJoXGU/U/mtZZqYBEvvTQp9dq6XvHH794WAWPuaaKP1KlvpL4vTir5Za9tvJ8T+aci6ZmVXi0DVTkv8kzCH+alwAaUpGKzAP2QU5/blYjlKlf4gEpnnw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751158; c=relaxed/simple; bh=pz9wWtvci81x+rjdPA5mZR7lSPuDfpOtPEnhMowVHkQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=uqpNo5ToGeNXaAuCeGx9TpsF7h7k9HUIzo0XJExR2nkQCcns/3HEyjIMUWbo6MZOB7db90F5yeIVCD1E98MjwM0wtDMmHmemgMu99PzrShcXj1SpS/y3uLffn9sGRLEqbx7120E0rXnTvpuv6gpPSWx1DsTEAzu+faaXooExgnA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=uEGtqDbA; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="uEGtqDbA" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2fded513994so369026eec.1 for ; Mon, 25 May 2026 16:19:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751156; x=1780355956; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=jkb+vzit+Cca2HE4wAYWYNlzyApLVwRmfy32ufnqmWg=; b=uEGtqDbANPOGEwlPjOQyJTPsHVDqLQYKsW5bELrJYASsTNXUO2/IKiTG8wSwi5UEu9 wPGurg+zwvgjjqph6M9LYjKmS7to3koIjymUfmzqPS9blUH7MOv/roush2YCgKGPSCrH V4Nu+k4DGSdNOeDSTQ33RWykJ+J1GZQM9gnDJznOrcOLxQzbIHyjg4+9vGJOq9Sf7n75 64OK/hHiDNPn8OPaVcgWPlMaup8S1SvqOekGAr9HtwCeN+e0h04BrLWZCvS+ohulsD58 2DWlXqHnq6yNl5nYPcCpfgQg//Ukym4rEJKiU5gCN4zKrwT5XqgdmCD5iRn49OquRvzl p8Jg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751156; x=1780355956; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=jkb+vzit+Cca2HE4wAYWYNlzyApLVwRmfy32ufnqmWg=; b=cmvA15YHwPOosCcC2UKyjwl8oVTWkuHjOIFwJZ0fe6Qn2ebz+MtzkSKDLH0Mn6uG2m bvCnSDubK7b5DuuVQhr94xrRA33fSXI+9XTROCiiMxAlrAB+dDwUklbFGinnMM4C+aAY VWvdsQW+YqFow01YckRHEEwdP/SssypPUeny/6azqOaBIncvqX1snTDS7raq1umalblE NOeqBwldpYTy8db4AgI24+vf8v49+A2/ZLElb/bcz4ynZuE9Xr6BcZ6MTIGfLWLPtujK z5B5fphMLlq6gmqjAtWX9z88qB27gQqXT2DzrA2rDg6lkNLzA1DHEQRSUbLTkj4SGvZS KeiQ== X-Forwarded-Encrypted: i=1; AFNElJ8oxvslP199iD+vMECHJ7U+p0u/joNgrxaxSS8bAMRy3JclodvEwea8IKbsWU7yrAfkpDtamcZ8vCc6jMo=@vger.kernel.org X-Gm-Message-State: AOJu0YzEhR7/1QOgwJWRDX7YfG9zlxxnkqE/zQnIu3b8gIjt7JIG+1hq RGsQ9p+FLnXBQQQ3MYIqPuK95KK1LcSOWEBsQoBrQDS8XocIA8FgxQ5PqEq5AtqWW8LZCRmXpl1 b9zzLluhMSA== X-Received: from dyem10.prod.google.com ([2002:a05:7300:818a:b0:2f7:767f:edba]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:ef89:b0:2be:142f:d499 with SMTP id 5a478bee46e88-30449148a1fmr7685930eec.16.1779751156198; Mon, 25 May 2026 16:19:16 -0700 (PDT) Date: Mon, 25 May 2026 16:18:49 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-4-irogers@google.com> Subject: [RFC PATCH v2 03/14] perf stat: Extend STD output linter to test basic New API checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends the standard console (STD) output linter script tools/perf/tests/shell/stat+std_output.sh to run the basic no-argument check a second time using the --new print flag: perf_cmd=3D"--new -o ${stat_output}" check_no_args "STD (New API)" "$perf_cmd" This ensures that standard console outputs produced by the decoupled printing callbacks are formally validated by the test suite. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Arnaldo Carvalho de Melo Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/stat+std_output.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/s= hell/stat+std_output.sh index 9c4b92ecf448..233e0a50eb33 100755 --- a/tools/perf/tests/shell/stat+std_output.sh +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -118,5 +118,9 @@ then else echo "[Skip] Skipping tests for system_wide_no_aggr, per_core, per_die an= d per_socket since socket id exposed via topology is invalid" fi +# New API basic checks +perf_cmd=3D"--new -o ${stat_output}" +check_no_args "STD (New API)" "$perf_cmd" + cleanup exit 0 --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2671D35F170 for ; Mon, 25 May 2026 23:19:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751160; cv=none; b=CHC5vFxcwthjdn+7L9IDX5TME3p6rygNSAxHid0FT7jpM877PouUM5RpZS5AG1FqN4UEgvl6jRhBxzjgmqgZ3QZBPiJK+jl4P9cX9Nc75S+5W0qy6XC6UPfkOJHK6HSl/0DUgCRNQY22yq/UiviZyUvN7L5mw3PhRZfbN9qd8r8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751160; c=relaxed/simple; bh=ExpM3z+KmSlM1Oxntu5RoLA8nq07uwVp4YjIR7ZUaP0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=CuvEn6Mrib0XaB10xvX1IsQgfc5TEZ6PbMZCfCb1aZSYe+Ul2v9FMPlOjFdxYfrj7DNJNsFxSUs3krEfUKDPO0cVYwJpXYeH4PfqUdoi/a7ljv5LYVk3FWtT352sZ6Hx9NZZ+8Bq0BUoFytd6fVlaKvEE8gqq25VoELpKSbENOc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=tchM55Jj; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="tchM55Jj" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-1361d52b3a0so7422371c88.0 for ; Mon, 25 May 2026 16:19:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751158; x=1780355958; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=JwB/penh1h7iB3PCt1rj+s/LDOfq1j86b86CSw3tD20=; b=tchM55JjDXx8oRxD203XWeVG86YDW4orhjS4aCH99w4tl1CJW/ZiCwJLq53AJsF2rv gtm/i727EzTkYRyt+nnf84Fu0eCz+DP1SGkhEQtpL9i94vqNpYLr2cROTAD46VSGM9RU rjxdmusbKVi2AUXeb8Y05rQp4StFSK6zOJQ8d7G1/CcA/Y/jcYKi5fouuZOaYIrDHD1I dmEwMgXrUw6hl7D4ce+/F6Y2AR+U87veJ0kdnqo8QfB8pGVUX2rau5J0EKA0Y7CZlRzb CSg+cn356TEnmzjnHAZVwKufADppSk9nAtnMKeSEbKuBCQXsDtyROmnn09ZgtxBNVIwT zSaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751158; x=1780355958; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=JwB/penh1h7iB3PCt1rj+s/LDOfq1j86b86CSw3tD20=; b=qSjMkpOXnhvuymm0blWH5bQVeJd4YGoydEh21dy6U3msoF9MyevGUAUZSmJe153h+w X1Q8GDmCVi4C8l35JxXgW32yCJJcs4cPLozMEzBLSWcDy1Cs6VJl3wHY5dyQ/0eg7xES 0CYexgtFh6Ys48MjY5D7Ms+l2vZ3KCV2wWmr3DJYfvXEDcK3RbDzTzB+2TxUVBFaZRht BIhcsrbFrSkmCvwBexAh18LM0hqCrDodW3Rp4A3+8ptMJ0HY8fYRf0zzNwrLHx5UvjqR ivV88os0H40igtlHC1An2udPQHsQ+XrppGnu3LXcOY9S1LfB9cn0iZ9yU98CXn7KHBeA IPjw== X-Forwarded-Encrypted: i=1; AFNElJ/2/x99kSGaNInEA1WHHpBau4ieL85XDcoLMrUHgrDfPWQxR1rZXIpJWXiyDsRJS74wzABEioQRodrhb08=@vger.kernel.org X-Gm-Message-State: AOJu0YyOvwTgtQWsJIHK4iKviosFRnWZXiG4zu9sqK5AgfTO2SARDvTp jgI2OLrUmIWRRpTrl8QGN+qyYd0DfFLEK1vK0FY1e8OVEL0xiPp0UMCbXQsmZzSIPpLWjCJSNm+ nuz9MYPT1wQ== X-Received: from dlboy5.prod.google.com ([2002:a05:7022:1285:b0:132:f653:503c]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:ba3:b0:135:a6a:f7e8 with SMTP id a92af1059eb24-1365fc65494mr5170412c88.36.1779751157932; Mon, 25 May 2026 16:19:17 -0700 (PDT) Date: Mon, 25 May 2026 16:18:50 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-5-irogers@google.com> Subject: [RFC PATCH v2 04/14] perf stat: Extend STD output linter to test core aggregation checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends tools/perf/tests/shell/stat+std_output.sh to run all standard CPU and thread aggregation checks under the --new print flag: - check_system_wide - check_system_wide_no_aggr - check_interval - check_per_thread - check_per_node - check_per_core - check_per_socket - check_per_die This guarantees that standard console outputs produced by the decoupled printing engine are verified across all core CPU-aggregation modes. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/stat+std_output.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/s= hell/stat+std_output.sh index 233e0a50eb33..8dee005a7281 100755 --- a/tools/perf/tests/shell/stat+std_output.sh +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -121,6 +121,17 @@ fi # New API basic checks perf_cmd=3D"--new -o ${stat_output}" check_no_args "STD (New API)" "$perf_cmd" +check_system_wide "STD (New API)" "$perf_cmd" +check_interval "STD (New API)" "$perf_cmd" +check_per_thread "STD (New API)" "$perf_cmd" +check_per_node "STD (New API)" "$perf_cmd" +if [ $skip_test -ne 1 ] +then + check_system_wide_no_aggr "STD (New API)" "$perf_cmd" + check_per_core "STD (New API)" "$perf_cmd" + check_per_die "STD (New API)" "$perf_cmd" + check_per_socket "STD (New API)" "$perf_cmd" +fi =20 cleanup exit 0 --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3914A36495D for ; Mon, 25 May 2026 23:19:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751163; cv=none; b=lpRobOXNBKGEzpnPhLYJvT5l4/yCGwhFDPp5h+QcHOnYKF1k5oYDig/6mwwZ8S5xFuGTvc+Ym9/EcWk1HhQMVgfMaAkz+tuMxf53vSnJRiFrol/4lK1W9cwo4xiW0eFnrIWv9sBypjWHKuN6etKBAYyh+IpPoXL9xqEMJ8hXgts= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751163; c=relaxed/simple; bh=U80gcFi4WzNGNIta+dVGhXSOXfz5DYs1o+sarzUoYQg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=fOvrl+gdTlu/De6sHPiUQIwqouv3GOQ/inBHEZcvOxPMb898hoBwStfNiALeHWq9snx+1Na8tJTC0OvHSu2EwXo9UVOKanuQdGvlEmGzyJOb3yS3n+/auYrv1GsFKe4QOCfGlAEdFMeGkMULvmh2b3cPFZHxBj595/vkw7OvEGs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=eszFliYQ; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="eszFliYQ" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-3048abb847eso598470eec.0 for ; Mon, 25 May 2026 16:19:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751161; x=1780355961; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=VDxe37y4tS4oHNnjFdOdFfV5ZDDklsO1V/H5cjSQvn4=; b=eszFliYQib+/XsItFNeDbHlXSGBZxEeJMNMBrph9OKbf8DqyPcQA1O2xu7fiatvniX PkJM2l64DjOSLvUCLdDG++pLa2cVhzuXgN4wEqlyvYJTBjvlen24URj7rtFH1HmCZ0OZ f5zvJJCC2uNtl/tOm87Spszo7o0DUymWcucdNqrt7LCik4iSdzEXES04IDaRi/ilr294 BdLGMfHdNfwEMx2+ZHcc1lswIygwPq6+h2eHWer8zxhz21nmgntLrHcqU6c/FhY6ilyH OHl9EM4YtphP4QWpZmrKkpkqrhzU0+OVyHgaR0VANz0MjKdd2Je2h1j/AC5MbHP6XTTQ nPoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751161; x=1780355961; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=VDxe37y4tS4oHNnjFdOdFfV5ZDDklsO1V/H5cjSQvn4=; b=syG9eZCnzD7htty4tFWLlBdM1hULd1f/1JhHxL5mGrxvtPde5wFptkq/lnHXE6rWTg 9sRbZ16WBXKvGL3VdsBIl494kBy45nEdVcrrksvOwLtA6ZibgmXm9/8rkKCDy3x7dR9a BZW4xdTYhaoJpKHAiAYhYwJ4zxsXkgaZkf3PqZOSP0M+Y9PmdQsXF2EQEDybe808rHdj v4MWPekpscRGmOUqdl/6OMoiDoiGdkncIHWq4X92OFJcyIZFaB3+EdNcDSp2DTgRPpNr 1zDsOn4sQNVKMjG9t6UhjQfWhqD4sNyfE6QCHvKIq+fpXR/el8Kxq75tC4QqRFV2m59F lJaQ== X-Forwarded-Encrypted: i=1; AFNElJ/Vrh3tt1EgYuqWiMKRHY2aA1GUQi2jFLmiDMLFiHbDLUObDDH1+PtH0+kvIVfb8++IECGJDRajd54Gwxk=@vger.kernel.org X-Gm-Message-State: AOJu0YyBAKZBGLLFGHWK8wbWZLO1NAOa5Hn5yGQHOFtOXCM8rmTOEqj/ ixcy3cEwYYMnJ6jlzQLqfLiVDLIxx5ZCmDIQI6Lfj7of9aPHJqRMAtPgjVlyVGXStch5MgO+Sp+ 69gHgMVWaAw== X-Received: from dlcip7.prod.google.com ([2002:a05:7023:c087:b0:136:9e6c:868b]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:ed06:b0:135:40b2:ede0 with SMTP id a92af1059eb24-13633b6ccbcmr4108255c88.6.1779751161128; Mon, 25 May 2026 16:19:21 -0700 (PDT) Date: Mon, 25 May 2026 16:18:51 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-6-irogers@google.com> Subject: [RFC PATCH v2 05/14] perf stat: Extend STD output linter to test advanced PMU checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends tools/perf/tests/shell/stat+std_output.sh to run the advanced hardware PMU and topology checks under the --new print flag: - check_per_cache_instance - check_per_cluster This ensures that standard console outputs are verified under advanced topology-aware aggregation environments. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/stat+std_output.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/s= hell/stat+std_output.sh index 8dee005a7281..0d38a1283967 100755 --- a/tools/perf/tests/shell/stat+std_output.sh +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -131,6 +131,8 @@ then check_per_core "STD (New API)" "$perf_cmd" check_per_die "STD (New API)" "$perf_cmd" check_per_socket "STD (New API)" "$perf_cmd" + check_per_cache_instance "STD (New API)" "$perf_cmd" + check_per_cluster "STD (New API)" "$perf_cmd" fi =20 cleanup --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 100C23655E3 for ; Mon, 25 May 2026 23:19:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751165; cv=none; b=rD1WzLH+bu4zDEg9kNXDlFT/0cS2LkPaW5lK8oy0eak97sRiEzzaminx2oMuVqlV2t+p9WvTi0m6DgKs8mBZ6hRqRe/lTK7AegfLKp5JDuXFkvS4tEysrX3ujLptNVLYe3Z/jxu1ekwL0ihyLd3nB2W/rC4Yj656VNBMKQRw5/s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751165; c=relaxed/simple; bh=cURiuYzNr2CBqT8pygcAgfyCBy0HxP7y/rD1ibkTquY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=FDxi/CAxVriiN5PqdUDPB2FLdtFEOWyjItlQfyvcPU2SKdUf6O0KzEYO1jmyodFtZvjgw85NcK5KqJiPEMDJZNcLvT/CY9FLhFnr4tMDCN1XOOlvCO+Qa5YnmSRPYLUsCluVg2NrYq/+/pJT+c00J/CfDBl0IpZ/tybEuVcXa1k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=cej0+ROA; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="cej0+ROA" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2f2d983d109so950505eec.0 for ; Mon, 25 May 2026 16:19:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751163; x=1780355963; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=xq4ITk+f921panuL8UoNawFY8vlZouj316Z+sTkCPNI=; b=cej0+ROAqjH5dP2scsWsk5DFIbPtGft21f3cBmC0kxA5Elvobie/gpJbocrS9ir941 KOdxK6iPhFgWLhGNpCvlyKqdiLxAcwkv0HQDZqihy2Km9BdfJykI4nrp9ImaYeR0Xyzs WyWUFCv97hgsMQxefLHfLslabCIrk7jhKQ20/XyeCNeKIw7QjXHqHQTxvtJnNCV6j+Wf XjZAy3vVNr/cBDjaSPMDFab1FQvsQYURfeEvfBWMYIgLXv35l8mzglYjXYnBnG67Ci+K DyRCsZse/v1rnLp2MHwYVSGlPpyl69S06rJvdjWg6Tr2H194Bc2GuKs/LdEhlNbWq0tK 8nhg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751163; x=1780355963; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=xq4ITk+f921panuL8UoNawFY8vlZouj316Z+sTkCPNI=; b=PxNfIEwtlNMoxNbxNmXPnA33SAZ4ZMoYQ/VSBwP8oQe5pvriQdcMn1ezOplfRxytX2 WGuyRKS/ZxBtTJ56jHkd/apMbRwYiVT1YqEjNDqn/VTR/2EParNVCwN64S1IxPbSxrXY dH3pQTCL80i6q5/9Qfmo71owA1Yfd3FtwWeDGHCBkuZb7qlbu0fXpvDUtcWNYOr1H0HU U0NCjXVOJ/+WWUHL3GaKnbV4uBrYcyMo2hkfjpvDEemwr5xzo/QB0LDUQerJcXmzRGAj J4eg7aVWeYkT7s1e/O5+gCCBDNMLQha6Um867rYurhMPRgzj1aqM5ROMcK5134K9ZG/d KwZQ== X-Forwarded-Encrypted: i=1; AFNElJ/j/ec56KgKlaIBgX/Bq82Lfh67lwlyhPuYjKzKJ0nrOuR8VHJ5ecdZ7WMavngsb8MN/VvZJiwUGeDDJy0=@vger.kernel.org X-Gm-Message-State: AOJu0YyeN1kxwEZALWwLUNSi9BSruPeKhEqUQmOKc2HsjDk5ceA8oNqH haGPv8tVGnD9+8JcLYaru9YlU32HJx/goBJuJJedohfr5V6PB7XgeldVluML9vWXWeDXz1VXKeZ UozFiZIMYlA== X-Received: from dyblz48.prod.google.com ([2002:a05:7301:1630:b0:304:59b1:e122]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:d89:b0:2ed:6f94:9d9f with SMTP id 5a478bee46e88-304490514cbmr8224624eec.11.1779751162980; Mon, 25 May 2026 16:19:22 -0700 (PDT) Date: Mon, 25 May 2026 16:18:52 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-7-irogers@google.com> Subject: [RFC PATCH v2 06/14] perf stat: Extend STD output linter to test metric-only checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends tools/perf/tests/shell/stat+std_output.sh to run the metric-only check under the --new print flag: - check_metric_only This guarantees that standard console metric-only outputs produced by the decoupled printing callbacks are formally validated by the test linter. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/stat+std_output.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/s= hell/stat+std_output.sh index 0d38a1283967..69720b19b908 100755 --- a/tools/perf/tests/shell/stat+std_output.sh +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -134,6 +134,7 @@ then check_per_cache_instance "STD (New API)" "$perf_cmd" check_per_cluster "STD (New API)" "$perf_cmd" fi +check_metric_only "STD (New API)" "$perf_cmd" =20 cleanup exit 0 --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AF6DD366542 for ; Mon, 25 May 2026 23:19:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751168; cv=none; b=npr0PvTafuD8tiU5u4WeMcF8E3ikOeO+moP0lSYeevIrhcA3TVLihVpBOBfufYNYiif8OEw1S0kD5lUPTvWi4xcGggdUJNWMjTJ+8PzBWGjQnbIMEsKVxbojwHb6uVwS+1GLLYGPSRIIkW68zwGlZw33msNwYnKFEJSNS2Uc7cM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751168; c=relaxed/simple; bh=8GJyw7cm0oX64XsR9Z0L19zVwL4oDKwlijNGSDNA9LY=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=KoOGtpk2VtV3u/anFxz/gFZHs2yUqCVWxlu/YNvT218I/XAkxpiLqCpm270L0AEyvuK5FZhN9lEzo1c3908bB1QCz0jGu4fMAMUpVYBZcaOAwZZ43NAEYGD0xM2xe+cET33r1GRWDIiL7qHLLIWU0NWHWK4MyZnOCJ2yzSAjusc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=tieiRJDc; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="tieiRJDc" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2efc342ef15so11608959eec.1 for ; Mon, 25 May 2026 16:19:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751165; x=1780355965; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=N2sz5qO6kQz+Lt48iS083AWn8N1X3typFP1MEfLbxz4=; b=tieiRJDcF1xS4Xg6BIt1dctTLCP9Q5hqSDLlgXqtl7/++6pR0Hzl7pt/2DHowYfBO6 c5F8ODXqug9dbhHMNRO0OUfcgoATp2l051TT7/nqpXVYlc5/hr8yM9IxFwExVr5dKaA7 jllFwXQA2FLoY0eNN0Cig9Cct4Kdk8koVsqkCER8p2fzjrHOpBv/KCWxu+Ijee6aETaM luXNueIzTw2KLvhmCiR+GIHDqzodG/OZZfv4JSLnHg0B7o9CACzESUkZZK5pOur+EvkO YGvADZcQNvd6qAYGJZwwX5Qcq50CmTDlixZSpMKV/ml6ios0ttN6LemeUq7F0YS9X7Fm uuMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751165; x=1780355965; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=N2sz5qO6kQz+Lt48iS083AWn8N1X3typFP1MEfLbxz4=; b=gginuhRIwoaqZ9C5Q8pIlWMrG7NQgcETrHrrO6DrshncDuaH8dABc+yF6Z7LSTdc7j vZrbnk+dlb7aQn14CCF7OKSoqUskVAowIj6zHbeVzr5jyGNTqbOMuvHB2Q/X6CD7aJNp SZxJkH9lSb5XsVMn7PMGiGhyK/mc9l5pDFvxMAaNeOnNhm0607065GabKE0/ij9QvnmX WEF7mspfrZ47VpS4sGaU/3MI5wZj4eCAj9pL9mvxXU6linbsq2fhv8WT8eUbipIE5X1D zH2FLw8nUw6zTiepbLa0j5aaNzJvttUXqv8PH4NQDLb83V11BBqbxOYulma0sTxc8gwx MO3A== X-Forwarded-Encrypted: i=1; AFNElJ8OeOG9S26eKFbsXHL39jPiTs1LVLTywLop4v6lK0+dTjFMR4oBbXawvHnS+ynSbQDXDqQJbk1QkCfL7lQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyHbMXBKRRBUpQapS8SlXuCPXoEcRYawN3Sww/l/W4K+MpRegwm XBYLhyNPk/bNni0Qqfdlh2pmHBfuoA/MtSf+pJWoxdZCa/Q+XPCX0f44vhg54u/XK6DZUrc99Wn gyLCd/mxPHg== X-Received: from dybsc18.prod.google.com ([2002:a05:7301:4b12:b0:2df:c53c:24a5]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:a903:b0:304:2af3:5ff2 with SMTP id 5a478bee46e88-3044905c53fmr7185321eec.21.1779751164721; Mon, 25 May 2026 16:19:24 -0700 (PDT) Date: Mon, 25 May 2026 16:18:53 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-8-irogers@google.com> Subject: [RFC PATCH v2 07/14] perf stat: Implement CSV formatting callbacks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch implements CSV output formatting callbacks inside util/stat-print-csv.c, replacing the empty stubs introduced in Commit 1. Defines the format-private `struct queued_event` and `struct queued_metric` DOM nodes to buffer traversal streams, and fully encapsulates CSV queued li= sts lifecycle and deallocations inside csv_print_start() and csv_print_end(). Utilizes the newly centralized unified aggregation helpers to format CPU and thread column prefixes cleanly, fixes metrics separators padding, and incorporates full interval-mode timestamp printing support. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/util/stat-print-csv.c | 537 ++++++++++++++++++++++++++++++- 1 file changed, 529 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/stat-print-csv.c b/tools/perf/util/stat-print-= csv.c index e9d1e7c30c90..35cd4505c6c1 100644 --- a/tools/perf/util/stat-print-csv.c +++ b/tools/perf/util/stat-print-csv.c @@ -1,13 +1,534 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#include "stat-print.h" +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include + #include +#include + +#include "cpumap.h" +#include "evlist.h" +#include "evsel.h" +#include "stat-print.h" +#include "stat.h" +#include "thread_map.h" +#include "debug.h" + +#define COMM_LEN 16 +#define PID_LEN 7 + +struct queued_metric { + struct list_head list; + char *name; + char *unit; + double val; + int aggr_idx; +}; + +/** + * struct queued_event - In-memory record of a buffered CSV counter event. + * @list: Linked list node for queueing. + * @evsel: The associated performance event selector. + * @name: The uniquely formatted/resolved event name. + * @unit: The event's unit (e.g. "msec", "cycles"). + * @val: Raw aggregated counter value. + * @ena: Enabled time for multiplexing percentage. + * @run: Running time for multiplexing percentage. + * @scale: Event scale factor. + * @supported: Event hardware support indicator. + * @aggr_idx: Aggregation index. + * @metrics_list: Linked list head containing nested queued_metric structu= res. + */ +struct queued_event { + struct list_head list; + struct evsel *evsel; + char *name; + char *unit; + u64 val, ena, run; + double scale; + bool supported; + int aggr_idx; + struct list_head metrics_list; +}; + +/** + * struct csv_print_state - Print state context for CSV output. + * @fp: File descriptor to output to. + * @sep: CSV column separator character/string. + * @timestamp: Formatted interval timestamp (optional). + * @events_list: Linked list head containing queued_event nodes. + * @current_event: Pointer to the currently active event being printed. + * Serves as a temporary bridge to associate streaming met= rics back to + * their parent event node during list buffering. This rel= ies on a + * strict temporal coupling in the traversal driver: the d= river always + * invokes print_metric() callbacks for a counter synchron= ously and + * immediately after its print_event() callback, prior to = advancing + * to the next event or aggregation node. This pointer is = completely + * private to CSV printing, keeping the traversal driver d= ecoupled + * and preserving strict encapsulation. + */ +struct csv_print_state { + FILE *fp; + const char *sep; + char timestamp[64]; + struct list_head events_list; + struct queued_event *current_event; +}; + +/** + * struct csv_metric_only_print_state - Metric-only print state context fo= r CSV output. + * @fp: File descriptor to output to. + * @sep: CSV column separator. + * @timestamp: Formatted interval timestamp (optional). + * @evlist: Evlist to query entries from. + * @queued_metrics: Linked list head containing queued_metric nodes. + */ +struct csv_metric_only_print_state { + FILE *fp; + const char *sep; + char timestamp[64]; + struct evlist *evlist; + struct list_head queued_metrics; +}; + +/** + * print_aggr_id_csv - Print the aggregation prefix for CSV format. + * + * Copied and adapted from stat-display.c. + */ +static void print_aggr_id_csv(const struct perf_stat_config *config, FILE = *output, + struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) +{ + const char *sep =3D config->csv_sep; + + switch (config->aggr_mode) { + case AGGR_CORE: + fprintf(output, "S%d-D%d-C%d%s%d%s", id.socket, id.die, id.core, sep, ag= gr_nr, sep); + break; + case AGGR_CACHE: + fprintf(output, "S%d-D%d-L%d-ID%d%s%d%s", id.socket, id.die, id.cache_lv= l, id.cache, + sep, aggr_nr, sep); + break; + case AGGR_CLUSTER: + fprintf(output, "S%d-D%d-CLS%d%s%d%s", id.socket, id.die, id.cluster, se= p, aggr_nr, + sep); + break; + case AGGR_DIE: + fprintf(output, "S%d-D%d%s%d%s", id.socket, id.die, sep, aggr_nr, sep); + break; + case AGGR_SOCKET: + fprintf(output, "S%d%s%d%s", id.socket, sep, aggr_nr, sep); + break; + case AGGR_NODE: + fprintf(output, "N%d%s%d%s", id.node, sep, aggr_nr, sep); + break; + case AGGR_NONE: + if (evsel->percore && !config->percore_show_thread) + fprintf(output, "S%d-D%d-C%d%s", id.socket, id.die, id.core, sep); + else if (id.cpu.cpu > -1) + fprintf(output, "CPU%d%s", id.cpu.cpu, sep); + break; + case AGGR_THREAD: + fprintf(output, "%s-%d%s", + perf_thread_map__comm(evsel->core.threads, id.thread_idx), + perf_thread_map__pid(evsel->core.threads, id.thread_idx), sep); + break; + case AGGR_GLOBAL: + case AGGR_UNSET: + case AGGR_MAX: + default: + break; + } +} + +/* + * CSV Output Callbacks - Normal Mode + */ + +static int csv_print_start(void *ctx, const struct perf_stat_config *confi= g __maybe_unused) +{ + struct csv_print_state *ps =3D ctx; + + INIT_LIST_HEAD(&ps->events_list); + ps->current_event =3D NULL; + return 0; +} + +static int csv_print_event(void *ctx, const struct perf_stat_config *confi= g __maybe_unused, + struct evsel *evsel, int aggr_idx, u64 val, u64 ena, u64 run, + double stdev_pct __maybe_unused) +{ + struct csv_print_state *ps =3D ctx; + struct queued_event *ev =3D malloc(sizeof(*ev)); + + if (!ev) + return -ENOMEM; + + ev->name =3D strdup(evsel__name(evsel)); + if (!ev->name) { + free(ev); + return -ENOMEM; + } + + if (evsel->unit) { + ev->unit =3D strdup(evsel->unit); + if (!ev->unit) { + free(ev->name); + free(ev); + return -ENOMEM; + } + } else { + ev->unit =3D NULL; + } + + ev->evsel =3D evsel; + ev->val =3D val; + ev->ena =3D ena; + ev->run =3D run; + ev->scale =3D evsel->scale; + ev->supported =3D evsel->supported; + ev->aggr_idx =3D aggr_idx; + INIT_LIST_HEAD(&ev->metrics_list); + + list_add_tail(&ev->list, &ps->events_list); + ps->current_event =3D ev; + + return 0; +} + +static int csv_print_metric(void *ctx, const struct perf_stat_config *conf= ig __maybe_unused, + struct evsel *evsel __maybe_unused, int aggr_idx __maybe_unused, + const char *name, const char *unit, double val, + enum metric_threshold_classify thresh __maybe_unused) +{ + struct csv_print_state *ps =3D ctx; + struct queued_metric *b; + + if (!ps->current_event) + return 0; + + if (evsel !=3D ps->current_event->evsel) { + pr_err("decoupled print engine: temporal coupling violation: evsel misma= tch!\n"); + return -EINVAL; + } + + b =3D malloc(sizeof(*b)); + if (!b) + return -ENOMEM; + + b->name =3D strdup(name); + if (!b->name) { + free(b); + return -ENOMEM; + } + + if (unit && unit[0]) { + b->unit =3D strdup(unit); + if (!b->unit) { + free(b->name); + free(b); + return -ENOMEM; + } + } else { + b->unit =3D NULL; + } + + b->val =3D val; + list_add_tail(&b->list, &ps->current_event->metrics_list); + + return 0; +} + +static int csv_print_end(void *ctx, const struct perf_stat_config *config) +{ + struct csv_print_state *ps =3D ctx; + struct queued_event *ev, *tmp_ev; + struct queued_metric *met, *tmp_met; + FILE *output =3D ps->fp; + const char *sep =3D ps->sep; + bool has_metrics; + + list_for_each_entry_safe(ev, tmp_ev, &ps->events_list, list) { + struct evsel *evsel =3D ev->evsel; + bool ok =3D (ev->run !=3D 0 && ev->ena !=3D 0); + const char *bad_count =3D ev->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SU= PPORTED; + double enabled_percent =3D 100; + + /* Print interval timestamp first if configured */ + if (config->interval && ps->timestamp[0]) + fprintf(output, "%s", ps->timestamp); + + /* Print aggregation prefix first in CSV normal mode */ + if (config->aggr_map && ev->aggr_idx >=3D 0) { + struct aggr_cpu_id id =3D config->aggr_map->map[ev->aggr_idx]; + int aggr_nr =3D 0; + + if (evsel->stats && evsel->stats->aggr) + aggr_nr =3D evsel->stats->aggr[ev->aggr_idx].nr; + + print_aggr_id_csv(config, output, evsel, id, aggr_nr); + } =20 -int perf_stat__print_csv(struct evlist *evlist __maybe_unused, - const struct perf_stat_config *config __maybe_unused, - const struct target *target __maybe_unused, - const struct timespec *ts __maybe_unused, - int argc __maybe_unused, - const char **argv __maybe_unused) + if (ok) { + double sc =3D ev->scale; + double avg =3D ev->val * sc; + const char *fmt =3D floor(sc) !=3D sc ? "%.2f%s" : "%.0f%s"; + + fprintf(output, fmt, avg, sep); + } else { + fprintf(output, "%s%s", bad_count, sep); + } + + if (ev->unit) + fprintf(output, "%s%s", ev->unit, sep); + else + fprintf(output, "%s", sep); + + fprintf(output, "%s", ev->name); + + if (ev->run !=3D ev->ena) + enabled_percent =3D 100.0 * ev->run / ev->ena; + fprintf(output, "%s%" PRIu64 "%s%.2f", sep, ev->run, sep, enabled_percen= t); + + /* Print metrics */ + has_metrics =3D false; + list_for_each_entry_safe(met, tmp_met, &ev->metrics_list, list) { + if (!has_metrics) { + has_metrics =3D true; + } else { + fprintf(output, "\n"); + if (config->interval && ps->timestamp[0]) + fprintf(output, "%s", ps->timestamp); + if (config->aggr_map && ev->aggr_idx >=3D 0) { + struct aggr_cpu_id id =3D config->aggr_map->map[ev->aggr_idx]; + int aggr_nr =3D 0; + + if (evsel->stats && evsel->stats->aggr) + aggr_nr =3D evsel->stats->aggr[ev->aggr_idx].nr; + + print_aggr_id_csv(config, output, evsel, id, aggr_nr); + } + /* Subsequent metrics have exactly 4 padding separators */ + fprintf(output, "%s%s%s%s", sep, sep, sep, sep); + } + fprintf(output, "%s%.2f%s", sep, met->val, sep); + if (met->name && met->name[0]) + fprintf(output, "%s", met->name); + + list_del(&met->list); + free(met->name); + free(met->unit); + free(met); + } + if (!has_metrics) + fprintf(output, "%s%s", sep, sep); + fprintf(output, "\n"); + + list_del(&ev->list); + free(ev->name); + free(ev->unit); + free(ev); + } + return 0; +} + +static const struct perf_stat_print_callbacks csv_print_callbacks =3D { + .print_start =3D csv_print_start, + .print_end =3D csv_print_end, + .print_event =3D csv_print_event, + .print_metric =3D csv_print_metric, +}; + +/* + * CSV Output Callbacks - Metric-Only Mode + */ + +static int csv_metric_only_print_start(void *ctx, + const struct perf_stat_config *config __maybe_unused) +{ + struct csv_metric_only_print_state *ps =3D ctx; + + INIT_LIST_HEAD(&ps->queued_metrics); + return 0; +} + +static int csv_metric_only_print_metric(void *ctx, + const struct perf_stat_config *config __maybe_unused, + struct evsel *evsel __maybe_unused, int aggr_idx, + const char *name, const char *unit, double val, + enum metric_threshold_classify thresh __maybe_unused) { + struct csv_metric_only_print_state *ps =3D ctx; + struct queued_metric *b =3D malloc(sizeof(*b)); + + if (!b) + return -ENOMEM; + + b->name =3D strdup(name); + if (!b->name) { + free(b); + return -ENOMEM; + } + + if (unit && unit[0]) { + b->unit =3D strdup(unit); + if (!b->unit) { + free(b->name); + free(b); + return -ENOMEM; + } + } else { + b->unit =3D NULL; + } + + b->val =3D val; + b->aggr_idx =3D aggr_idx; + list_add_tail(&b->list, &ps->queued_metrics); + return 0; } + +static int csv_metric_only_print_end(void *ctx, const struct perf_stat_con= fig *config) +{ + struct csv_metric_only_print_state *ps =3D ctx; + FILE *output =3D ps->fp; + const char *sep =3D ps->sep; + struct queued_metric *b, *tmp; + int first_aggr =3D -1; + /* Initialize to -2 to distinguish from -1 (a valid index in AGGR_GLOBAL = mode) */ + int current_aggr =3D -2; + int ret =3D 0; + int err; + + if (list_empty(&ps->queued_metrics)) + return 0; + + first_aggr =3D list_first_entry(&ps->queued_metrics, struct queued_metric= , list)->aggr_idx; + + if (!config->metric_only_headers_printed) { + /* Print interval timestamp header if configured */ + if (config->interval) + fprintf(output, "time%s", sep); + + /* Print static aggregation prefix header in CSV metric-only mode */ + if (config->aggr_map && first_aggr >=3D 0) { + const char *p =3D aggr_header_csv[config->aggr_mode]; + + while (*p) { + if (*p =3D=3D ',') + fputs(sep, output); + else + fputc(*p, output); + p++; + } + } + + /* Print headers */ + list_for_each_entry(b, &ps->queued_metrics, list) { + if (b->aggr_idx =3D=3D first_aggr) { + char *header_name; + + if (b->unit && b->unit[0]) { + err =3D asprintf(&header_name, "%s %s", b->unit, b->name); + } else { + header_name =3D strdup(b->name); + err =3D header_name ? 0 : -1; + } + if (err < 0) { + ret =3D -ENOMEM; + goto cleanup; + } + fprintf(output, "%s%s", header_name, sep); + free(header_name); + } + } + fprintf(output, "\n"); + ((struct perf_stat_config *)config)->metric_only_headers_printed =3D tru= e; + } + + /* Print values */ + list_for_each_entry_safe(b, tmp, &ps->queued_metrics, list) { + if (b->aggr_idx !=3D current_aggr) { + if (current_aggr !=3D -2) + fprintf(output, "\n"); + current_aggr =3D b->aggr_idx; + if (config->interval && ps->timestamp[0]) + fprintf(output, "%s", ps->timestamp); + if (config->aggr_map && current_aggr >=3D 0) { + struct aggr_cpu_id id =3D config->aggr_map->map[current_aggr]; + struct evsel *mock_evsel =3D list_first_entry( + &ps->evlist->core.entries, struct evsel, core.node); + int aggr_nr =3D 0; + + if (mock_evsel->stats && mock_evsel->stats->aggr) + aggr_nr =3D mock_evsel->stats->aggr[current_aggr].nr; + + print_aggr_id_csv(config, output, mock_evsel, id, aggr_nr); + } + } + fprintf(output, "%.1f%s", b->val, sep); + + list_del(&b->list); + free(b->name); + free(b->unit); + free(b); + } + fprintf(output, "\n"); + return 0; + +cleanup: + list_for_each_entry_safe(b, tmp, &ps->queued_metrics, list) { + list_del(&b->list); + free(b->name); + free(b->unit); + free(b); + } + return ret; +} + +static const struct perf_stat_print_callbacks csv_metric_only_print_callba= cks =3D { + .print_start =3D csv_metric_only_print_start, + .print_end =3D csv_metric_only_print_end, + .print_event =3D NULL, + .print_metric =3D csv_metric_only_print_metric, +}; + +int perf_stat__print_csv(struct evlist *evlist, const struct perf_stat_con= fig *config, + const struct target *target, const struct timespec *ts, int argc, + const char **argv) +{ + if (config->metric_only) { + struct csv_metric_only_print_state ps =3D { + .fp =3D config->output, + .sep =3D config->csv_sep, + .evlist =3D evlist, + }; + if (config->interval && ts) { + scnprintf(ps.timestamp, sizeof(ps.timestamp), "%lu.%09lu%s", + (unsigned long)ts->tv_sec, ts->tv_nsec, config->csv_sep); + } else { + ps.timestamp[0] =3D '\0'; + } + return perf_stat__print_cb(evlist, config, target, ts, argc, argv, + &csv_metric_only_print_callbacks, &ps); + } else { + struct csv_print_state ps =3D { + .fp =3D config->output, + .sep =3D config->csv_sep, + }; + + + + if (config->interval && ts) { + scnprintf(ps.timestamp, sizeof(ps.timestamp), "%lu.%09lu%s", + (unsigned long)ts->tv_sec, ts->tv_nsec, config->csv_sep); + } else { + ps.timestamp[0] =3D '\0'; + } + return perf_stat__print_cb(evlist, config, target, ts, argc, argv, + &csv_print_callbacks, &ps); + } +} --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dl1-f74.google.com (mail-dl1-f74.google.com [74.125.82.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E0E603672B0 for ; Mon, 25 May 2026 23:19:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751169; cv=none; b=HxQtgbEAEJQvTsWkWdgTb5Orqz47EtwF/6PiLb6SOmrdqmikqwP/njDt9MWe67IW7tstGnEIVAN7yUT5z2Gf/KLxbVS64ZZ3UlkxCG6ajLPkaKRw0Hordq1q3Z3FPPYZ2FFnIWrH5sxBLd2PRroqa3Y59rLOJhMhAXVIyhgeosw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751169; c=relaxed/simple; bh=OqCGNIWQE4SGgQW9dylZWrPK9fmTcI8i2GJr+bk6yfc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=FOCDmOVS1fEhTI44xKJ6jucxGX9jOBQcsQcqAv8ncPYODGts9/kI1mDj5F3MjyqlzdpyENisf/SU6/pK4lAkTOO7PGAJ2dx+tpcNdw4p1RZJc1VIZUlLQNcb3CuYT0qKsy1V4kEAY6QqjrHsOB2TA3CcFI79JUfnrdwFTpmDUig= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=E7sziPzX; arc=none smtp.client-ip=74.125.82.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="E7sziPzX" Received: by mail-dl1-f74.google.com with SMTP id a92af1059eb24-13709ab38a8so710387c88.0 for ; Mon, 25 May 2026 16:19:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751167; x=1780355967; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=XMbgr0fXMrIqPCRvG6UsLccNRcvJPlMGrNsKwpwREcU=; b=E7sziPzXJO0L1y1cK/13tvt93TjvUORSxZTdy9OisampHtX4NYxclGk54hfd1r8U9g On8VTFs8QpGNYWIGzxG2Z++b3cJbS3X9m/F7z0AIyM1ol8NGEtzAYgAA/KDSbXtlnNbr xL4l1RavWDbAn+4sfa4LOIdy4/MA9oiJAmrlnIBxIBQMdViaCQlN6f5z2eoWzp6XymlV gNbmJGRaVkiCbf7plysS1J/97qNQhOAo2jvofSCU5epCq8yIymE9PY/fhNF6HfKI5BeO DACLfGZLFZPvUsF5KXwDTSGKSrN9y9xJBm7kQMFCCZOoJWigrkoGB91/aTqlimjeo1I5 w4AQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751167; x=1780355967; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=XMbgr0fXMrIqPCRvG6UsLccNRcvJPlMGrNsKwpwREcU=; b=tIEoLrnxz4n1OUbPMY+ufpP86lyWHM5m0GIv6LbzovjNCQJ+5E5Zx6cK5/Q97cfE0t oJqw3XfCSTVbp5j6gH2P01Eg3Oazi6fUQJHfwdP3otQjTBre2ei1vVU9JXngdRkAwPeD TMYFsOK9QExWdhftCPkqHCG33GVMJgKWcSXlZxUoFzHK3gjD0dKOcs6m3aQqSg8+0bT/ OnZHt8dDJ03L7mVi+ep/XZLhc3wfCRVc3hiqaR2fdcWTkT/zp/QuwOo28x67cyr1Xn9I KStG4uFrwllWHev0aqiduUSDt9vZDl5TTF1rZQ3o4xRjtwJ5fO6dJPlsqDaTFjuaaOFX 6dXg== X-Forwarded-Encrypted: i=1; AFNElJ/8tkBkUgfQYqm2goLOWKaysJTMzWMwMywGOHDcPLOrPhR7rOyrbQa+wMwhRHg4Q4vqfUtjw83WDp+bL3s=@vger.kernel.org X-Gm-Message-State: AOJu0YyxukyMvX//cLhW9jZqQ6Hk9fxsH5V0/9pIKwoGd+l5jC4HVC93 tv2ZA/Z818E3yWwGR6om0UkdJIfoVcDz/FuAW6n3YPz56p4ZDApcFuon+ytkaMcS4IJwOgfVV8Z u/ySoM65UOA== X-Received: from dlber12.prod.google.com ([2002:a05:7022:264c:b0:135:53c6:ecc8]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7022:60c:b0:12d:b329:987d with SMTP id a92af1059eb24-1365fa3828emr5999771c88.24.1779751166786; Mon, 25 May 2026 16:19:26 -0700 (PDT) Date: Mon, 25 May 2026 16:18:54 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-9-irogers@google.com> Subject: [RFC PATCH v2 08/14] perf stat: Extend CSV output linter to test core aggregation checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends tools/perf/tests/shell/stat+csv_output.sh to run all basic and core aggregation linter checks a second time under the --new CSV print flag: - check_no_args - check_system_wide - check_interval - check_event - check_per_thread - check_per_node - check_system_wide_no_aggr - check_per_core - check_per_socket - check_per_die This guarantees that CSV outputs produced by the decoupled printing engine are formally verified and column-valid across standard and interval modes. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/stat+csv_output.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/perf/tests/shell/stat+csv_output.sh b/tools/perf/tests/s= hell/stat+csv_output.sh index cd6fff597091..e4153a03d716 100755 --- a/tools/perf/tests/shell/stat+csv_output.sh +++ b/tools/perf/tests/shell/stat+csv_output.sh @@ -88,5 +88,21 @@ then else echo "[Skip] Skipping tests for system_wide_no_aggr, per_core, per_die an= d per_socket since socket id exposed via topology is invalid" fi +# New API CSV checks +perf_cmd=3D"--new -x$csv_sep -o ${stat_output}" +check_no_args "CSV (New API)" "$perf_cmd" +check_system_wide "CSV (New API)" "$perf_cmd" +check_interval "CSV (New API)" "$perf_cmd" +check_event "CSV (New API)" "$perf_cmd" +check_per_thread "CSV (New API)" "$perf_cmd" +check_per_node "CSV (New API)" "$perf_cmd" +if [ $skip_test -ne 1 ] +then + check_system_wide_no_aggr "CSV (New API)" "$perf_cmd" + check_per_core "CSV (New API)" "$perf_cmd" + check_per_die "CSV (New API)" "$perf_cmd" + check_per_socket "CSV (New API)" "$perf_cmd" +fi + cleanup exit 0 --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E1E103672BE for ; Mon, 25 May 2026 23:19:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751171; cv=none; b=rTWx518VZPL1zEQ8yh4ARXkp6gdw6hhzaeW4xsd4M8W+4gSNIKJCMLEuBFdGOsBYUFV64rsfzX0eIM+12UnP70BsfW1uURZ07k0Q1PcUBhVnrbimzbwBz9Y1qLSdDKkLpUx4nONH+TBSt+cLyJSuyI8XhWhsyfywpMlj5MhfwYM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751171; c=relaxed/simple; bh=qIOP2PLMSCjaI+oZ1XK3BnopPxnw5s3AUOSP0CpPkUo=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=erIGB8B0j8Jhmf9+Mfde8UOZfS/6WpioOGNCbejS6oR7I5iMpWTbY0ekXZI/43Vu91Q4VlTDrT9/k01nxTpe3gVR5aZkUlKNzUEOnDeWeybdFwselZScaSlCW4A6K95ywnJy5uYW1+CwA8blzKaebbGdoLSgzXaoB86oX3eKnJ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=cLpRUMhd; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="cLpRUMhd" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-304448ab58cso1403665eec.1 for ; Mon, 25 May 2026 16:19:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751169; x=1780355969; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=UOgBhfkhz9Rl+cCIvRmHfpLylwK0trKuV+4bVEafs/0=; b=cLpRUMhdxM0YCKUtpcRf3lvlDOuQ6yjzg/yhTQgB8p2s4m2HJGIuitZQHUi2rD6MQv gCY9EtIS1TcU/MfOlLpk2EvU16H+l5HE0toimOFpbRRokWi31CgU2r3F0XS3KvnjEXBb UhLCh5cSUwe5LZyLHHn0XZggVYHXPKWbB2jTsVqIlMZWTLWJ5qxSr1JW9vsguF0+Ag4G nT4bejOWE72UNnSE019zL5kskM0wpvoRMHd8EXQQ90039/uGyXTYhdXeGILjQF+b8BAs wheL8vTk69vv+yzdduqd6iTbbQffRPIHnTnk3a67G7qG7PIdd8Dnptj7KwyQY3lH4wNt bgAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751169; x=1780355969; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=UOgBhfkhz9Rl+cCIvRmHfpLylwK0trKuV+4bVEafs/0=; b=Jf2JPrp2pPryzG0C4av0AgqjAe9sCvH1Q3qwjaGvXERDUAwcP7VsfV+x/cKpPtxSKv cinSSdqhkr3MuttqLz5Ej66EzfbpC2q5TYF4FXOFjuPfYe0X/1KOXDT8BVwon/3BOkDZ YBaV0B2Pqw+8cz2sg2hfYkUMpX6f0fGdydCuKaSMIdf4NasQZI7eR25hW2CBkYjSDtRD LJjI9y5t6MOKhO9cZjhLFc7d/iwQuKRZi/xHjyHznSbLpRz0Dm0UReEZfnY2Ik9LryJf MJPrslwhTpV5TNm8KGzq3IctY9FdMfCBDS5tcPF5rL/RE0o5+4naiPTBXyN9gSPGnQB6 SPMw== X-Forwarded-Encrypted: i=1; AFNElJ9Y9c1V9jjURPtS6HIUGAVdlrWyyQ2ostFTgmIHMks4U9/l+67+hlBNFnR9cNuEZBU2FB3AEATF1yrWu4E=@vger.kernel.org X-Gm-Message-State: AOJu0YwERG/ECWtfTGa80tcpvi/lWl0F3ytqQjbe91HHK1yq4sanDQe+ d1rM61Sh2VsvFhNKBl4aT6si9Xe15Gz+5jOrVKc3gBC9QVHwp0nbnMol3tTKlmYd3eeHAigpUk5 wOeJO7J+e8Q== X-Received: from dybsc18.prod.google.com ([2002:a05:7301:4b12:b0:2df:c53c:24a5]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:6589:b0:2ed:e17:d50c with SMTP id 5a478bee46e88-304491fb381mr7595907eec.31.1779751168980; Mon, 25 May 2026 16:19:28 -0700 (PDT) Date: Mon, 25 May 2026 16:18:55 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-10-irogers@google.com> Subject: [RFC PATCH v2 09/14] perf stat: Extend CSV output linter to test advanced PMU and metric-only checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends tools/perf/tests/shell/stat+csv_output.sh to run the advanced hardware PMU, topology-aware aggregation, and metric-only checks a second time under the --new CSV print flag: - check_per_cache_instance - check_per_cluster - check_metric_only This guarantees that CSV outputs produced by the decoupled printing callbac= ks are verified and column-valid under advanced aggregation modes and metric-o= nly CSV row-column layouts. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/stat+csv_output.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/tests/shell/stat+csv_output.sh b/tools/perf/tests/s= hell/stat+csv_output.sh index e4153a03d716..c48d9e2b6429 100755 --- a/tools/perf/tests/shell/stat+csv_output.sh +++ b/tools/perf/tests/shell/stat+csv_output.sh @@ -102,7 +102,10 @@ then check_per_core "CSV (New API)" "$perf_cmd" check_per_die "CSV (New API)" "$perf_cmd" check_per_socket "CSV (New API)" "$perf_cmd" + check_per_cache_instance "CSV (New API)" "$perf_cmd" + check_per_cluster "CSV (New API)" "$perf_cmd" fi +check_metric_only "CSV (New API)" "$perf_cmd" =20 cleanup exit 0 --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8232636897C for ; Mon, 25 May 2026 23:19:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751174; cv=none; b=hNNtnLqrFnYDgt7Uiziu1IiKdNUp5XNVab68pNSACsbjDVHGjEsNzDF0OK4k4c924SvQsNjk5ZjhApVDNYsriBOSZ0y2AloajO8BSkesRbYNCQpmfeJ6L9/gHhQWj7pS+dlpTSPiwyqtWxjYIDzNRjyjRcpEJ3/DIt7R2xYlJak= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751174; c=relaxed/simple; bh=UpO1gaEZ2KS8z0UYLzPd3sSYWFgNZ9Kaf057qRhpFV0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=W4AdZFZPBqmy3a6nz4X3pX/+wo1u2oLv16tnQ6Ij7e9R1AV0HL4qhq77txqQfU015OWt1prrjA2Y+yDLAIuvp0CErtwxt9VckaV1Z5GgJxzWI1lal+LXOcGWSfrPanrteVp6o6OkjqQwupfc6ES8EF0KyQhoGJPW4PTlyEt3Xsg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=sERoZICa; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="sERoZICa" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2ef37c3f773so10070234eec.1 for ; Mon, 25 May 2026 16:19:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751172; x=1780355972; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=R8Hnr9DfV8xYtjeCIEgx34YHn89jhF6OWLrXqsen2mg=; b=sERoZICadTi8fnUENbbDcZzZCZ96a2dYnTwvmoK5LzuwNWYYZfnuKxH2sOVgTOmx+U uG5ZjToLV5ANt9vGZ6fRz24HiT+5yN0CmRTeNKFN2DBBKBSvozh3GFaYzNCGn9wgrP44 7bO5WjxFgDMzMru2LnnmjRM81zP87nfMFPa6XIzrNv8nKMYguZjwJZ5luU0tfwwlmQnF 7DCA+gRAEl2KwiHfmDh09gR1BcxfcLUtenZZBbArYQOpyJ4nymlVf4P1G6pfXED4MjSw B2WBk3jNXBEe5jDGPi0kDrmqg72g0Rv6DtV7D4/T5kR5ENuHnvCpeq1tj5onkBc6Ey3h UjBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751172; x=1780355972; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=R8Hnr9DfV8xYtjeCIEgx34YHn89jhF6OWLrXqsen2mg=; b=mRHHYcLq9+ffiz7g8m+E4thkrxZHgI14KZSlmXOxz2ZwCKrL+WX9UUZLozkzp75hw7 9gfBLKeWylZq6Dfz7KQFBatXJDlGDe05c/9TZNpJKDNX6Pk4rJm0NAZ+5RTOwcujAl7Q trE7WSb4AEPD8pLw7odrS8yJ2Z+bSPdP5mm3YX5GaR2JRBWTHa87jSxprkXciBXmOBdo epUOtsKz5vTBCfTTeJamMIPploe54+/ZLnkf7zrQN1kajrTIGnyvv1SyncEczkjAEjU3 lSUpZB9EJ1CNhJt5wKNKcOcH2W10AEAxl4Owckp7XCAtAlNZBpXQyIAKapICQIKpYVKj q1Uw== X-Forwarded-Encrypted: i=1; AFNElJ8nrNGKuKsQNWuULJ0e4nW+L0WXt2ubdCNyRhtAISGuFBfPIKqfvxguKI+3HI+FiQxth94jbLCxs54m7Hs=@vger.kernel.org X-Gm-Message-State: AOJu0YxnQf/dhr8zn9sZkipUl7NKu0xSm+Y8itvUxl+1dsq/OdkDJeWb zS2DekjiDCzOCLQpa9oyhqayiEzr9v3avfZpk+A1SY/f35RaVsYo1bMUQ8cgLZ7QCBQ6AM9qj1q nHwQ++IGGaA== X-Received: from dybux5.prod.google.com ([2002:a05:7301:5005:b0:2f2:3f95:c586]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:e605:b0:2d4:94cc:eebb with SMTP id 5a478bee46e88-30449001e3bmr7127132eec.13.1779751171638; Mon, 25 May 2026 16:19:31 -0700 (PDT) Date: Mon, 25 May 2026 16:18:56 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-11-irogers@google.com> Subject: [RFC PATCH v2 10/14] perf stat: Implement streaming JSON formatting callbacks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch implements streaming JSON formatting callbacks inside util/stat-print-json.c, replacing the empty stubs introduced in Commit 1. Delivers a highly optimized, zero-allocation, and 100% streaming print engi= ne for JSON normal and metric-only modes. It bypasses dynamic queue events and metric lists entirely, formatting and streaming JSON objects directly onto = the output file descriptor. Utilizes the newly centralized unified aggregation helpers to format CPU and thread keys inside the JSON objects, and incorporates full interval-mode timestamp printing support. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/util/stat-print-json.c | 333 +++++++++++++++++++++++++++++- 1 file changed, 325 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/stat-print-json.c b/tools/perf/util/stat-print= -json.c index 72df7a94095d..f168dca70ee0 100644 --- a/tools/perf/util/stat-print-json.c +++ b/tools/perf/util/stat-print-json.c @@ -1,13 +1,330 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#include "stat-print.h" +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include + #include +#include + +#include "cpumap.h" +#include "evlist.h" +#include "evsel.h" +#include "stat-print.h" +#include "stat.h" +#include "thread_map.h" + +static const char *metric_threshold_classify__str(enum metric_threshold_cl= assify thresh) +{ + const char *const strs[] =3D { + "unknown", "bad", "nearly bad", "less good", "good", + }; + _Static_assert(ARRAY_SIZE(strs) - 1 =3D=3D METRIC_THRESHOLD_GOOD, "missin= g enum value"); + return strs[thresh]; +} + +/** + * struct json_print_state - Print state context for JSON output. + * @fp: File descriptor to output to. + * @timestamp: Formatted interval timestamp (optional). + */ +struct json_print_state { + FILE *fp; + char timestamp[64]; +}; + +/** + * struct json_metric_only_print_state - Metric-only print state context f= or JSON output. + * @fp: File descriptor to output to. + * @timestamp: Formatted interval timestamp (optional). + * @evlist: Evlist to query entries from. + * @last_aggr_idx: The aggregation index of the last printed metric. + * @first_in_group: Whether the current metric is the first in its group. + */ +struct json_metric_only_print_state { + FILE *fp; + char timestamp[64]; + struct evlist *evlist; + int last_aggr_idx; + bool first_in_group; +}; + +/** + * print_aggr_id_json - Print the aggregation prefix for JSON format. + * + * Copied and adapted from stat-display.c. + */ +static void print_aggr_id_json(const struct perf_stat_config *config, FILE= *output, + struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) +{ + switch (config->aggr_mode) { + case AGGR_CORE: + fprintf(output, "\"core\" : \"S%d-D%d-C%d\", \"counters\" : %d, ", id.so= cket, + id.die, id.core, aggr_nr); + break; + case AGGR_CACHE: + fprintf(output, "\"cache\" : \"S%d-D%d-L%d-ID%d\", \"counters\" : %d, ",= id.socket, + id.die, id.cache_lvl, id.cache, aggr_nr); + break; + case AGGR_CLUSTER: + fprintf(output, "\"cluster\" : \"S%d-D%d-CLS%d\", \"counters\" : %d, ", = id.socket, + id.die, id.cluster, aggr_nr); + break; + case AGGR_DIE: + fprintf(output, "\"die\" : \"S%d-D%d\", \"counters\" : %d, ", id.socket,= id.die, + aggr_nr); + break; + case AGGR_SOCKET: + fprintf(output, "\"socket\" : \"S%d\", \"counters\" : %d, ", id.socket, = aggr_nr); + break; + case AGGR_NODE: + fprintf(output, "\"node\" : \"N%d\", \"counters\" : %d, ", id.node, aggr= _nr); + break; + case AGGR_NONE: + if (evsel->percore && !config->percore_show_thread) + fprintf(output, "\"core\" : \"S%d-D%d-C%d\", ", id.socket, id.die, id.c= ore); + else if (id.cpu.cpu > -1) + fprintf(output, "\"cpu\" : \"%d\", ", id.cpu.cpu); + break; + case AGGR_THREAD: + fprintf(output, "\"thread\" : \"%s-%d\", ", + perf_thread_map__comm(evsel->core.threads, id.thread_idx), + perf_thread_map__pid(evsel->core.threads, id.thread_idx)); + break; + case AGGR_GLOBAL: + case AGGR_UNSET: + case AGGR_MAX: + default: + break; + } +} + +/* + * JSON Output Callbacks - Normal Mode (100% Streaming & Zero-Allocation) + */ + +static int json_print_start(void *ctx __maybe_unused, + const struct perf_stat_config *config __maybe_unused) +{ + return 0; +} + +static int json_print_event(void *ctx, const struct perf_stat_config *conf= ig, struct evsel *evsel, + int aggr_idx, u64 val, u64 ena, u64 run, + double stdev_pct __maybe_unused) +{ + struct json_print_state *ps =3D ctx; + FILE *output =3D config->output; + bool ok =3D (run !=3D 0 && ena !=3D 0); + double enabled_percent =3D 100.0; + + fprintf(output, "{"); + + /* Print interval timestamp first if configured */ + if (config->interval && ps && ps->timestamp[0]) + fprintf(output, "%s", ps->timestamp); + + /* Print aggregation JSON fields if configured */ + if (config->aggr_map && aggr_idx >=3D 0) { + struct aggr_cpu_id id =3D config->aggr_map->map[aggr_idx]; + int aggr_nr =3D 0; + + if (evsel->stats && evsel->stats->aggr) + aggr_nr =3D evsel->stats->aggr[aggr_idx].nr; + + print_aggr_id_json(config, output, evsel, id, aggr_nr); + } + + if (ok) { + double sc =3D evsel->scale; + double avg =3D val * sc; + + fprintf(output, "\"counter-value\" : \"%f\"", avg); + } else { + const char *bad_count =3D evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT= _SUPPORTED; + + fprintf(output, "\"counter-value\" : \"%s\"", bad_count); + } + + fprintf(output, ", \"unit\" : \"%s\"", evsel->unit ?: ""); + /* Cast away const for legacy evsel__name */ + fprintf(output, ", \"event\" : \"%s\"", evsel__name((struct evsel *)evsel= )); + + if (run !=3D ena) + enabled_percent =3D 100.0 * run / ena; + fprintf(output, ", \"event-runtime\" : %" PRIu64 ", \"pcnt-running\" : %.= 2f", run, + enabled_percent); + fprintf(output, "}\n"); + + return 0; +} + +static int json_print_metric(void *ctx, const struct perf_stat_config *con= fig, struct evsel *evsel, + int aggr_idx, const char *name, const char *unit __maybe_unused, + double val, enum metric_threshold_classify thresh) +{ + struct json_print_state *ps =3D ctx; + FILE *output =3D config->output; + u64 run =3D 0, ena =3D 0; + double enabled_percent =3D 100.0; + struct perf_stat_evsel *ps_evsel =3D evsel->stats; + + if (ps_evsel && ps_evsel->aggr) { + run =3D ps_evsel->aggr[aggr_idx].counts.run; + ena =3D ps_evsel->aggr[aggr_idx].counts.ena; + } =20 -int perf_stat__print_json(struct evlist *evlist __maybe_unused, - const struct perf_stat_config *config __maybe_unused, - const struct target *target __maybe_unused, - const struct timespec *ts __maybe_unused, - int argc __maybe_unused, - const char **argv __maybe_unused) + fprintf(output, "{"); + + /* Print interval timestamp first if configured */ + if (config->interval && ps && ps->timestamp[0]) + fprintf(output, "%s", ps->timestamp); + + /* Print aggregation JSON fields if configured */ + if (config->aggr_map && aggr_idx >=3D 0) { + struct aggr_cpu_id id =3D config->aggr_map->map[aggr_idx]; + int aggr_nr =3D 0; + + if (evsel->stats && evsel->stats->aggr) + aggr_nr =3D evsel->stats->aggr[aggr_idx].nr; + + print_aggr_id_json(config, output, evsel, id, aggr_nr); + } + + if (run !=3D ena) + enabled_percent =3D 100.0 * run / ena; + fprintf(output, "\"event-runtime\" : %" PRIu64 ", \"pcnt-running\" : %.2f= ", run, + enabled_percent); + fprintf(output, ", \"metric-value\" : \"%f\"", val); + if (name && name[0]) + fprintf(output, ", \"metric-unit\" : \"%s\"", name); + if (thresh !=3D METRIC_THRESHOLD_UNKNOWN) { + fprintf(output, ", \"metric-threshold\" : \"%s\"", + metric_threshold_classify__str(thresh)); + } + fprintf(output, "}\n"); + + return 0; +} + +static int json_print_end(void *ctx __maybe_unused, + const struct perf_stat_config *config __maybe_unused) { return 0; } + +static const struct perf_stat_print_callbacks json_print_callbacks =3D { + .print_start =3D json_print_start, + .print_end =3D json_print_end, + .print_event =3D json_print_event, + .print_metric =3D json_print_metric, +}; + +/* + * JSON Output Callbacks - Metric-Only Mode (100% Streaming & Zero-Allocat= ion) + */ + +static int json_metric_only_print_start(void *ctx, + const struct perf_stat_config *config __maybe_unused) +{ + struct json_metric_only_print_state *ps =3D ctx; + + /* Initialize to -2 to distinguish from -1 (a valid index in AGGR_GLOBAL = mode) */ + ps->last_aggr_idx =3D -2; + ps->first_in_group =3D true; + return 0; +} + +static int json_metric_only_print_metric(void *ctx, + const struct perf_stat_config *config __maybe_unused, + struct evsel *evsel __maybe_unused, int aggr_idx, + const char *name, const char *unit, double val, + enum metric_threshold_classify thresh __maybe_unused) +{ + struct json_metric_only_print_state *ps =3D ctx; + FILE *output =3D ps->fp; + + + if (aggr_idx !=3D ps->last_aggr_idx) { + if (ps->last_aggr_idx !=3D -2) + fprintf(output, "}\n"); + fprintf(output, "{"); + if (config->interval && ps->timestamp[0]) + fprintf(output, "%s", ps->timestamp); + if (config->aggr_map && aggr_idx >=3D 0) { + struct aggr_cpu_id id =3D config->aggr_map->map[aggr_idx]; + struct evsel *mock_evsel =3D list_first_entry(&ps->evlist->core.entries, + struct evsel, core.node); + int aggr_nr =3D 0; + + if (mock_evsel->stats && mock_evsel->stats->aggr) + aggr_nr =3D mock_evsel->stats->aggr[aggr_idx].nr; + + print_aggr_id_json(config, output, mock_evsel, id, aggr_nr); + } + ps->last_aggr_idx =3D aggr_idx; + ps->first_in_group =3D true; + } + + if (!ps->first_in_group) + fprintf(output, ", "); + ps->first_in_group =3D false; + + if (unit && unit[0]) + fprintf(output, "\"%s %s\" : \"%.1f\"", unit, name, val); + else + fprintf(output, "\"%s\" : \"%.1f\"", name, val); + return 0; +} + +static int json_metric_only_print_end(void *ctx, + const struct perf_stat_config *config __maybe_unused) +{ + struct json_metric_only_print_state *ps =3D ctx; + FILE *output =3D ps->fp; + + if (ps->last_aggr_idx !=3D -2) + fprintf(output, "}\n"); + return 0; +} + +static const struct perf_stat_print_callbacks json_metric_only_print_callb= acks =3D { + .print_start =3D json_metric_only_print_start, + .print_end =3D json_metric_only_print_end, + .print_event =3D NULL, + .print_metric =3D json_metric_only_print_metric, +}; + +int perf_stat__print_json(struct evlist *evlist, const struct perf_stat_co= nfig *config, + const struct target *target, const struct timespec *ts, int argc, + const char **argv) +{ + if (config->metric_only) { + struct json_metric_only_print_state ps =3D { + .fp =3D config->output, + .evlist =3D evlist, + }; + if (config->interval && ts) { + scnprintf(ps.timestamp, sizeof(ps.timestamp), "\"interval\" : %lu.%09lu= , ", + (unsigned long)ts->tv_sec, ts->tv_nsec); + } else { + ps.timestamp[0] =3D '\0'; + } + return perf_stat__print_cb(evlist, config, target, ts, argc, argv, + &json_metric_only_print_callbacks, &ps); + } else { + struct json_print_state ps =3D { + .fp =3D config->output, + }; + if (config->interval && ts) { + scnprintf(ps.timestamp, sizeof(ps.timestamp), "\"interval\" : %lu.%09lu= , ", + (unsigned long)ts->tv_sec, ts->tv_nsec); + } else { + ps.timestamp[0] =3D '\0'; + } + return perf_stat__print_cb(evlist, config, target, ts, argc, argv, + &json_print_callbacks, &ps); + } +} --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0899736921C for ; Mon, 25 May 2026 23:19:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751176; cv=none; b=JyjAHb2eqNP2ooxR46Cd5XMOzPPfPnzNVk0VZHDYoeYv7NXUcunLP7ROvqbKzWT5aYzPnvTLwL/9XNcRuhQz5HbKjabLf0HZh8GU20keEIg5XgOzXDK/SDsbrgVp1ZY9LmHAHzDczi/GlveTgPWWFImu4ccIb+ubHZifdnIcNlE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751176; c=relaxed/simple; bh=CBcp3mjRjUXria6L/xOPhOG5ZcnbMunq5pBGmIbwDCQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=aLeKW7QsdnFFyOUBKDrpSb4mNu0omwWKnIG9myev9dQ4abq5Lg1YRFGbCvSEJCziikq7tHa8wpLedu7LrezzJa/wE/ywcWriU45ThrNjbHIn++A9Ko4jg7Qm98uTuN5HJqJEWgTsJF7KaBko1ncPrQozeF+65Q6Jg6nAMQl7XJg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=FgpKYCw/; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="FgpKYCw/" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2ef62078ee7so10920569eec.0 for ; Mon, 25 May 2026 16:19:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751174; x=1780355974; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=9kqLDSTLjZL1UfE+kREvz0ReHAL/l7gQcB7q3yu1QaY=; b=FgpKYCw/b/zxOi6YHi9Vcgx9Ts1x39AGfgbl7TlWyXhNjPoHctD6tLbB4U3JzrppD1 BxfMzL5zfn5NXNpP9Ml9ohFkYTyFQ7N3S5CKDemX+utFHRkFAuZxLvnQQq6pyJy13guv erSmjYiPHjCDaGMMohZEi8pLmNKfa2IqgOYS6mlCYq595EgnpXvlh4eL60S8fwIlRcHF g9RyjDcooqhMXEkxXCBmEhVhEE09gk4zrXPhjPf8/+Y+IL04InLBwBmoiX0gw8+Hrdzl 3D36pgttpkRxMumKz2qYrV24X3o6EZjnI/Gef/wfjbGVflHhF6Brgpq+de90Xomspczc 4rvg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751174; x=1780355974; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=9kqLDSTLjZL1UfE+kREvz0ReHAL/l7gQcB7q3yu1QaY=; b=DiRAsr4IGqgd0otlVVVgAi7Qm7jhoRJ0WZMl11DUuocxjdOfxm5EwwCsIFIexA5NjC Kmig3lys02l/ipqfiY2y2RdpHf5i6zP8W9qD8mwee+L2Bkk6HK63C+5MGHGeTxD4XBVY 9AJuIv1k0kHyozoXpl4FSByEhePnhHf6tAtsWfY3WEn0MgQ65Tk9YUkz9lLZLeS+l/N8 AEuOdG+frDdfkZUa+ZmaItU8gHcJvTyqP7gQaWpUSOQACxWMxmn6tAjFypipkrT0CFlf wLwjxtIFGtrUoYcYJRCfYqAezgb8m9eVBCBgrkSVaI8BqMS0audzQ16q8XDK/nk4B3SS V8Pw== X-Forwarded-Encrypted: i=1; AFNElJ+9nH2LCb0DaYzJk7kn6ELApd6KdIDgpm/JQZBvWjHwDPh6vxcFWLUH9ul3/5ZUfpJ+bZHJjybdpXwRzWE=@vger.kernel.org X-Gm-Message-State: AOJu0YzP+03rSafQwuvwdCfsV/+XPT6sHRAIMMFAE8TD4lwQs2J3djMw TyM467rHuXF7Uh4RqZQPi4yQ9C0q+RozgH9nY9XjTr/xF90r0x1iGQSOBKyhvypsCGGJn234FpF KBZ8HUnesjQ== X-Received: from dycsz9.prod.google.com ([2002:a05:693c:3949:b0:2dd:3fe1:f5b2]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:cb86:b0:2be:1f58:32a3 with SMTP id 5a478bee46e88-304491e075dmr7622939eec.29.1779751173724; Mon, 25 May 2026 16:19:33 -0700 (PDT) Date: Mon, 25 May 2026 16:18:57 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-12-irogers@google.com> Subject: [RFC PATCH v2 11/14] perf stat: Extend JSON output linter to test core aggregation checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends tools/perf/tests/shell/stat+json_output.sh to run all basic and standard CPU/thread aggregation checks under the --new JSON print flag: - check_no_args - check_system_wide - check_interval - check_event - check_per_thread - check_per_node - check_system_wide_no_aggr - check_per_core - check_per_socket - check_per_die This guarantees that JSON outputs produced by the decoupled, zero-allocatio= n, and streaming print callbacks are formally validated across standard and in= terval modes. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/stat+json_output.sh | 71 ++++++++++++++-------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/tools/perf/tests/shell/stat+json_output.sh b/tools/perf/tests/= shell/stat+json_output.sh index 85d1ad7186c6..8168b861ade7 100755 --- a/tools/perf/tests/shell/stat+json_output.sh +++ b/tools/perf/tests/shell/stat+json_output.sh @@ -13,6 +13,7 @@ shelldir=3D$(dirname "$0") . "${shelldir}"/lib/setup_python.sh pythonchecker=3D$(dirname $0)/lib/perf_json_output_lint.py =20 +perf_new_opt=3D"" stat_output=3D$(mktemp /tmp/__perf_test.stat_output.json.XXXXX) =20 cleanup() { @@ -35,42 +36,42 @@ function ParanoidAndNotRoot() =20 check_no_args() { - echo -n "Checking json output: no args " - perf stat -j -o "${stat_output}" true + echo -n "Checking $api_label: no args " + perf stat -j $perf_new_opt -o "${stat_output}" true $PYTHON $pythonchecker --no-args --file "${stat_output}" echo "[Success]" } =20 check_system_wide() { - echo -n "Checking json output: system wide " + echo -n "Checking $api_label: system wide " if ParanoidAndNotRoot 0 then echo "[Skip] paranoia and not root" return fi - perf stat -j -a -o "${stat_output}" true + perf stat -j $perf_new_opt -a -o "${stat_output}" true $PYTHON $pythonchecker --system-wide --file "${stat_output}" echo "[Success]" } =20 check_system_wide_no_aggr() { - echo -n "Checking json output: system wide no aggregation " + echo -n "Checking $api_label: system wide no aggregation " if ParanoidAndNotRoot 0 then echo "[Skip] paranoia and not root" return fi - perf stat -j -A -a --no-merge -o "${stat_output}" true + perf stat -j $perf_new_opt -A -a --no-merge -o "${stat_output}" true $PYTHON $pythonchecker --system-wide-no-aggr --file "${stat_output}" echo "[Success]" } =20 check_interval() { - echo -n "Checking json output: interval " - perf stat -j -I 1000 -o "${stat_output}" true + echo -n "Checking $api_label: interval " + perf stat -j $perf_new_opt -I 1000 -o "${stat_output}" true $PYTHON $pythonchecker --interval --file "${stat_output}" echo "[Success]" } @@ -78,110 +79,110 @@ check_interval() =20 check_event() { - echo -n "Checking json output: event " - perf stat -j -e cpu-clock -o "${stat_output}" true + echo -n "Checking $api_label: event " + perf stat -j $perf_new_opt -e cpu-clock -o "${stat_output}" true $PYTHON $pythonchecker --event --file "${stat_output}" echo "[Success]" } =20 check_per_core() { - echo -n "Checking json output: per core " + echo -n "Checking $api_label: per core " if ParanoidAndNotRoot 0 then echo "[Skip] paranoia and not root" return fi - perf stat -j --per-core -a -o "${stat_output}" true + perf stat -j $perf_new_opt --per-core -a -o "${stat_output}" true $PYTHON $pythonchecker --per-core --file "${stat_output}" echo "[Success]" } =20 check_per_thread() { - echo -n "Checking json output: per thread " + echo -n "Checking $api_label: per thread " if ParanoidAndNotRoot 0 then echo "[Skip] paranoia and not root" return fi - perf stat -j --per-thread -p $$ -o "${stat_output}" true + perf stat -j $perf_new_opt --per-thread -p $$ -o "${stat_output}" true $PYTHON $pythonchecker --per-thread --file "${stat_output}" echo "[Success]" } =20 check_per_cache_instance() { - echo -n "Checking json output: per cache_instance " + echo -n "Checking $api_label: per cache_instance " if ParanoidAndNotRoot 0 then echo "[Skip] paranoia and not root" return fi - perf stat -j --per-cache -a true 2>&1 | $PYTHON $pythonchecker --per-cache + perf stat -j $perf_new_opt --per-cache -a true 2>&1 | $PYTHON $pythonchec= ker --per-cache echo "[Success]" } =20 check_per_cluster() { - echo -n "Checking json output: per cluster " + echo -n "Checking $api_label: per cluster " if ParanoidAndNotRoot 0 then echo "[Skip] paranoia and not root" return fi - perf stat -j --per-cluster -a true 2>&1 | $PYTHON $pythonchecker --per-cl= uster + perf stat -j $perf_new_opt --per-cluster -a true 2>&1 | $PYTHON $pythonch= ecker --per-cluster echo "[Success]" } =20 check_per_die() { - echo -n "Checking json output: per die " + echo -n "Checking $api_label: per die " if ParanoidAndNotRoot 0 then echo "[Skip] paranoia and not root" return fi - perf stat -j --per-die -a -o "${stat_output}" true + perf stat -j $perf_new_opt --per-die -a -o "${stat_output}" true $PYTHON $pythonchecker --per-die --file "${stat_output}" echo "[Success]" } =20 check_per_node() { - echo -n "Checking json output: per node " + echo -n "Checking $api_label: per node " if ParanoidAndNotRoot 0 then echo "[Skip] paranoia and not root" return fi - perf stat -j --per-node -a -o "${stat_output}" true + perf stat -j $perf_new_opt --per-node -a -o "${stat_output}" true $PYTHON $pythonchecker --per-node --file "${stat_output}" echo "[Success]" } =20 check_per_socket() { - echo -n "Checking json output: per socket " + echo -n "Checking $api_label: per socket " if ParanoidAndNotRoot 0 then echo "[Skip] paranoia and not root" return fi - perf stat -j --per-socket -a -o "${stat_output}" true + perf stat -j $perf_new_opt --per-socket -a -o "${stat_output}" true $PYTHON $pythonchecker --per-socket --file "${stat_output}" echo "[Success]" } =20 check_metric_only() { - echo -n "Checking json output: metric only " + echo -n "Checking $api_label: metric only " if [ "$(uname -m)" =3D "s390x" ] && ! grep '^facilities' /proc/cpuinfo |= grep -qw 67 then echo "[Skip] CPU-measurement counter facility not installed" return fi - perf stat -j --metric-only -M page_faults_per_second -o "${stat_output}" = true + perf stat -j $perf_new_opt --metric-only -M page_faults_per_second -o "${= stat_output}" true $PYTHON $pythonchecker --metric-only --file "${stat_output}" echo "[Success]" } @@ -214,6 +215,7 @@ check_for_topology() } =20 check_for_topology +api_label=3D"json output" check_no_args check_system_wide check_interval @@ -232,5 +234,22 @@ then else echo "[Skip] Skipping tests for system_wide_no_aggr, per_core, per_die an= d per_socket since socket id exposed via topology is invalid" fi +# Run New API JSON basic and standard aggregation checks +perf_new_opt=3D"--new" +api_label=3D"json (New API)" +check_no_args +check_system_wide +check_interval +check_event +check_per_thread +check_per_node +if [ $skip_test -ne 1 ] +then + check_system_wide_no_aggr + check_per_core + check_per_die + check_per_socket +fi + cleanup exit 0 --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-pg1-f201.google.com (mail-pg1-f201.google.com [209.85.215.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D5755369234 for ; Mon, 25 May 2026 23:19:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751178; cv=none; b=Nw+wgVk9+WmBlHLRO6IQkish14D7y+0oND94Z1mAGZxKIRDTYYRLKXh4+KukQbVrvUlhe4W4p+YlL0xl5sfS9D8dY2y40y2rqzCCOr9KW6NbU4CS3q093Ll30P9C941ogjUxqYEwlSG/aLxaurHAg2jYUy8B/s3EwVONhgEWOR4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751178; c=relaxed/simple; bh=8Vj2sUDbgzfz9JgyapQiYs3hu3CxfybFZtiHlHvtRrE=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=gyZHIGUsHTKCfjJaNj1iIQ3OamGxJgLW/LXkts6ST2a2+E2wA155qy1dzYFYHygGnjw1c89e5L+R8hLBNhL0ouCujm+KlZShIXSLIAyIZQJ2hDAN3Z0Ve3/ZJIG4tzh6FUA/2zjyRcZ0xc0iDY697Vx4AhHEbJXQiHijlguX97o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=CnsbH/O7; arc=none smtp.client-ip=209.85.215.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="CnsbH/O7" Received: by mail-pg1-f201.google.com with SMTP id 41be03b00d2f7-c828b1b7fddso5581993a12.3 for ; Mon, 25 May 2026 16:19:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751176; x=1780355976; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=kc5rwQIsP6Vuppfcuur6klK9jrgMryjFKDK5RBygvYk=; b=CnsbH/O7aTbnicr3qtvfv9sGhsPKHidduHLHhwnKAhzkRzV9X7b8ioe0Hir2FDynof o2mWGT64Ndl0NSOiUFd1U8A59VOOVEjSHBR1F/L/klbRktT3jhzoYgj16d9TLvbAS3kW mFqnBho9Zs9n8nPkkdpFF0p0HbxkxCuLLNZEgPvI1iTo/8MLu/Ms5Ehpej7gGhdsckW+ fSZvO36VZ85gZF24M6IgHc2E/e9v7wm+cyGj0tXwNIrpKFPxW4ULQ/djgPP60HgIrvc+ ThMBepb5XaORAX09LDeHuFb/4jFvzdg7Ym5wbOmg+11yxuoGiu3ZnNX7DbkkKrZMXWVo IKcA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751176; x=1780355976; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=kc5rwQIsP6Vuppfcuur6klK9jrgMryjFKDK5RBygvYk=; b=GXoEFXgoN33Y+YmsO0yEJALJNodr+HuYfOI0rqaHl+xikkCe49m/m+UHC59zut3zaD 0UIbNi1VU3fiRnJ77VhlCyEQ69n4qrkPASkQ8F26Re9ri19wOcK12IJ9D4jPAy8z7hVM u+8mWRJh78IipXuaLQQCHAT65Clot0ZJEoQ7WcmQw1zxmoB7g7SYeobMjuKQoBgU67qy ujkG5tzfMUCznxgOU7teq2a8wCBBxlvM6C17jlgVBsE07N6myPBvTwwul2Fp7NxI9hKi vvMf/qj4bGZ7yxbjkFHuU9wGR/TfZKERQODggUPLO2U59kgjoD3hEv0+qzcLangloOOx KWWg== X-Forwarded-Encrypted: i=1; AFNElJ95DWZ9hLcutv3JIRo675xaGwe0oHxNFoMrQ7NIMoEN2tDo5dA3cIrCRrr6vLSEWcpN4nxGaxHxDthwTik=@vger.kernel.org X-Gm-Message-State: AOJu0YxYtMPonGchKKdSEi+IcvZCaBdw10/wybTrQce3Gxw05ikT5pLI eR8im5hRaPDDo/+7xNvrjJkUsMLzeL5U7MPYC1Erwzw/nMxA01OEUMV/7plNZi3K3FBN6LQ7lsv gTCIpF2Z5ow== X-Received: from pfbks23.prod.google.com ([2002:a05:6a00:4b97:b0:83f:24a9:a829]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:15cf:b0:3aa:f9cb:d43c with SMTP id adf61e73a8af0-3b328e7457cmr16181153637.34.1779751175934; Mon, 25 May 2026 16:19:35 -0700 (PDT) Date: Mon, 25 May 2026 16:18:58 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-13-irogers@google.com> Subject: [RFC PATCH v2 12/14] perf stat: Extend JSON output linter to test advanced PMU and metric-only checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends tools/perf/tests/shell/stat+json_output.sh to run the advanced hardware PMU, topology-aware aggregation, and metric-only JSON checks a second time under the --new print flag: - check_per_cache_instance - check_per_cluster - check_metric_only This guarantees that JSON outputs produced by the decoupled streaming print= ing callbacks are verified and structurally valid under advanced topology-aware modes and= metric-only JSON row-column layouts. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/stat+json_output.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/tests/shell/stat+json_output.sh b/tools/perf/tests/= shell/stat+json_output.sh index 8168b861ade7..d18cac5dcff1 100755 --- a/tools/perf/tests/shell/stat+json_output.sh +++ b/tools/perf/tests/shell/stat+json_output.sh @@ -249,7 +249,10 @@ then check_per_core check_per_die check_per_socket + check_per_cache_instance + check_per_cluster fi +check_metric_only =20 cleanup exit 0 --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E36D036A36D for ; Mon, 25 May 2026 23:19:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751180; cv=none; b=dUi++KrIOYlci1XrjPBS2Ly/MDJh3oj2sqLGXT5MFkZG7y8mrAeRiS23qqGmAdcjKsAUgs6ejeareHcpFnLVP2vXHdsblkFXZ6LyIZKsxBiMTwvpMJYozTyetOUR0s3thgqKPV4BPffSl8cOK+E3keZ4qkkxEB8NTCLCgkFNrWo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751180; c=relaxed/simple; bh=gVN5EXaFGx6G0pY9zuHWRUUzy1F1X7z5N0UX6G7LzqI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=RbB81CQZMd5z8bBSbyos0ElGYIJTfVijRaRHZ6HCGNIRYgQk1YT1YNgl5A2UeyDHw3tsfjGlGJiHR3Ja/Zch4YZfxxLFQFSYcmaPQA7x2fb4xmJv8Xx7FeboKrQgZYM8YhXMYfwhuKeTw8uFeIbkgb38WQqBf/aoxZJrhFf+V4U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=mqc8yihV; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="mqc8yihV" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-2f3eb8f3419so2649525eec.1 for ; Mon, 25 May 2026 16:19:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751178; x=1780355978; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=LSw2EEH4NjktSR5L7b6AUMjjHwCrxvXJAhOFEWfE4Fk=; b=mqc8yihV0TIq1slRVnDg4XoohKyNiWyHXinTMAWlCtMp0NAZiEbyA+b89nqnmoNPyd tvDGUvU6Ka5MfTeTc6SPB02mKsVeWT7Zzeqly9vir/tdq/7wuwY30wVXS4kRIHzJGNmC nbxvyevF8CUglHMxB8ztCS6bk/5o7NtU8MMH+h8yxoMct8ROr6HziiQ6mOmIiGVBClha rEtulplgsYSM068EbbzUtquLG1VOGbj9YYl7YCtiq0T1++XCtaExiFCVo5nuOFuzDms9 XCMtyNZoO3zjx8AE/vLjIeQd+KGu+lhN0DMUTQMprrOX0q0a3NgkLvSndJWu1cuDx9/t EoiQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751178; x=1780355978; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=LSw2EEH4NjktSR5L7b6AUMjjHwCrxvXJAhOFEWfE4Fk=; b=R3/w4FFcq42UN88dFu0Z/TuI6WuoWTjPY8lzmWqEjp+UWQd9L6AeqYNEIFwFF3avfB p6UJ4Bv8wIaXj0JTy8j/MXq9rL3VhRk6aCj9irKUuQl3HrxiMlJPf0EwS72Yy8y3c8tE bCY6udCVyvKVkk06U87FZaB8nw7xLCRHoNx9+GvUdiYkVeCOIswNhq+qiDMwb5IpwRXN fj5/P1y8IaYwJpS8C6NVrqspVgHa03nTHtskOICA10vVP2+MQiKJ60nef3LLkC1Nnvpn CCL6oUpo9f3OTNbOC9pPNXGzTKNY2qFF2QDlPRB9kvSSITYCY2aP1NoKoTQFrvz9NLM6 f2Fg== X-Forwarded-Encrypted: i=1; AFNElJ9aK6O9rZScCtMRAHh0AovrJrR8Yl/u8iu4q3zpBpTsQAbE1oMDOPVdPJUrCeED4dDOaFNDlnNhDID8hoc=@vger.kernel.org X-Gm-Message-State: AOJu0YwLQUGUWelRUXXH04JAoa0gUtVv9u/yiO/FVYE/y6r+DjKLi616 /bb88TQuJUqhatO81zSmcxmvfnoFqrmRP9zksMsrVCmhriXAF/te07ikHGMhp/F6Mi1Az7HA/IO V//Gm3colog== X-Received: from dycnd20.prod.google.com ([2002:a05:7301:c14:b0:2fd:bdd1:8f]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:e781:b0:2be:2cfe:68b7 with SMTP id 5a478bee46e88-30448ffb8a1mr7404196eec.11.1779751177856; Mon, 25 May 2026 16:19:37 -0700 (PDT) Date: Mon, 25 May 2026 16:18:59 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-14-irogers@google.com> Subject: [RFC PATCH v2 13/14] perf stat: Add --new support to PMU metrics Python validator From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch extends the performance metrics value Python validation script tools/perf/tests/shell/lib/perf_metric_validation.py to support a new command-line argument `-new`: parser.add_argument("-new", help=3D"Use new printing API (--new)", ...) When set, the Validator class appends the `--new` option flag to its intern= ally spawned `perf stat` commands: command =3D [tool, 'stat'] if self.new_print: command.append('--new') This enables validating Intel PMU metric mathematical values generated specifically by the decoupled, streaming JSON printing callbacks. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/lib/perf_metric_validation.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/shell/lib/perf_metric_validation.py b/tools/p= erf/tests/shell/lib/perf_metric_validation.py index dea8ef1977bf..f69cf0e0de57 100644 --- a/tools/perf/tests/shell/lib/perf_metric_validation.py +++ b/tools/perf/tests/shell/lib/perf_metric_validation.py @@ -36,7 +36,7 @@ class TestError: =20 class Validator: def __init__(self, rulefname, reportfname=3D'', t=3D5, debug=3DFalse, = datafname=3D'', fullrulefname=3D'', - workload=3D'true', metrics=3D'', cputype=3D'cpu'): + workload=3D'true', metrics=3D'', cputype=3D'cpu', new_pri= nt=3DFalse): self.rulefname =3D rulefname self.reportfname =3D reportfname self.rules =3D None @@ -68,6 +68,7 @@ class Validator: self.datafname =3D datafname self.debug =3D debug self.fullrulefname =3D fullrulefname + self.new_print =3D new_print =20 def __set_metrics(self, metrics=3D''): if metrics !=3D '': @@ -379,7 +380,10 @@ class Validator: =20 def _run_perf(self, metric, workload: str): tool =3D 'perf' - command =3D [tool, 'stat', '--cputype', self.cputype, '-j', '-M', = f"{metric}", "-a"] + command =3D [tool, 'stat'] + if self.new_print: + command.append('--new') + command.extend(['--cputype', self.cputype, '-j', '-M', f"{metric}"= , "-a"]) wl =3D workload.split() command.extend(wl) print(" ".join(command)) @@ -584,6 +588,8 @@ def main() -> None: parser.add_argument("-m", help=3D"Metric list to validate", default=3D= "") parser.add_argument("-cputype", help=3D"Only test metrics for the give= n CPU/PMU type", default=3D"cpu") + parser.add_argument("-new", help=3D"Use new printing API (--new)", + action=3D"store_true", default=3DFalse) args =3D parser.parse_args() outpath =3D Path(args.output_dir) reportf =3D Path.joinpath(outpath, 'perf_report.json') @@ -592,7 +598,7 @@ def main() -> None: =20 validator =3D Validator(args.rule, reportf, debug=3Dargs.debug, datafname=3Ddatafile, fullrulefname=3Dfullrule, = workload=3Dargs.wl, - metrics=3Dargs.m, cputype=3Dargs.cputype) + metrics=3Dargs.m, cputype=3Dargs.cputype, new_pr= int=3Dargs.new) ret =3D validator.test() =20 return ret --=20 2.54.0.794.g4f17f83d09-goog From nobody Mon Jun 8 22:52:29 2026 Received: from mail-dy1-f202.google.com (mail-dy1-f202.google.com [74.125.82.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 28291369234 for ; Mon, 25 May 2026 23:19:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751182; cv=none; b=fHadwCI4mHVaiJfD58gKl+2D6+uuH/Q3RGS2zE3d7zvgaGBcLs8BcTxSBJnlDhQQm5UWxlKki0t/GbmGyYNFgbsUpAB9ojms478MF7Af+L/XzeDZAGFALK1s8X2ec4feSCnnxOQ2PhxhWU329Oi/NoE5FmQLn4VnWZtTnY7EfB8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779751182; c=relaxed/simple; bh=hxbLB2LEq8NNt0do8A4kQ52q5KzoHe/RBvk/H6KIoBs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=bcSZ7qGe1D3q/dyeuAlrxvjjfrRpm/CD+Bj3a+JP1Q/beG76d2xK7Ubyh6eo2opiJOwQWUb4PTYQKu4zBHjepEsthoISedWbjgX3oNTJWDneLj62PZbZwV52DTQp0LHCi2gG4qUKa7hoPnOAAjMK9gmy+4Nyhl8/H9uCwQZsnBk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=OKTOxB8T; arc=none smtp.client-ip=74.125.82.202 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="OKTOxB8T" Received: by mail-dy1-f202.google.com with SMTP id 5a478bee46e88-3041ab826ddso8596954eec.0 for ; Mon, 25 May 2026 16:19:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1779751180; x=1780355980; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Npd//55GHZWJhH7nvE5upXpdkQJTmyWRacTGdZE3uoo=; b=OKTOxB8Tw6kMmfiBO5b25tVEi7XO6tKULfGUtRVfFEyQj/okF9SPtz0TsvTG1fgElH DRGUAsb0zBYmxFTY5e9e5Fr9rlHjcB/mqqaFVL/OE5bOfzCHIbWo63VL5s8DKJ0PJHrD R77Vw6UBzzUCA7aLVNalzMZP5HLa4H5Yf5A2VR/Bgppf2tIIR5H0K1+qaFDs9y9YdkL3 3myFNuY0xnn01Hg5YKCBki29RRqgMK8IjGUnMr1rBVH+4Jr0pSWPvwA/x4T4AKHeM9Bw 8CM7Rb8DdeocMX5Mu3NVuTOcqUTKlWA/U65MUQOhjhdBFXVIdfRUNpmV7eHDyMAypgMD Ex+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779751180; x=1780355980; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Npd//55GHZWJhH7nvE5upXpdkQJTmyWRacTGdZE3uoo=; b=sPEUYncwQtln/f1dBsEnhxWMCqCHUSqVt+VD3/jDCKriEUhUFaA9IT/eXiZznww3H1 7QhqVT8y3vNmYzhGTJBa1jPR5+YefY39igzCojxhnkgsOXLIGbi0UPewnaeBplg6qqPn +66m9XGd4wAeZSq0BhhrNbx/TT9QJ33Ct2aiMEhk50Xt2DWYe8EEM2Ey4LxzaO1d2zYI ZpJsdlzBZpJmc3ihPWfcvi/9qzFLwefEiQXiY9gBmBq53w0ubOrB3jSEoOsn1H5TmUuA ArJE8BRcADLiQFkI+5R9Yt3LNx/Ie1CteYBn86i6jXjJoYw3ONRdt2EBAm5IjKg8pzaL ZoBg== X-Forwarded-Encrypted: i=1; AFNElJ+W++2xxDOAccYDPOdtKjr3AcXaimFZpur1iObXXlE9fWCmAKt0cOIOtRYKP4fTBQQCBUdzYyygeUWZGSo=@vger.kernel.org X-Gm-Message-State: AOJu0YznYEnFJNk8bk5G1z001Hu8EbXWysYFRFcZV2PyEMHmMAjdl8Bj M53vQktYGSmLYUqD43BIX8sz4Hen99evBBPXK+K7mhw8FNZeFE7EiYNm/CtAyLmM/EMUA8h6nev oltj1d85bFQ== X-Received: from dycnn12.prod.google.com ([2002:a05:7301:140c:b0:304:6bd9:6beb]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:430f:b0:304:5b0d:489b with SMTP id 5a478bee46e88-3045b0d4dd5mr6291926eec.27.1779751180073; Mon, 25 May 2026 16:19:40 -0700 (PDT) Date: Mon, 25 May 2026 16:19:00 -0700 In-Reply-To: <20260525231900.3527228-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260522223342.2393553-1-irogers@google.com> <20260525231900.3527228-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.794.g4f17f83d09-goog Message-ID: <20260525231900.3527228-15-irogers@google.com> Subject: [RFC PATCH v2 14/14] perf stat: Extend PMU metrics value linter to validate --new outputs From: Ian Rogers To: irogers@google.com, acme@kernel.org, linux-perf-users@vger.kernel.org, namhyung@kernel.org Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, mingo@redhat.com, peterz@infradead.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch updates tools/perf/tests/shell/stat_metrics_values.sh to run the metrics value validation linter twice: once for the legacy path, and once under the --new print flag using the newly introduced python validator `-new` argument: $PYTHON $pythonvalidator -rule $rulefile -output_dir $tmpdir ... -new This mathematically guarantees that calculated Intel PMU metric values staged inside the decoupled streaming JSON callbacks are parsed and validated cleanly against the formal rules engine. Signed-off-by: Ian Rogers Assisted-by: Antigravity:gemini-3.5-flash Acked-by: Chun-Tse Shao --- tools/perf/tests/shell/stat_metrics_values.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/shell/stat_metrics_values.sh b/tools/perf/tes= ts/shell/stat_metrics_values.sh index 30566f0b5427..02c0bcf6e078 100755 --- a/tools/perf/tests/shell/stat_metrics_values.sh +++ b/tools/perf/tests/shell/stat_metrics_values.sh @@ -22,9 +22,20 @@ for cputype in /sys/bus/event_source/devices/cpu_*; do $PYTHON $pythonvalidator -rule $rulefile -output_dir $tmpdir -wl "${workl= oad}" \ -cputype "${cputype}" ret=3D$? - rm -rf $tmpdir if [ $ret -ne 0 ]; then echo "Metric validation return with errors. Please check metrics reporte= d with errors." + rm -rf $tmpdir + exit $ret + fi + + echo "Testing metrics for: $cputype (New API)" + $PYTHON $pythonvalidator -rule $rulefile -output_dir $tmpdir -wl "${workl= oad}" \ + -cputype "${cputype}" -new + ret=3D$? + rm -rf $tmpdir + if [ $ret -ne 0 ]; then + echo "Metric validation return with errors (New API). Please check metri= cs reported with errors." + exit $ret fi done exit $ret --=20 2.54.0.794.g4f17f83d09-goog