Re: [PATCH v3] perf test: Fixes for check branch stack sampling
From: Ian Rogers
Date: Sun Aug 09 2026 - 01:21:25 EST
On Sat, Aug 8, 2026 at 9:16 PM Ian Rogers <irogers@xxxxxxxxxx> wrote:
>
> When filtering branch stack samples on user events they sample in user
> land but may have come from the kernel. Avoid the target address being
> a kernel address but allow the source to be the kernel.
>
> When filtering branch stack samples on kernel events they sample in
> kernel land but may have come from user land. Avoid the target being a
> user address but allow the source to be in user land.
>
> Increase the duration of the system call sampling test to make the
> likelihood of sampling a system call higher (increased from 1000 to
> 8000 loops - a number found through experimentation on an Intel
> Tigerlake laptop), also make the period of the event a prime number.
>
> Put unneeded perf record output into a temporary file so that the test
> output isn't cluttered. More clearly state which test is running and
> the pass, fail or skipped result of the test.
>
> These changes make the test on an Intel tigerlake laptop reliably pass
> rather than reliably fail.
>
> Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
Sorry, sent in error. This change is already merged.
Thanks,
Ian
> ---
> tools/perf/tests/shell/test_brstack.sh | 134 ++++++++++++++++---------
> 1 file changed, 84 insertions(+), 50 deletions(-)
>
> diff --git a/tools/perf/tests/shell/test_brstack.sh b/tools/perf/tests/shell/test_brstack.sh
> index 85233d435be6..025ed9d3d110 100755
> --- a/tools/perf/tests/shell/test_brstack.sh
> +++ b/tools/perf/tests/shell/test_brstack.sh
> @@ -40,7 +40,7 @@ is_arm64() {
>
> check_branches() {
> if ! tr -s ' ' '\n' < "$TMPDIR/perf.script" | grep -E -m1 -q "$1"; then
> - echo "Branches missing $1"
> + echo "ERROR: Branches missing $1"
> err=1
> fi
> }
> @@ -48,6 +48,8 @@ check_branches() {
> test_user_branches() {
> echo "Testing user branch stack sampling"
>
> + start_err=$err
> + err=0
> perf record -o "$TMPDIR/perf.data" --branch-filter any,save_type,u -- ${TESTPROG} > "$TMPDIR/record.txt" 2>&1
> perf script -i "$TMPDIR/perf.data" --fields brstacksym > "$TMPDIR/perf.script"
>
> @@ -73,59 +75,80 @@ test_user_branches() {
> perf script -i "$TMPDIR/perf.data" --fields brstack | \
> tr ' ' '\n' > "$TMPDIR/perf.script"
>
> - # There should be no kernel addresses with the u option, in either
> - # source or target addresses.
> - if grep -E -m1 "0x[89a-f][0-9a-f]{15}" $TMPDIR/perf.script; then
> - echo "ERROR: Kernel address found in user mode"
> + # There should be no kernel addresses in the target with the u option.
> + if grep -q -E -m1 "^0x[0-9a-f]{0,16}/0x[89a-f][0-9a-f]{15}/" $TMPDIR/perf.script; then
> + echo "Testing user branch stack sampling [Failed kernel address found in user mode]"
> err=1
> fi
> # some branch types are still not being tested:
> # IND COND_CALL COND_RET SYSRET SERROR NO_TX
> + if [ $err -eq 0 ]; then
> + echo "Testing user branch stack sampling [Passed]"
> + err=$start_err
> + else
> + echo "Testing user branch stack sampling [Failed]"
> + fi
> }
>
> test_trap_eret_branches() {
> echo "Testing trap & eret branches"
> +
> if ! is_arm64; then
> - echo "skip: not arm64"
> + echo "Testing trap & eret branches [Skipped not arm64]"
> + return
> + fi
> + start_err=$err
> + err=0
> + perf record -o $TMPDIR/perf.data --branch-filter any,save_type,u,k -- \
> + perf test -w traploop 1000 > "$TMPDIR/record.txt" 2>&1
> + perf script -i $TMPDIR/perf.data --fields brstacksym | \
> + tr ' ' '\n' > $TMPDIR/perf.script
> +
> + # BRBINF<n>.TYPE == TRAP are mapped to PERF_BR_IRQ by the BRBE driver
> + check_branches "^trap_bench\+[^ ]+/[^ ]/IRQ/"
> + check_branches "^[^ ]+/trap_bench\+[^ ]+/ERET/"
> + if [ $err -eq 0 ]; then
> + echo "Testing trap & eret branches [Passed]"
> + err=$start_err
> else
> - perf record -o $TMPDIR/perf.data --branch-filter any,save_type,u,k -- \
> - perf test -w traploop 1000
> - perf script -i $TMPDIR/perf.data --fields brstacksym | \
> - tr ' ' '\n' > $TMPDIR/perf.script
> -
> - # BRBINF<n>.TYPE == TRAP are mapped to PERF_BR_IRQ by the BRBE driver
> - check_branches "^trap_bench\+[^ ]+/[^ ]/IRQ/"
> - check_branches "^[^ ]+/trap_bench\+[^ ]+/ERET/"
> + echo "Testing trap & eret branches [Failed]"
> fi
> }
>
> test_kernel_branches() {
> - echo "Testing that k option only includes kernel source addresses"
> + echo "Testing kernel branch sampling"
>
> - if ! perf record --branch-filter any,k -o- -- true > /dev/null; then
> - echo "skip: not enough privileges"
> + if ! perf record --branch-filter any,k -o- -- true > "$TMPDIR/record.txt" 2>&1; then
> + echo "Testing that k option [Skipped not enough privileges]"
> + return
> + fi
> + start_err=$err
> + err=0
> + perf record -o $TMPDIR/perf.data --branch-filter any,k -- \
> + perf bench syscall basic --loop 1000 > "$TMPDIR/record.txt" 2>&1
> + perf script -i $TMPDIR/perf.data --fields brstack | \
> + tr ' ' '\n' > $TMPDIR/perf.script
> +
> + # Example of branch entries:
> + # "0xffffffff93bda241/0xffffffff93bda20f/M/-/-/..."
> + # Source addresses come first in user or kernel code. Next is the target
> + # address that must be in the kernel.
> +
> + # Look for source addresses with top bit set
> + if ! grep -q -E -m1 "^0x[89a-f][0-9a-f]{15}" $TMPDIR/perf.script; then
> + echo "Testing kernel branch sampling [Failed kernel branches missing]"
> + err=1
> + fi
> + # Look for no target addresses without top bit set
> + if grep -q -E -m1 "^0x[0-9a-f]{0,16}/0x[0-7][0-9a-f]{0,15}/" $TMPDIR/perf.script; then
> + echo "Testing kernel branch sampling [Failed user branches found]"
> + err=1
> + fi
> + if [ $err -eq 0 ]; then
> + echo "Testing kernel branch sampling [Passed]"
> + err=$start_err
> else
> - perf record -o $TMPDIR/perf.data --branch-filter any,k -- \
> - perf bench syscall basic --loop 1000
> - perf script -i $TMPDIR/perf.data --fields brstack | \
> - tr ' ' '\n' > $TMPDIR/perf.script
> -
> - # Example of branch entries:
> - # "0xffffffff93bda241/0xffffffff93bda20f/M/-/-/..."
> - # Source addresses come first and target address can be either
> - # userspace or kernel even with k option, as long as the source
> - # is in kernel.
> -
> - #Look for source addresses with top bit set
> - if ! grep -E -m1 "^0x[89a-f][0-9a-f]{15}" $TMPDIR/perf.script; then
> - echo "ERROR: Kernel branches missing"
> - err=1
> - fi
> - # Look for no source addresses without top bit set
> - if grep -E -m1 "^0x[0-7][0-9a-f]{0,15}" $TMPDIR/perf.script; then
> - echo "ERROR: User branches found with kernel filter"
> - err=1
> - fi
> + echo "Testing kernel branch sampling [Failed]"
> fi
> }
>
> @@ -136,14 +159,15 @@ test_filter() {
> test_filter_expect=$2
>
> echo "Testing branch stack filtering permutation ($test_filter_filter,$test_filter_expect)"
> - perf record -o "$TMPDIR/perf.data" --branch-filter "$test_filter_filter,save_type,u" -- ${TESTPROG} > "$TMPDIR/record.txt" 2>&1
> + perf record -o "$TMPDIR/perf.data" --branch-filter "$test_filter_filter,save_type,u" -- \
> + ${TESTPROG} > "$TMPDIR/record.txt" 2>&1
> perf script -i "$TMPDIR/perf.data" --fields brstack > "$TMPDIR/perf.script"
>
> # fail if we find any branch type that doesn't match any of the expected ones
> # also consider UNKNOWN branch types (-)
> if [ ! -s "$TMPDIR/perf.script" ]
> then
> - echo "Empty script output"
> + echo "Testing branch stack filtering [Failed empty script output]"
> err=1
> return
> fi
> @@ -154,26 +178,36 @@ test_filter() {
> > "$TMPDIR/perf.script-filtered" || true
> if [ -s "$TMPDIR/perf.script-filtered" ]
> then
> - echo "Unexpected branch filter in script output"
> + echo "Testing branch stack filtering [Failed unexpected branch filter]"
> cat "$TMPDIR/perf.script"
> err=1
> return
> fi
> + echo "Testing branch stack filtering [Passed]"
> }
>
> test_syscall() {
> echo "Testing syscalls"
> # skip if perf doesn't have enough privileges
> - if ! perf record --branch-filter any,k -o- -- true > /dev/null; then
> - echo "skip: not enough privileges"
> + if ! perf record --branch-filter any,k -o- -- true > "$TMPDIR/record.txt" 2>&1; then
> + echo "Testing syscalls [Skipped: not enough privileges]"
> + return
> + fi
> + start_err=$err
> + err=0
> + perf record -o $TMPDIR/perf.data --branch-filter \
> + any_call,save_type,u,k -c 10007 -- \
> + perf bench syscall basic --loop 8000 > "$TMPDIR/record.txt" 2>&1
> + perf script -i $TMPDIR/perf.data --fields brstacksym | \
> + tr ' ' '\n' > $TMPDIR/perf.script
> +
> + check_branches "getppid[^ ]*/SYSCALL/"
> +
> + if [ $err -eq 0 ]; then
> + echo "Testing syscalls [Passed]"
> + err=$start_err
> else
> - perf record -o $TMPDIR/perf.data --branch-filter \
> - any_call,save_type,u,k -c 10000 -- \
> - perf bench syscall basic --loop 1000
> - perf script -i $TMPDIR/perf.data --fields brstacksym | \
> - tr ' ' '\n' > $TMPDIR/perf.script
> -
> - check_branches "getppid[^ ]*/SYSCALL/"
> + echo "Testing syscalls [Failed]"
> fi
> }
> set -e
> --
> 2.53.0.1213.gd9a14994de-goog
>