1#!/bin/bash 2# perf stat tests 3# SPDX-License-Identifier: GPL-2.0 4 5set -e 6 7err=0 8stat_output=$(mktemp /tmp/perf-stat-test-output.XXXXX) 9 10cleanup() { 11 rm -f "${stat_output}" 12 trap - EXIT TERM INT 13} 14 15trap_cleanup() { 16 echo "Unexpected signal in ${FUNCNAME[1]}" 17 cleanup 18 exit 1 19} 20 21trap trap_cleanup EXIT TERM INT 22 23test_default_stat() { 24 echo "Basic stat command test" 25 if ! perf stat true 2>&1 | grep -E -q "Performance counter stats for 'true':" 26 then 27 echo "Basic stat command test [Failed]" 28 err=1 29 return 30 fi 31 echo "Basic stat command test [Success]" 32} 33 34test_null_stat() { 35 echo "Null stat command test" 36 if ! perf stat --null true 2>&1 | grep -E -q "Performance counter stats for 'true':" 37 then 38 echo "Null stat command test [Failed]" 39 err=1 40 return 41 fi 42 echo "Null stat command test [Success]" 43} 44 45find_offline_cpu() { 46 for i in $(seq 1 4096) 47 do 48 if [[ ! -f /sys/devices/system/cpu/cpu$i/online || \ 49 $(cat /sys/devices/system/cpu/cpu$i/online) == "0" ]] 50 then 51 echo $i 52 return 53 fi 54 done 55 echo "Failed to find offline CPU" 56 exit 1 57} 58 59test_offline_cpu_stat() { 60 cpu=$(find_offline_cpu) 61 echo "Offline CPU stat command test (cpu $cpu)" 62 if ! perf stat "-C$cpu" -e cycles true 2>&1 | grep -E -q "No supported events found." 63 then 64 echo "Offline CPU stat command test [Failed]" 65 err=1 66 return 67 fi 68 echo "Offline CPU stat command test [Success]" 69} 70 71test_stat_record_report() { 72 echo "stat record and report test" 73 if ! perf stat record -e task-clock -o - true | perf stat report -i - 2>&1 | \ 74 grep -E -q "Performance counter stats for 'pipe':" 75 then 76 echo "stat record and report test [Failed]" 77 err=1 78 return 79 fi 80 echo "stat record and report test [Success]" 81} 82 83test_stat_record_script() { 84 echo "stat record and script test" 85 if ! perf stat record -e task-clock -o - true | perf script -i - 2>&1 | \ 86 grep -E -q "CPU[[:space:]]+THREAD[[:space:]]+VAL[[:space:]]+ENA[[:space:]]+RUN[[:space:]]+TIME[[:space:]]+EVENT" 87 then 88 echo "stat record and script test [Failed]" 89 err=1 90 return 91 fi 92 echo "stat record and script test [Success]" 93} 94 95test_stat_repeat_weak_groups() { 96 echo "stat repeat weak groups test" 97 if ! perf stat -e '{cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles}' \ 98 true 2>&1 | grep -q 'seconds time elapsed' 99 then 100 echo "stat repeat weak groups test [Skipped event parsing failed]" 101 return 102 fi 103 if ! perf stat -r2 -e '{cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles}:W' \ 104 true > /dev/null 2>&1 105 then 106 echo "stat repeat weak groups test [Failed]" 107 err=1 108 return 109 fi 110 echo "stat repeat weak groups test [Success]" 111} 112 113test_topdown_groups() { 114 # Topdown events must be grouped with the slots event first. Test that 115 # parse-events reorders this. 116 echo "Topdown event group test" 117 if ! perf stat -e '{slots,topdown-retiring}' true > /dev/null 2>&1 118 then 119 echo "Topdown event group test [Skipped event parsing failed]" 120 return 121 fi 122 td_err=0 123 do_topdown_group_test() { 124 events=$1 125 failure=$2 126 if perf stat -e "$events" true 2>&1 | grep -E -q "<not supported>" 127 then 128 echo "Topdown event group test [Failed $failure for '$events']" 129 td_err=1 130 return 131 fi 132 } 133 do_topdown_group_test "{slots,topdown-retiring}" "events not supported" 134 do_topdown_group_test "{instructions,r400,r8000}" "raw format slots not reordered first" 135 filler_events=("instructions" "cycles" 136 "context-switches" "faults") 137 for ((i = 0; i < ${#filler_events[@]}; i+=2)) 138 do 139 filler1=${filler_events[i]} 140 filler2=${filler_events[i+1]} 141 do_topdown_group_test "$filler1,topdown-retiring,slots" \ 142 "slots not reordered first in no-group case" 143 do_topdown_group_test "slots,$filler1,topdown-retiring" \ 144 "topdown metrics event not reordered in no-group case" 145 do_topdown_group_test "{$filler1,topdown-retiring,slots}" \ 146 "slots not reordered first in single group case" 147 do_topdown_group_test "{$filler1,slots},topdown-retiring" \ 148 "topdown metrics event not move into slots group" 149 do_topdown_group_test "topdown-retiring,{$filler1,slots}" \ 150 "topdown metrics event not move into slots group last" 151 do_topdown_group_test "{$filler1,slots},{topdown-retiring}" \ 152 "topdown metrics group not merge into slots group" 153 do_topdown_group_test "{topdown-retiring},{$filler1,slots}" \ 154 "topdown metrics group not merge into slots group last" 155 do_topdown_group_test "{$filler1,slots},$filler2,topdown-retiring" \ 156 "non-adjacent topdown metrics group not move into slots group" 157 do_topdown_group_test "$filler2,topdown-retiring,{$filler1,slots}" \ 158 "non-adjacent topdown metrics group not move into slots group last" 159 do_topdown_group_test "{$filler1,slots},{$filler2,topdown-retiring}" \ 160 "metrics group not merge into slots group" 161 do_topdown_group_test "{$filler1,topdown-retiring},{$filler2,slots}" \ 162 "metrics group not merge into slots group last" 163 done 164 if test "$td_err" -eq 0 165 then 166 echo "Topdown event group test [Success]" 167 else 168 err="$td_err" 169 fi 170} 171 172test_topdown_weak_groups() { 173 # Weak groups break if the perf_event_open of multiple grouped events 174 # fails. Breaking a topdown group causes the events to fail. Test a very large 175 # grouping to see that the topdown events aren't broken out. 176 echo "Topdown weak groups test" 177 ok_grouping="{slots,topdown-bad-spec,topdown-be-bound,topdown-fe-bound,topdown-retiring},branch-instructions,branch-misses,bus-cycles,cache-misses,cache-references,cpu-cycles,instructions,mem-loads,mem-stores,ref-cycles,cache-misses,cache-references" 178 if ! perf stat --no-merge -e "$ok_grouping" true > /dev/null 2>&1 179 then 180 echo "Topdown weak groups test [Skipped event parsing failed]" 181 return 182 fi 183 group_needs_break="{slots,topdown-bad-spec,topdown-be-bound,topdown-fe-bound,topdown-retiring,branch-instructions,branch-misses,bus-cycles,cache-misses,cache-references,cpu-cycles,instructions,mem-loads,mem-stores,ref-cycles,cache-misses,cache-references}:W" 184 if perf stat --no-merge -e "$group_needs_break" true 2>&1 | grep -E -q "<not supported>" 185 then 186 echo "Topdown weak groups test [Failed events not supported]" 187 err=1 188 return 189 fi 190 echo "Topdown weak groups test [Success]" 191} 192 193test_cputype() { 194 # Test --cputype argument. 195 echo "cputype test" 196 197 # Bogus PMU should fail. 198 if perf stat --cputype="123" -e instructions true > /dev/null 2>&1 199 then 200 echo "cputype test [Bogus PMU didn't fail]" 201 err=1 202 return 203 fi 204 205 # Find a known PMU for cputype. 206 pmu="" 207 devs="/sys/bus/event_source/devices" 208 for i in $devs/cpu $devs/cpu_atom $devs/armv8_pmuv3_0 $devs/armv8_cortex_* 209 do 210 i_base=$(basename "$i") 211 if test -d "$i" 212 then 213 pmu="$i_base" 214 break 215 fi 216 if perf stat -e "$i_base/instructions/" true > /dev/null 2>&1 217 then 218 pmu="$i_base" 219 break 220 fi 221 done 222 if test "x$pmu" = "x" 223 then 224 echo "cputype test [Skipped known PMU not found]" 225 return 226 fi 227 228 # Test running with cputype produces output. 229 if ! perf stat --cputype="$pmu" -e instructions true 2>&1 | grep -E -q "instructions" 230 then 231 echo "cputype test [Failed count missed with given filter]" 232 err=1 233 return 234 fi 235 echo "cputype test [Success]" 236} 237 238test_hybrid() { 239 # Test the default stat command on hybrid devices opens one cycles event for 240 # each CPU type. 241 echo "hybrid test" 242 243 # Count the number of core PMUs, assume minimum of 1 244 pmus=$(ls /sys/bus/event_source/devices/*/cpus 2>/dev/null | wc -l) 245 if [ "$pmus" -lt 1 ] 246 then 247 pmus=1 248 fi 249 250 # Run default Perf stat 251 cycles_events=$(perf stat -a -- sleep 0.1 2>&1 | grep -E "/cpu-cycles/[uH]*| cpu-cycles[:uH]* " | wc -l) 252 253 # The expectation is that default output will have a cycles events on each 254 # hybrid PMU. In situations with no cycles PMU events, like virtualized, this 255 # can fall back to task-clock and so the end count may be 0. Fail if neither 256 # condition holds. 257 if [ "$pmus" -ne "$cycles_events" ] && [ "0" -ne "$cycles_events" ] 258 then 259 echo "hybrid test [Found $pmus PMUs but $cycles_events cycles events. Failed]" 260 err=1 261 return 262 fi 263 echo "hybrid test [Success]" 264} 265 266test_stat_cpu() { 267 echo "stat -C <cpu> test" 268 # Test the full online CPU list (ranges and lists) 269 online_cpus=$(cat /sys/devices/system/cpu/online) 270 if ! perf stat -C "$online_cpus" -a true > "${stat_output}" 2>&1 271 then 272 echo "stat -C <cpu> test [Failed - command failed for cpus $online_cpus]" 273 cat "${stat_output}" 274 err=1 275 return 276 fi 277 278 if ! grep -E -q "Performance counter stats for" "${stat_output}" 279 then 280 echo "stat -C <cpu> test [Failed - missing output for cpus $online_cpus]" 281 cat "${stat_output}" 282 err=1 283 return 284 fi 285 286 # Test each individual online CPU 287 for cpu_dir in /sys/devices/system/cpu/cpu[0-9]*; do 288 cpu=${cpu_dir##*/cpu} 289 # Check if online 290 if [ -f "$cpu_dir/online" ] && [ "$(cat "$cpu_dir/online")" -eq 0 ] 291 then 292 continue 293 fi 294 295 if ! perf stat -C "$cpu" -a true > "${stat_output}" 2>&1 296 then 297 echo "stat -C <cpu> test [Failed - command failed for cpu $cpu]" 298 cat "${stat_output}" 299 err=1 300 return 301 fi 302 if ! grep -E -q "Performance counter stats for" "${stat_output}" 303 then 304 echo "stat -C <cpu> test [Failed - missing output for cpu $cpu]" 305 cat "${stat_output}" 306 err=1 307 return 308 fi 309 done 310 311 # Test synthetic list and range if cpu0 and cpu1 are online 312 c0_online=0 313 c1_online=0 314 if [ -d "/sys/devices/system/cpu/cpu0" ] 315 then 316 if [ ! -f "/sys/devices/system/cpu/cpu0/online" ] || [ "$(cat /sys/devices/system/cpu/cpu0/online)" -eq 1 ] 317 then 318 c0_online=1 319 fi 320 fi 321 if [ -d "/sys/devices/system/cpu/cpu1" ] 322 then 323 if [ ! -f "/sys/devices/system/cpu/cpu1/online" ] || [ "$(cat /sys/devices/system/cpu/cpu1/online)" -eq 1 ] 324 then 325 c1_online=1 326 fi 327 fi 328 329 if [ $c0_online -eq 1 ] && [ $c1_online -eq 1 ] 330 then 331 # Test list "0,1" 332 if ! perf stat -C "0,1" -a true > "${stat_output}" 2>&1 333 then 334 echo "stat -C <cpu> test [Failed - command failed for cpus 0,1]" 335 cat "${stat_output}" 336 err=1 337 return 338 fi 339 if ! grep -E -q "Performance counter stats for" "${stat_output}" 340 then 341 echo "stat -C <cpu> test [Failed - missing output for cpus 0,1]" 342 cat "${stat_output}" 343 err=1 344 return 345 fi 346 347 # Test range "0-1" 348 if ! perf stat -C "0-1" -a true > "${stat_output}" 2>&1 349 then 350 echo "stat -C <cpu> test [Failed - command failed for cpus 0-1]" 351 cat "${stat_output}" 352 err=1 353 return 354 fi 355 if ! grep -E -q "Performance counter stats for" "${stat_output}" 356 then 357 echo "stat -C <cpu> test [Failed - missing output for cpus 0-1]" 358 cat "${stat_output}" 359 err=1 360 return 361 fi 362 fi 363 364 echo "stat -C <cpu> test [Success]" 365} 366 367test_stat_no_aggr() { 368 echo "stat -A test" 369 if ! perf stat -A -a true > "${stat_output}" 2>&1 370 then 371 echo "stat -A test [Failed - command failed]" 372 cat "${stat_output}" 373 err=1 374 return 375 fi 376 377 if ! grep -E -q "CPU" "${stat_output}" 378 then 379 echo "stat -A test [Failed - missing CPU column]" 380 cat "${stat_output}" 381 err=1 382 return 383 fi 384 echo "stat -A test [Success]" 385} 386 387test_stat_detailed() { 388 echo "stat -d test" 389 if ! perf stat -d true > "${stat_output}" 2>&1 390 then 391 echo "stat -d test [Failed - command failed]" 392 cat "${stat_output}" 393 err=1 394 return 395 fi 396 397 if ! grep -E -q "Performance counter stats" "${stat_output}" 398 then 399 echo "stat -d test [Failed - missing output]" 400 cat "${stat_output}" 401 err=1 402 return 403 fi 404 405 if ! perf stat -dd true > "${stat_output}" 2>&1 406 then 407 echo "stat -dd test [Failed - command failed]" 408 cat "${stat_output}" 409 err=1 410 return 411 fi 412 413 if ! grep -E -q "Performance counter stats" "${stat_output}" 414 then 415 echo "stat -dd test [Failed - missing output]" 416 cat "${stat_output}" 417 err=1 418 return 419 fi 420 421 if ! perf stat -ddd true > "${stat_output}" 2>&1 422 then 423 echo "stat -ddd test [Failed - command failed]" 424 cat "${stat_output}" 425 err=1 426 return 427 fi 428 429 if ! grep -E -q "Performance counter stats" "${stat_output}" 430 then 431 echo "stat -ddd test [Failed - missing output]" 432 cat "${stat_output}" 433 err=1 434 return 435 fi 436 437 echo "stat -d test [Success]" 438} 439 440test_stat_repeat() { 441 echo "stat -r test" 442 if ! perf stat -r 2 true > "${stat_output}" 2>&1 443 then 444 echo "stat -r test [Failed - command failed]" 445 cat "${stat_output}" 446 err=1 447 return 448 fi 449 450 if ! grep -E -q "\([[:space:]]*\+-.*%[[:space:]]*\)" "${stat_output}" 451 then 452 echo "stat -r test [Failed - missing variance]" 453 cat "${stat_output}" 454 err=1 455 return 456 fi 457 echo "stat -r test [Success]" 458} 459 460test_stat_pid() { 461 echo "stat -p test" 462 sleep 1 & 463 pid=$! 464 if ! perf stat -p $pid > "${stat_output}" 2>&1 465 then 466 echo "stat -p test [Failed - command failed]" 467 cat "${stat_output}" 468 err=1 469 kill $pid 2>/dev/null || true 470 wait $pid 2>/dev/null || true 471 return 472 fi 473 474 if ! grep -E -q "Performance counter stats" "${stat_output}" 475 then 476 echo "stat -p test [Failed - missing output]" 477 cat "${stat_output}" 478 err=1 479 else 480 echo "stat -p test [Success]" 481 fi 482 kill $pid 2>/dev/null || true 483 wait $pid 2>/dev/null || true 484} 485 486test_default_stat 487test_null_stat 488test_offline_cpu_stat 489test_stat_record_report 490test_stat_record_script 491test_stat_repeat_weak_groups 492test_topdown_groups 493test_topdown_weak_groups 494test_cputype 495test_hybrid 496test_stat_cpu 497test_stat_no_aggr 498test_stat_detailed 499test_stat_repeat 500test_stat_pid 501 502cleanup 503exit $err 504