Re: [PATCH 7/9] perf test cs-etm: Test branch history on existing samples
James Clark <[email protected]>
| Newsgroups | org.kernel.vger.linux-perf-users,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-doc |
|---|---|
| Message-ID | <[email protected]> |
On 03/08/2026 10:06, Amir Ayupov wrote: > Add a CoreSight shell test for --itrace=L. Record timestamped ETM trace > with explicit -T sample timestamps and AUX pause/resume events, then > check that the pause samples carry both a multi-frame callchain and a > non-empty branch stack for each of the workload's two processes. > > Decode the same recording with L4 and L64 and reject any branch stack > deeper than the requested depth. > > The test skips when cs_etm is absent, when not run as root, or when the > recording turns out to lack virtual timestamps. It exercises the > timestamp-gated path and the requested-depth bound; it does not attempt > to verify that the attached history is correlated to the sample. > > Signed-off-by: Amir Ayupov <[email protected]> > --- > .../tests/shell/coresight/add_last_branch.sh | 175 ++++++++++++++++++ > 1 file changed, 175 insertions(+) > create mode 100755 tools/perf/tests/shell/coresight/add_last_branch.sh > > diff --git a/tools/perf/tests/shell/coresight/add_last_branch.sh b/tools/perf/tests/shell/coresight/add_last_branch.sh > new file mode 100755 > index 0000000000000..4654069ad651f > --- /dev/null > +++ b/tools/perf/tests/shell/coresight/add_last_branch.sh > @@ -0,0 +1,175 @@ > +#!/bin/bash -e > +# SPDX-License-Identifier: GPL-2.0 > +# CoreSight branch history on existing samples (exclusive) > + > +perf list pmu | grep -q 'cs_etm//' || exit 2 > + > +if [ "$(id -u)" != 0 ]; then > + echo "[Skip] No root permission" > + exit 2 > +fi Is this so you can use -C 0? It's not completely obvious what that has to do with the test. Can you not drop the -C option or use --per-thread mode with a simpler non-forking workload? I don't mind keeping it for some variety in the tests, but it should be documented. > + > +tmpdir=$(mktemp -d /tmp/perf-cs-add-last-branch.XXXXX) > + > +cleanup() > +{ > + rm -rf "$tmpdir" > + trap - EXIT TERM INT > +} > + > +# shellcheck disable=SC2317 # Called through trap. > +trap_cleanup() > +{ > + cleanup > + exit 1 > +} > +trap trap_cleanup EXIT TERM INT > + > +record_data() > +{ > + if perf record -T -o "$tmpdir/data" -C 0 \ > + -e cs_etm/aux-action=start-paused,timestamp/u \ Timestamp needs a value on newer kernels or Perf returns an error. But do you need to provide the option at all? It's on by default for per-CPU mode. > + -e cycles/aux-action=resume,period=550019/u \ > + -e cycles/aux-action=pause,period=100003,call-graph=fp/u -- \ > + taskset --cpu-list 0 perf test -w context_switch_loop 100000 \ The other Coresight tests use --workload-ctl to record less data and save some decode time. I think this test might benefit from it too. > + >/dev/null 2>"$tmpdir/stderr"; then > + return 0 > + fi > + > + echo "Failed to record ETM trace with AUX pause/resume" >&2 > + cat "$tmpdir/stderr" >&2 > + return 1 > +} > + > +decode() > +{ > + local size=$1 > + local output=$2 > + > + if perf script -i "$tmpdir/data" --itrace="L$size" \ > + -F comm,pid,tid,event,ip,brstack >"$output" \ > + 2>"$tmpdir/stderr"; then > + return 0 > + fi > + > + if grep -q "itrace=L requires virtual timestamped trace" \ > + "$tmpdir/stderr"; then > + echo "[Skip] Virtual CoreSight timestamps are not available" > + cleanup > + exit 2 > + fi > + > + cat "$tmpdir/stderr" >&2 > + return 1 > +} > + > +check_process_samples() > +{ > + local output=$1 > + local comm > + > + for comm in proc1 proc2; do > + awk -v comm="$comm" ' > + $1 == comm && /cycles\/aux-action=pause/ { > + in_sample = 1 > + next > + } > + !NF { > + in_sample = 0 > + next > + } > + in_sample && /0x[[:xdigit:]]+\/0x[[:xdigit:]]+\// { > + found = 1 > + } > + END { exit !found } > + ' "$output" || { > + echo "No pause-event branch stack found for $comm" >&2 > + return 1 > + } > + done > +} > + > +check_callchains() > +{ > + local output="$tmpdir/script-callchain" > + > + perf script -i "$tmpdir/data" -F comm,event,ip >"$output" 2>/dev/null > + > + awk ' > + /cycles\/aux-action=pause/ { > + in_sample = 1 > + frames = 0 > + next > + } > + !NF { > + if (in_sample && frames >= 2) > + found = 1 > + in_sample = 0 > + next > + } > + in_sample && /^[[:space:]]+[[:xdigit:]]+([[:space:]]|$)/ { > + frames++ > + } > + END { > + if (in_sample && frames >= 2) > + found = 1 > + exit !found > + } > + ' "$output" || { > + echo "No multi-frame pause-event callchain found" >&2 > + return 1 > + } Can you add some example output in the test saying what these awks are looking for. It failed for me but I wasn't sure why. I've attached my script-callchain file if that helps. > +} > + > +check_branch_stacks() > +{ > + local output=$1 > + local max_entries=$2 > + > + local ret > + > + if awk -v max="$max_entries" ' > + /0x[[:xdigit:]]+\/0x[[:xdigit:]]+\// { > + entries = 0 > + for (i = 1; i <= NF; i++) > + if ($i ~ /^0x[[:xdigit:]]+\/0x[[:xdigit:]]+\//) > + entries++ > + if (entries) > + found = 1 > + if (entries > max) { > + status = 2 > + exit > + } > + } > + END { > + if (status) > + exit status > + if (!found) > + exit 1 > + } > + ' "$output"; then > + return 0 > + else > + ret=$? > + fi > + > + case $ret in > + 1) echo "No ETM branch stacks found" >&2 ;; > + 2) echo "Branch stack exceeds requested L$max_entries depth" >&2 ;; > + esac > + return 1 > +} > + > +record_data > +check_callchains > + > +decode 4 "$tmpdir/script-l4" > +check_process_samples "$tmpdir/script-l4" > +check_branch_stacks "$tmpdir/script-l4" 4 > + > +decode 64 "$tmpdir/script-l64" > +check_process_samples "$tmpdir/script-l64" > +check_branch_stacks "$tmpdir/script-l64" 64 > + > +cleanup > +exit 0