1#!/bin/bash 2# Miscellaneous Intel PT testing (exclusive) 3# SPDX-License-Identifier: GPL-2.0 4 5set -e 6 7# Skip if no Intel PT 8perf list pmu | grep -q 'intel_pt//' || exit 2 9 10shelldir=$(dirname "$0") 11# shellcheck source=lib/waiting.sh 12. "${shelldir}"/lib/waiting.sh 13 14skip_cnt=0 15ok_cnt=0 16err_cnt=0 17 18temp_dir=$(mktemp -d /tmp/perf-test-intel-pt-sh.XXXXXXXXXX) 19 20tmpfile="${temp_dir}/tmp-perf.data" 21perfdatafile="${temp_dir}/test-perf.data" 22outfile="${temp_dir}/test-out.txt" 23errfile="${temp_dir}/test-err.txt" 24awkscript="${temp_dir}/awkscript" 25maxbrstack="${temp_dir}/maxbrstack.py" 26 27cleanup() 28{ 29 trap - EXIT TERM INT 30 sane=$(echo "${temp_dir}" | cut -b 1-26) 31 if [ "${sane}" = "/tmp/perf-test-intel-pt-sh" ] ; then 32 echo "--- Cleaning up ---" 33 rm -f "${temp_dir}/"* 34 rmdir "${temp_dir}" 35 fi 36} 37 38trap_cleanup() 39{ 40 cleanup 41 exit 1 42} 43 44trap trap_cleanup EXIT TERM INT 45 46# perf record for testing without decoding 47perf_record_no_decode() 48{ 49 # Options to speed up recording: no post-processing, no build-id cache update, 50 # and no BPF events. 51 perf record -B -N --no-bpf-event "$@" 52} 53 54# perf record for testing should not need BPF events 55perf_record_no_bpf() 56{ 57 # Options for no BPF events 58 perf record --no-bpf-event "$@" 59} 60 61can_cpu_wide() 62{ 63 echo "Checking for CPU-wide recording on CPU $1" 64 if ! perf_record_no_decode -o "${tmpfile}" -e dummy:u -C "$1" true >/dev/null 2>&1 ; then 65 echo "No so skipping" 66 return 2 67 fi 68 echo OK 69 return 0 70} 71 72test_system_wide_side_band() 73{ 74 echo "--- Test system-wide sideband ---" 75 76 # Need CPU 0 and CPU 1 77 can_cpu_wide 0 || return $? 78 can_cpu_wide 1 || return $? 79 80 # Record on CPU 0 a task running on CPU 1 81 perf_record_no_decode -o "${perfdatafile}" -e intel_pt//u -C 0 -- taskset --cpu-list 1 uname 82 83 # Should get MMAP events from CPU 1 because they can be needed to decode 84 mmap_cnt=$(perf script -i "${perfdatafile}" --no-itrace --show-mmap-events -C 1 2>/dev/null | grep -c MMAP) 85 86 if [ "${mmap_cnt}" -gt 0 ] ; then 87 echo OK 88 return 0 89 fi 90 91 echo "Failed to record MMAP events on CPU 1 when tracing CPU 0" 92 return 1 93} 94 95can_kernel() 96{ 97 if [ -z "${can_kernel_trace}" ] ; then 98 can_kernel_trace=0 99 perf_record_no_decode -o "${tmpfile}" -e dummy:k true >/dev/null 2>&1 && can_kernel_trace=1 100 fi 101 if [ ${can_kernel_trace} -eq 0 ] ; then 102 echo "SKIP: no kernel tracing" 103 return 2 104 fi 105 return 0 106} 107 108test_per_thread() 109{ 110 k="$1" 111 desc="$2" 112 113 echo "--- Test per-thread ${desc}recording ---" 114 115 if [ "${k}" = "k" ] ; then 116 can_kernel || return 2 117 fi 118 119 cat <<- "_end_of_file_" > "${awkscript}" 120 BEGIN { 121 s = "[ ]*" 122 u = s"[0-9]+"s 123 d = s"[0-9-]+"s 124 x = s"[0-9a-fA-FxX]+"s 125 mmapping = "idx"u": mmapping fd"u 126 set_output = "idx"u": set output fd"u"->"u 127 perf_event_open = "sys_perf_event_open: pid"d"cpu"d"group_fd"d"flags"x"="u 128 } 129 130 /perf record opening and mmapping events/ { 131 if (!done) 132 active = 1 133 } 134 135 /perf record done opening and mmapping events/ { 136 active = 0 137 done = 1 138 } 139 140 $0 ~ perf_event_open && active { 141 match($0, perf_event_open) 142 $0 = substr($0, RSTART, RLENGTH) 143 pid = $3 144 cpu = $5 145 fd = $11 146 print "pid " pid " cpu " cpu " fd " fd " : " $0 147 fd_array[fd] = fd 148 pid_array[fd] = pid 149 cpu_array[fd] = cpu 150 } 151 152 $0 ~ mmapping && active { 153 match($0, mmapping) 154 $0 = substr($0, RSTART, RLENGTH) 155 fd = $5 156 print "fd " fd " : " $0 157 if (fd in fd_array) { 158 mmap_array[fd] = 1 159 } else { 160 print "Unknown fd " fd 161 exit 1 162 } 163 } 164 165 $0 ~ set_output && active { 166 match($0, set_output) 167 $0 = substr($0, RSTART, RLENGTH) 168 fd = $6 169 fd_to = $8 170 print "fd " fd " fd_to " fd_to " : " $0 171 if (fd in fd_array) { 172 if (fd_to in fd_array) { 173 set_output_array[fd] = fd_to 174 } else { 175 print "Unknown fd " fd_to 176 exit 1 177 } 178 } else { 179 print "Unknown fd " fd 180 exit 1 181 } 182 } 183 184 END { 185 print "Checking " length(fd_array) " fds" 186 for (fd in fd_array) { 187 if (fd in mmap_array) { 188 pid = pid_array[fd] 189 if (pid != -1) { 190 if (pid in pids) { 191 print "More than 1 mmap for PID " pid 192 exit 1 193 } 194 pids[pid] = 1 195 } 196 cpu = cpu_array[fd] 197 if (cpu != -1) { 198 if (cpu in cpus) { 199 print "More than 1 mmap for CPU " cpu 200 exit 1 201 } 202 cpus[cpu] = 1 203 } 204 } else if (!(fd in set_output_array)) { 205 print "No mmap for fd " fd 206 exit 1 207 } 208 } 209 n = length(pids) 210 if (n != thread_cnt) { 211 print "Expected " thread_cnt " per-thread mmaps - found " n 212 exit 1 213 } 214 } 215 _end_of_file_ 216 217 perf test -w thloop 30 2 & 218 w1=$! 219 perf test -w thloop 30 2 & 220 w2=$! 221 echo "Workload PIDs are $w1 and $w2" 222 wait_for_threads ${w1} 2 223 wait_for_threads ${w2} 2 224 225 perf_record_no_decode -o "${perfdatafile}" -e intel_pt//u"${k}" -vvv --per-thread -p "${w1},${w2}" 2>"${errfile}" >"${outfile}" & 226 ppid=$! 227 echo "perf PID is $ppid" 228 wait_for_perf_to_start ${ppid} "${errfile}" || return 1 229 230 kill ${w1} 231 wait_for_process_to_exit ${w1} || return 1 232 is_running ${ppid} || return 1 233 234 kill ${w2} 235 wait_for_process_to_exit ${w2} || return 1 236 wait_for_process_to_exit ${ppid} || return 1 237 238 awk -v thread_cnt=4 -f "${awkscript}" "${errfile}" || return 1 239 240 echo OK 241 return 0 242} 243 244test_jitdump() 245{ 246 echo "--- Test tracing self-modifying code that uses jitdump ---" 247 248 if ! perf check feature -q libelf ; then 249 echo "SKIP: libelf is needed for jitdump" 250 return 2 251 fi 252 253 # Change to temp_dir so jitdump collateral files go there 254 cd "${temp_dir}" 255 perf_record_no_bpf -o "${tmpfile}" -e intel_pt//u perf test -w jitdump 256 perf inject -i "${tmpfile}" -o "${perfdatafile}" --jit 257 decode_br_cnt=$(perf script -i "${perfdatafile}" --itrace=b | wc -l) 258 # Note that overflow and lost errors are suppressed for the error count 259 decode_err_cnt=$(perf script -i "${perfdatafile}" --itrace=e-o-l | grep -ci error) 260 cd - 261 # Should be thousands of branches 262 if [ "${decode_br_cnt}" -lt 1000 ] ; then 263 echo "Decode failed, only ${decode_br_cnt} branches" 264 return 1 265 fi 266 # Should be no errors 267 if [ "${decode_err_cnt}" -ne 0 ] ; then 268 echo "Decode failed, ${decode_err_cnt} errors" 269 perf script -i "${perfdatafile}" --itrace=e-o-l --show-mmap-events | cat 270 return 1 271 fi 272 273 echo OK 274 return 0 275} 276 277test_packet_filter() 278{ 279 echo "--- Test with MTC and TSC disabled ---" 280 # Disable MTC and TSC 281 perf_record_no_decode -o "${perfdatafile}" -e intel_pt/mtc=0,tsc=0/u uname 282 # Should not get MTC packet 283 mtc_cnt=$(perf script -i "${perfdatafile}" -D 2>/dev/null | grep -c "MTC 0x") 284 if [ "${mtc_cnt}" -ne 0 ] ; then 285 echo "Failed to filter with mtc=0" 286 return 1 287 fi 288 # Should not get TSC package 289 tsc_cnt=$(perf script -i "${perfdatafile}" -D 2>/dev/null | grep -c "TSC 0x") 290 if [ "${tsc_cnt}" -ne 0 ] ; then 291 echo "Failed to filter with tsc=0" 292 return 1 293 fi 294 echo OK 295 return 0 296} 297 298test_disable_branch() 299{ 300 echo "--- Test with branches disabled ---" 301 # Disable branch 302 perf_record_no_decode -o "${perfdatafile}" -e intel_pt/branch=0/u uname 303 # Should not get branch related packets 304 tnt_cnt=$(perf script -i "${perfdatafile}" -D 2>/dev/null | grep -c "TNT 0x") 305 tip_cnt=$(perf script -i "${perfdatafile}" -D 2>/dev/null | grep -c "TIP 0x") 306 fup_cnt=$(perf script -i "${perfdatafile}" -D 2>/dev/null | grep -c "FUP 0x") 307 if [ "${tnt_cnt}" -ne 0 ] || [ "${tip_cnt}" -ne 0 ] || [ "${fup_cnt}" -ne 0 ] ; then 308 echo "Failed to disable branches" 309 return 1 310 fi 311 echo OK 312 return 0 313} 314 315test_time_cyc() 316{ 317 echo "--- Test with/without CYC ---" 318 # Check if CYC is supported 319 cyc=$(cat /sys/bus/event_source/devices/intel_pt/caps/psb_cyc) 320 if [ "${cyc}" != "1" ] ; then 321 echo "SKIP: CYC is not supported" 322 return 2 323 fi 324 # Enable CYC 325 perf_record_no_decode -o "${perfdatafile}" -e intel_pt/cyc/u uname 326 # should get CYC packets 327 cyc_cnt=$(perf script -i "${perfdatafile}" -D 2>/dev/null | grep -c "CYC 0x") 328 if [ "${cyc_cnt}" = "0" ] ; then 329 echo "Failed to get CYC packet" 330 return 1 331 fi 332 # Without CYC 333 perf_record_no_decode -o "${perfdatafile}" -e intel_pt//u uname 334 # Should not get CYC packets 335 cyc_cnt=$(perf script -i "${perfdatafile}" -D 2>/dev/null | grep -c "CYC 0x") 336 if [ "${cyc_cnt}" -gt 0 ] ; then 337 echo "Still get CYC packet without cyc" 338 return 1 339 fi 340 echo OK 341 return 0 342} 343 344test_sample() 345{ 346 echo "--- Test recording with sample mode ---" 347 # Check if recording with sample mode is working 348 if ! perf_record_no_decode -o "${perfdatafile}" --aux-sample=8192 -e '{intel_pt//u,branch-misses:u}' uname ; then 349 echo "perf record failed with --aux-sample" 350 return 1 351 fi 352 # Check with event with PMU name 353 if perf_record_no_decode -o "${perfdatafile}" -e br_misp_retired.all_branches:u uname ; then 354 if ! perf_record_no_decode -o "${perfdatafile}" -e '{intel_pt//,br_misp_retired.all_branches/aux-sample-size=8192/}:u' uname ; then 355 echo "perf record failed with --aux-sample-size" 356 return 1 357 fi 358 fi 359 echo OK 360 return 0 361} 362 363test_kernel_trace() 364{ 365 echo "--- Test with kernel trace ---" 366 # Check if recording with kernel trace is working 367 can_kernel || return 2 368 if ! perf_record_no_decode -o "${perfdatafile}" -e intel_pt//k -m1,128 uname ; then 369 echo "perf record failed with intel_pt//k" 370 return 1 371 fi 372 echo OK 373 return 0 374} 375 376test_virtual_lbr() 377{ 378 echo "--- Test virtual LBR ---" 379 # Check if python script is supported 380 libpython=$(perf version --build-options | grep python | grep -cv OFF) 381 if [ "${libpython}" != "1" ] ; then 382 echo "SKIP: python scripting is not supported" 383 return 2 384 fi 385 386 # Python script to determine the maximum size of branch stacks 387 cat << "_end_of_file_" > "${maxbrstack}" 388from __future__ import print_function 389 390bmax = 0 391 392def process_event(param_dict): 393 if "brstack" in param_dict: 394 brstack = param_dict["brstack"] 395 n = len(brstack) 396 global bmax 397 if n > bmax: 398 bmax = n 399 400def trace_end(): 401 print("max brstack", bmax) 402_end_of_file_ 403 404 # Check if virtual lbr is working 405 perf_record_no_bpf -o "${perfdatafile}" --aux-sample -e '{intel_pt//,cycles}:u' uname 406 times_val=$(perf script -i "${perfdatafile}" --itrace=L -s "${maxbrstack}" 2>/dev/null | grep "max brstack " | cut -d " " -f 3) 407 case "${times_val}" in 408 [0-9]*) ;; 409 *) times_val=0;; 410 esac 411 if [ "${times_val}" -lt 2 ] ; then 412 echo "Failed with virtual lbr" 413 return 1 414 fi 415 echo OK 416 return 0 417} 418 419test_power_event() 420{ 421 echo "--- Test power events ---" 422 # Check if power events are supported 423 power_event=$(cat /sys/bus/event_source/devices/intel_pt/caps/power_event_trace) 424 if [ "${power_event}" != "1" ] ; then 425 echo "SKIP: power_event_trace is not supported" 426 return 2 427 fi 428 if ! perf_record_no_decode -o "${perfdatafile}" -a -e intel_pt/pwr_evt/u uname ; then 429 echo "perf record failed with pwr_evt" 430 return 1 431 fi 432 echo OK 433 return 0 434} 435 436test_no_tnt() 437{ 438 echo "--- Test with TNT packets disabled ---" 439 # Check if TNT disable is supported 440 notnt=$(cat /sys/bus/event_source/devices/intel_pt/caps/tnt_disable) 441 if [ "${notnt}" != "1" ] ; then 442 echo "SKIP: tnt_disable is not supported" 443 return 2 444 fi 445 perf_record_no_decode -o "${perfdatafile}" -e intel_pt/notnt/u uname 446 # Should be no TNT packets 447 tnt_cnt=$(perf script -i "${perfdatafile}" -D | grep -c TNT) 448 if [ "${tnt_cnt}" -ne 0 ] ; then 449 echo "TNT packets still there after notnt" 450 return 1 451 fi 452 echo OK 453 return 0 454} 455 456test_event_trace() 457{ 458 echo "--- Test with event_trace ---" 459 # Check if event_trace is supported 460 event_trace=$(cat /sys/bus/event_source/devices/intel_pt/caps/event_trace) 461 if [ "${event_trace}" != 1 ] ; then 462 echo "SKIP: event_trace is not supported" 463 return 2 464 fi 465 if ! perf_record_no_decode -o "${perfdatafile}" -e intel_pt/event/u uname ; then 466 echo "perf record failed with event trace" 467 return 1 468 fi 469 echo OK 470 return 0 471} 472 473test_pipe() 474{ 475 echo "--- Test with pipe mode ---" 476 # Check if it works with pipe 477 if ! perf_record_no_bpf -o- -e intel_pt//u uname | perf report -q -i- --itrace=i10000 ; then 478 echo "perf record + report failed with pipe mode" 479 return 1 480 fi 481 if ! perf_record_no_bpf -o- -e intel_pt//u uname | perf inject -b > /dev/null ; then 482 echo "perf record + inject failed with pipe mode" 483 return 1 484 fi 485 echo OK 486 return 0 487} 488 489test_pause_resume() 490{ 491 echo "--- Test with pause / resume ---" 492 if ! perf_record_no_decode -o "${perfdatafile}" -e intel_pt/aux-action=start-paused/u uname ; then 493 echo "SKIP: pause / resume is not supported" 494 return 2 495 fi 496 if ! perf_record_no_bpf -o "${perfdatafile}" \ 497 -e intel_pt/aux-action=start-paused/u \ 498 -e instructions/period=50000,aux-action=resume,name=Resume/u \ 499 -e instructions/period=100000,aux-action=pause,name=Pause/u uname ; then 500 echo "perf record with pause / resume failed" 501 return 1 502 fi 503 if ! perf script -i "${perfdatafile}" --itrace=b -Fperiod,event | \ 504 awk 'BEGIN {paused=1;branches=0} 505 /Resume/ {paused=0} 506 /branches/ {if (paused) exit 1;branches=1} 507 /Pause/ {paused=1} 508 END {if (!branches) exit 1}' ; then 509 echo "perf record with pause / resume failed" 510 return 1 511 fi 512 echo OK 513 return 0 514} 515 516count_result() 517{ 518 if [ "$1" -eq 2 ] ; then 519 skip_cnt=$((skip_cnt + 1)) 520 return 521 fi 522 if [ "$1" -eq 0 ] ; then 523 ok_cnt=$((ok_cnt + 1)) 524 return 525 fi 526 err_cnt=$((err_cnt + 1)) 527} 528 529ret=0 530test_system_wide_side_band || ret=$? ; count_result $ret ; ret=0 531test_per_thread "" "" || ret=$? ; count_result $ret ; ret=0 532test_per_thread "k" "(incl. kernel) " || ret=$? ; count_result $ret ; ret=0 533test_jitdump || ret=$? ; count_result $ret ; ret=0 534test_packet_filter || ret=$? ; count_result $ret ; ret=0 535test_disable_branch || ret=$? ; count_result $ret ; ret=0 536test_time_cyc || ret=$? ; count_result $ret ; ret=0 537test_sample || ret=$? ; count_result $ret ; ret=0 538test_kernel_trace || ret=$? ; count_result $ret ; ret=0 539test_virtual_lbr || ret=$? ; count_result $ret ; ret=0 540test_power_event || ret=$? ; count_result $ret ; ret=0 541test_no_tnt || ret=$? ; count_result $ret ; ret=0 542test_event_trace || ret=$? ; count_result $ret ; ret=0 543test_pipe || ret=$? ; count_result $ret ; ret=0 544test_pause_resume || ret=$? ; count_result $ret ; ret=0 545 546cleanup 547 548echo "--- Done ---" 549 550if [ ${err_cnt} -gt 0 ] ; then 551 exit 1 552fi 553 554if [ ${ok_cnt} -gt 0 ] ; then 555 exit 0 556fi 557 558exit 2 559