* [PATCH v1 0/2] Fix cgroup metric association with BPF counters
@ 2026-05-18 22:22 Ian Rogers
2026-05-18 22:22 ` [PATCH v1 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers
` (2 more replies)
0 siblings, 3 replies; 20+ messages in thread
From: Ian Rogers @ 2026-05-18 22:22 UTC (permalink / raw)
To: skanev, Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-kernel, linux-perf-users
This series fixes an issue where cgroup metrics were not being correctly
associated and reported (showing `nan %`) when using BPF counters
(i.e., with `--bpf-counters`).
The root cause is that cgroup BPF counters only open the "leader" events
(for the first cgroup) and leave "follower" events unopened. Unopened
events have their `supported` flag set to `false` by default. During
metric calculation, `prepare_metric` checks `evsel->supported` and
discards the value (setting it to `NAN`) if it is `false`, leading to
`nan %` in the output.
The first patch fixes this by propagating the `supported` flag from the
leader events to the follower events in `bperf_load_program`.
The second patch adds a new shell test (`stat_metrics_cgrp.sh`) to
permanently cover this scenario (testing both with and without BPF
counters) and prevent regressions.
Reported-by: Svilen Kanev <skanev@google.com>
Ian Rogers (2):
perf stat: Propagate supported flag to follower cgroup BPF events
perf test: Add stat metrics --for-each-cgroup test
tools/perf/tests/shell/stat_metrics_cgrp.sh | 123 ++++++++++++++++++++
tools/perf/util/bpf_counter_cgroup.c | 15 +++
2 files changed, 138 insertions(+)
create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh
--
2.54.0.631.ge1b05301d1-goog
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v1 1/2] perf stat: Propagate supported flag to follower cgroup BPF events 2026-05-18 22:22 [PATCH v1 0/2] Fix cgroup metric association with BPF counters Ian Rogers @ 2026-05-18 22:22 ` Ian Rogers 2026-05-18 22:22 ` [PATCH v1 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers 2026-05-19 1:51 ` [PATCH v2 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2 siblings, 0 replies; 20+ messages in thread From: Ian Rogers @ 2026-05-18 22:22 UTC (permalink / raw) To: skanev, Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark, linux-kernel, linux-perf-users When using BPF counters with cgroups, follower events (for cgroups other than the first one) are not opened. Because they are not opened, their `supported` flag was left as `false`. During metric calculation, `prepare_metric` checks if the event is supported. If it is not supported (like the follower events), it explicitly sets the value to `NAN`, which eventually causes the metric to be reported as `nan %`. Fix this by propagating the `supported` flag from the "leader" events (the ones opened for the first cgroup) to the "follower" events. Reported-by: Svilen Kanev <skanev@google.com> Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/bpf_counter_cgroup.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c index 519fee3dc3d0..dd1851634087 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -186,6 +186,21 @@ static int bperf_load_program(struct evlist *evlist) i++; } + /* + * Propagate supported flag from leaders to followers. Follower events + * are not opened, so their supported flag remains false. + */ + { + struct evsel *leader; + int num_events = evlist->core.nr_entries / nr_cgroups; + + evlist__for_each_entry(evlist, evsel) { + leader = evlist__find_evsel(evlist, evsel->core.idx % num_events); + if (leader) + evsel->supported = leader->supported; + } + } + /* * bperf uses BPF_PROG_TEST_RUN to get accurate reading. Check * whether the kernel support it -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v1 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-18 22:22 [PATCH v1 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-18 22:22 ` [PATCH v1 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers @ 2026-05-18 22:22 ` Ian Rogers 2026-05-18 22:43 ` sashiko-bot 2026-05-19 1:51 ` [PATCH v2 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2 siblings, 1 reply; 20+ messages in thread From: Ian Rogers @ 2026-05-18 22:22 UTC (permalink / raw) To: skanev, Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim, Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark, linux-kernel, linux-perf-users Add a new shell test `stat_metrics_cgrp.sh` to verify metric reporting with `--for-each-cgroup`, both with and without `--bpf-counters`. The test: - Checks if system-wide monitoring is supported (skips if not). - Checks if `insn_per_cycle` metric is supported (skips if not). - Finds cgroups to test. - Runs `perf stat` with `insn_per_cycle` metric and verifies that the metric is reported for each cgroup and does not contain invalid values (like `nan` or `nested`) or empty values when counts are present. - Tests both standard mode and BPF counters mode (if supported). Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/tests/shell/stat_metrics_cgrp.sh | 123 ++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh new file mode 100755 index 000000000000..4a29eb9a9067 --- /dev/null +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# perf stat metrics --for-each-cgroup test +# SPDX-License-Identifier: GPL-2.0 + +set -e + +test_cgroups= + +log_verbose() { + echo "$1" +} + +# skip if system-wide is not supported +check_system_wide() +{ + log_verbose "Checking system-wide..." + if ! perf stat -a -e instructions sleep 0.01 > /dev/null 2>&1 + then + log_verbose "Skipping: system-wide monitoring not supported" + exit 2 + fi +} + +# find two cgroups to measure +find_cgroups() +{ + log_verbose "Finding cgroups..." + # try usual systemd slices first + if [ -d /sys/fs/cgroup/system.slice ] && [ -d /sys/fs/cgroup/user.slice ] + then + test_cgroups="system.slice,user.slice" + log_verbose "Found cgroups: ${test_cgroups}" + return + fi + + # try root and self cgroups + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) + if [ -z ${find_cgroups_self_cgrp} ] + then + # cgroup v2 doesn't specify perf_event + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) + fi + + if [ -z ${find_cgroups_self_cgrp} ] + then + test_cgroups="/" + else + test_cgroups="/,${find_cgroups_self_cgrp}" + fi + log_verbose "Found cgroups: ${test_cgroups}" +} + +# Check if metric is reported for each cgroup +# $1: extra options (e.g. --bpf-counters) +check_metric_reported() +{ + local opts="$1" + local output + + log_verbose "Running check_metric_reported with opts '${opts}'..." + # Run perf stat + if ! output=$(perf stat -a ${opts} \ + --metrics=insn_per_cycle \ + --for-each-cgroup ${test_cgroups} \ + -x, sleep 0.1 2>&1) + then + echo "FAIL: perf stat failed with exit code $?" + echo "Output: ${output}" + exit 1 + fi + + log_verbose "perf stat output:" + log_verbose "${output}" + + # Split test_cgroups by comma + IFS=',' read -r -a cgrps <<< "${test_cgroups}" + + for cgrp in "${cgrps[@]}"; do + # Find lines for this cgroup and metric + local cgrp_lines + cgrp_lines=$(echo "${output}" | grep "${cgrp}" | grep "insn_per_cycle" || true) + + if [ -z "${cgrp_lines}" ] + then + echo "FAIL: No metric lines found for cgroup '${cgrp}'" + exit 1 + fi + + # Check for nan or nested + if echo "${cgrp_lines}" | grep -q -i -E ",nan,|,nested," + then + echo "FAIL: Invalid metric value (nan/nested) for cgroup '${cgrp}'" + echo "Line: ${cgrp_lines}" + exit 1 + fi + # Check for empty metric value + if echo "${cgrp_lines}" | grep -q -E ",,[[:space:]]*[^,]*insn_per_cycle" + then + echo "FAIL: Empty metric value for cgroup '${cgrp}'" + echo "Line: ${cgrp_lines}" + exit 1 + fi + done + log_verbose "check_metric_reported passed for opts '${opts}'" +} + +check_system_wide +find_cgroups + +# Test 1: Without BPF counters +check_metric_reported "" + +# Test 2: With BPF counters (if supported) +log_verbose "Checking BPF support..." +if perf stat -a --bpf-counters --for-each-cgroup / true > /dev/null 2>&1 +then + log_verbose "BPF supported, running Test 2..." + check_metric_reported "--bpf-counters" +else + log_verbose "BPF not supported, skipping Test 2" +fi + +exit 0 -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v1 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-18 22:22 ` [PATCH v1 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers @ 2026-05-18 22:43 ` sashiko-bot 0 siblings, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-05-18 22:43 UTC (permalink / raw) To: Ian Rogers; +Cc: linux-perf-users Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Medium] Missing 'insn_per_cycle' metric support check contradicts the commit message and causes false-positive test failures. - [Low] Unquoted variables in bash script can cause syntax errors and command execution failures. - [Low] Unsafe regex matching with `grep` for cgroup paths. -- commit eeae523b4cc9fc618f3db63cdc5973fc899f15e2 Author: Ian Rogers <irogers@google.com> perf test: Add stat metrics --for-each-cgroup test This commit adds a new shell test stat_metrics_cgrp.sh to verify metric reporting when using --for-each-cgroup. It tests metric outputs for cgroups with and without BPF counters. > The test: > - Checks if system-wide monitoring is supported (skips if not). > - Checks if insn_per_cycle metric is supported (skips if not). > - Finds cgroups to test. The commit message mentions checking if the insn_per_cycle metric is supported, but the script only seems to check for system-wide support using 'perf stat -a -e instructions'. If the metric is unavailable on the test architecture, will the script fail with exit code 1 ("No metric lines found") instead of gracefully skipping? > diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh > new file mode 100755 > index 0000000000000..4a29eb9a90674 > --- /dev/null > +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh [ ... ] > + # try root and self cgroups > + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) > + if [ -z ${find_cgroups_self_cgrp} ] This isn't a bug, but could this cause a bash syntax error ("too many arguments") if the cgroup path happens to contain spaces? Enclosing the variable in double quotes would prevent word splitting. > + then > + # cgroup v2 doesn't specify perf_event > + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) > + fi > + > + if [ -z ${find_cgroups_self_cgrp} ] Are double quotes also needed here to prevent word splitting? > + then > + test_cgroups="/" > + else > + test_cgroups="/,${find_cgroups_self_cgrp}" > + fi [ ... ] > +check_metric_reported() > +{ > + local opts="$1" > + local output > + > + log_verbose "Running check_metric_reported with opts '${opts}'..." > + # Run perf stat > + if ! output=$(perf stat -a ${opts} \ > + --metrics=insn_per_cycle \ > + --for-each-cgroup ${test_cgroups} \ Similarly, should ${test_cgroups} be quoted to ensure perf stat doesn't misinterpret space-separated cgroup paths as commands? > + -x, sleep 0.1 2>&1) > + then > + echo "FAIL: perf stat failed with exit code $?" > + echo "Output: ${output}" > + exit 1 > + fi [ ... ] > + for cgrp in "${cgrps[@]}"; do > + # Find lines for this cgroup and metric > + local cgrp_lines > + cgrp_lines=$(echo "${output}" | grep "${cgrp}" | grep "insn_per_cycle" || true) Since cgroup names can contain dots (like "system.slice"), will grep interpret the dot as a regex wildcard? Using 'grep -F "${cgrp}"' might be safer to ensure a literal string match. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260518222228.3027004-1-irogers@google.com?part=2 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 0/2] Fix cgroup metric association with BPF counters 2026-05-18 22:22 [PATCH v1 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-18 22:22 ` [PATCH v1 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers 2026-05-18 22:22 ` [PATCH v1 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers @ 2026-05-19 1:51 ` Ian Rogers 2026-05-19 1:51 ` [PATCH v2 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers ` (2 more replies) 2 siblings, 3 replies; 20+ messages in thread From: Ian Rogers @ 2026-05-19 1:51 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev This series fixes an issue where cgroup metrics were not being correctly associated and reported (showing `nan %`) when using BPF counters (i.e., with `--bpf-counters`). The root cause is that cgroup BPF counters only open the "leader" events (for the first cgroup) and leave "follower" events unopened. Unopened events have their `supported` flag set to `false` by default. During metric calculation, `prepare_metric` checks `evsel->supported` and discards the value (setting it to `NAN`) if it is `false`, leading to `nan %` in the output. The first patch fixes this by propagating the `supported` flag from the leader events to the follower events in `bperf_load_program`. The second patch adds a new shell test (`stat_metrics_cgrp.sh`) to permanently cover this scenario (testing both with and without BPF counters) and prevent regressions. Reported-by: Svilen Kanev <skanev@google.com> Changes since v1: - Fix commit message for patch 2 to remove the mention of insn_per_cycle check (which was simplified out). - Quote variables in shell script to prevent word splitting. - Use grep -F in shell script for literal matching of cgroup names containing dots. Ian Rogers (2): perf stat: Propagate supported flag to follower cgroup BPF events perf test: Add stat metrics --for-each-cgroup test tools/perf/tests/shell/stat_metrics_cgrp.sh | 123 ++++++++++++++++++++ tools/perf/util/bpf_counter_cgroup.c | 15 +++ 2 files changed, 138 insertions(+) create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 1/2] perf stat: Propagate supported flag to follower cgroup BPF events 2026-05-19 1:51 ` [PATCH v2 0/2] Fix cgroup metric association with BPF counters Ian Rogers @ 2026-05-19 1:51 ` Ian Rogers 2026-05-19 1:51 ` [PATCH v2 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers 2026-05-19 5:01 ` [PATCH v3 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2 siblings, 0 replies; 20+ messages in thread From: Ian Rogers @ 2026-05-19 1:51 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev When using BPF counters with cgroups, follower events (for cgroups other than the first one) are not opened. Because they are not opened, their `supported` flag was left as `false`. During metric calculation, `prepare_metric` checks if the event is supported. If it is not supported (like the follower events), it explicitly sets the value to `NAN`, which eventually causes the metric to be reported as `nan %`. Fix this by propagating the `supported` flag from the "leader" events (the ones opened for the first cgroup) to the "follower" events. Reported-by: Svilen Kanev <skanev@google.com> Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/bpf_counter_cgroup.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c index 519fee3dc3d0..dd1851634087 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -186,6 +186,21 @@ static int bperf_load_program(struct evlist *evlist) i++; } + /* + * Propagate supported flag from leaders to followers. Follower events + * are not opened, so their supported flag remains false. + */ + { + struct evsel *leader; + int num_events = evlist->core.nr_entries / nr_cgroups; + + evlist__for_each_entry(evlist, evsel) { + leader = evlist__find_evsel(evlist, evsel->core.idx % num_events); + if (leader) + evsel->supported = leader->supported; + } + } + /* * bperf uses BPF_PROG_TEST_RUN to get accurate reading. Check * whether the kernel support it -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-19 1:51 ` [PATCH v2 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-19 1:51 ` [PATCH v2 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers @ 2026-05-19 1:51 ` Ian Rogers 2026-05-19 2:59 ` sashiko-bot 2026-05-19 5:01 ` [PATCH v3 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2 siblings, 1 reply; 20+ messages in thread From: Ian Rogers @ 2026-05-19 1:51 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev Add a new shell test `stat_metrics_cgrp.sh` to verify metric reporting with `--for-each-cgroup`, both with and without `--bpf-counters`. The test: - Checks if system-wide monitoring is supported (skips if not). - Finds cgroups to test. - Runs `perf stat` with `insn_per_cycle` metric and verifies that the metric is reported for each cgroup and does not contain invalid values (like `nan` or `nested`) or empty values when counts are present. - Tests both standard mode and BPF counters mode (if supported). Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/tests/shell/stat_metrics_cgrp.sh | 123 ++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh new file mode 100755 index 000000000000..2f74bd9e3f2e --- /dev/null +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# perf stat metrics --for-each-cgroup test +# SPDX-License-Identifier: GPL-2.0 + +set -e + +test_cgroups= + +log_verbose() { + echo "$1" +} + +# skip if system-wide is not supported +check_system_wide() +{ + log_verbose "Checking system-wide..." + if ! perf stat -a -e instructions sleep 0.01 > /dev/null 2>&1 + then + log_verbose "Skipping: system-wide monitoring not supported" + exit 2 + fi +} + +# find two cgroups to measure +find_cgroups() +{ + log_verbose "Finding cgroups..." + # try usual systemd slices first + if [ -d /sys/fs/cgroup/system.slice ] && [ -d /sys/fs/cgroup/user.slice ] + then + test_cgroups="system.slice,user.slice" + log_verbose "Found cgroups: ${test_cgroups}" + return + fi + + # try root and self cgroups + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) + if [ -z "${find_cgroups_self_cgrp}" ] + then + # cgroup v2 doesn't specify perf_event + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) + fi + + if [ -z "${find_cgroups_self_cgrp}" ] + then + test_cgroups="/" + else + test_cgroups="/,${find_cgroups_self_cgrp}" + fi + log_verbose "Found cgroups: ${test_cgroups}" +} + +# Check if metric is reported for each cgroup +# $1: extra options (e.g. --bpf-counters) +check_metric_reported() +{ + local opts="$1" + local output + + log_verbose "Running check_metric_reported with opts '${opts}'..." + # Run perf stat + if ! output=$(perf stat -a ${opts} \ + --metrics=insn_per_cycle \ + --for-each-cgroup "${test_cgroups}" \ + -x, sleep 0.1 2>&1) + then + echo "FAIL: perf stat failed with exit code $?" + echo "Output: ${output}" + exit 1 + fi + + log_verbose "perf stat output:" + log_verbose "${output}" + + # Split test_cgroups by comma + IFS=',' read -r -a cgrps <<< "${test_cgroups}" + + for cgrp in "${cgrps[@]}"; do + # Find lines for this cgroup and metric + local cgrp_lines + cgrp_lines=$(echo "${output}" | grep -F "${cgrp}" | grep "insn_per_cycle" || true) + + if [ -z "${cgrp_lines}" ] + then + echo "FAIL: No metric lines found for cgroup '${cgrp}'" + exit 1 + fi + + # Check for nan or nested + if echo "${cgrp_lines}" | grep -q -i -E ",nan,|,nested," + then + echo "FAIL: Invalid metric value (nan/nested) for cgroup '${cgrp}'" + echo "Line: ${cgrp_lines}" + exit 1 + fi + # Check for empty metric value + if echo "${cgrp_lines}" | grep -q -E ",,[[:space:]]*[^,]*insn_per_cycle" + then + echo "FAIL: Empty metric value for cgroup '${cgrp}'" + echo "Line: ${cgrp_lines}" + exit 1 + fi + done + log_verbose "check_metric_reported passed for opts '${opts}'" +} + +check_system_wide +find_cgroups + +# Test 1: Without BPF counters +check_metric_reported "" + +# Test 2: With BPF counters (if supported) +log_verbose "Checking BPF support..." +if perf stat -a --bpf-counters --for-each-cgroup / true > /dev/null 2>&1 +then + log_verbose "BPF supported, running Test 2..." + check_metric_reported "--bpf-counters" +else + log_verbose "BPF not supported, skipping Test 2" +fi + +exit 0 -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v2 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-19 1:51 ` [PATCH v2 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers @ 2026-05-19 2:59 ` sashiko-bot 0 siblings, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-05-19 2:59 UTC (permalink / raw) To: Ian Rogers; +Cc: linux-perf-users Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Medium] The test will falsely fail on healthy systems if the tested cgroups are idle. - [Medium] The capability checks verify unrelated events instead of the tested metric, risking false failures on unsupported hardware. - [Medium] A loose substring match causes the test to falsely pass if metrics for the root cgroup are missing. -- commit 87b9c1deb7ab23590530747e324c9b8bf5728b9d Author: Ian Rogers <irogers@google.com> perf test: Add stat metrics --for-each-cgroup test This commit adds a new shell test stat_metrics_cgrp.sh to verify metric reporting with --for-each-cgroup. It checks if system-wide monitoring is supported and tests both standard mode and BPF counters mode. > diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh > new file mode 100755 > index 0000000000000..2f74bd9e3f2ee > --- /dev/null > +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh [ ... ] > +# skip if system-wide is not supported > +check_system_wide() > +{ > + log_verbose "Checking system-wide..." > + if ! perf stat -a -e instructions sleep 0.01 > /dev/null 2>&1 > + then > + log_verbose "Skipping: system-wide monitoring not supported" > + exit 2 > + fi > +} This check verifies the instructions event, but the test measures insn_per_cycle which also requires cycles. If a test environment supports instructions but lacks hardware support for cycles (such as in some virtual environments), will this preliminary check pass but cause the actual test to fail? Could this capability check use --metrics=insn_per_cycle instead? [ ... ] > + # Split test_cgroups by comma > + IFS=',' read -r -a cgrps <<< "${test_cgroups}" > + > + for cgrp in "${cgrps[@]}"; do > + # Find lines for this cgroup and metric > + local cgrp_lines > + cgrp_lines=$(echo "${output}" | grep -F "${cgrp}" | grep "insn_per_cycle" || true) When cgrp is / (the root cgroup), won't grep -F "/" match any sub-cgroup containing a slash, like /user.slice? If perf stat fails to output metrics for the root cgroup, it seems cgrp_lines might still be populated by the child's metrics, potentially causing the test to pass and hide a failure. > + > + if [ -z "${cgrp_lines}" ] > + then > + echo "FAIL: No metric lines found for cgroup '${cgrp}'" > + exit 1 > + fi > + > + # Check for nan or nested > + if echo "${cgrp_lines}" | grep -q -i -E ",nan,|,nested," > + then > + echo "FAIL: Invalid metric value (nan/nested) for cgroup '${cgrp}'" > + echo "Line: ${cgrp_lines}" > + exit 1 > + fi > + # Check for empty metric value > + if echo "${cgrp_lines}" | grep -q -E ",,[[:space:]]*[^,]*insn_per_cycle" > + then > + echo "FAIL: Empty metric value for cgroup '${cgrp}'" If a tested cgroup like user.slice is completely idle during the 0.1 second sleep, its cycles and instructions counts will be 0. Would the insn_per_cycle metric evaluate to NaN and be output as an empty field in CSV mode? If so, will this check cause the test to falsely fail on healthy systems where the tested cgroups happen to be idle? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260519015108.3094513-1-irogers@google.com?part=2 ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v3 0/2] Fix cgroup metric association with BPF counters 2026-05-19 1:51 ` [PATCH v2 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-19 1:51 ` [PATCH v2 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers 2026-05-19 1:51 ` [PATCH v2 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers @ 2026-05-19 5:01 ` Ian Rogers 2026-05-19 5:01 ` [PATCH v3 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers ` (2 more replies) 2 siblings, 3 replies; 20+ messages in thread From: Ian Rogers @ 2026-05-19 5:01 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev This series fixes an issue where cgroup metrics were not being correctly associated and reported (showing `nan %`) when using BPF counters (i.e., with `--bpf-counters`). The root cause is that cgroup BPF counters only open the "leader" events (for the first cgroup) and leave "follower" events unopened. Unopened events have their `supported` flag set to `false` by default. During metric calculation, `prepare_metric` checks `evsel->supported` and discards the value (setting it to `NAN`) if it is `false`, leading to `nan %` in the output. The first patch fixes this by propagating the `supported` flag from the leader events to the follower events in `bperf_load_program`. The second patch adds a new shell test (`stat_metrics_cgrp.sh`) to permanently cover this scenario (testing both with and without BPF counters) and prevent regressions. Reported-by: Svilen Kanev <skanev@google.com> Changes since v2: - Use `--metrics=insn_per_cycle` for the system-wide capability check in `check_system_wide` to ensure both cycles and instructions are supported. - Use exact cgroup field matching with surrounding commas (`,${cgrp},`) in the `grep` check to prevent false positives when matching the root cgroup (`/`). - Only enforce metric validation if both `cycles` and `instructions` counts are numeric and non-zero, preventing false failures on idle cgroups. - Wrap long lines and fix trailing whitespace in the test script to comply with style guidelines. Changes since v1: - Fix commit message for patch 2 to remove the mention of insn_per_cycle check (which was simplified out). - Quote variables in shell script to prevent word splitting. - Use grep -F in shell script for literal matching of cgroup names containing dots. Ian Rogers (2): perf stat: Propagate supported flag to follower cgroup BPF events perf test: Add stat metrics --for-each-cgroup test tools/perf/tests/shell/stat_metrics_cgrp.sh | 174 ++++++++++++++++++++ tools/perf/util/bpf_counter_cgroup.c | 15 ++ 2 files changed, 189 insertions(+) create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v3 1/2] perf stat: Propagate supported flag to follower cgroup BPF events 2026-05-19 5:01 ` [PATCH v3 0/2] Fix cgroup metric association with BPF counters Ian Rogers @ 2026-05-19 5:01 ` Ian Rogers 2026-05-19 5:39 ` sashiko-bot 2026-05-19 5:47 ` Namhyung Kim 2026-05-19 5:01 ` [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers 2026-05-19 15:27 ` [PATCH v4 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2 siblings, 2 replies; 20+ messages in thread From: Ian Rogers @ 2026-05-19 5:01 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev When using BPF counters with cgroups, follower events (for cgroups other than the first one) are not opened. Because they are not opened, their `supported` flag was left as `false`. During metric calculation, `prepare_metric` checks if the event is supported. If it is not supported (like the follower events), it explicitly sets the value to `NAN`, which eventually causes the metric to be reported as `nan %`. Fix this by propagating the `supported` flag from the "leader" events (the ones opened for the first cgroup) to the "follower" events. Reported-by: Svilen Kanev <skanev@google.com> Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/bpf_counter_cgroup.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c index 519fee3dc3d0..dd1851634087 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -186,6 +186,21 @@ static int bperf_load_program(struct evlist *evlist) i++; } + /* + * Propagate supported flag from leaders to followers. Follower events + * are not opened, so their supported flag remains false. + */ + { + struct evsel *leader; + int num_events = evlist->core.nr_entries / nr_cgroups; + + evlist__for_each_entry(evlist, evsel) { + leader = evlist__find_evsel(evlist, evsel->core.idx % num_events); + if (leader) + evsel->supported = leader->supported; + } + } + /* * bperf uses BPF_PROG_TEST_RUN to get accurate reading. Check * whether the kernel support it -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v3 1/2] perf stat: Propagate supported flag to follower cgroup BPF events 2026-05-19 5:01 ` [PATCH v3 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers @ 2026-05-19 5:39 ` sashiko-bot 2026-05-19 5:47 ` Namhyung Kim 1 sibling, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-05-19 5:39 UTC (permalink / raw) To: Ian Rogers; +Cc: linux-perf-users Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] The patch introduces a potential modulo by zero crash when computing `evsel->core.idx % num_events`. -- commit d07a1cf9e5bf6b04df795e0b41782b1fa5a160cb Author: Ian Rogers <irogers@google.com> perf stat: Propagate supported flag to follower cgroup BPF events When using BPF counters with cgroups, follower events are not opened, leaving their supported flag as false and causing metric calculations to report nan. This commit fixes the issue by propagating the supported flag from the leader events to the follower events. > diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c > index 519fee3dc3d03..dd1851634087e 100644 > --- a/tools/perf/util/bpf_counter_cgroup.c > +++ b/tools/perf/util/bpf_counter_cgroup.c [ ... ] > @@ -186,6 +186,21 @@ static int bperf_load_program(struct evlist *evlist) > i++; > } > > + /* > + * Propagate supported flag from leaders to followers. Follower events > + * are not opened, so their supported flag remains false. > + */ > + { > + struct evsel *leader; > + int num_events = evlist->core.nr_entries / nr_cgroups; > + > + evlist__for_each_entry(evlist, evsel) { > + leader = evlist__find_evsel(evlist, evsel->core.idx % num_events); Can num_events evaluate to 0 here and cause a modulo by zero exception? If a user runs perf stat with a trailing comma in the cgroup list (e.g., perf stat --bpf-counters -e event1 -G cgrp1,), parse_cgroups() in tools/perf/util/cgroup.c skips the empty string but still increments nr_cgroups. This results in nr_cgroups being greater than the number of events, making evlist->core.nr_entries / nr_cgroups evaluate to 0. While setup_rodata() called earlier contains a BUG_ON(evlist_size % nr_cgroups != 0) check, non-debug builds compile with -DNDEBUG=1 by default, making BUG_ON a no-op. This allows execution to reach this point and crash with a SIGFPE. > + if (leader) > + evsel->supported = leader->supported; > + } > + } > + > /* > * bperf uses BPF_PROG_TEST_RUN to get accurate reading. Check > * whether the kernel support it -- Sashiko AI review · https://sashiko.dev/#/patchset/20260519050150.3144907-1-irogers@google.com?part=1 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 1/2] perf stat: Propagate supported flag to follower cgroup BPF events 2026-05-19 5:01 ` [PATCH v3 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers 2026-05-19 5:39 ` sashiko-bot @ 2026-05-19 5:47 ` Namhyung Kim 1 sibling, 0 replies; 20+ messages in thread From: Namhyung Kim @ 2026-05-19 5:47 UTC (permalink / raw) To: Ian Rogers Cc: acme, adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev Hi Ian, On Mon, May 18, 2026 at 10:01:49PM -0700, Ian Rogers wrote: > When using BPF counters with cgroups, follower events (for cgroups > other than the first one) are not opened. Because they are not opened, > their `supported` flag was left as `false`. > > During metric calculation, `prepare_metric` checks if the event is > supported. If it is not supported (like the follower events), it > explicitly sets the value to `NAN`, which eventually causes the metric > to be reported as `nan %`. > > Fix this by propagating the `supported` flag from the "leader" events > (the ones opened for the first cgroup) to the "follower" events. > > Reported-by: Svilen Kanev <skanev@google.com> > Assisted-by: Antigravity:gemini-3-flash > Signed-off-by: Ian Rogers <irogers@google.com> Acked-by: Namhyung Kim <namhyung@kernel.org> Thanks, Namhyung > --- > tools/perf/util/bpf_counter_cgroup.c | 15 +++++++++++++++ > 1 file changed, 15 insertions(+) > > diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c > index 519fee3dc3d0..dd1851634087 100644 > --- a/tools/perf/util/bpf_counter_cgroup.c > +++ b/tools/perf/util/bpf_counter_cgroup.c > @@ -186,6 +186,21 @@ static int bperf_load_program(struct evlist *evlist) > i++; > } > > + /* > + * Propagate supported flag from leaders to followers. Follower events > + * are not opened, so their supported flag remains false. > + */ > + { > + struct evsel *leader; > + int num_events = evlist->core.nr_entries / nr_cgroups; > + > + evlist__for_each_entry(evlist, evsel) { > + leader = evlist__find_evsel(evlist, evsel->core.idx % num_events); > + if (leader) > + evsel->supported = leader->supported; > + } > + } > + > /* > * bperf uses BPF_PROG_TEST_RUN to get accurate reading. Check > * whether the kernel support it > -- > 2.54.0.631.ge1b05301d1-goog > ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-19 5:01 ` [PATCH v3 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-19 5:01 ` [PATCH v3 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers @ 2026-05-19 5:01 ` Ian Rogers 2026-05-19 5:47 ` sashiko-bot 2026-05-19 5:54 ` Namhyung Kim 2026-05-19 15:27 ` [PATCH v4 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2 siblings, 2 replies; 20+ messages in thread From: Ian Rogers @ 2026-05-19 5:01 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev Add a new shell test `stat_metrics_cgrp.sh` to verify metric reporting with `--for-each-cgroup`, both with and without `--bpf-counters`. The test: - Checks if system-wide monitoring is supported (skips if not). - Finds cgroups to test. - Runs `perf stat` with `insn_per_cycle` metric and verifies that the metric is reported for each cgroup and does not contain invalid values (like `nan` or `nested`) or empty values when counts are present. - Tests both standard mode and BPF counters mode (if supported). Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/tests/shell/stat_metrics_cgrp.sh | 174 ++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh new file mode 100755 index 000000000000..b413849193a0 --- /dev/null +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh @@ -0,0 +1,174 @@ +#!/bin/bash +# perf stat metrics --for-each-cgroup test +# SPDX-License-Identifier: GPL-2.0 + +set -e + +test_cgroups= + +log_verbose() { + echo "$1" +} + +is_numeric_and_non_zero() { + local val="$1" + if [[ "${val}" =~ ^[0-9]+$ ]] && [ "${val}" -gt 0 ] + then + return 0 # True + fi + return 1 # False +} + +# skip if system-wide is not supported +check_system_wide() +{ + log_verbose "Checking system-wide..." + if ! perf stat -a --metrics=insn_per_cycle sleep 0.01 > /dev/null 2>&1 + then + log_verbose "Skipping: system-wide monitoring not supported" + exit 2 + fi +} + +# find two cgroups to measure +find_cgroups() +{ + log_verbose "Finding cgroups..." + # try usual systemd slices first + if [ -d /sys/fs/cgroup/system.slice ] && [ -d /sys/fs/cgroup/user.slice ] + then + test_cgroups="system.slice,user.slice" + log_verbose "Found cgroups: ${test_cgroups}" + return + fi + + # try root and self cgroups + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) + if [ -z "${find_cgroups_self_cgrp}" ] + then + # cgroup v2 doesn't specify perf_event + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) + fi + + if [ -z "${find_cgroups_self_cgrp}" ] + then + test_cgroups="/" + else + test_cgroups="/,${find_cgroups_self_cgrp}" + fi + log_verbose "Found cgroups: ${test_cgroups}" +} + +# Check if metric is reported for each cgroup +# $1: extra options (e.g. --bpf-counters) +check_metric_reported() +{ + local opts="$1" + local output + + log_verbose "Running check_metric_reported with opts '${opts}'..." + # Run perf stat + if ! output=$(perf stat -a ${opts} \ + --metrics=insn_per_cycle \ + --for-each-cgroup "${test_cgroups}" \ + -x, sleep 0.1 2>&1) + then + echo "FAIL: perf stat failed with exit code $?" + echo "Output: ${output}" + exit 1 + fi + + log_verbose "perf stat output:" + log_verbose "${output}" + + # Split test_cgroups by comma + IFS=',' read -r -a cgrps <<< "${test_cgroups}" + + for cgrp in "${cgrps[@]}"; do + # Find metric lines for this cgroup + # We use exact cgroup match with surrounding commas + local cgrp_lines + cgrp_lines=$(echo "${output}" | grep -F ",${cgrp}," | grep "insn_per_cycle" || true) + + if [ -z "${cgrp_lines}" ] + then + echo "FAIL: No metric lines found for cgroup '${cgrp}'" + exit 1 + fi + + # Parse each metric line + while read -r line; do + [ -z "${line}" ] && continue + + local cycles_val + cycles_val=$(echo "${line}" | cut -d, -f1) + + local event_name + event_name=$(echo "${line}" | cut -d, -f3) + + # Find corresponding instructions event line + local inst_event_name + inst_event_name="${event_name/cpu-cycles/instructions}" + inst_event_name="${inst_event_name/cycles/instructions}" + + local inst_line + inst_line=$(echo "${output}" | \ + grep -F ",${cgrp}," | \ + grep -F "${inst_event_name}" || true) + + local inst_val + inst_val=$(echo "${inst_line}" | cut -d, -f1) + + log_verbose "Cgroup '${cgrp}': event '${event_name}' \ +val '${cycles_val}', inst event '${inst_event_name}' val '${inst_val}'" + + # Only enforce metric check if both cycles and + # instructions have non-zero numeric counts + if is_numeric_and_non_zero "${cycles_val}" && \ + is_numeric_and_non_zero "${inst_val}" + then + log_verbose "Enforcing metric check for cgroup '${cgrp}' \ +event '${event_name}'" + # Check for nan or nested in the metric value (7th field) + # Actually we can just check the whole line for simplicity + if echo "${line}" | grep -q -i -E ",nan,|,nested," + then + echo "FAIL: Invalid metric value (nan/nested) \ +for cgroup '${cgrp}'" + echo "Line: ${line}" + exit 1 + fi + # Check for empty metric value (2 consecutive + # commas before the unit) + if echo "${line}" | grep -q -E ",,[[:space:]]*[^,]*insn_per_cycle" + then + echo "FAIL: Empty metric value for cgroup '${cgrp}'" + echo "Line: ${line}" + exit 1 + fi + else + log_verbose "Skipping metric check for cgroup '${cgrp}' \ +event '${event_name}' (idle or not counted)" + fi + done <<< "${cgrp_lines}" + done + log_verbose "check_metric_reported passed for opts '${opts}'" +} + +check_system_wide +find_cgroups + +# Test 1: Without BPF counters +check_metric_reported "" + +# Test 2: With BPF counters (if supported) +log_verbose "Checking BPF support..." +if perf stat -a --bpf-counters --for-each-cgroup / true > /dev/null 2>&1 +then + log_verbose "BPF supported, running Test 2..." + check_metric_reported "--bpf-counters" +else + log_verbose "BPF not supported, skipping Test 2" +fi + +exit 0 -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-19 5:01 ` [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers @ 2026-05-19 5:47 ` sashiko-bot 2026-05-19 5:54 ` Namhyung Kim 1 sibling, 0 replies; 20+ messages in thread From: sashiko-bot @ 2026-05-19 5:47 UTC (permalink / raw) To: Ian Rogers; +Cc: linux-perf-users Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] The test incorrectly reads the `instructions` event count for both `cycles_val` and `inst_val`, failing to verify if the `cycles` event was actually counted before enforcing the metric check. -- commit fb13ca0af650ea803b39745454aa3404e54a2239 Author: Ian Rogers <irogers@google.com> perf test: Add stat metrics --for-each-cgroup test This commit adds a new shell test to verify metric reporting with the --for-each-cgroup flag in perf stat. It checks that metrics like insn_per_cycle are properly reported per-cgroup and do not contain invalid values when events are actively counted. > diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh > new file mode 100755 > index 0000000000000..b413849193a0b > --- /dev/null > +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh [ ... ] > + for cgrp in "${cgrps[@]}"; do > + # Find metric lines for this cgroup > + # We use exact cgroup match with surrounding commas > + local cgrp_lines > + cgrp_lines=$(echo "${output}" | grep -F ",${cgrp}," | grep "insn_per_cycle" || true) [ ... ] > + # Parse each metric line > + while read -r line; do > + [ -z "${line}" ] && continue > + > + local cycles_val > + cycles_val=$(echo "${line}" | cut -d, -f1) > + > + local event_name > + event_name=$(echo "${line}" | cut -d, -f3) > + > + # Find corresponding instructions event line > + local inst_event_name > + inst_event_name="${event_name/cpu-cycles/instructions}" > + inst_event_name="${inst_event_name/cycles/instructions}" > + > + local inst_line > + inst_line=$(echo "${output}" | \ > + grep -F ",${cgrp}," | \ > + grep -F "${inst_event_name}" || true) > + > + local inst_val > + inst_val=$(echo "${inst_line}" | cut -d, -f1) Since cgrp_lines is filtered by insn_per_cycle earlier, the event_name extracted from the third field will typically be instructions rather than cycles. Does this cause both cycles_val and inst_val to read the exact same instructions count? If event_name is already instructions, the string replacement leaves it unchanged, and inst_line will match the original line. This could mean the script never actually verifies if the cycles event itself was successfully counted before enforcing the metric check. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260519050150.3144907-1-irogers@google.com?part=2 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-19 5:01 ` [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers 2026-05-19 5:47 ` sashiko-bot @ 2026-05-19 5:54 ` Namhyung Kim 2026-05-19 15:13 ` Ian Rogers 1 sibling, 1 reply; 20+ messages in thread From: Namhyung Kim @ 2026-05-19 5:54 UTC (permalink / raw) To: Ian Rogers Cc: acme, adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev On Mon, May 18, 2026 at 10:01:50PM -0700, Ian Rogers wrote: > Add a new shell test `stat_metrics_cgrp.sh` to verify metric reporting > with `--for-each-cgroup`, both with and without `--bpf-counters`. > > The test: > - Checks if system-wide monitoring is supported (skips if not). > - Finds cgroups to test. > - Runs `perf stat` with `insn_per_cycle` metric and verifies that the > metric is reported for each cgroup and does not contain invalid > values (like `nan` or `nested`) or empty values when counts are > present. > - Tests both standard mode and BPF counters mode (if supported). > > Assisted-by: Antigravity:gemini-3-flash > Signed-off-by: Ian Rogers <irogers@google.com> > --- > tools/perf/tests/shell/stat_metrics_cgrp.sh | 174 ++++++++++++++++++++ > 1 file changed, 174 insertions(+) > create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh > > diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh > new file mode 100755 > index 000000000000..b413849193a0 > --- /dev/null > +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh > @@ -0,0 +1,174 @@ > +#!/bin/bash > +# perf stat metrics --for-each-cgroup test > +# SPDX-License-Identifier: GPL-2.0 > + > +set -e > + > +test_cgroups= > + > +log_verbose() { > + echo "$1" > +} Why is this function used? > + > +is_numeric_and_non_zero() { > + local val="$1" > + if [[ "${val}" =~ ^[0-9]+$ ]] && [ "${val}" -gt 0 ] > + then > + return 0 # True > + fi > + return 1 # False > +} > + > +# skip if system-wide is not supported > +check_system_wide() > +{ > + log_verbose "Checking system-wide..." > + if ! perf stat -a --metrics=insn_per_cycle sleep 0.01 > /dev/null 2>&1 > + then > + log_verbose "Skipping: system-wide monitoring not supported" > + exit 2 > + fi > +} > + > +# find two cgroups to measure > +find_cgroups() > +{ > + log_verbose "Finding cgroups..." > + # try usual systemd slices first > + if [ -d /sys/fs/cgroup/system.slice ] && [ -d /sys/fs/cgroup/user.slice ] > + then > + test_cgroups="system.slice,user.slice" > + log_verbose "Found cgroups: ${test_cgroups}" > + return > + fi > + > + # try root and self cgroups > + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) > + if [ -z "${find_cgroups_self_cgrp}" ] > + then > + # cgroup v2 doesn't specify perf_event > + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) > + fi > + > + if [ -z "${find_cgroups_self_cgrp}" ] > + then > + test_cgroups="/" > + else > + test_cgroups="/,${find_cgroups_self_cgrp}" > + fi > + log_verbose "Found cgroups: ${test_cgroups}" > +} > + > +# Check if metric is reported for each cgroup > +# $1: extra options (e.g. --bpf-counters) > +check_metric_reported() > +{ > + local opts="$1" > + local output > + > + log_verbose "Running check_metric_reported with opts '${opts}'..." > + # Run perf stat > + if ! output=$(perf stat -a ${opts} \ > + --metrics=insn_per_cycle \ > + --for-each-cgroup "${test_cgroups}" \ > + -x, sleep 0.1 2>&1) > + then > + echo "FAIL: perf stat failed with exit code $?" > + echo "Output: ${output}" > + exit 1 > + fi > + > + log_verbose "perf stat output:" > + log_verbose "${output}" > + > + # Split test_cgroups by comma > + IFS=',' read -r -a cgrps <<< "${test_cgroups}" > + > + for cgrp in "${cgrps[@]}"; do > + # Find metric lines for this cgroup > + # We use exact cgroup match with surrounding commas > + local cgrp_lines > + cgrp_lines=$(echo "${output}" | grep -F ",${cgrp}," | grep "insn_per_cycle" || true) > + > + if [ -z "${cgrp_lines}" ] > + then > + echo "FAIL: No metric lines found for cgroup '${cgrp}'" > + exit 1 > + fi > + > + # Parse each metric line > + while read -r line; do > + [ -z "${line}" ] && continue > + > + local cycles_val > + cycles_val=$(echo "${line}" | cut -d, -f1) > + > + local event_name > + event_name=$(echo "${line}" | cut -d, -f3) > + > + # Find corresponding instructions event line > + local inst_event_name > + inst_event_name="${event_name/cpu-cycles/instructions}" > + inst_event_name="${inst_event_name/cycles/instructions}" > + > + local inst_line > + inst_line=$(echo "${output}" | \ > + grep -F ",${cgrp}," | \ > + grep -F "${inst_event_name}" || true) > + > + local inst_val > + inst_val=$(echo "${inst_line}" | cut -d, -f1) > + > + log_verbose "Cgroup '${cgrp}': event '${event_name}' \ > +val '${cycles_val}', inst event '${inst_event_name}' val '${inst_val}'" Why not make log_verbose() take multiple arguments and align them properly? Maybe you can just use 'echo'? The same goes to the below lines. Thanks, Namhyung > + > + # Only enforce metric check if both cycles and > + # instructions have non-zero numeric counts > + if is_numeric_and_non_zero "${cycles_val}" && \ > + is_numeric_and_non_zero "${inst_val}" > + then > + log_verbose "Enforcing metric check for cgroup '${cgrp}' \ > +event '${event_name}'" > + # Check for nan or nested in the metric value (7th field) > + # Actually we can just check the whole line for simplicity > + if echo "${line}" | grep -q -i -E ",nan,|,nested," > + then > + echo "FAIL: Invalid metric value (nan/nested) \ > +for cgroup '${cgrp}'" > + echo "Line: ${line}" > + exit 1 > + fi > + # Check for empty metric value (2 consecutive > + # commas before the unit) > + if echo "${line}" | grep -q -E ",,[[:space:]]*[^,]*insn_per_cycle" > + then > + echo "FAIL: Empty metric value for cgroup '${cgrp}'" > + echo "Line: ${line}" > + exit 1 > + fi > + else > + log_verbose "Skipping metric check for cgroup '${cgrp}' \ > +event '${event_name}' (idle or not counted)" > + fi > + done <<< "${cgrp_lines}" > + done > + log_verbose "check_metric_reported passed for opts '${opts}'" > +} > + > +check_system_wide > +find_cgroups > + > +# Test 1: Without BPF counters > +check_metric_reported "" > + > +# Test 2: With BPF counters (if supported) > +log_verbose "Checking BPF support..." > +if perf stat -a --bpf-counters --for-each-cgroup / true > /dev/null 2>&1 > +then > + log_verbose "BPF supported, running Test 2..." > + check_metric_reported "--bpf-counters" > +else > + log_verbose "BPF not supported, skipping Test 2" > +fi > + > +exit 0 > -- > 2.54.0.631.ge1b05301d1-goog > ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-19 5:54 ` Namhyung Kim @ 2026-05-19 15:13 ` Ian Rogers 2026-05-19 17:09 ` Namhyung Kim 0 siblings, 1 reply; 20+ messages in thread From: Ian Rogers @ 2026-05-19 15:13 UTC (permalink / raw) To: Namhyung Kim Cc: acme, adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev On Mon, May 18, 2026 at 10:54 PM Namhyung Kim <namhyung@kernel.org> wrote: > > On Mon, May 18, 2026 at 10:01:50PM -0700, Ian Rogers wrote: > > Add a new shell test `stat_metrics_cgrp.sh` to verify metric reporting > > with `--for-each-cgroup`, both with and without `--bpf-counters`. > > > > The test: > > - Checks if system-wide monitoring is supported (skips if not). > > - Finds cgroups to test. > > - Runs `perf stat` with `insn_per_cycle` metric and verifies that the > > metric is reported for each cgroup and does not contain invalid > > values (like `nan` or `nested`) or empty values when counts are > > present. > > - Tests both standard mode and BPF counters mode (if supported). > > > > Assisted-by: Antigravity:gemini-3-flash > > Signed-off-by: Ian Rogers <irogers@google.com> > > --- > > tools/perf/tests/shell/stat_metrics_cgrp.sh | 174 ++++++++++++++++++++ > > 1 file changed, 174 insertions(+) > > create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh > > > > diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh > > new file mode 100755 > > index 000000000000..b413849193a0 > > --- /dev/null > > +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh > > @@ -0,0 +1,174 @@ > > +#!/bin/bash > > +# perf stat metrics --for-each-cgroup test > > +# SPDX-License-Identifier: GPL-2.0 > > + > > +set -e > > + > > +test_cgroups= > > + > > +log_verbose() { > > + echo "$1" > > +} > > Why is this function used? At some point I'd like verbose flags to be passed to tests to cut down on output. A suitable series would be the test improvements in: https://lore.kernel.org/linux-perf-users/20260513230450.529380-1-irogers@google.com/ > > + > > +is_numeric_and_non_zero() { > > + local val="$1" > > + if [[ "${val}" =~ ^[0-9]+$ ]] && [ "${val}" -gt 0 ] > > + then > > + return 0 # True > > + fi > > + return 1 # False > > +} > > + > > +# skip if system-wide is not supported > > +check_system_wide() > > +{ > > + log_verbose "Checking system-wide..." > > + if ! perf stat -a --metrics=insn_per_cycle sleep 0.01 > /dev/null 2>&1 > > + then > > + log_verbose "Skipping: system-wide monitoring not supported" > > + exit 2 > > + fi > > +} > > + > > +# find two cgroups to measure > > +find_cgroups() > > +{ > > + log_verbose "Finding cgroups..." > > + # try usual systemd slices first > > + if [ -d /sys/fs/cgroup/system.slice ] && [ -d /sys/fs/cgroup/user.slice ] > > + then > > + test_cgroups="system.slice,user.slice" > > + log_verbose "Found cgroups: ${test_cgroups}" > > + return > > + fi > > + > > + # try root and self cgroups > > + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) > > + if [ -z "${find_cgroups_self_cgrp}" ] > > + then > > + # cgroup v2 doesn't specify perf_event > > + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) > > + fi > > + > > + if [ -z "${find_cgroups_self_cgrp}" ] > > + then > > + test_cgroups="/" > > + else > > + test_cgroups="/,${find_cgroups_self_cgrp}" > > + fi > > + log_verbose "Found cgroups: ${test_cgroups}" > > +} > > + > > +# Check if metric is reported for each cgroup > > +# $1: extra options (e.g. --bpf-counters) > > +check_metric_reported() > > +{ > > + local opts="$1" > > + local output > > + > > + log_verbose "Running check_metric_reported with opts '${opts}'..." > > + # Run perf stat > > + if ! output=$(perf stat -a ${opts} \ > > + --metrics=insn_per_cycle \ > > + --for-each-cgroup "${test_cgroups}" \ > > + -x, sleep 0.1 2>&1) > > + then > > + echo "FAIL: perf stat failed with exit code $?" > > + echo "Output: ${output}" > > + exit 1 > > + fi > > + > > + log_verbose "perf stat output:" > > + log_verbose "${output}" > > + > > + # Split test_cgroups by comma > > + IFS=',' read -r -a cgrps <<< "${test_cgroups}" > > + > > + for cgrp in "${cgrps[@]}"; do > > + # Find metric lines for this cgroup > > + # We use exact cgroup match with surrounding commas > > + local cgrp_lines > > + cgrp_lines=$(echo "${output}" | grep -F ",${cgrp}," | grep "insn_per_cycle" || true) > > + > > + if [ -z "${cgrp_lines}" ] > > + then > > + echo "FAIL: No metric lines found for cgroup '${cgrp}'" > > + exit 1 > > + fi > > + > > + # Parse each metric line > > + while read -r line; do > > + [ -z "${line}" ] && continue > > + > > + local cycles_val > > + cycles_val=$(echo "${line}" | cut -d, -f1) > > + > > + local event_name > > + event_name=$(echo "${line}" | cut -d, -f3) > > + > > + # Find corresponding instructions event line > > + local inst_event_name > > + inst_event_name="${event_name/cpu-cycles/instructions}" > > + inst_event_name="${inst_event_name/cycles/instructions}" > > + > > + local inst_line > > + inst_line=$(echo "${output}" | \ > > + grep -F ",${cgrp}," | \ > > + grep -F "${inst_event_name}" || true) > > + > > + local inst_val > > + inst_val=$(echo "${inst_line}" | cut -d, -f1) > > + > > + log_verbose "Cgroup '${cgrp}': event '${event_name}' \ > > +val '${cycles_val}', inst event '${inst_event_name}' val '${inst_val}'" > > Why not make log_verbose() take multiple arguments and align them > properly? Maybe you can just use 'echo'? The same goes to the below > lines. This fixes checkpatch warnings on line length and the odd alignment avoids spaces appearing in the output. Thanks, Ian > Thanks, > Namhyung > > > + > > + # Only enforce metric check if both cycles and > > + # instructions have non-zero numeric counts > > + if is_numeric_and_non_zero "${cycles_val}" && \ > > + is_numeric_and_non_zero "${inst_val}" > > + then > > + log_verbose "Enforcing metric check for cgroup '${cgrp}' \ > > +event '${event_name}'" > > + # Check for nan or nested in the metric value (7th field) > > + # Actually we can just check the whole line for simplicity > > + if echo "${line}" | grep -q -i -E ",nan,|,nested," > > + then > > + echo "FAIL: Invalid metric value (nan/nested) \ > > +for cgroup '${cgrp}'" > > + echo "Line: ${line}" > > + exit 1 > > + fi > > + # Check for empty metric value (2 consecutive > > + # commas before the unit) > > + if echo "${line}" | grep -q -E ",,[[:space:]]*[^,]*insn_per_cycle" > > + then > > + echo "FAIL: Empty metric value for cgroup '${cgrp}'" > > + echo "Line: ${line}" > > + exit 1 > > + fi > > + else > > + log_verbose "Skipping metric check for cgroup '${cgrp}' \ > > +event '${event_name}' (idle or not counted)" > > + fi > > + done <<< "${cgrp_lines}" > > + done > > + log_verbose "check_metric_reported passed for opts '${opts}'" > > +} > > + > > +check_system_wide > > +find_cgroups > > + > > +# Test 1: Without BPF counters > > +check_metric_reported "" > > + > > +# Test 2: With BPF counters (if supported) > > +log_verbose "Checking BPF support..." > > +if perf stat -a --bpf-counters --for-each-cgroup / true > /dev/null 2>&1 > > +then > > + log_verbose "BPF supported, running Test 2..." > > + check_metric_reported "--bpf-counters" > > +else > > + log_verbose "BPF not supported, skipping Test 2" > > +fi > > + > > +exit 0 > > -- > > 2.54.0.631.ge1b05301d1-goog > > ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-19 15:13 ` Ian Rogers @ 2026-05-19 17:09 ` Namhyung Kim 0 siblings, 0 replies; 20+ messages in thread From: Namhyung Kim @ 2026-05-19 17:09 UTC (permalink / raw) To: Ian Rogers Cc: acme, adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev On Tue, May 19, 2026 at 08:13:02AM -0700, Ian Rogers wrote: > On Mon, May 18, 2026 at 10:54 PM Namhyung Kim <namhyung@kernel.org> wrote: > > > > On Mon, May 18, 2026 at 10:01:50PM -0700, Ian Rogers wrote: > > > Add a new shell test `stat_metrics_cgrp.sh` to verify metric reporting > > > with `--for-each-cgroup`, both with and without `--bpf-counters`. > > > > > > The test: > > > - Checks if system-wide monitoring is supported (skips if not). > > > - Finds cgroups to test. > > > - Runs `perf stat` with `insn_per_cycle` metric and verifies that the > > > metric is reported for each cgroup and does not contain invalid > > > values (like `nan` or `nested`) or empty values when counts are > > > present. > > > - Tests both standard mode and BPF counters mode (if supported). > > > > > > Assisted-by: Antigravity:gemini-3-flash > > > Signed-off-by: Ian Rogers <irogers@google.com> > > > --- > > > tools/perf/tests/shell/stat_metrics_cgrp.sh | 174 ++++++++++++++++++++ > > > 1 file changed, 174 insertions(+) > > > create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh > > > > > > diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh > > > new file mode 100755 > > > index 000000000000..b413849193a0 > > > --- /dev/null > > > +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh > > > @@ -0,0 +1,174 @@ > > > +#!/bin/bash > > > +# perf stat metrics --for-each-cgroup test > > > +# SPDX-License-Identifier: GPL-2.0 > > > + > > > +set -e > > > + > > > +test_cgroups= > > > + > > > +log_verbose() { > > > + echo "$1" > > > +} > > > > Why is this function used? > > At some point I'd like verbose flags to be passed to tests to cut down > on output. A suitable series would be the test improvements in: > https://lore.kernel.org/linux-perf-users/20260513230450.529380-1-irogers@google.com/ I see. > > > > + > > > +is_numeric_and_non_zero() { > > > + local val="$1" > > > + if [[ "${val}" =~ ^[0-9]+$ ]] && [ "${val}" -gt 0 ] > > > + then > > > + return 0 # True > > > + fi > > > + return 1 # False > > > +} > > > + > > > +# skip if system-wide is not supported > > > +check_system_wide() > > > +{ > > > + log_verbose "Checking system-wide..." > > > + if ! perf stat -a --metrics=insn_per_cycle sleep 0.01 > /dev/null 2>&1 > > > + then > > > + log_verbose "Skipping: system-wide monitoring not supported" > > > + exit 2 > > > + fi > > > +} > > > + > > > +# find two cgroups to measure > > > +find_cgroups() > > > +{ > > > + log_verbose "Finding cgroups..." > > > + # try usual systemd slices first > > > + if [ -d /sys/fs/cgroup/system.slice ] && [ -d /sys/fs/cgroup/user.slice ] > > > + then > > > + test_cgroups="system.slice,user.slice" > > > + log_verbose "Found cgroups: ${test_cgroups}" > > > + return > > > + fi > > > + > > > + # try root and self cgroups > > > + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) > > > + if [ -z "${find_cgroups_self_cgrp}" ] > > > + then > > > + # cgroup v2 doesn't specify perf_event > > > + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) > > > + fi > > > + > > > + if [ -z "${find_cgroups_self_cgrp}" ] > > > + then > > > + test_cgroups="/" > > > + else > > > + test_cgroups="/,${find_cgroups_self_cgrp}" > > > + fi > > > + log_verbose "Found cgroups: ${test_cgroups}" > > > +} > > > + > > > +# Check if metric is reported for each cgroup > > > +# $1: extra options (e.g. --bpf-counters) > > > +check_metric_reported() > > > +{ > > > + local opts="$1" > > > + local output > > > + > > > + log_verbose "Running check_metric_reported with opts '${opts}'..." > > > + # Run perf stat > > > + if ! output=$(perf stat -a ${opts} \ > > > + --metrics=insn_per_cycle \ > > > + --for-each-cgroup "${test_cgroups}" \ > > > + -x, sleep 0.1 2>&1) > > > + then > > > + echo "FAIL: perf stat failed with exit code $?" > > > + echo "Output: ${output}" > > > + exit 1 > > > + fi > > > + > > > + log_verbose "perf stat output:" > > > + log_verbose "${output}" > > > + > > > + # Split test_cgroups by comma > > > + IFS=',' read -r -a cgrps <<< "${test_cgroups}" > > > + > > > + for cgrp in "${cgrps[@]}"; do > > > + # Find metric lines for this cgroup > > > + # We use exact cgroup match with surrounding commas > > > + local cgrp_lines > > > + cgrp_lines=$(echo "${output}" | grep -F ",${cgrp}," | grep "insn_per_cycle" || true) > > > + > > > + if [ -z "${cgrp_lines}" ] > > > + then > > > + echo "FAIL: No metric lines found for cgroup '${cgrp}'" > > > + exit 1 > > > + fi > > > + > > > + # Parse each metric line > > > + while read -r line; do > > > + [ -z "${line}" ] && continue > > > + > > > + local cycles_val > > > + cycles_val=$(echo "${line}" | cut -d, -f1) > > > + > > > + local event_name > > > + event_name=$(echo "${line}" | cut -d, -f3) > > > + > > > + # Find corresponding instructions event line > > > + local inst_event_name > > > + inst_event_name="${event_name/cpu-cycles/instructions}" > > > + inst_event_name="${inst_event_name/cycles/instructions}" > > > + > > > + local inst_line > > > + inst_line=$(echo "${output}" | \ > > > + grep -F ",${cgrp}," | \ > > > + grep -F "${inst_event_name}" || true) > > > + > > > + local inst_val > > > + inst_val=$(echo "${inst_line}" | cut -d, -f1) > > > + > > > + log_verbose "Cgroup '${cgrp}': event '${event_name}' \ > > > +val '${cycles_val}', inst event '${inst_event_name}' val '${inst_val}'" > > > > Why not make log_verbose() take multiple arguments and align them > > properly? Maybe you can just use 'echo'? The same goes to the below > > lines. > > This fixes checkpatch warnings on line length and the odd alignment > avoids spaces appearing in the output. How about this? Thanks, Namhyung diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh index d4226ee0ae9801cb..ecb2c053ea5158d4 100755 --- a/tools/perf/tests/shell/stat_metrics_cgrp.sh +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh @@ -7,7 +7,7 @@ set -e test_cgroups= log_verbose() { - echo "$1" + echo "$*" } is_numeric_and_non_zero() { @@ -145,22 +145,22 @@ check_metric_reported() cycles_val=$(echo "${cycles_line}" | cut -d, -f1) fi - log_verbose "Cgroup '${cgrp}': event '${event_name}' \ -val '${cycles_val}', inst val '${inst_val}'" + log_verbose "Cgroup '${cgrp}': event '${event_name}'" \ + "val '${cycles_val}', inst val '${inst_val}'" # Only enforce metric check if both cycles and # instructions have non-zero numeric counts if is_numeric_and_non_zero "${cycles_val}" && \ is_numeric_and_non_zero "${inst_val}" then - log_verbose "Enforcing metric check for cgroup '${cgrp}' \ -event '${event_name}'" + log_verbose "Enforcing metric check for cgroup '${cgrp}'" \ + "event '${event_name}'" # Check for nan or nested in the metric value (7th field) # Actually we can just check the whole line for simplicity if echo "${line}" | grep -q -i -E ",nan,|,nested," then - echo "FAIL: Invalid metric value (nan/nested) \ -for cgroup '${cgrp}'" + echo "FAIL: Invalid metric value (nan/nested)" \ + "for cgroup '${cgrp}'" echo "Line: ${line}" exit 1 fi @@ -173,8 +173,8 @@ for cgroup '${cgrp}'" exit 1 fi else - log_verbose "Skipping metric check for cgroup '${cgrp}' \ -event '${event_name}' (idle or not counted)" + log_verbose "Skipping metric check for cgroup '${cgrp}'" \ + "event '${event_name}' (idle or not counted)" fi done <<< "${cgrp_lines}" done ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v4 0/2] Fix cgroup metric association with BPF counters 2026-05-19 5:01 ` [PATCH v3 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-19 5:01 ` [PATCH v3 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers 2026-05-19 5:01 ` [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers @ 2026-05-19 15:27 ` Ian Rogers 2026-05-19 15:27 ` [PATCH v4 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers 2026-05-19 15:27 ` [PATCH v4 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers 2 siblings, 2 replies; 20+ messages in thread From: Ian Rogers @ 2026-05-19 15:27 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev This series fixes an issue where cgroup metrics were not being correctly associated and reported (showing `nan %`) when using BPF counters (i.e., with `--bpf-counters`). The root cause is that cgroup BPF counters only open the "leader" events (for the first cgroup) and leave "follower" events unopened. Unopened events have their `supported` flag set to `false` by default. During metric calculation, `prepare_metric` checks `evsel->supported` and discards the value (setting it to `NAN`) if it is `false`, leading to `nan %` in the output. The first patch fixes this by propagating the `supported` flag from the leader events to the follower events in `bperf_load_program`. It also adds a validation check to prevent potential division-by-zero (SIGFPE) crashes. The second patch adds a new shell test (`stat_metrics_cgrp.sh`) to permanently cover this scenario (testing both with and without BPF counters) and prevent regressions. Reported-by: Svilen Kanev <skanev@google.com> Changes since v3: - Add validation check in `bperf_load_program` to prevent potential division-by-zero (SIGFPE) when `num_events` is 0 (e.g., due to a trailing comma in cgroups list). - In the test script, dynamically pair instructions and cycles events using the PMU name. This ensures that we correctly verify if the associated cycles event was counted before performing the metric check, regardless of which event (cycles or instructions) is the metric leader. - Collect Namhyung's Acked-by for patch 1. Changes since v2: - Use `--metrics=insn_per_cycle` for the system-wide capability check in `check_system_wide` to ensure both cycles and instructions are supported. - Use exact cgroup field matching with surrounding commas (`,${cgrp},`) in the `grep` check to prevent false positives when matching the root cgroup (`/`). - Only enforce metric validation if both `cycles` and `instructions` counts are numeric and non-zero, preventing false failures on idle cgroups. - Wrap long lines and fix trailing whitespace in the test script to comply with style guidelines. Changes since v1: - Fix commit message for patch 2 to remove the mention of insn_per_cycle check (which was simplified out). - Quote variables in shell script to prevent word splitting. - Use grep -F in shell script for literal matching of cgroup names containing dots. Ian Rogers (2): perf stat: Propagate supported flag to follower cgroup BPF events perf test: Add stat metrics --for-each-cgroup test tools/perf/tests/shell/stat_metrics_cgrp.sh | 200 ++++++++++++++++++++ tools/perf/util/bpf_counter_cgroup.c | 20 ++ 2 files changed, 220 insertions(+) create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v4 1/2] perf stat: Propagate supported flag to follower cgroup BPF events 2026-05-19 15:27 ` [PATCH v4 0/2] Fix cgroup metric association with BPF counters Ian Rogers @ 2026-05-19 15:27 ` Ian Rogers 2026-05-19 15:27 ` [PATCH v4 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers 1 sibling, 0 replies; 20+ messages in thread From: Ian Rogers @ 2026-05-19 15:27 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev When using BPF counters with cgroups, follower events (for cgroups other than the first one) are not opened. Because they are not opened, their `supported` flag was left as `false`. During metric calculation, `prepare_metric` checks if the event is supported. If it is not supported (like the follower events), it explicitly sets the value to `NAN`, which eventually causes the metric to be reported as `nan %`. Fix this by propagating the `supported` flag from the "leader" events (the ones opened for the first cgroup) to the "follower" events. Also add a validation check to `bperf_load_program` to ensure `nr_cgroups` is not zero and the number of events is a multiple of `nr_cgroups`, preventing a potential division-by-zero (SIGFPE) exception when `num_events` evaluates to 0 (e.g., with a trailing comma in cgroups list). Reported-by: Svilen Kanev <skanev@google.com> Acked-by: Namhyung Kim <namhyung@kernel.org> Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/util/bpf_counter_cgroup.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c index 519fee3dc3d0..e1ce5aa3b957 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -104,6 +104,11 @@ static int bperf_load_program(struct evlist *evlist) set_max_rlimit(); + if (nr_cgroups == 0 || evlist->core.nr_entries % nr_cgroups != 0) { + pr_err("Invalid cgroup or event count\n"); + return -EINVAL; + } + test_max_events_program_load(); skel = bperf_cgroup_bpf__open(); @@ -186,6 +191,21 @@ static int bperf_load_program(struct evlist *evlist) i++; } + /* + * Propagate supported flag from leaders to followers. Follower events + * are not opened, so their supported flag remains false. + */ + { + struct evsel *leader; + int num_events = evlist->core.nr_entries / nr_cgroups; + + evlist__for_each_entry(evlist, evsel) { + leader = evlist__find_evsel(evlist, evsel->core.idx % num_events); + if (leader) + evsel->supported = leader->supported; + } + } + /* * bperf uses BPF_PROG_TEST_RUN to get accurate reading. Check * whether the kernel support it -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v4 2/2] perf test: Add stat metrics --for-each-cgroup test 2026-05-19 15:27 ` [PATCH v4 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-19 15:27 ` [PATCH v4 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers @ 2026-05-19 15:27 ` Ian Rogers 1 sibling, 0 replies; 20+ messages in thread From: Ian Rogers @ 2026-05-19 15:27 UTC (permalink / raw) To: irogers, acme, namhyung Cc: adrian.hunter, james.clark, jolsa, linux-kernel, linux-perf-users, mingo, peterz, skanev Add a new shell test `stat_metrics_cgrp.sh` to verify metric reporting with `--for-each-cgroup`, both with and without `--bpf-counters`. The test: - Checks if system-wide monitoring is supported (skips if not). - Finds cgroups to test. - Runs `perf stat` with `insn_per_cycle` metric and verifies that the metric is reported for each cgroup. - Dynamically pairs and verifies instructions and cycles counts to avoid false failures on idle cgroups. - Tests both standard mode and BPF counters mode (if supported). Assisted-by: Antigravity:gemini-3-flash Signed-off-by: Ian Rogers <irogers@google.com> --- tools/perf/tests/shell/stat_metrics_cgrp.sh | 200 ++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100755 tools/perf/tests/shell/stat_metrics_cgrp.sh diff --git a/tools/perf/tests/shell/stat_metrics_cgrp.sh b/tools/perf/tests/shell/stat_metrics_cgrp.sh new file mode 100755 index 000000000000..d4226ee0ae98 --- /dev/null +++ b/tools/perf/tests/shell/stat_metrics_cgrp.sh @@ -0,0 +1,200 @@ +#!/bin/bash +# perf stat metrics --for-each-cgroup test +# SPDX-License-Identifier: GPL-2.0 + +set -e + +test_cgroups= + +log_verbose() { + echo "$1" +} + +is_numeric_and_non_zero() { + local val="$1" + if [[ "${val}" =~ ^[0-9]+$ ]] && [ "${val}" -gt 0 ] + then + return 0 # True + fi + return 1 # False +} + +# skip if system-wide is not supported +check_system_wide() +{ + log_verbose "Checking system-wide..." + if ! perf stat -a --metrics=insn_per_cycle sleep 0.01 > /dev/null 2>&1 + then + log_verbose "Skipping: system-wide monitoring not supported" + exit 2 + fi +} + +# find two cgroups to measure +find_cgroups() +{ + log_verbose "Finding cgroups..." + # try usual systemd slices first + if [ -d /sys/fs/cgroup/system.slice ] && [ -d /sys/fs/cgroup/user.slice ] + then + test_cgroups="system.slice,user.slice" + log_verbose "Found cgroups: ${test_cgroups}" + return + fi + + # try root and self cgroups + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) + if [ -z "${find_cgroups_self_cgrp}" ] + then + # cgroup v2 doesn't specify perf_event + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) + fi + + if [ -z "${find_cgroups_self_cgrp}" ] + then + test_cgroups="/" + else + test_cgroups="/,${find_cgroups_self_cgrp}" + fi + log_verbose "Found cgroups: ${test_cgroups}" +} + +# Check if metric is reported for each cgroup +# $1: extra options (e.g. --bpf-counters) +check_metric_reported() +{ + local opts="$1" + local output + + log_verbose "Running check_metric_reported with opts '${opts}'..." + # Run perf stat + if ! output=$(perf stat -a ${opts} \ + --metrics=insn_per_cycle \ + --for-each-cgroup "${test_cgroups}" \ + -x, sleep 0.1 2>&1) + then + echo "FAIL: perf stat failed with exit code $?" + echo "Output: ${output}" + exit 1 + fi + + log_verbose "perf stat output:" + log_verbose "${output}" + + # Split test_cgroups by comma + IFS=',' read -r -a cgrps <<< "${test_cgroups}" + + for cgrp in "${cgrps[@]}"; do + # Find metric lines for this cgroup + # We use exact cgroup match with surrounding commas + local cgrp_lines + cgrp_lines=$(echo "${output}" | grep -F ",${cgrp}," | grep "insn_per_cycle" || true) + + if [ -z "${cgrp_lines}" ] + then + echo "FAIL: No metric lines found for cgroup '${cgrp}'" + exit 1 + fi + + # Parse each metric line + while read -r line; do + [ -z "${line}" ] && continue + + local val1 + val1=$(echo "${line}" | cut -d, -f1) + + local event_name + event_name=$(echo "${line}" | cut -d, -f3) + + local cycles_val="" + local inst_val="" + + if echo "${event_name}" | grep -q -i "cycles" + then + cycles_val="${val1}" + # Find corresponding instructions event + local inst_event_name + inst_event_name="${event_name/cpu-cycles/instructions}" + inst_event_name="${inst_event_name/cycles/instructions}" + + local inst_line + inst_line=$(echo "${output}" | \ + grep -F ",${cgrp}," | \ + grep -F "${inst_event_name}" || true) + inst_val=$(echo "${inst_line}" | cut -d, -f1) + elif echo "${event_name}" | grep -q -i "instructions" + then + inst_val="${val1}" + # Find corresponding cycles event (try cpu-cycles + # first, then cycles) + local cycles_event_name + cycles_event_name="${event_name/instructions/cpu-cycles}" + local cycles_line + cycles_line=$(echo "${output}" | \ + grep -F ",${cgrp}," | \ + grep -F "${cycles_event_name}" || true) + + if [ -z "${cycles_line}" ] + then + # Try "cycles" instead of "cpu-cycles" + cycles_event_name="${event_name/instructions/cycles}" + cycles_line=$(echo "${output}" | \ + grep -F ",${cgrp}," | \ + grep -F "${cycles_event_name}" || true) + fi + cycles_val=$(echo "${cycles_line}" | cut -d, -f1) + fi + + log_verbose "Cgroup '${cgrp}': event '${event_name}' \ +val '${cycles_val}', inst val '${inst_val}'" + + # Only enforce metric check if both cycles and + # instructions have non-zero numeric counts + if is_numeric_and_non_zero "${cycles_val}" && \ + is_numeric_and_non_zero "${inst_val}" + then + log_verbose "Enforcing metric check for cgroup '${cgrp}' \ +event '${event_name}'" + # Check for nan or nested in the metric value (7th field) + # Actually we can just check the whole line for simplicity + if echo "${line}" | grep -q -i -E ",nan,|,nested," + then + echo "FAIL: Invalid metric value (nan/nested) \ +for cgroup '${cgrp}'" + echo "Line: ${line}" + exit 1 + fi + # Check for empty metric value (2 consecutive + # commas before the unit) + if echo "${line}" | grep -q -E ",,[[:space:]]*[^,]*insn_per_cycle" + then + echo "FAIL: Empty metric value for cgroup '${cgrp}'" + echo "Line: ${line}" + exit 1 + fi + else + log_verbose "Skipping metric check for cgroup '${cgrp}' \ +event '${event_name}' (idle or not counted)" + fi + done <<< "${cgrp_lines}" + done + log_verbose "check_metric_reported passed for opts '${opts}'" +} + +check_system_wide +find_cgroups + +# Test 1: Without BPF counters +check_metric_reported "" + +# Test 2: With BPF counters (if supported) +log_verbose "Checking BPF support..." +if perf stat -a --bpf-counters --for-each-cgroup / true > /dev/null 2>&1 +then + log_verbose "BPF supported, running Test 2..." + check_metric_reported "--bpf-counters" +else + log_verbose "BPF not supported, skipping Test 2" +fi + +exit 0 -- 2.54.0.631.ge1b05301d1-goog ^ permalink raw reply related [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-05-19 17:09 UTC | newest] Thread overview: 20+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-18 22:22 [PATCH v1 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-18 22:22 ` [PATCH v1 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers 2026-05-18 22:22 ` [PATCH v1 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers 2026-05-18 22:43 ` sashiko-bot 2026-05-19 1:51 ` [PATCH v2 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-19 1:51 ` [PATCH v2 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers 2026-05-19 1:51 ` [PATCH v2 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers 2026-05-19 2:59 ` sashiko-bot 2026-05-19 5:01 ` [PATCH v3 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-19 5:01 ` [PATCH v3 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers 2026-05-19 5:39 ` sashiko-bot 2026-05-19 5:47 ` Namhyung Kim 2026-05-19 5:01 ` [PATCH v3 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers 2026-05-19 5:47 ` sashiko-bot 2026-05-19 5:54 ` Namhyung Kim 2026-05-19 15:13 ` Ian Rogers 2026-05-19 17:09 ` Namhyung Kim 2026-05-19 15:27 ` [PATCH v4 0/2] Fix cgroup metric association with BPF counters Ian Rogers 2026-05-19 15:27 ` [PATCH v4 1/2] perf stat: Propagate supported flag to follower cgroup BPF events Ian Rogers 2026-05-19 15:27 ` [PATCH v4 2/2] perf test: Add stat metrics --for-each-cgroup test Ian Rogers
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox