1#!/bin/bash 2# perf record tests (exclusive) 3# SPDX-License-Identifier: GPL-2.0 4 5set -e 6 7shelldir=$(dirname "$0") 8# shellcheck source=lib/waiting.sh 9. "${shelldir}"/lib/waiting.sh 10 11# shellcheck source=lib/perf_has_symbol.sh 12. "${shelldir}"/lib/perf_has_symbol.sh 13 14testsym="test_loop" 15 16skip_test_missing_symbol ${testsym} 17 18err=0 19perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) 20script_output=$(mktemp /tmp/__perf_test.perf.data.XXXXX.script) 21testprog="perf test -w thloop" 22cpu_pmu_dir="/sys/bus/event_source/devices/cpu*" 23br_cntr_file="/caps/branch_counter_nr" 24br_cntr_output="branch stack counters" 25br_cntr_script_output="br_cntr: A" 26 27default_fd_limit=$(ulimit -Sn) 28# With option --threads=cpu the number of open file descriptors should be 29# equal to sum of: nmb_cpus * nmb_events (2+dummy), 30# nmb_threads for perf.data.n (equal to nmb_cpus) and 31# 2*nmb_cpus of pipes = 4*nmb_cpus (each pipe has 2 ends) 32# All together it needs 8*nmb_cpus file descriptors plus some are also used 33# outside of testing, thus raising the limit to 16*nmb_cpus 34min_fd_limit=$(($(getconf _NPROCESSORS_ONLN) * 16)) 35 36cleanup() { 37 rm -f "${perfdata}" 38 rm -f "${perfdata}".old 39 rm -f "${script_output}" 40 41 trap - EXIT TERM INT 42} 43 44trap_cleanup() { 45 echo "Unexpected signal in ${FUNCNAME[1]}" 46 cleanup 47 exit 1 48} 49trap trap_cleanup EXIT TERM INT 50 51test_per_thread() { 52 echo "Basic --per-thread mode test" 53 if ! perf record -o /dev/null --quiet ${testprog} 2> /dev/null 54 then 55 echo "Per-thread record [Skipped event not supported]" 56 return 57 fi 58 if ! perf record --per-thread -o "${perfdata}" ${testprog} 2> /dev/null 59 then 60 echo "Per-thread record [Failed record]" 61 err=1 62 return 63 fi 64 if ! perf report -i "${perfdata}" -q | grep -q "${testsym}" 65 then 66 echo "Per-thread record [Failed missing output]" 67 err=1 68 return 69 fi 70 71 # run the test program in background (for 30 seconds) 72 ${testprog} 30 & 73 TESTPID=$! 74 75 rm -f "${perfdata}" 76 77 wait_for_threads ${TESTPID} 2 78 perf record -p "${TESTPID}" --per-thread -o "${perfdata}" sleep 1 2> /dev/null 79 kill ${TESTPID} 80 81 if [ ! -e "${perfdata}" ] 82 then 83 echo "Per-thread record [Failed record -p]" 84 err=1 85 return 86 fi 87 if ! perf report -i "${perfdata}" -q | grep -q "${testsym}" 88 then 89 echo "Per-thread record [Failed -p missing output]" 90 err=1 91 return 92 fi 93 94 echo "Basic --per-thread mode test [Success]" 95} 96 97test_register_capture() { 98 echo "Register capture test" 99 if ! perf list pmu | grep -q 'br_inst_retired.near_call' 100 then 101 echo "Register capture test [Skipped missing event]" 102 return 103 fi 104 if ! perf record --intr-regs=\? 2>&1 | grep -q 'available registers: AX BX CX DX SI DI BP SP IP FLAGS CS SS R8 R9 R10 R11 R12 R13 R14 R15' 105 then 106 echo "Register capture test [Skipped missing registers]" 107 return 108 fi 109 if ! perf record -o - --intr-regs=di,r8,dx,cx -e br_inst_retired.near_call \ 110 -c 1000 --per-thread ${testprog} 2> /dev/null \ 111 | perf script -F ip,sym,iregs -i - 2> /dev/null \ 112 | grep -q "DI:" 113 then 114 echo "Register capture test [Failed missing output]" 115 err=1 116 return 117 fi 118 echo "Register capture test [Success]" 119} 120 121test_system_wide() { 122 echo "Basic --system-wide mode test" 123 if ! perf record -aB --synth=no -o "${perfdata}" ${testprog} 2> /dev/null 124 then 125 echo "System-wide record [Skipped not supported]" 126 return 127 fi 128 if ! perf report -i "${perfdata}" -q | grep -q "${testsym}" 129 then 130 echo "System-wide record [Failed missing output]" 131 err=1 132 return 133 fi 134 if ! perf record -aB --synth=no -e cpu-clock,cs --threads=cpu \ 135 -o "${perfdata}" ${testprog} 2> /dev/null 136 then 137 echo "System-wide record [Failed record --threads option]" 138 err=1 139 return 140 fi 141 if ! perf report -i "${perfdata}" -q | grep -q "${testsym}" 142 then 143 echo "System-wide record [Failed --threads missing output]" 144 err=1 145 return 146 fi 147 echo "Basic --system-wide mode test [Success]" 148} 149 150test_workload() { 151 echo "Basic target workload test" 152 if ! perf record -o "${perfdata}" ${testprog} 2> /dev/null 153 then 154 echo "Workload record [Failed record]" 155 err=1 156 return 157 fi 158 if ! perf report -i "${perfdata}" -q | grep -q "${testsym}" 159 then 160 echo "Workload record [Failed missing output]" 161 err=1 162 return 163 fi 164 if ! perf record -e cpu-clock,cs --threads=package \ 165 -o "${perfdata}" ${testprog} 2> /dev/null 166 then 167 echo "Workload record [Failed record --threads option]" 168 err=1 169 return 170 fi 171 if ! perf report -i "${perfdata}" -q | grep -q "${testsym}" 172 then 173 echo "Workload record [Failed --threads missing output]" 174 err=1 175 return 176 fi 177 echo "Basic target workload test [Success]" 178} 179 180test_branch_counter() { 181 echo "Branch counter test" 182 # Check if the branch counter feature is supported 183 for dir in $cpu_pmu_dir 184 do 185 if [ ! -e "$dir$br_cntr_file" ] 186 then 187 echo "branch counter feature not supported on all core PMUs ($dir) [Skipped]" 188 return 189 fi 190 done 191 if ! perf record -o "${perfdata}" -e "{branches:p,instructions}" -j any,counter ${testprog} 2> /dev/null 192 then 193 echo "Branch counter record test [Failed record]" 194 err=1 195 return 196 fi 197 if ! perf report -i "${perfdata}" -D -q | grep -q "$br_cntr_output" 198 then 199 echo "Branch counter report test [Failed missing output]" 200 err=1 201 return 202 fi 203 if ! perf script -i "${perfdata}" -F +brstackinsn,+brcntr | grep -q "$br_cntr_script_output" 204 then 205 echo " Branch counter script test [Failed missing output]" 206 err=1 207 return 208 fi 209 echo "Branch counter test [Success]" 210} 211 212test_cgroup() { 213 echo "Cgroup sampling test" 214 if ! perf record -aB --synth=cgroup --all-cgroups -o "${perfdata}" ${testprog} 2> /dev/null 215 then 216 echo "Cgroup sampling [Skipped not supported]" 217 return 218 fi 219 if ! perf report -i "${perfdata}" -D | grep -q "CGROUP" 220 then 221 echo "Cgroup sampling [Failed missing output]" 222 err=1 223 return 224 fi 225 if ! perf script -i "${perfdata}" -F cgroup | grep -q -v "unknown" 226 then 227 echo "Cgroup sampling [Failed cannot resolve cgroup names]" 228 err=1 229 return 230 fi 231 echo "Cgroup sampling test [Success]" 232} 233 234test_leader_sampling() { 235 echo "Basic leader sampling test" 236 if ! perf record -o "${perfdata}" -e "{cycles,cycles}:Su" -- \ 237 perf test -w brstack 2> /dev/null 238 then 239 echo "Leader sampling [Failed record]" 240 err=1 241 return 242 fi 243 perf script -i "${perfdata}" | grep brstack > $script_output 244 # Check if the two instruction counts are equal in each record. 245 # However, the throttling code doesn't consider event grouping. During throttling, only the 246 # leader is stopped, causing the slave's counts significantly higher. To temporarily solve this, 247 # let's set the tolerance rate to 80%. 248 # TODO: Revert the code for tolerance once the throttling mechanism is fixed. 249 index=0 250 valid_counts=0 251 invalid_counts=0 252 tolerance_rate=0.8 253 while IFS= read -r line 254 do 255 cycles=$(echo $line | awk '{for(i=1;i<=NF;i++) if($i=="cycles:") print $(i-1)}') 256 if [ $(($index%2)) -ne 0 ] && [ ${cycles}x != ${prev_cycles}x ] 257 then 258 invalid_counts=$(($invalid_counts+1)) 259 else 260 valid_counts=$(($valid_counts+1)) 261 fi 262 index=$(($index+1)) 263 prev_cycles=$cycles 264 done < "${script_output}" 265 total_counts=$(bc <<< "$invalid_counts+$valid_counts") 266 if (( $(bc <<< "$total_counts <= 0") )) 267 then 268 echo "Leader sampling [No sample generated]" 269 err=1 270 return 271 fi 272 isok=$(bc <<< "scale=2; if (($invalid_counts/$total_counts) < (1-$tolerance_rate)) { 0 } else { 1 };") 273 if [ $isok -eq 1 ] 274 then 275 echo "Leader sampling [Failed inconsistent cycles count]" 276 err=1 277 else 278 echo "Basic leader sampling test [Success]" 279 fi 280} 281 282test_topdown_leader_sampling() { 283 echo "Topdown leader sampling test" 284 if ! perf stat -e "{slots,topdown-retiring}" true 2> /dev/null 285 then 286 echo "Topdown leader sampling [Skipped event parsing failed]" 287 return 288 fi 289 if ! perf record -o "${perfdata}" -e "{instructions,slots,topdown-retiring}:S" true 2> /dev/null 290 then 291 echo "Topdown leader sampling [Failed topdown events not reordered correctly]" 292 err=1 293 return 294 fi 295 echo "Topdown leader sampling test [Success]" 296} 297 298test_precise_max() { 299 local -i skipped=0 300 301 echo "precise_max attribute test" 302 # Just to make sure event cycles is supported for sampling 303 if perf record -o "${perfdata}" -e "cycles" true 2> /dev/null 304 then 305 if ! perf record -o "${perfdata}" -e "cycles:P" true 2> /dev/null 306 then 307 echo "precise_max attribute [Failed cycles:P event]" 308 err=1 309 return 310 fi 311 else 312 echo "precise_max attribute [Skipped no cycles:P event]" 313 ((skipped+=1)) 314 fi 315 # On s390 event instructions is not supported for perf record 316 if perf record -o "${perfdata}" -e "instructions" true 2> /dev/null 317 then 318 # On AMD, cycles and instructions events are treated differently 319 if ! perf record -o "${perfdata}" -e "instructions:P" true 2> /dev/null 320 then 321 echo "precise_max attribute [Failed instructions:P event]" 322 err=1 323 return 324 fi 325 else 326 echo "precise_max attribute [Skipped no instructions:P event]" 327 ((skipped+=1)) 328 fi 329 if [ $skipped -eq 2 ] 330 then 331 echo "precise_max attribute [Skipped no hardware events]" 332 else 333 echo "precise_max attribute test [Success]" 334 fi 335} 336 337# raise the limit of file descriptors to minimum 338if [[ $default_fd_limit -lt $min_fd_limit ]]; then 339 ulimit -Sn $min_fd_limit 340fi 341 342test_per_thread 343test_register_capture 344test_system_wide 345test_workload 346test_branch_counter 347test_cgroup 348test_leader_sampling 349test_topdown_leader_sampling 350test_precise_max 351 352# restore the default value 353ulimit -Sn $default_fd_limit 354 355cleanup 356exit $err 357