From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pg1-f202.google.com (mail-pg1-f202.google.com [209.85.215.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 74CDF37F8C1 for ; Thu, 16 Jul 2026 04:32:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176382; cv=none; b=FnaAhJvimFevcyawKaU/TxNfW/0L0XsAQj5VLwPLfeYpyLyCth5xA8M4396+vGs0wPuH2Voru/C8si+cV7MTTsmKEDJiHbcEm9tle7eriR0792+mGSv2ZlkUuzySE1pi4dJ+XzFqWPcQa+swpaFUbLit3PIOvUsbR7G/1ziRHAg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176382; c=relaxed/simple; bh=hIxpFNEn4i6RNo/DdPCuatGhf+1ZDRCFurvkNsfgJIk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=eCiIEh+TSdEyBQyER4CYeAnIpMEtgYaWpVwwfmj79urwii21kA0LdVG61PLECUuaLI8zaYVONjvECB4TC0dVfW9++P1Wq3vwAA7Nhs0ZFMDxzJ4xIjB/Kf064bxkEJ3ZH51cPsc3zl6wU4ehiJlcb+AXRRGtK6eBTDX95UPslaI= 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=tHfS+Ae6; arc=none smtp.client-ip=209.85.215.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="tHfS+Ae6" Received: by mail-pg1-f202.google.com with SMTP id 41be03b00d2f7-cab048cdb3eso6922479a12.0 for ; Wed, 15 Jul 2026 21:32:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176379; x=1784781179; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=myD8RB3VQcJoHmDZRgr6MvaG8bYHft9D919t62PHA48=; b=tHfS+Ae6GLWsKYqfWPHBp5YECJFQ6lEPSw09gK1GhH6q3FdJhIPlBYIdL8CcQhswJJ aK0yDoH2jOAnoT+SIv4kE9t+YFe5DtoxdBinCNgrQkoQ/hj2bi1TZXYjjTw28OsW7Lbw xl9JzPiurHOA14aDDDzF+/7xjVjrBDq+gkPpvWNlioT2rZKwHhZf7d9x2z+k63/8VjZ0 DTRl25YwGVKR9B+ohHpf+DyBIBLNIQz7rg2EJ0I9EEgD1f4eAcAl2Vmhm0uc6PtrNklu NCJxW6cpdkYb+LQX2pTEF7yQURLKpy6MGIexo4JKjE9qyaK7lXsT/lcxByU1vzAYnL82 HIpw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176379; x=1784781179; h=content-type: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:content-type; bh=myD8RB3VQcJoHmDZRgr6MvaG8bYHft9D919t62PHA48=; b=jesU+aI3V9Oj2eF3gAgakTR8izRfuQ1URl9ZM71tVpSEhIn1jy7FbYDgi2ipZds13S /yXl+hAcZn0ylG68NyMJCbUvU/bpEujsBLXtYJiArrHsf3VAo4Z2ZeLWFCVC38bFXJm0 kMpLSXb0PQ1eSr3bN/ThsIALiPVaPWBJYUAKgsBnkClwxMjwMkao/CnQe2vueRCfUYI1 iWC2YjCvWMVFvsvosH0kNln/UXz2B76hUJDdtMSRUs1TJ2JULJgUJEmZIV950dmwgkfV ayvTWMUGymewCCaybAZx/LLsGVCQKYCRbZo9ut+WuByhd3kPcFqxo+THxzWCDG8Vnm1p vs0Q== X-Forwarded-Encrypted: i=1; AHgh+RqNwYT9ImZRsmp6Zyva0/URHkkHFPeyRnxXLUg7pUSDKX2fpGuKXRX+jDkGNPG9yIKP2+rHm/Ncs/mEy9M=@vger.kernel.org X-Gm-Message-State: AOJu0Yz/dbyUSDKAgRLb9u1keCfRymaa2EGfQqre+Rhtf3CVEsfN1Ubr Qt3KfTtQhMHHXIzelmjb2woIaYFSbHFOk0kvk8nQPyOxKsvd73V1WhPH6TcoqTjBbqSex8nFYjD C2BrtxdQNBQ== X-Received: from dycue6.prod.google.com ([2002:a05:7300:86:b0:311:54ad:bcad]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:6456:b0:3c3:719d:dfe4 with SMTP id adf61e73a8af0-3c3719de4b4mr4647859637.35.1784176378531; Wed, 15 Jul 2026 21:32:58 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:10 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-2-irogers@google.com> Subject: [PATCH v3 01/14] perf stat: Introduce core generic print traversal engine and header stubs From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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/Build, 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 | 96 +++--- tools/perf/util/Build | 4 + tools/perf/util/stat-display.c | 6 +- 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 | 489 ++++++++++++++++++++++++++++++ tools/perf/util/stat-print.h | 133 ++++++++ tools/perf/util/stat.h | 2 + 9 files changed, 722 insertions(+), 47 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 3f685beba384..532856f8be90 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; @@ -1094,7 +1097,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; @@ -2593,6 +2599,8 @@ int cmd_stat(int argc, const char **argv) "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 interval mode"), OPT_BOOLEAN(0, "no-csv-summary", &stat_config.no_csv_summary, diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 330311cac550..0cf5bf625664 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 f94f1324d24a..d71d413e7f31 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", diff --git a/tools/perf/util/stat-print-csv.c b/tools/perf/util/stat-print-= csv.c new file mode 100644 index 000000000000..ee86d07636ea --- /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..4690c4286fd2 --- /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..a1311f950559 --- /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..0fff50dce80b --- /dev/null +++ b/tools/perf/util/stat-print.c @@ -0,0 +1,489 @@ +// 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) { + bool skip_hw =3D evsel__is_hybrid(counter) ? + (config->hybrid_merge && + counter->first_wildcard_match !=3D NULL) : + (counter->first_wildcard_match !=3D NULL); + + if (skip_hw) + 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) { + bool skip_hw =3D evsel__is_hybrid(counter) ? + (config->hybrid_merge && + counter->first_wildcard_match !=3D NULL) : + (counter->first_wildcard_match !=3D NULL); + + if (skip_hw) + 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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pg1-f202.google.com (mail-pg1-f202.google.com [209.85.215.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 13FB4382F15 for ; Thu, 16 Jul 2026 04:33:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176384; cv=none; b=XAYAohJwjcQUFKfyo9iSqEgmNEd8Duj0g7PI1TGmjN7y8sFao//yNH638m6UJz6s/psttQGKFLchv3x/IoGbMMGpyXOYWMyvH0eIKKIZgyfHo60OhSo3mibN9/pStYZ8SmGgv5f2rmHt2GFeF8YswGcs0d5H3++hyJRtGsqNyVw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176384; c=relaxed/simple; bh=dT0dka2xYm9cxwI0eW7SW5DccqS7vxC1Q0dOYTdrSmI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=cGIXyZ/Y2mEOp/6eE1iMDcdKRlzTCK+DGw6HmPciaCnNYAE2cyH5TetMzdcp6OJz8xLYVe16j/09TlxvTdq9tiSa9LQXlTFXcQ3ODDoqcaDhwB93ssJWmtkAGkn7RwzuDD0HOtyE3h/xIGZw7q9SJK2xQRZ1z21oHScyRxTOFaI= 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=coIG7oeO; arc=none smtp.client-ip=209.85.215.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="coIG7oeO" Received: by mail-pg1-f202.google.com with SMTP id 41be03b00d2f7-c860544c077so6777127a12.3 for ; Wed, 15 Jul 2026 21:33:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176380; x=1784781180; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=e3wul1U38Tv98tpibR06mpA+7fjpNpIAEdNJSQwqDYA=; b=coIG7oeOaaneOShoI4Vu5vvQmcNWmBgeBFtwh3vbAO/MhcT5Ayes8A/AarlEbPnRIg 8Mb8Zw2WqXmf6CFgzNPKWvvQBGfY2yj0dgtJe46KdlnYoy78kE84ROxRxx3anZ4DAp2K 1dTiazFBb2CVmciEnidNZFveL0CnOOO+N3bWgHzhc3LJJmYJJhOTrewAfEDn+YQsHSP+ fQmUUEgaD0R0O7AagS8x8EMBzlSdhvUOSkNdinr4bSuiocDZ1wK5rlu6KxXpV0GsY7b/ /S4wwyDfVy8XEcvoR/BUM8vKVHYa6F5fjbSSJiPcXfEGDMIdQ6g0lX6iLlcKMMkwRRNP hTrg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176380; x=1784781180; h=content-type: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:content-type; bh=e3wul1U38Tv98tpibR06mpA+7fjpNpIAEdNJSQwqDYA=; b=S9r1wKKFtJ6ktozOQ+yMyGJRNfi45KtEe3UfDnI2JKNg8juU2QlCmh/6ZE0xrQv88D FNPhPmT/AeX/pyNDISsL1s0UxxvP1uwkqCgjUWfP3NFIuI2rXEvx8IjZu92kjgpB5Y6B b6OYZ4T+ycM3atyigAI/GmmFzIFMJNG9+HI2Oj/7vT7VzMHqFhWFVfEVYWP4VjrTkzys mPOTYKAdnak5QEbXUSVuU7cBu0P+R4WQsW7xTVJ5Ys7joBbcNivTMgi2nBrfyx54NZ2j h9a9StNSYG5BwPN+AFfTX7i1JKLDLUnoYPhUubooBPCzRIS3kmp2CM89JaY/b2+Wgbfe hqQA== X-Forwarded-Encrypted: i=1; AHgh+Rr0SKGzVqhHjwLJUdp9bB8jCWeyVXpUrJP9Fq6LDdfhx5S1B9kPfRngkM/tdC5omvjZA2T+RKUhXVdvNTs=@vger.kernel.org X-Gm-Message-State: AOJu0YySVv9obLO9e1TbGAYGWZT/fKXk4qlj+iUg7XIFW2x8+pnEKfZb Y7MP+hCbj4diEcnUdMJUDDxQ0CGhALbe3RBuyUE4eLR0/L3ZZzFmrtVSt6YjSlscPsg01mQE8/q nw1teemMR5A== X-Received: from dybmb10.prod.google.com ([2002:a05:7301:190a:b0:30e:e3f7:78b1]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:7286:b0:3af:6704:18ca with SMTP id adf61e73a8af0-3c110775a7cmr23623400637.35.1784176380108; Wed, 15 Jul 2026 21:33:00 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:11 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-3-irogers@google.com> Subject: [PATCH v3 02/14] perf stat: Implement standard console (STD) formatting callbacks From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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_metric` DOM nodes to buffer traversal streams, and fully encapsulates DOM state initialization and queue cleanups inside std_print_start() and std_print_end(). Utilizes the newly centralized unified aggregation helpers to resolve CPU and 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 | 829 ++++++++++++++++++++++++++++++- 1 file changed, 822 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/stat-print-std.c b/tools/perf/util/stat-print-= std.c index a1311f950559..eb841a729e9e 100644 --- a/tools/perf/util/stat-print-std.c +++ b/tools/perf/util/stat-print-std.c @@ -1,13 +1,828 @@ // SPDX-License-Identifier: GPL-2.0 -#include "stat-print.h" +#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 "pmus.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 ", aggr_header_lens[config->aggr_mode], buf, 4,= aggr_nr); +} =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) +/** + * 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; + + 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]; + struct perf_stat_evsel *se =3D evsel->stats; + int nr =3D (se && se->aggr) ? + se->aggr[ev->aggr_idx].nr : 0; + + print_aggr_id_std(config, out, evsel, id, 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; + size_t mlen; + size_t header_len; + int ret =3D 0; + int err; + + if (list_empty(&ps->queued_metrics)) + return 0; + + if (!config->metric_only_headers_printed) { + first_aggr =3D list_first_entry(&ps->queued_metrics, + struct queued_metric, list)->aggr_idx; + + /* Print the formatted header prefix */ + if (config->interval) { + /* Timestamp header: 17 chars wide ("# time ") */ + fprintf(out, "#%15s ", "time"); + } else { + print_header_std(config, ps->target, ps->argc, ps->argv); + } + + if (config->aggr_map && first_aggr >=3D 0 && + config->aggr_mode !=3D AGGR_GLOBAL) { + int len =3D aggr_header_lens[config->aggr_mode]; + + if (config->aggr_mode =3D=3D AGGR_CORE || + config->aggr_mode =3D=3D AGGR_NONE || + config->aggr_mode =3D=3D AGGR_THREAD) { + fprintf(out, "%-*s ", len, + aggr_header_std[config->aggr_mode]); + } else { + /* + * Socket/Die/Node/Cache/Cluster modes print + * label and "ctrs" + */ + fprintf(out, "%-*s %4s ", len, + aggr_header_std[config->aggr_mode], + "ctrs"); + } + } + + /* 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); + header_len =3D strlen(b->unit) + 2 + strlen(b->name); + } else { + header_name =3D strdup(b->name); + header_len =3D strlen(b->name); + err =3D header_name ? 0 : -1; + } + if (err < 0) { + ret =3D -ENOMEM; + goto cleanup; + } + mlen =3D config->metric_only_len; + if (mlen < header_len) + mlen =3D header_len; + fprintf(out, "%*s ", (int)mlen, header_name); + free(header_name); + } + } + fprintf(out, "\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.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_std(config, out, mock_evsel, id, aggr_nr); + } + } + color =3D metric_threshold_classify__color(b->thresh); + if (b->unit && b->unit[0]) + header_len =3D strlen(b->unit) + 2 + strlen(b->name); + else + header_len =3D strlen(b->name); + + mlen =3D config->metric_only_len; + if (mlen < header_len) + mlen =3D header_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 ", (int)mlen, str); + free(str); + + list_del(&b->list); + free(b->name); + free(b->unit); + free(b); + } + if (config->interval && current_aggr !=3D -2) + fprintf(out, "\n"); + 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_mode], + 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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pg1-f202.google.com (mail-pg1-f202.google.com [209.85.215.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 D2107382394 for ; Thu, 16 Jul 2026 04:33:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176385; cv=none; b=C/xD3arm2oUWar2nj8dBOGD2yMu1+ooY28mGXI9Z1Hl9JV8bQ9yVSR7MITUti9MJqsT/tPEfEo0T8uQEY12adHA/GHqInROPy26vuBRZb30ErGXlV+IjV9FLMwhbeYGAQJ1G6y8A8BLWBR9dYSKuJKsVdS2nU7Jjp7ZIPDf+J3Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176385; c=relaxed/simple; bh=VC5bd3wbqVARSSYlOBQAUgo/LsveZVzmlW7GBDDKkFU=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ZKSqNCUFtF+pjmdk4QS4G43cWzWW+/jX77Zlm7wycmZMfA5co5/VMCHLvHb8GFwBzW1mia/CjkNG6n28UM3Z6yXa3AXGYspvy1FcrxWPSXzdiwZeshul/r2M3U1n810RZRjuge3KPhh7pPfGbbg+NdPO9eXbi3WEpTAR7gjzAMI= 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=Jkif7qZZ; arc=none smtp.client-ip=209.85.215.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="Jkif7qZZ" Received: by mail-pg1-f202.google.com with SMTP id 41be03b00d2f7-c85798977dcso6411028a12.0 for ; Wed, 15 Jul 2026 21:33:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176382; x=1784781182; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=h1E2KgxI86OBAZTp6hMwxuGK9/mjtCxRoV2z2R0u4iU=; b=Jkif7qZZ9EZrZw+xSObLreFSc4JFBJAgSvadcbKBy87L1wnPUDEI5aRhrNL3LABdWg PCgXYWD6yTGVfw7D4aHLrFuctrAo56SgC3ALvnhVjaH8IoGd2SvroUdKyfpQbdpjCUM1 5nfAtqh7KqDFA8rKOebabK8E/HK1snT3WW8TkpNyNM/kik+ArriLJV29FljsnyXNyM1F sp5NH2JQhmeL9OJCvj5BPJMsPgl3rZWX3g78C3TuJei+OQP3w4ngP6Jzc8JLic+OePXw jQvbUX10PTHXfoty4ZK0leCh62WR28u3zKGkVSTj1FEwRAueMjjuxtek/Hav3HEyDfIy ze4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176382; x=1784781182; h=content-type: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:content-type; bh=h1E2KgxI86OBAZTp6hMwxuGK9/mjtCxRoV2z2R0u4iU=; b=hih0N9V72ZQu3B1fiY+JKCjroXSHFPkI66baoKzCRIkMqEHSHsCzHrL27t6k8f5XNm dWkzrunoS9l3ZuzgiGAUgRt1Q8ZQJifUcrLWUdzhSJdWyCTL6XjMVTACjHvEtXMYbjHF Qt5LVA9oyWouveyxNRLWaBJzWnp5jxSzVvUrHJH44Yf951a7MIlIqiP1eKP5IloyGC+k Eq5nR6BNB6sIQcAjuPnYD3l+77g5K4Zl8szfkX7bwxCT7mntC8y18xwDLlpNuSDTzGJ3 66AGmus5o5h1vvZuwIw7zTk7c2elE2K0JUJP7GCql1YZc8s7s17hzC9Z8nO6gjZmK3Fs d44g== X-Forwarded-Encrypted: i=1; AHgh+Rovcm5e1Pube5GtxwaHbIKMv8NpgtZrvlUCz/dpmBB3qMdhUWramDYqeqMEkqpHc2q/cjTDSHm4a72g8Xc=@vger.kernel.org X-Gm-Message-State: AOJu0YzIKjCkSoYbXqSNHNnQpKe+llLpoxLL/Yw4JPrs4FGe/eYepb5X 9vJMAQhXW+dRjsov8RkSZ87FLmWYLDPuAMI3mHZjedM2DB24Fo6NdO706OFdL9a7v9BVuTqOwML bu03av5PhtQ== X-Received: from dyau17.prod.google.com ([2002:a05:7300:6011:b0:311:99c6:bab7]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:6d9b:b0:3b4:71a9:cd8f with SMTP id adf61e73a8af0-3c36c365548mr6280952637.41.1784176382072; Wed, 15 Jul 2026 21:33:02 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:12 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-4-irogers@google.com> Subject: [PATCH v3 03/14] perf stat: Extend STD output linter to test basic New API checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Arnaldo Carvalho de Melo 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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.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 F0237382F1F for ; Thu, 16 Jul 2026 04:33:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176386; cv=none; b=HBMCu3sK8YJEE1zoPNZH/c2n5NAfkAY5XxxZWlC788j9xW2v5f5xzJwW+oBBloSxvzd4bNakwoUQvkF/jrWwVgj5L1KJEALa7/ELXtwthAmt3FS9h3eobcLfsjlsXm+n1x78s5HvYiNa49Im2mkjZCzcFqOZkNh15140Kd0wcks= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176386; c=relaxed/simple; bh=+/jde5xJEcvJ9w9A9h5wWVZW2A/MnCJXHnhVGKQdU6Y=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=FKETGmTGeMn8TfXoVHpLA/YguisgmMIlGhqPih076sREiMBhJgjpdDo5zrwa30CodEdkckuLqkQbCq+KZm+PD5Nvlxl0BSkq/1SEUrnrGtC40WQnOY3wJJVjLGBCEZBHtn+shLTLnN1ftYOZ5tKFJ/msUojS/8y6DoN50XyupW8= 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=O0ewmFB0; arc=none smtp.client-ip=209.85.216.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="O0ewmFB0" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-38de842a1a9so5294103a91.3 for ; Wed, 15 Jul 2026 21:33:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176384; x=1784781184; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=7F66QJIywv4Z3XOH3MXKYaS2H77ys6CAb27lLyQhbHk=; b=O0ewmFB0PGdvO8FfX1Z4JU1L+8JgmTZK/yrjsyrxt5VtjXaJewKKFUFVjUPP/XNBDx Edg9cKIuDKOy0/A65X84ApEpxMtZ2JAcC7A+z7NpBoEXzgrsBhGTRQwH1TuZB3uICWWl uXNLv8dpm/GcoQ3AivUhXzUY7q/akPF7rnwXSJlav2kdoBJ6/7hhdtBTrsXpxmwdBUD1 u5tEdT1yjZ2WAAN+jFBSFfxERpWEvKgZSo0/fTkzXJmJbifKKm/O7F/QbUXzSywZeok+ L7zLmmUHuly3zr6wGChAJnONjczem9oe0upiN11gBz64+ucTAc1xMfU2gIs4UTDCveBa EKAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176384; x=1784781184; h=content-type: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:content-type; bh=7F66QJIywv4Z3XOH3MXKYaS2H77ys6CAb27lLyQhbHk=; b=YBK/Zzd3MbgCtjKBlL00cIgdzkkZeduqLjeQlSFhWdotQgpqw/T5VNtnPxS97og3vZ 73malYf/uJNEC3F4pZaynluu6mz9z+PTtA9Nex4ZzIo5pIU3Fh94xYvjeDCpgirUWqR4 9OcXUYCTiLYv7SiStpBwSRbe4Q9lAvgjrPrbqJC6OImC++ArzV8IXNCXQdP3WuI7UggA bmwcgW5gaHLpiY5CcFcapnJjX5THPuWkc5BrUeXmeSSaih08mjoa9MxxUdVL+UjBGoaQ wyu7H9lh9uzc4ZxMemOdNFa7PCNTKS9ByFT3DIbMP2QJ8OPBhfPHXx0kswMGKEuKD2mY Fryw== X-Forwarded-Encrypted: i=1; AHgh+Ro7OnlBAwWVD64EjJSth5Zmgkm5hnzJA//DmC55a23jUohtFFqygrrXA1Lvr79vALNoy1AETdYKQMWPDz8=@vger.kernel.org X-Gm-Message-State: AOJu0YzU7iW/8T5J8tBdEytm5wD+dsDnjYYTlKi3/ZoyaS0h37YtgdJR Ae+Zwyczupx7nQLKmj+mDBUnbicd332wN9trc926HgonndoEmpHrV6Rx7LjYUNAuiLyHWc85HZY HcuCpm8V/OA== X-Received: from dybqh10.prod.google.com ([2002:a05:7301:624a:b0:313:c814:320f]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:1d8d:b0:37f:9ce1:cdaf with SMTP id 98e67ed59e1d1-38e2a0a31f4mr4986135a91.29.1784176384005; Wed, 15 Jul 2026 21:33:04 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:13 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-5-irogers@google.com> Subject: [PATCH v3 04/14] perf stat: Extend STD output linter to test core aggregation checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 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 3210138736C for ; Thu, 16 Jul 2026 04:33:07 +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=1784176391; cv=none; b=j7nqnQK8Gx+pu62Y6ZLuw9z9ibmVW9NcnymbMyKZyQczeIkweXXYGUtmwZn4A0RBONhRUCD1WQhi3ncJLdeY4mxBgTyqcENB0TDNkE1H95ZFUKAZFCpXMBl229kYHvPwknlDobgKruVos70zauw89MLqOFW75wtAyDQqQMurh50= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176391; c=relaxed/simple; bh=0HOkidq/JgII6EOEBzjcyadgWl9Zn8UdVxiAfdkS3hg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=E5PTT5TEjQlc/UW9pFYcBo6Hh29GoIXb62DKvPiQyCrw/CjNUrpwwOZFeCl4lp8CuQvzkKuadXOdG0czok5E5ziwTYTgmJ3X3khxQWwevfr7CK/9VMnI48k96GqM7cCF8Q+iM2gA2aHv1EiAs/fGM5bSevhBnsIf2Tg/3WjFVOo= 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=LnnMfsvb; 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="LnnMfsvb" Received: by mail-pg1-f201.google.com with SMTP id 41be03b00d2f7-c88fc985a65so11129172a12.2 for ; Wed, 15 Jul 2026 21:33:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176386; x=1784781186; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=dDJIkBGbayFSIkGaw5ve84wjBBBADcohPC36m7XQyvw=; b=LnnMfsvbgztY8aWrg7/3DcrkkU296xOT/EALFsp+9STL0MvY92cecAvZ45gER4Enae /nJ4OvQvgs7v8NRcmc6a7Tms8u9+3iD7Fo6R3OXXWmCJn5GKVGQyVj/Rlkxgq8jV+xjp gr7TWdRS55uy2i7vSni42FaK/thXanMpDu5s8MvlUtsr0am3Sjs5Q2jjVe0HGSnsB8pV CMN7Sd8uWpJZkou8JY5raM1RoV5PH/7uq9xA6vVydvWgSpdnqi6o3dkdQSYEWuhBtk2J OIy0kPLm4q7zCvOqtxbooW0GAuVVAZtGTXPFoNRW6goI4CFsrYGA3XlqQx11zv6BRLQG pXeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176386; x=1784781186; h=content-type: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:content-type; bh=dDJIkBGbayFSIkGaw5ve84wjBBBADcohPC36m7XQyvw=; b=FcDw0LgVNwWubcTLKCR+fO0wf0y7uPYM/R0+xJ9NO4OMtd+GuiD9EBDMwDPjLf1X51 lSwRFBQyNIYStWxucGiWRny/R+UQA0WtgIRYojBQ4MpQF/S7Nhsuj30kmDOM7MLgi//N 1luRoyeHAk6TZkRhRwG85AzndP1FVLgLf1e/0UTfCTZL6XRX/FB+YgArA/hO6XawPUe+ dvC1QYlOIuHtJfAn5odimWyfGkXh+PlbY6JBJi47h/IhlbFYAUyTvDRLDy4vsmhuzkCo oo/I12v7FT0vgsk0WABgIN6lGd9fwvDs45F8H/mJ+ChQ79swKKmBHZ2oAJIZfQzudY8q FNNQ== X-Forwarded-Encrypted: i=1; AHgh+Rom7BNLRBpW3etERk4/WAJY8gZfT6/8tMQ5r/oFpNyuQ9dtq3Ibt4Kcm0MGWNK+ql0/L6S2THh8Lcbly7M=@vger.kernel.org X-Gm-Message-State: AOJu0Yz5Jg6pgPKuYgFpIniCL7AFJ2zsU4xcUoEmCckOg0XJROwpu2Wx x3Ain15mrTuGbtqSQ8Bw9w7SUGBFwNTKWO0vYFmKLlG7jQXUo2wJqrUhrAkNtpMfxchNh5ErXHB L+FvBcKfKTw== X-Received: from dlbsw11.prod.google.com ([2002:a05:7022:3a8b:b0:13b:9f1a:204]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:6191:b0:3bf:9e25:1a0d with SMTP id adf61e73a8af0-3c36c3e7467mr5807966637.52.1784176386171; Wed, 15 Jul 2026 21:33:06 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:14 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-6-irogers@google.com> Subject: [PATCH v3 05/14] perf stat: Extend STD output linter to test advanced PMU checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pg1-f202.google.com (mail-pg1-f202.google.com [209.85.215.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 CA4A438734D for ; Thu, 16 Jul 2026 04:33:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176391; cv=none; b=AmSGc0DGznvkIknP9ZUyqnevBtnpRx4vbxBQsBT4gPyqXHaKdBoJDkvZRuVHEK3dG9iz0ef+ahMti7IVzeKG8+yY6z46c3dLRFq7XhjTMf8PZ3J8k7tMhJIMLkRQpK647zhnWxuk3n/uXVyic+XYjFRoorgeufDEk4rTsEHvbfg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176391; c=relaxed/simple; bh=We+b1fByLnDgUIDAqu/HeccWcKu5G27hVgENOtZCCXM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=eYu+vRwL0GeRKKvLLW5oWZKgJxdor3oTP0GEonVGLqW8+xLK1/n7hx9YLItkQbcMSXYkddm267zEh7bcUuSptritCH9Qrx4gjV1v9Wdjgnj8CQu3jQFzsdubsVYEQZIVRWghpc+J0KSYJtx+XkKJ9Wbdk2Ilw+QjHJvfYoaepCs= 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=vw58IPcF; arc=none smtp.client-ip=209.85.215.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="vw58IPcF" Received: by mail-pg1-f202.google.com with SMTP id 41be03b00d2f7-ca124bf0189so207526a12.0 for ; Wed, 15 Jul 2026 21:33:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176388; x=1784781188; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=MFt2EFIQw9Bgq4BFm5b6OTBYmlNpBKOowjzMhZ8MjQA=; b=vw58IPcFO05lUyDf5zpQmV7q2E9qOoq/SibYUO4H/mfGJHcdC7SYyTCNDNxbIJ98Wb OfGfEkzsdkMccAe81EIJ6O1NkWqxV3Ttv6OmA/Q/S6V6N33EZkA8jRqQhW7jjCDyzX3R ciPq+E2szeDBDZfb3m2npr2EMq/sjd5Jk6ISAzitPWFr2/ni45gYOZkx0iUwS3M4DEGn 779F276TkY9kAfEl4kA+Q4YeDpi8l/oOPEotHYGgontsUs/XE0JK4DuMT9EJTmCl5j/V 5UZRFcM1CrCrJj71lpAqgNFrACrWK4jWA1Dwk1BeKHTZSjuWVTF7PYEAPcTFHJ8Y2ilM InDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176388; x=1784781188; h=content-type: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:content-type; bh=MFt2EFIQw9Bgq4BFm5b6OTBYmlNpBKOowjzMhZ8MjQA=; b=o0HsQmmyMEse/2GPph9jECH2R2DmVHVcQzs57q4QsoKpJKuNIYKf/xXjo67ocrVDC2 ANpAL1jrs8kzXe38CdDyY5ODILyJtHRRX5My5DIQPahBkvCtN/6JjPbk/bP324llM8+P 7OPzwuHbqSttknc/g56H1CUIPITYOrGNuZCfTiCIuSAmtAREim47RU9/XOSj68ktfQH5 4KKrZ0GzO9ny0NHtFAwOrNaAMei0FPJnxAG2hIdymGMAfIYQB06vGPv7rpRfGx1g+MLV /z959tirO5UJ92QrnLgaSTPrky9j76RgoSvCvnPmnMEzoP7oa/5T1gb6AParsikZp0wO LEEA== X-Forwarded-Encrypted: i=1; AHgh+RpWyQGyrqeX9E+8N2S0d4EUMBnkWYNm+Px+NBCX0t6g0zmztibTY5jWkJKkIH1HDn+wB0UPK9C8R4JnEZM=@vger.kernel.org X-Gm-Message-State: AOJu0Yyn1oJA1ydo/JUoLn7mcIJrIN9KON8Wxj/ZqcEn6RhOkTC5cWyn 7Zc5D0H0URlJPRs3sYZ34wKjAkIm93E+JQdirPkSwjMGXe1N6nQ4gNtnZctvAmTa7bQDir31fIt M4POHLy1ZbQ== X-Received: from dlnn34.prod.google.com ([2002:a05:7022:61a2:b0:138:106:9053]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:728a:b0:3bf:8230:1b71 with SMTP id adf61e73a8af0-3c38db35551mr1181870637.25.1784176388119; Wed, 15 Jul 2026 21:33:08 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:15 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-7-irogers@google.com> Subject: [PATCH v3 06/14] perf stat: Extend STD output linter to test metric-only checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 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 E4BB338734E for ; Thu, 16 Jul 2026 04:33:10 +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=1784176393; cv=none; b=XVpFCYsD+y/i4O/eBKvCn62fScornngoahvaV+3GYD2T/ARogWqlhb/T3QUEvUy1SPyZy+VF9APeHb9hnzp6b74LAnyW1fvJlgGP6cuoeEtRRLVEDqzZmVaWFn+rn5mnomGNtmPajKr8iGfUu9IdScEswzyxFgZLFMzsz2yppsU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176393; c=relaxed/simple; bh=7NG3Bdscz/VsB7Q9lzcB4ue8bbyP7NoLsT9Utsud4/A=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=F0VFXXfbB7r3v7kRmAb+e62wfJHcpOcCH+hnYqujKFy32HvotkD2XDXaw6J0RREtd036MFoExYvFraurDXWT7oB4f7U6c4ivEh2K55Edf197b9PzO5X27Ak2adO+oV2S8/3RWNs0qc8bIYmc0Rk6izzHZnSWMuts1vdpHGXGYqQ= 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=BZCwLzMA; 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="BZCwLzMA" Received: by mail-pg1-f201.google.com with SMTP id 41be03b00d2f7-c88aab7c1fcso14774961a12.3 for ; Wed, 15 Jul 2026 21:33:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176390; x=1784781190; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=sq/4iQ48wRC89XmU0lzntzZ6QgO63vZ7PoiupUrpu3A=; b=BZCwLzMAWnC4QZLdjdcrLX7HL+U0pqXtWwSKBa9HkkHgTL30s++FtA30UwxKn6hjKF 6U5UV2yMcdcw3A9uDUZuCXKmAgzxxdKa+SdAKY3vZVcoN+YwsgFKio2QfRRjZ5Piinja JjCkNCl3k71L5GRU4QjHwwasQIqR6roa4wrDv2Qfv/09TZyplIHpv9O0nzonycCG5xhu ZdyLEKAaVgZvlFUSdFkE2rKEmXVtxDVUOews0RdPpYour7YkEr9EqGhp8NDJ7bK/l+CK IGyZOYxii9RSC30VT8OqvuGJXgWb48+bzYwSVOz+XtdyNhfjsZI1FH/oWLW6o/MWut4+ 4k2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176390; x=1784781190; h=content-type: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:content-type; bh=sq/4iQ48wRC89XmU0lzntzZ6QgO63vZ7PoiupUrpu3A=; b=cBSYGbKdRpfHRnXW1F3lcO7abPHjUAVWT32qVhk91LvbDyxC0Fm9R45ysibY+Mx2lD 4MJYBhMcqzowjnfndgpAZBRV9/t/2OkORCDpflel4IVrQE/+9EFNjJ76oW7B2gcq9+tJ SnB2a0LWrhGszEaKT6SBCuggSoDiUh50iKzqRwz3D4ELBXA4ubVA5LN2ZPd3smW56dVA ogktf0N4ouu7rkX7t2GnyxZXKwx1GbtHO/sp9w1Yt39ma24gUywjrZj1aQcikz4qaWti 3C+nldJg4oI3xe961NDLxMkaQ+uXTyMuEpNUXOno2ZMNkHvFXbgoXNaiKBC5YyUH4i0M 3+NQ== X-Forwarded-Encrypted: i=1; AHgh+Rq3TblLqYDQyJlq2bQvOOtFX8r9GPdV984HW0IG4Q0pPgbjdJsjyESTR91HRb1l3kVZsi79OTb8lX7UTPY=@vger.kernel.org X-Gm-Message-State: AOJu0YzNUBKEq2/BiYSXnkrQUEIFoaUE7nwmcUC2ccZDmxXzpiGKL2I6 8ut4A1V/d+aTNhoPtO5Z3LzFARYHAOtt3pSp7Z2Nj4gDeHQVJVqod1R3jidD9gcqmhXs9cuLoIF dMQS6sSH22A== X-Received: from dlbbo4.prod.google.com ([2002:a05:7022:4384:b0:13b:2994:d65a]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:c6c9:b0:3bf:735d:7fb8 with SMTP id adf61e73a8af0-3c357021bb1mr10776416637.27.1784176389834; Wed, 15 Jul 2026 21:33:09 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:16 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-8-irogers@google.com> Subject: [PATCH v3 07/14] perf stat: Implement CSV formatting callbacks From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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 lists 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 | 535 ++++++++++++++++++++++++++++++- 1 file changed, 528 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/stat-print-csv.c b/tools/perf/util/stat-print-= csv.c index ee86d07636ea..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" +#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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 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 6716A388878 for ; Thu, 16 Jul 2026 04:33:12 +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=1784176394; cv=none; b=Ir2UYPgeEg2Cd0TCKW/CjcZDT/OHdqHQDHtJg97ADdcy5SYg8+phdecReYmYkZsLdTGv3zlVcb5gZi3HmD49qpMjbnYN9LMC48kELFflcR1NpmvBFlkIL7ggIQYiFDZHVdgiv31WTX9fXaqdSTMT1TP4fhBtF3jVZECDQY5lSl8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176394; c=relaxed/simple; bh=vGl4tCWC6XmoeUT9EluY6FtlT9J2IYBqxQu5uqu/7os=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=jimiDB7Pb7cjgHwcueS8WGJNWae4oIkIRrO6FIdRp3KwJWhIB0d2aB020Xh5VI5/DFk0S+AI4eTQJjwRvS4peP5MA7by/XyZ40L+mhF6ZAAu8vTJHhK/+Av0qJzMB+YwVI5Sirlv30qmxXHGZY5B6FgEM2qEI7+qhqeH5rceK9E= 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=LRFL5HC+; 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="LRFL5HC+" Received: by mail-pg1-f201.google.com with SMTP id 41be03b00d2f7-c889d1eedcdso9650873a12.1 for ; Wed, 15 Jul 2026 21:33:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176392; x=1784781192; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=pJYAVkaPoyTHHP/SZhmg8GoGiAJrE4cdA9XN/tRK8Ko=; b=LRFL5HC+m5BP3E0lvvU5hkaLwcVQbwk7F+MGMyNsXMqUk6T5ah3R9MAud+H63vRwWa jId5IdpldXklnyHtmNKadv4Tqp/gDv7qd4EaUrieQ4TQ00lLxjoG7q1nM8z/PIWGrtGF l+3ow8Tdb8S5iIDoPBPsXzA/JpcrcjK/bkXIjhtQGJENDLi3KDKnOZIAQAljV4apLMP8 gqxnAm1X+KrFanxF0OdmNMB5EUjsRZP2HePEErsGbD2rxrvxD/wOLW9KaXvYuLdYHUOD gV5416CptHJmaog5rczzaOZowzhJ7FnFA9ae4AqLJ6LpZFph+FeUbCzatRbXeemnuEVH mjOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176392; x=1784781192; h=content-type: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:content-type; bh=pJYAVkaPoyTHHP/SZhmg8GoGiAJrE4cdA9XN/tRK8Ko=; b=Vslej3Cr7wQo8Rfa3MHLwTK+hhZNr7fXdKrapw1NImsDjLQTfU3Py7h6gVnQXu4SCD vS5FAIo0kZ87qpmW1B/r0WA59mia9w7+fjtEkjz8cdPKDfgAoNDxiU3QAbwOXhwdCtzz FtdW9E3KLdd5sSe/lx2m6AG7PpmXcwECaQqbGrKsStUSIRURnmzz5Tg52p0eN4dNSi0N 08KUp/eTbmTKf1aV38n95jXKqdkzpPcnHnbuZh9zInP7vS0LEQ7Tx62v8SY3nhTBvHTq oIQMyZdfhwiLB6Msh3LLtqh7e0rJCbVzUgYrRS9DfzCQPqepEjP9JEh1vgvS8pggsFHJ XtWg== X-Forwarded-Encrypted: i=1; AHgh+Rp1oFtOefTSq/Ebvm6Gtr2KlfbsES950suaYZseHgH8i8xFpMkIajPDMUnmUU7nB8jRr7vP1lBdowmwUi4=@vger.kernel.org X-Gm-Message-State: AOJu0YwbJK7eixwRfbjq3jDHEX5H2sCtezqE78ydM/KC5kw78VMbKTZS aso65w1MyVrOCC3r9l3cDyzwuBCAsAujE3WD+VgPzLfVQ5s8WPC0JbP4QSDM8higenXIateA2Qj U79qZoel8wg== X-Received: from dlaj7.prod.google.com ([2002:a05:701b:2807:b0:139:a243:2e06]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:a10e:b0:3b9:5efb:cb2a with SMTP id adf61e73a8af0-3c34d529812mr12213898637.3.1784176391485; Wed, 15 Jul 2026 21:33:11 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:17 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-9-irogers@google.com> Subject: [PATCH v3 08/14] perf stat: Extend CSV output linter to test core aggregation checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pl1-f202.google.com (mail-pl1-f202.google.com [209.85.214.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 5C8A4389106 for ; Thu, 16 Jul 2026 04:33:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176396; cv=none; b=G+SidoZqpcCMfWA69tN6yVpvfxLvIB1wSjR0k+j+apylW63/sLeXZ8/CBa1q2nQU9SLUduIN7ofw+ma0GRlh723CgZfwJ8DPBPP6u8IXY933JQqAJDCj4RS9n25ZOsFpUHdvAxkUOon8PXAkPAK/NXeeLfZm0HZohDCYmhG6Hl0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176396; c=relaxed/simple; bh=zR7mlcbI4TNldle6aO32Mnii3tEtVON8sRL9dr2eLsA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ISm968Go9JmKJOba7OZfC6wty4ECizwjmiYTXuLXL0q+Yx0xJcU+HA7fvZoObBhP+oEEuzeGYJX/VPtfc/TJfTMLGFQr2zizgJNbJZOGlxJguEJ30uQ7u1e5Uppkh8WiStVCKEdcgx6k/OBRC0Q1xGMLJt12jFZhKIRlrz9AET4= 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=QSdpzRdK; arc=none smtp.client-ip=209.85.214.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="QSdpzRdK" Received: by mail-pl1-f202.google.com with SMTP id d9443c01a7336-2cee894b3d8so52575885ad.2 for ; Wed, 15 Jul 2026 21:33:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176393; x=1784781193; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=qfUl4LGt9dp20bOLuYgeMbQL4uHxb1wteh9z3KRDuS0=; b=QSdpzRdKzoGyhfM3I8YJBV97aynkknv631p8UxEh+AqfyviS5cTdH6CHu3kua6Pkxu WVMuR1Wm868tLmH6notlCvwwxvfKCVZAnPGXNg6s9N5uPRxb6WbGWhwP7LTTCZGa0qtc A4ISP1bQijd9aBjrJ35KxGWp9c9EHkmWt1CfQ06+5OgjmwEH42TGWHhdAC74Ld6Q40lt 4PFgJpk8HiduDC1QlDpozIIhD7dWNq5OX0WoFJvOOKfzFGecVTqjvsAwuR00UBAJplDi ol33a9lRY2caAHNR0HSNjgoBrHrvhlep6PaA8x5NQKCkzrjz9eVcSHTlct9U91V0iXpe GUpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176393; x=1784781193; h=content-type: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:content-type; bh=qfUl4LGt9dp20bOLuYgeMbQL4uHxb1wteh9z3KRDuS0=; b=G3FvKDt6kiniNro9Z2L5dF2Ur3pOcLbEX51C3YOXdtNI1X9B5c2rngTM6Z2zarFNfJ pkJoJhjGSkU4iuZdOgpWVwDyxEa+gaLjJBt2iYIlmiyIjXHgyv5HdeO21tZ/hH1POs/+ 1W8UBGFaVNuweA4xnXuCzjmLK09zGnixwxCrEBM3TgpO0TE1jKyyMLmP7HAaz/XIfxH7 X99lk3y4d0DA0AsbjIJknMJEBdYVXnVIcTXyrtfeYUehU/kBjD6zsJrS+kknW0cnGDc5 s+trU26qWsPK+ULFVV3xGv/DsQeAhZBrb9hX7X+CMa1DclweoWuVx7TH89LJZFfntB3j ePVw== X-Forwarded-Encrypted: i=1; AHgh+RogUrIEOj79MQ/2hFbY8TC8rN0n4t1jWhB6q8trr1MIvo29WMTzR4Bqhprkacnl+9rmh3IKxQlGGE02Ql0=@vger.kernel.org X-Gm-Message-State: AOJu0YzEf8p2c6YPiYUkJyygvk2dnjkYyjO/PoWedYG0Hh5VOs4FHQYo uAJrEe2zYrGNXa5MA0sWNvCDNc5HkmGCrPg/06K4GHFK2bwhupwANKMWNPFu32OXdRqU9Tgcaai 6fZvp2B6fiQ== X-Received: from dlbrd17.prod.google.com ([2002:a05:7022:f111:b0:138:5e8:1b36]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:3a42:b0:3c3:8c91:5da0 with SMTP id adf61e73a8af0-3c38c916004mr1415933637.15.1784176393355; Wed, 15 Jul 2026 21:33:13 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:18 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-10-irogers@google.com> Subject: [PATCH v3 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, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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 callbacks are verified and column-valid under advanced aggregation modes and metric-only 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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.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 4104F3502A6 for ; Thu, 16 Jul 2026 04:33:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176398; cv=none; b=HkwChhnSG4CRBNAYa5/iaVKpd+9S17n377razkc7qrey46WBqKU0KE3lHhAmeaKBTAMCk87Uk5/bbPU88G/y6UkOtICGqbTiVFqvV83JC/bBqZ2BEYxCZXxkMCt4ACEgtD5L9vNoeqpk+Hl7JGrlwe6ihReT2TtDIxEVsyVdAvA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176398; c=relaxed/simple; bh=Z/JBNSMqvJiYPoWJcHV2jwKGt2fXurEJ2l3bGBPkX9I=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=axIRqxEMOJsDZn05mCQub/IYk2D5oILccFR1QU8gFUs7B4i44bCoAkjBRvqFvygtDW3nc0hz+lPYFsMq2JlvkO0gk8EeEeTVkCboKB5h3O486Ku9ZQmDrTDsHd/qEnIipGuBPstoz7dIOiVSolr8DjwlYW4wTp2K0kCFcm+KLnI= 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=DZOBA9qo; arc=none smtp.client-ip=209.85.216.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="DZOBA9qo" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-38e1118e4abso4657990a91.0 for ; Wed, 15 Jul 2026 21:33:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176395; x=1784781195; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=cCpmfC9ncJYNju5loOzVLdBvPgwOC9w3yOrCEls1z5I=; b=DZOBA9qo9B22Ztvyz+9BRYyVywt9MMoeRYTLVBT/aozjSABxJMshFDJFhWQzCRT/6k N49poKiUkTm4GB5LVveZ7BxeMffwHnMQbQRYRokTdvOthBx5iVS40VeoLHYaPnQGuk7d XpB5PIs0YDUni9fe1+AxBcrGJwrGsCvHOds5rOkxv038FOB6Jn5EHXl0AuFD9DUQqxmX EGE1Z1F/QT+l73uqvB2oyFS3LGJeKVb53U7KO13LAeMjmGjXrHYSTY5BbaIgxAOc7JCd cfdLKqMsAn7c7dvat0O8GfkwTS1riLB9eep+4B84BQIiVPPYiepp283x/OlDqntdR2cK 3C7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176395; x=1784781195; h=content-type: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:content-type; bh=cCpmfC9ncJYNju5loOzVLdBvPgwOC9w3yOrCEls1z5I=; b=ZFh5cC+5HlD5uGpnG/3gha/yK19twHy4jzR+pzTQOqWpz2ZO0uCuH57ZjZktAr0iks tR3nw84NsK00hZSPoJ2KjpHN8cSOIIvFgKtbpNPJ8U9/cui2b83lAeSRPsI1+o9caKkg fCXPNlxs3cMEie8p0agaMU1HyYUJMfG7wgejvrtHFGYhh9fuVGPHHHXBDg4WysWDDVT7 Xtws4djKkwOJfFQEirdkilgtWG99NKP3SIy95NETGehTTCDoTOQkSF9GxkGuKIPCm8r+ 0BD7iLhnQ+hW4XKZWEpKotx7SopC9zsBFdbxk+tlvnUWnvKwYf/4iIl11GVHCZg40HtG mcjw== X-Forwarded-Encrypted: i=1; AHgh+RqV1SATOLcW+kQpcurqChKOb5GUOEsbWDZB+UNu0UibvXOprqtO+sIndukOhMA5Z9UnvsH+d/zN8wMgdJE=@vger.kernel.org X-Gm-Message-State: AOJu0Yzjjt9Gtv3ZEW27NBIrcZmGcJw56RhGUKXwhpYw5O9oZmVz7WIK QrMOWksQONztxfss4MTJo3pyCuX/3t5hQkRTVG5D6aNW4IS5LrQgC99LiE8/APuabsYH6PP7Vo5 IvOZ55qj5Hw== X-Received: from dycne10.prod.google.com ([2002:a05:7301:c8a:b0:30f:3ca0:fb5d]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:2d0c:b0:367:d850:6a5f with SMTP id 98e67ed59e1d1-38e2a077fd0mr4874856a91.25.1784176395211; Wed, 15 Jul 2026 21:33:15 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:19 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-11-irogers@google.com> Subject: [PATCH v3 10/14] perf stat: Implement streaming JSON formatting callbacks From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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 engine 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 | 331 +++++++++++++++++++++++++++++- 1 file changed, 324 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/stat-print-json.c b/tools/perf/util/stat-print= -json.c index 4690c4286fd2..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" +#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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.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 3176938735D for ; Thu, 16 Jul 2026 04:33:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176400; cv=none; b=cQrkwbRZVGZJXH9SAwb51iPqh7nYXJXITZ+36OX9Ij8QzlEBFv1X8HxA+Wndqya6gvuJn1KOTp/edHMhhs9sfnnycx/6K7tCebs8vDRlOy87au1mqEbnTetCpSjLzGix7xjxgjl50sIWM1AgkWSOZm5OgqiFg8vMch0QBJRmRpM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176400; c=relaxed/simple; bh=nTYCyrNr3Dopwvrdc9lhpjMUkBgrCvKzCBft9TlBxgs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=sZoHhfQLll7j9aLGZ280CHL7LQUe08hS2/YyPGPPM4TxSX9l+zQqt5WJ0YYcKkqw4JfFxjdSDOj2UE2LrZnYxOMw0v9uRgdaeAVPI0myBgnYKLtWLTn8qIqpZ4rUvhNPilMWqxwiE2oBQYNw7RkXNJhG3B/wYVr+n/EEyMjwEpw= 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=SZ0ULsGL; arc=none smtp.client-ip=209.85.214.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="SZ0ULsGL" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-2ce8a76df2dso109051655ad.2 for ; Wed, 15 Jul 2026 21:33:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176397; x=1784781197; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=fhU3XiQPN4YP2a3S4XXH/u+7fM9ZJ52r4mxVp56qzW8=; b=SZ0ULsGLy83Bw4fU85pzIr/FlE5nYfKKLBro3FqDi/SsEJVdFiaA7Dfb6v+4TYjAYB 2J4X6kq9P1ZiuJBE27wvNFexImW+XBllkGFcLQoERg37Hb+qckJRw5TiO6Nz4akA74vx YwHfXJAabLwfbbcwtk8gRvKzrzdX4Mz0DA1iEmtbMxZTme+8r2o9kOApPgL5utIzuQYr oAzuPB6FVI+mGbTwRnKhlhqMLtEgcUtuFrzgHRe2ELfur24lfRrbvAXyxzdElByujD4w 1ZBajlXWlB/MDQlSuU+miSumx4Vj9BhZAf/KaA/rHN26GOqkmZxfFSzg7rWE+jksLVne bR1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176397; x=1784781197; h=content-type: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:content-type; bh=fhU3XiQPN4YP2a3S4XXH/u+7fM9ZJ52r4mxVp56qzW8=; b=r7t/g+EDAFRVeNzDAPV+myLRJex6VF6w6JHiPlpIHkTFtAaSrvBung7R/UWcCwxISO sIraaBylMN8H50Ig7AxjNS0jK1ZSLATZSZ4m4AheUd3uGd8gcvWUtLbL4ablfTecxtJz 2Gn0jdA+CsxHxoXTxj00+28MLGLlJynIKA8txzlc0xBe02rtMFAkuvggzzmSQ6zr1Rul dxAL8wpwwT3dauUiAcZUas1S2rT4mCthijNk2eCxPMgD/PcvuukYZodaYVVo8cxKLSuL Rp97Aff4zDjTU9iiskJmXPL2Rx6aKiP9l6KJ/2HHl3+Z0rODC2JGk6CmAaRUn2BQuc/O URLQ== X-Forwarded-Encrypted: i=1; AHgh+RrY6YdFst4HljtyWFW9xfynakzPAeVDNavUFHbp6LOse7aRxodTHojqS6DVDz1m8Ws2lSQs6nmTTsb3y6U=@vger.kernel.org X-Gm-Message-State: AOJu0Yzy/r46CY+ShResPGgTX55i5eDjdqnUjZnZsFVmrA/etnlNS48N DHeIfYUEt4iCCeUIuHa4w23QzU645gshkMAeb0oIzHsaofDico4ceUL5orGASHmMvWQxDVGHjZF ql5dCmOM2QQ== X-Received: from dleb2-n1.prod.google.com ([2002:a05:701b:4242:10b0:13c:d45f:48f9]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:9210:b0:3b4:8f18:33a with SMTP id adf61e73a8af0-3c35731da43mr11363765637.1.1784176397266; Wed, 15 Jul 2026 21:33:17 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:20 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-12-irogers@google.com> Subject: [PATCH v3 11/14] perf stat: Extend JSON output linter to test core aggregation checks From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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-allocation, and streaming print callbacks are formally validated 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+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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (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 94F7738B15B for ; Thu, 16 Jul 2026 04:33:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176404; cv=none; b=aAMZR2r6qjdYU5vvMtxr183k6yOh+ixoTsTn5EaCwvD8OQt0B4fWQRrLUAmUcJ6/FBX4modEbSQDTmijopRKkxh+BisLJSKVITAQEiEC/qS9tPj0O75AsjX7wDsuBshvSUUsFnf7QvIEI2k4V25D8XYG7E3SOcQnh7YWffSIfeE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176404; c=relaxed/simple; bh=QV7teflAaTCw5mXJY+79rE8M7brnuuS42FrYnQ7qeZ4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=tOT7Dk39s5SjIOtjiWFH/BOTYaCWXcNUXqdahsS2QvTJWszI5nq3FMV37Tfj5Ubnt2/l5LopLTFPnuH77SPg0rNhuukXickj55gUZV9c8u87YRvV3WSN2nZ+NTtHhxCKbQ+h9EpZP/ztCPbz5wNP3mabOddBqBvADR5BhZ9lPCI= 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=cw1pDx7U; arc=none smtp.client-ip=209.85.216.73 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="cw1pDx7U" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-381250979d5so3602700a91.0 for ; Wed, 15 Jul 2026 21:33:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176400; x=1784781200; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=s1G5dLctYbcsHMXWYnv46B4pMvQkJnPXkD8MipKrFPU=; b=cw1pDx7U8QyyYjdErBhzy8SmqQhM90ho0npT1AfIj2yqU2JZieFoC9vrQcjSlPv7wE xXbjkBZxXwDgNpZjemcRd1rsTvIRC1/K27zvWPMLDboHYgSOzC9rS5hupecSgLJnYOH1 X9o7WA9Fg42uA2pziVI9yGgz9WrYRrUePBgGe1QMMn+JRvzwv0kLHML1oAkc/CZbrZJr Md382/a5DQQCLf25qXtzmuXsscfHEMb9RXkik5NV5RI1xGGm+W39H81Iy9WodgwVSRzm gCSJ1PeiJtgz7/hDWtsZlcANKsxDj+7X6mydmYd2q1LwBK0ZIye4lsHm9S6imoEcv+gj hGag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176400; x=1784781200; h=content-type: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:content-type; bh=s1G5dLctYbcsHMXWYnv46B4pMvQkJnPXkD8MipKrFPU=; b=TKueHx2Zcd/5RKxkaN9HKuZwMXUDz1cx94ugKAJkYc/r1ys0/TtN3ZptGcSewzFIVP DP09ItRwg9ZJN2RP5zI+PIsMjwiFOFBIKtUXpWq2+7rIgYZA9AFBK7+NAdvCHu4p5A6D dy5JWLb4TexPus33uh43AQutgrIMMGJOUkc0XGttGnPAtBWch3a1/TaXGm/ecRpJ9CLO 3eJ+UU8ebTo4qeuG3FIIRqT8+YOGQGH8NUCsKx15R92Qr3R9VOWlwUAho0jcjjbh2Nsk K0XHeNBZrYneRmfvJNEzlqTNkhmfWPV1P/pGRbmQ+V6csQLpG7gal24UlsF9fQbyfB7L loaw== X-Forwarded-Encrypted: i=1; AHgh+RqKFsLg/rvvfRREj/URJchjps2ms2X6oA3SYwluP54Fqi8ngYW3FHd48n8NGLkaHZE8wai6O8Qdy7uh00w=@vger.kernel.org X-Gm-Message-State: AOJu0Yy3xqnwDtHsqGoczWaOWhrl/XpN8KLlmVlpJaWW8tU7ABo0Zv8J lBDJD2wn/kn832sL6B68VZgbN7wxiXknYpQI2ZQx12NHsF3tt3Wix6lkgYc2Vk2OlXCEv+iHk7r ufCw0FS62IQ== X-Received: from dybb11.prod.google.com ([2002:a05:693c:608b:b0:313:f3ec:ae85]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:224a:b0:37f:b0a9:1a68 with SMTP id 98e67ed59e1d1-38dc782c68emr17741391a91.7.1784176399347; Wed, 15 Jul 2026 21:33:19 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:21 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-13-irogers@google.com> Subject: [PATCH v3 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, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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 printing 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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 2026 Received: from mail-pg1-f202.google.com (mail-pg1-f202.google.com [209.85.215.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 5AAAB383310 for ; Thu, 16 Jul 2026 04:33:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.202 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176404; cv=none; b=BbtnDdO0tMSoKdSvvD58rYXcQqOmFxYxkJJbsWVPkmZ6XfOBQJqKb+3u2Ah0Ha9U+oFXGhFGMQy8zBgaW8Nifx6rkwUWmYtHMvhbURUHeuFjNGP19WpOURPOKUqcNO3/IsNP/s1aHTHWecW/89D3TOFCulCD2CFr2ztXsN/v4aM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176404; c=relaxed/simple; bh=QzphN24cGC7dXeEx102xNKX8MMd2qiq7lJq1PP46ZV4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Kv0yVUvxLzmFkyQb8MBqV5Ek5AWBXfhmSy6W6xHAziPPKWL03IKlN0BWfc40+lX66tMT11WiMsZfQKluXNb6R1RsqTa3K/uKLKs8FYA2q2JjhwDcu0gMcEAWU1KBO3fknOPnAbGwZUuQHltd3fqlSSvo8/Zr5vZeziP5E/W9ssU= 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=OpuEGJX3; arc=none smtp.client-ip=209.85.215.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="OpuEGJX3" Received: by mail-pg1-f202.google.com with SMTP id 41be03b00d2f7-c894391f000so6461777a12.1 for ; Wed, 15 Jul 2026 21:33:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176401; x=1784781201; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=k1WhRdEJ7RWHtx42f6OPWFDBvGHlQ0rQIm7rFijMP1Q=; b=OpuEGJX37fNPH6uDi5Z0FVyvU7+V4VYpAN83YXwVq2aRRpd8nXn4wHKkxA0cHX5qDh NGLD1TN24V8L/pxzFqmZzqvbp/YrAYIOW0pQ4DlxtmgAwmQgPMTV3/vNrZiaA5mALf3y kPZbxaKtFV8oK1qds91YcHgK92kI1W8+UQbfOmBbOBOmYihr/rNQSgbFo6FbpStvF2qV QZf0amnAq15e0Xs5doEeQvQuQ0we7PNiuXRswpkgGFlCcYzmiCroR6lySxzPjnJruqDr vUUSd90vX7MPgn74/TILhylHLCZoIW5WNAwmTtI+4j714vHcCXkWay9jceMS9gZGJ7Ku ff8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176401; x=1784781201; h=content-type: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:content-type; bh=k1WhRdEJ7RWHtx42f6OPWFDBvGHlQ0rQIm7rFijMP1Q=; b=r0EYeFrYU4FBIyXE1W4RgMaO5nx2Pg+6uSFoXoxK2nJ9l9EEoj9EE5HREmHop5hdcU xRo4MYU1kD2Dqvz968NcCcDocPhbBPiElK54UsxfZN58vFHRmWpmRnO0lxbAObr+Vkas cKs4r8i3rST7jQRlczidZP+TYlpVE0WrUAV+dcX50i+/Cse4V9vvSdXh2WmXzrRvHxn6 TeFfWRk6GaVyL3JVwqangvNGmN37+V8tTG2BO4RQ6zSYKfYxswKyRAe3V0qJFEAJt6fb I7QEbfOBfIpPnS2+qcnYK+7/wayuh2WdePvoK9CFQqMEEDwQ02KCVwzAa3e0ZoVlDBfV HMtA== X-Forwarded-Encrypted: i=1; AHgh+Rq/t3blCSG5Zjs05cs+E3SlEpPWBoBF17/yg+JrJAdzMFmXIt5XYpnnanSQ9IKe1TNx91ffvRHohKgnQ2E=@vger.kernel.org X-Gm-Message-State: AOJu0Yxhrs8L0/2hV+jB1a1s8bSiBgZbgGgDYUKV087wuLAnOg27TNIQ 9NvvfcbYn0eaHTeN6RmZzwuXPgPAKrDXGR/YA/XMqFV59uTak+sIRIHso8J41GcuHHPytey/IDD QxVNeADIM6g== X-Received: from dldyq19-n2.prod.google.com ([2002:a05:701b:4553:20b0:13b:9b4d:358d]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:1b8a:b0:3b2:924c:567d with SMTP id adf61e73a8af0-3c1107747a5mr23563859637.46.1784176401127; Wed, 15 Jul 2026 21:33:21 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:22 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-14-irogers@google.com> Subject: [PATCH v3 13/14] perf stat: Add --new support to PMU metrics Python validator From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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 internally 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 3d52f94f22b9..b8397234eebc 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)) @@ -587,6 +591,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') @@ -595,7 +601,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.55.0.141.g00534a21ce-goog From nobody Sat Jul 25 16:18:33 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 0060938C2C6 for ; Thu, 16 Jul 2026 04:33:23 +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=1784176405; cv=none; b=s7ssQgaQbOt794JNWZP0RKhEwklCVI2gKXlpQ/pEQxa2efIMsMDzGqjeEp4vz7dF+HpBU0eVEc04A760PdZkBPSIPxWaqTziGqS16M5Rv2CbHBq56etDDXEctroj1EOf9U6xqTw0JDZrplmOsapOUsvea0fOGpoK8YkeRDaJhSA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784176405; c=relaxed/simple; bh=VDUmLuSZcvBj8Pc0HkUyVB9RsVfDT4G+/3WiPg0VcDk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=GU/IBapQeOv+LQIvj/v9yArlBU64yR9QeLp8GTiSqZxUFrcJM2ZeY9iI78N0NnNidNDIB5NGyJdK9/ibyazEXQ+66f+A3hBvX12jl1S6CXVVA7pr3dfmGVO88RmYlRVRW6eeIb0u40Z8nHvdNBJ4hNaXivPas2tAZsubrvt3W6o= 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=Tgx+8ZMe; 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="Tgx+8ZMe" Received: by mail-pg1-f201.google.com with SMTP id 41be03b00d2f7-ca6bd8a190cso9000887a12.0 for ; Wed, 15 Jul 2026 21:33:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1784176403; x=1784781203; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=uydAi+vK6q1cioWG+UZXBfJqvaBaHWiznGYu8rZg/b0=; b=Tgx+8ZMeKyhrdz3vVetdgQVHPWCYDh+RtJ4pQgCtVvsuoI7TCH8gG3otvjQF2TJO9e e1dNV5bHc9hrwwU+8XIU3DF4eAIRMkKUXPHWyus/NtgDx1kQwA9VP1EuFaK2B3pTQuZ5 lARuBUKQXgndK75A8r3f05qxb83HtItHz7heSjFx7GQTZs3ZVRhIcStrTBZEc4sAGzbz vj/wpVHgNSDB4U0OmmB9aYjUBtCY1CYMVbjQqqqOTV24STn9KTnbFea0Wx8z4jlsnRAl l/hlEMxbsD/Jme4gVuyfOspUFB8UZ4zaS4YdLlBQWdrvzxv2l0X3W8L129VUIQbYK4sw 7kuQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784176403; x=1784781203; h=content-type: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:content-type; bh=uydAi+vK6q1cioWG+UZXBfJqvaBaHWiznGYu8rZg/b0=; b=gxgicElS7Eb+0M9x7T00+zaXIixSoRVisA0nn6PaRVYbQC2jX9DIdc7Ahs758UJYuW 19kSRn1h9HAqL/1yZ90yRfytkIfpufW+qzDVNEx9FDiWfHRIW8FJuQGn3OwBmUlyshvB /5ArrnhzGNT87JRQVV4vO+FgAaXljM5UuygapqwevjywUxTpV29eX1G23bfQQlxJmzAN bv1YSOs+B9CG7eg4wmz5btbjMlZ7pXdsPLTIhgm31KxOm0/dHwGrHT4n5+JoihlR68RB Pnzg7z/36ghlK512NSMYA76jpWxOLqF6o/l+uMNxuoFPMmUokDeYNTu4SxRaUL2XxyHp Chlw== X-Forwarded-Encrypted: i=1; AHgh+RpL1pinL9W11ludVPu8c8c0ol216zMWhW/jjcTJ/RtiO2UJvjr3sIkvZaWQoH7q48Q7dv5o2zGVWkh2hUA=@vger.kernel.org X-Gm-Message-State: AOJu0YzP4VMMt8wsQobkDcx4kaeqcqlxUiwZudAZoHPzbpxtBqBWg9Lm d1JWcVs74WYLgZyT42UMdu9wQBO3xTwCyrIpe6/0KtNapHE8UrvumGZAeCUMUFqj866uplEbsbN ucMGSc0vQwA== X-Received: from dlbbo10.prod.google.com ([2002:a05:7022:438a:b0:13b:847b:5d65]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:69c:b0:3c1:85d:fa2f with SMTP id adf61e73a8af0-3c36c34dd88mr5937557637.36.1784176403130; Wed, 15 Jul 2026 21:33:23 -0700 (PDT) Date: Wed, 15 Jul 2026 21:32:23 -0700 In-Reply-To: <20260716043223.388233-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: <20260525231900.3527228-1-irogers@google.com> <20260716043223.388233-1-irogers@google.com> X-Mailer: git-send-email 2.55.0.141.g00534a21ce-goog Message-ID: <20260716043223.388233-15-irogers@google.com> Subject: [PATCH v3 14/14] perf stat: Extend PMU metrics value linter to validate --new outputs From: Ian Rogers To: irogers@google.com, acme@kernel.org, namhyung@kernel.org, ctshao@google.com Cc: adrian.hunter@intel.com, james.clark@linaro.org, jolsa@kernel.org, linux-kernel@vger.kernel.org, linux-perf-users@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 | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/shell/stat_metrics_values.sh b/tools/perf/tes= ts/shell/stat_metrics_values.sh index 86c5c7f70933..3f545e88b69f 100755 --- a/tools/perf/tests/shell/stat_metrics_values.sh +++ b/tools/perf/tests/shell/stat_metrics_values.sh @@ -26,13 +26,29 @@ echo "Output will be stored in: $tmpdir" for cputype in /sys/bus/event_source/devices/cpu_*; do cputype=3D$(basename "$cputype") echo "Testing metrics for: $cputype" - $PYTHON $pythonvalidator -rule $rulefile -output_dir $tmpdir -wl "${workl= oad}" \ + mkdir -p "$tmpdir/legacy" + $PYTHON $pythonvalidator -rule $rulefile -output_dir "$tmpdir/legacy" -wl= "${workload}" \ -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." + echo "Metric validation return with errors. " \ + "Please check metrics reported with errors in: " \ + "$tmpdir/legacy" + exit $ret + fi + + echo "Testing metrics for: $cputype (New API)" + mkdir -p "$tmpdir/new" + $PYTHON $pythonvalidator -rule $rulefile -output_dir "$tmpdir/new" -wl "$= {workload}" \ + -cputype "${cputype}" -new + ret=3D$? + if [ $ret -ne 0 ]; then + echo "Metric validation return with errors (New API). " \ + "Please check metrics reported with errors in: " \ + "$tmpdir/new" + exit $ret fi done +rm -rf "$tmpdir" exit $ret =20 --=20 2.55.0.141.g00534a21ce-goog