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