1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# perf record tests 4 5set -e 6 7shelldir=$(dirname "$0") 8. "${shelldir}"/lib/perf_record.sh 9 10 11# shellcheck source=lib/waiting.sh 12. "${shelldir}"/lib/waiting.sh 13 14# shellcheck source=lib/perf_has_symbol.sh 15. "${shelldir}"/lib/perf_has_symbol.sh 16 17testsym="test_loop" 18testsym2="brstack" 19 20skip_test_missing_symbol ${testsym} 21skip_test_missing_symbol ${testsym2} 22 23err=0 24perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) 25script_output=$(mktemp /tmp/__perf_test.perf.data.XXXXX.script) 26testprog="perf test -w thloop" 27cpu_pmu_dir="/sys/bus/event_source/devices/cpu*" 28br_cntr_file="/caps/branch_counter_nr" 29br_cntr_output="branch stack counters" 30br_cntr_script_output="br_cntr: A" 31 32default_fd_limit=$(ulimit -Sn) 33# With option --threads=cpu the number of open file descriptors should be 34# equal to sum of: nmb_cpus * nmb_events (2+dummy), 35# nmb_threads for perf.data.n (equal to nmb_cpus) and 36# 2*nmb_cpus of pipes = 4*nmb_cpus (each pipe has 2 ends) 37# All together it needs 8*nmb_cpus file descriptors plus some are also used 38# outside of testing, thus raising the limit to 16*nmb_cpus 39min_fd_limit=$(($(getconf _NPROCESSORS_ONLN) * 16)) 40 41cleanup() { 42 rm -f "${perfdata}" 43 rm -f "${perfdata}".old 44 rm -f "${script_output}" 45 perf_record_cleanup 46 47 trap - EXIT TERM INT 48} 49 50trap_cleanup() { 51 echo "Unexpected signal in ${FUNCNAME[1]}" 52 cleanup 53 exit 1 54} 55trap trap_cleanup EXIT TERM INT 56 57check_per_thread() { 58 perf report -i "${perfdata}" -q | grep -q "${testsym}" 59} 60 61test_per_thread() { 62 echo "Basic --per-thread mode test" 63 local ret=0 64 perf_record_with_retry "${perfdata}" "check_per_thread" "perf test -w thloop" \ 65 --per-thread || ret=$? 66 if [ $ret -eq 2 ]; then 67 echo "Per-thread record [Skipped event not supported]" 68 return 69 elif [ $ret -eq 1 ]; then 70 echo "Per-thread record [Failed record or missing output]" 71 err=1 72 return 73 fi 74 75 # run the test program in background (for 30 seconds) 76 ${testprog} 30 & 77 TESTPID=$! 78 79 rm -f "${perfdata}" 80 81 wait_for_threads ${TESTPID} 2 82 perf record -p "${TESTPID}" --per-thread -o "${perfdata}" sleep 1 2> /dev/null 83 kill ${TESTPID} 84 85 if [ ! -e "${perfdata}" ] 86 then 87 echo "Per-thread record [Failed record -p]" 88 err=1 89 return 90 fi 91 if ! perf report -i "${perfdata}" -q | grep -q "${testsym}" 92 then 93 echo "Per-thread record [Failed -p missing output]" 94 err=1 95 return 96 fi 97 98 echo "Basic --per-thread mode test [Success]" 99} 100 101check_register_capture() { 102 perf script -F ip,sym,iregs -i "${perfdata}" 2>/dev/null | grep -q "DI:" 103} 104 105test_register_capture() { 106 echo "Register capture test" 107 if ! perf list pmu | grep -q 'br_inst_retired.near_call' 108 then 109 echo "Register capture test [Skipped missing event]" 110 return 111 fi 112 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' 113 then 114 echo "Register capture test [Skipped missing registers]" 115 return 116 fi 117 118 local ret=0 119 perf_record_with_retry "${perfdata}" "check_register_capture" "perf test -w thloop" \ 120 --intr-regs=di,r8,dx,cx -e br_inst_retired.near_call -c 1000 --per-thread || ret=$? 121 122 if [ $ret -ne 0 ]; then 123 echo "Register capture test [Failed missing output]" 124 err=1 125 return 126 fi 127 echo "Register capture test [Success]" 128} 129 130check_system_wide() { 131 perf report -i "${perfdata}" -q | grep -q "${testsym}" 132} 133 134test_system_wide() { 135 echo "Basic --system-wide mode test" 136 local ret=0 137 perf_record_with_retry "${perfdata}" "check_system_wide" "perf test -w thloop" \ 138 -aB --synth=no || ret=$? 139 if [ $ret -eq 2 ]; then 140 echo "System-wide record [Skipped not supported]" 141 return 142 elif [ $ret -eq 1 ]; then 143 echo "System-wide record [Failed missing output]" 144 err=1 145 return 146 fi 147 148 ret=0 149 perf_record_with_retry "${perfdata}" "check_system_wide" "perf test -w thloop" \ 150 -aB --synth=no -e cpu-clock,cs --threads=cpu || ret=$? 151 if [ $ret -ne 0 ]; then 152 echo "System-wide record [Failed record --threads option or missing output]" 153 err=1 154 return 155 fi 156 echo "Basic --system-wide mode test [Success]" 157} 158 159check_workload() { 160 perf report -i "${perfdata}" -q | grep -q "${testsym}" 161} 162 163test_workload() { 164 echo "Basic target workload test" 165 local ret=0 166 perf_record_with_retry "${perfdata}" "check_workload" "perf test -w thloop" || ret=$? 167 if [ $ret -ne 0 ]; then 168 echo "Workload record [Failed record or missing output]" 169 err=1 170 return 171 fi 172 173 ret=0 174 perf_record_with_retry "${perfdata}" "check_workload" "perf test -w thloop" \ 175 -e cpu-clock,cs --threads=package || ret=$? 176 if [ $ret -ne 0 ]; then 177 echo "Workload record [Failed record --threads option or missing output]" 178 err=1 179 return 180 fi 181 echo "Basic target workload test [Success]" 182} 183 184check_branch_counter() { 185 perf report -i "${perfdata}" -D -q 2>/dev/null | grep -q "$br_cntr_output" && \ 186 perf script -i "${perfdata}" -F +brstackinsn,+brcntr 2>/dev/null | \ 187 grep -q "$br_cntr_script_output" 188} 189 190test_branch_counter() { 191 echo "Branch counter test" 192 # Check if the branch counter feature is supported 193 for dir in $cpu_pmu_dir 194 do 195 if [ ! -e "$dir$br_cntr_file" ] 196 then 197 echo "branch counter feature not supported on all core PMUs ($dir) [Skipped]" 198 return 199 fi 200 done 201 local ret=0 202 perf_record_with_retry "${perfdata}" "check_branch_counter" "perf test -w thloop" \ 203 -e "{branches:p,instructions}" -j any,counter || ret=$? 204 if [ $ret -ne 0 ]; then 205 echo "Branch counter test [Failed record or missing output]" 206 err=1 207 return 208 fi 209 echo "Branch counter test [Success]" 210} 211 212check_cgroup() { 213 perf report -i "${perfdata}" -D 2>/dev/null | grep -q "CGROUP" && \ 214 perf script -i "${perfdata}" -F cgroup 2>/dev/null | grep -q -v "unknown" 215} 216 217test_cgroup() { 218 echo "Cgroup sampling test" 219 local ret=0 220 perf_record_with_retry "${perfdata}" "check_cgroup" "perf test -w thloop" \ 221 -aB --synth=cgroup --all-cgroups || ret=$? 222 if [ $ret -eq 2 ]; then 223 echo "Cgroup sampling [Skipped not supported]" 224 return 225 elif [ $ret -eq 1 ]; then 226 echo "Cgroup sampling [Failed missing output]" 227 err=1 228 return 229 fi 230 echo "Cgroup sampling test [Success]" 231} 232 233check_uid() { 234 perf report -i "${perfdata}" -q | grep -q "${testsym}" 235} 236 237test_uid() { 238 echo "Uid sampling test" 239 local ret=0 240 perf_record_with_retry "${perfdata}" "check_uid" "perf test -w thloop" \ 241 -aB --synth=no --uid "$(id -u)" || ret=$? 242 if [ $ret -eq 2 ]; then 243 local logfile="${PERF_RECORD_LOGS[${#PERF_RECORD_LOGS[@]}-1]}" 244 if grep -q -E "libbpf.*EPERM|Access to performance monitoring" "$logfile" || \ 245 grep -q -E "Permission denied|Failure to open any events" "$logfile" 246 then 247 echo "Uid sampling [Skipped permissions]" 248 return 249 else 250 echo "Uid sampling [Failed to record]" 251 err=1 252 return 253 fi 254 elif [ $ret -eq 1 ]; then 255 echo "Uid sampling [Failed missing output]" 256 err=1 257 return 258 fi 259 echo "Uid sampling test [Success]" 260} 261 262test_leader_sampling() { 263 echo "Basic leader sampling test" 264 events="{cycles,cycles}:Su" 265 [ "$(uname -m)" = "s390x" ] && { 266 [ ! -d /sys/devices/cpum_sf ] && { 267 echo "No CPUMF [Skipped record]" 268 return 269 } 270 events="{cpum_sf/SF_CYCLES_BASIC/,cycles}:Su" 271 perf record -o "${perfdata}" -e "$events" -- perf test -w brstack 2> /dev/null 272 # Perf grouping might be unsupported, depends on version. 273 [ "$?" -ne 0 ] && { 274 echo "Grouping not support [Skipped record]" 275 return 276 } 277 } 278 if ! perf record -o "${perfdata}" -e "$events" -- \ 279 perf test -w brstack 2> /dev/null 280 then 281 echo "Leader sampling [Failed record]" 282 err=1 283 return 284 fi 285 perf script -i "${perfdata}" | grep brstack > $script_output 286 # Check if the two instruction counts are equal in each record. 287 # However, the throttling code doesn't consider event grouping. During throttling, only the 288 # leader is stopped, causing the slave's counts significantly higher. To temporarily solve this, 289 # let's set the tolerance rate to 80%. 290 # TODO: Revert the code for tolerance once the throttling mechanism is fixed. 291 index=0 292 valid_counts=0 293 invalid_counts=0 294 tolerance_rate=0.8 295 while IFS= read -r line 296 do 297 cycles=$(echo $line | awk '{for(i=1;i<=NF;i++) if($i=="cycles:") print $(i-1)}') 298 if [ $(($index%2)) -ne 0 ] && [ ${cycles}x != ${prev_cycles}x ] 299 then 300 invalid_counts=$(($invalid_counts+1)) 301 else 302 valid_counts=$(($valid_counts+1)) 303 fi 304 index=$(($index+1)) 305 prev_cycles=$cycles 306 done < "${script_output}" 307 total_counts=$(bc <<< "$invalid_counts+$valid_counts") 308 if (( $(bc <<< "$total_counts <= 0") )) 309 then 310 echo "Leader sampling [No sample generated]" 311 err=1 312 return 313 fi 314 isok=$(bc <<< "scale=2; if (($invalid_counts/$total_counts) < (1-$tolerance_rate)) { 0 } else { 1 };") 315 if [ $isok -eq 1 ] 316 then 317 echo "Leader sampling [Failed inconsistent cycles count]" 318 err=1 319 else 320 echo "Basic leader sampling test [Success]" 321 fi 322} 323 324test_topdown_leader_sampling() { 325 echo "Topdown leader sampling test" 326 if ! perf stat -e "{slots,topdown-retiring}" true 2> /dev/null 327 then 328 echo "Topdown leader sampling [Skipped event parsing failed]" 329 return 330 fi 331 if ! perf record -o "${perfdata}" -e "{instructions,slots,topdown-retiring}:S" true 2> /dev/null 332 then 333 echo "Topdown leader sampling [Failed topdown events not reordered correctly]" 334 err=1 335 return 336 fi 337 echo "Topdown leader sampling test [Success]" 338} 339 340test_precise_max() { 341 local -i skipped=0 342 343 echo "precise_max attribute test" 344 # Just to make sure event cycles is supported for sampling 345 if perf record -o "${perfdata}" -e "cycles" true 2> /dev/null 346 then 347 if ! perf record -o "${perfdata}" -e "cycles:P" true 2> /dev/null 348 then 349 echo "precise_max attribute [Failed cycles:P event]" 350 err=1 351 return 352 fi 353 else 354 echo "precise_max attribute [Skipped no cycles:P event]" 355 ((skipped+=1)) 356 fi 357 # On s390 event instructions is not supported for perf record 358 if perf record -o "${perfdata}" -e "instructions" true 2> /dev/null 359 then 360 # On AMD, cycles and instructions events are treated differently 361 if ! perf record -o "${perfdata}" -e "instructions:P" true 2> /dev/null 362 then 363 echo "precise_max attribute [Failed instructions:P event]" 364 err=1 365 return 366 fi 367 else 368 echo "precise_max attribute [Skipped no instructions:P event]" 369 ((skipped+=1)) 370 fi 371 if [ $skipped -eq 2 ] 372 then 373 echo "precise_max attribute [Skipped no hardware events]" 374 else 375 echo "precise_max attribute test [Success]" 376 fi 377} 378 379test_callgraph() { 380 echo "Callgraph test" 381 382 case $(uname -m) 383 in s390x) 384 cmd_flags="--call-graph dwarf -e cpu-clock";; 385 *) 386 cmd_flags="-g";; 387 esac 388 389 if ! perf record -o "${perfdata}" $cmd_flags perf test -w brstack 390 then 391 echo "Callgraph test [Failed missing output]" 392 err=1 393 return 394 fi 395 396 if ! perf report -i "${perfdata}" 2>&1 | grep "${testsym2}" 397 then 398 echo "Callgraph test [Failed missing symbol]" 399 err=1 400 return 401 fi 402 403 echo "Callgraph test [Success]" 404} 405 406test_acr_sampling() { 407 events="{instructions/period=40000,acr_mask=0x2/u,cycles/period=20000,acr_mask=0x3/u}" 408 pebs_events="{instructions/period=40000,acr_mask=0x2/pu,cycles/period=20000,acr_mask=0x3/u}" 409 echo "Auto counter reload (ACR) sampling test" 410 if ! perf record -o "${perfdata}" -e "${events}" ${testprog} 2> /dev/null 411 then 412 echo "Auto counter reload sampling [Skipped not supported]" 413 return 414 fi 415 if ! perf script -i "${perfdata}" -F event | grep -q "instructions" 416 then 417 echo "Auto counter reload sampling [Failed missing instructions event]" 418 err=1 419 return 420 fi 421 if perf script -i "${perfdata}" -F event | grep -q "cycles" 422 then 423 echo "Auto counter reload sampling [Failed cycles event shouldn't be sampled]" 424 err=1 425 return 426 fi 427 if ! perf record -o "${perfdata}" -e "${pebs_events}" ${testprog} 2> /dev/null 428 then 429 echo "Auto counter reload PEBS sampling [Skipped not supported]" 430 echo "Auto counter reload sampling [Success]" 431 return 432 fi 433 if ! perf script -i "${perfdata}" -F event | grep -q "instructions" 434 then 435 echo "Auto counter reload PEBS sampling [Failed missing instructions event]" 436 err=1 437 return 438 fi 439 if perf script -i "${perfdata}" -F event | grep -q "cycles" 440 then 441 echo "Auto counter reload PEBS sampling [Failed cycles event shouldn't be sampled]" 442 err=1 443 return 444 fi 445 echo "Auto counter reload sampling [Success]" 446} 447 448test_ratio_to_prev() { 449 echo "ratio-to-prev test" 450 if ! perf record -o /dev/null -e "{instructions, cycles/period=100000,ratio-to-prev=0.5/}" \ 451 true 2> /dev/null 452 then 453 echo "ratio-to-prev [Skipped not supported]" 454 return 455 fi 456 if ! perf record -o /dev/null -e "instructions, cycles/period=100000,ratio-to-prev=0.5/" \ 457 true |& grep -q 'Invalid use of ratio-to-prev term without preceding element in group' 458 then 459 echo "ratio-to-prev test [Failed elements must be in same group]" 460 err=1 461 return 462 fi 463 if ! perf record -o /dev/null -e "{instructions,dummy,cycles/period=100000,ratio-to-prev=0.5/}" \ 464 true |& grep -q 'must have same PMU' 465 then 466 echo "ratio-to-prev test [Failed elements must have same PMU]" 467 err=1 468 return 469 fi 470 if ! perf record -o /dev/null -e "{instructions,cycles/ratio-to-prev=0.5/}" \ 471 true |& grep -q 'Event period term or count (-c) must be set when using ratio-to-prev term.' 472 then 473 echo "ratio-to-prev test [Failed period must be set]" 474 err=1 475 return 476 fi 477 if ! perf record -o /dev/null -e "{cycles/ratio-to-prev=0.5/}" \ 478 true |& grep -q 'Invalid use of ratio-to-prev term without preceding element in group' 479 then 480 echo "ratio-to-prev test [Failed need 2+ events]" 481 err=1 482 return 483 fi 484 echo "Basic ratio-to-prev record test [Success]" 485} 486 487# raise the limit of file descriptors to minimum 488if [[ $default_fd_limit -lt $min_fd_limit ]]; then 489 ulimit -Sn $min_fd_limit 490fi 491 492test_per_thread 493test_register_capture 494test_system_wide 495test_workload 496test_branch_counter 497test_cgroup 498test_uid 499test_leader_sampling 500test_topdown_leader_sampling 501test_precise_max 502test_callgraph 503test_acr_sampling 504test_ratio_to_prev 505 506# restore the default value 507ulimit -Sn $default_fd_limit 508 509cleanup 510exit $err 511