1#!/bin/bash 2# Check branch stack sampling 3 4# SPDX-License-Identifier: GPL-2.0 5# German Gomez <german.gomez@arm.com>, 2022 6 7shelldir=$(dirname "$0") 8# shellcheck source=lib/perf_has_symbol.sh 9. "${shelldir}"/lib/perf_has_symbol.sh 10 11# skip the test if the hardware doesn't support branch stack sampling 12# and if the architecture doesn't support filter types: any,save_type,u 13if ! perf record -o- --no-buildid --branch-filter any,save_type,u -- true > /dev/null 2>&1 ; then 14 echo "skip: system doesn't support filter types: any,save_type,u" 15 exit 2 16fi 17 18skip_test_missing_symbol brstack_bench 19 20err=0 21TMPDIR=$(mktemp -d /tmp/__perf_test.program.XXXXX) 22TESTPROG="perf test -w brstack" 23 24cleanup() { 25 rm -rf $TMPDIR 26 trap - EXIT TERM INT 27} 28 29trap_cleanup() { 30 set +e 31 echo "Unexpected signal in ${FUNCNAME[1]}" 32 cleanup 33 exit 1 34} 35trap trap_cleanup EXIT TERM INT 36 37is_arm64() { 38 [ "$(uname -m)" = "aarch64" ]; 39} 40 41check_branches() { 42 if ! tr -s ' ' '\n' < "$TMPDIR/perf.script" | grep -E -m1 -q "$1"; then 43 echo "Branches missing $1" 44 err=1 45 fi 46} 47 48test_user_branches() { 49 echo "Testing user branch stack sampling" 50 51 perf record -o "$TMPDIR/perf.data" --branch-filter any,save_type,u -- ${TESTPROG} > "$TMPDIR/record.txt" 2>&1 52 perf script -i "$TMPDIR/perf.data" --fields brstacksym > "$TMPDIR/perf.script" 53 54 # example of branch entries: 55 # brstack_foo+0x14/brstack_bar+0x40/P/-/-/0/CALL 56 57 expected=( 58 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/IND_CALL/.*$" 59 "^brstack_foo\+[^ ]*/brstack_bar\+[^ ]*/CALL/.*$" 60 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/CALL/.*$" 61 "^brstack_bench\+[^ ]*/brstack_bar\+[^ ]*/CALL/.*$" 62 "^brstack_bar\+[^ ]*/brstack_foo\+[^ ]*/RET/.*$" 63 "^brstack_foo\+[^ ]*/brstack_bench\+[^ ]*/RET/.*$" 64 "^brstack_bench\+[^ ]*/brstack_bench\+[^ ]*/COND/.*$" 65 "^brstack\+[^ ]*/brstack\+[^ ]*/UNCOND/.*$" 66 ) 67 for x in "${expected[@]}" 68 do 69 check_branches "$x" 70 done 71 72 # Dump addresses only this time 73 perf script -i "$TMPDIR/perf.data" --fields brstack | \ 74 tr ' ' '\n' > "$TMPDIR/perf.script" 75 76 # There should be no kernel addresses with the u option, in either 77 # source or target addresses. 78 if grep -E -m1 "0x[89a-f][0-9a-f]{15}" $TMPDIR/perf.script; then 79 echo "ERROR: Kernel address found in user mode" 80 err=1 81 fi 82 # some branch types are still not being tested: 83 # IND COND_CALL COND_RET SYSRET SERROR NO_TX 84} 85 86test_trap_eret_branches() { 87 echo "Testing trap & eret branches" 88 if ! is_arm64; then 89 echo "skip: not arm64" 90 else 91 perf record -o $TMPDIR/perf.data --branch-filter any,save_type,u,k -- \ 92 perf test -w traploop 1000 93 perf script -i $TMPDIR/perf.data --fields brstacksym | \ 94 tr ' ' '\n' > $TMPDIR/perf.script 95 96 # BRBINF<n>.TYPE == TRAP are mapped to PERF_BR_IRQ by the BRBE driver 97 check_branches "^trap_bench\+[^ ]+/[^ ]/IRQ/" 98 check_branches "^[^ ]+/trap_bench\+[^ ]+/ERET/" 99 fi 100} 101 102test_kernel_branches() { 103 echo "Testing that k option only includes kernel source addresses" 104 105 if ! perf record --branch-filter any,k -o- -- true > /dev/null; then 106 echo "skip: not enough privileges" 107 else 108 perf record -o $TMPDIR/perf.data --branch-filter any,k -- \ 109 perf bench syscall basic --loop 1000 110 perf script -i $TMPDIR/perf.data --fields brstack | \ 111 tr ' ' '\n' > $TMPDIR/perf.script 112 113 # Example of branch entries: 114 # "0xffffffff93bda241/0xffffffff93bda20f/M/-/-/..." 115 # Source addresses come first and target address can be either 116 # userspace or kernel even with k option, as long as the source 117 # is in kernel. 118 119 #Look for source addresses with top bit set 120 if ! grep -E -m1 "^0x[89a-f][0-9a-f]{15}" $TMPDIR/perf.script; then 121 echo "ERROR: Kernel branches missing" 122 err=1 123 fi 124 # Look for no source addresses without top bit set 125 if grep -E -m1 "^0x[0-7][0-9a-f]{0,15}" $TMPDIR/perf.script; then 126 echo "ERROR: User branches found with kernel filter" 127 err=1 128 fi 129 fi 130} 131 132# first argument <arg0> is the argument passed to "--branch-stack <arg0>,save_type,u" 133# second argument are the expected branch types for the given filter 134test_filter() { 135 test_filter_filter=$1 136 test_filter_expect=$2 137 138 echo "Testing branch stack filtering permutation ($test_filter_filter,$test_filter_expect)" 139 perf record -o "$TMPDIR/perf.data" --branch-filter "$test_filter_filter,save_type,u" -- ${TESTPROG} > "$TMPDIR/record.txt" 2>&1 140 perf script -i "$TMPDIR/perf.data" --fields brstack > "$TMPDIR/perf.script" 141 142 # fail if we find any branch type that doesn't match any of the expected ones 143 # also consider UNKNOWN branch types (-) 144 if [ ! -s "$TMPDIR/perf.script" ] 145 then 146 echo "Empty script output" 147 err=1 148 return 149 fi 150 # Look for lines not matching test_filter_expect ignoring issues caused 151 # by empty output 152 tr -s ' ' '\n' < "$TMPDIR/perf.script" | grep '.' | \ 153 grep -E -vm1 "^[^ ]*/($test_filter_expect|-|( *))/.*$" \ 154 > "$TMPDIR/perf.script-filtered" || true 155 if [ -s "$TMPDIR/perf.script-filtered" ] 156 then 157 echo "Unexpected branch filter in script output" 158 cat "$TMPDIR/perf.script" 159 err=1 160 return 161 fi 162} 163 164test_syscall() { 165 echo "Testing syscalls" 166 # skip if perf doesn't have enough privileges 167 if ! perf record --branch-filter any,k -o- -- true > /dev/null; then 168 echo "skip: not enough privileges" 169 else 170 perf record -o $TMPDIR/perf.data --branch-filter \ 171 any_call,save_type,u,k -c 10000 -- \ 172 perf bench syscall basic --loop 1000 173 perf script -i $TMPDIR/perf.data --fields brstacksym | \ 174 tr ' ' '\n' > $TMPDIR/perf.script 175 176 check_branches "getppid[^ ]*/SYSCALL/" 177 fi 178} 179set -e 180 181test_user_branches 182test_syscall 183test_kernel_branches 184test_trap_eret_branches 185 186any_call="CALL|IND_CALL|COND_CALL|SYSCALL|IRQ" 187 188if is_arm64; then 189 any_call="$any_call|FAULT_DATA|FAULT_INST" 190fi 191 192test_filter "any_call" "$any_call" 193test_filter "call" "CALL|SYSCALL" 194test_filter "cond" "COND" 195test_filter "any_ret" "RET|COND_RET|SYSRET|ERET" 196 197test_filter "call,cond" "CALL|SYSCALL|COND" 198test_filter "any_call,cond" "$any_call|COND" 199test_filter "any_call,cond,any_ret" "$any_call|COND|RET|COND_RET" 200 201cleanup 202exit $err 203