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_uid() { 235 echo "Uid sampling test" 236 if ! perf record -aB --synth=no --uid "$(id -u)" -o "${perfdata}" ${testprog} \ 237 > "${script_output}" 2>&1 238 then 239 if grep -q "libbpf.*EPERM" "${script_output}" 240 then 241 echo "Uid sampling [Skipped permissions]" 242 return 243 else 244 echo "Uid sampling [Failed to record]" 245 err=1 246 # cat "${script_output}" 247 return 248 fi 249 fi 250 if ! perf report -i "${perfdata}" -q | grep -q "${testsym}" 251 then 252 echo "Uid sampling [Failed missing output]" 253 err=1 254 return 255 fi 256 echo "Uid sampling test [Success]" 257} 258 259test_leader_sampling() { 260 echo "Basic leader sampling test" 261 if ! perf record -o "${perfdata}" -e "{cycles,cycles}:Su" -- \ 262 perf test -w brstack 2> /dev/null 263 then 264 echo "Leader sampling [Failed record]" 265 err=1 266 return 267 fi 268 perf script -i "${perfdata}" | grep brstack > $script_output 269 # Check if the two instruction counts are equal in each record. 270 # However, the throttling code doesn't consider event grouping. During throttling, only the 271 # leader is stopped, causing the slave's counts significantly higher. To temporarily solve this, 272 # let's set the tolerance rate to 80%. 273 # TODO: Revert the code for tolerance once the throttling mechanism is fixed. 274 index=0 275 valid_counts=0 276 invalid_counts=0 277 tolerance_rate=0.8 278 while IFS= read -r line 279 do 280 cycles=$(echo $line | awk '{for(i=1;i<=NF;i++) if($i=="cycles:") print $(i-1)}') 281 if [ $(($index%2)) -ne 0 ] && [ ${cycles}x != ${prev_cycles}x ] 282 then 283 invalid_counts=$(($invalid_counts+1)) 284 else 285 valid_counts=$(($valid_counts+1)) 286 fi 287 index=$(($index+1)) 288 prev_cycles=$cycles 289 done < "${script_output}" 290 total_counts=$(bc <<< "$invalid_counts+$valid_counts") 291 if (( $(bc <<< "$total_counts <= 0") )) 292 then 293 echo "Leader sampling [No sample generated]" 294 err=1 295 return 296 fi 297 isok=$(bc <<< "scale=2; if (($invalid_counts/$total_counts) < (1-$tolerance_rate)) { 0 } else { 1 };") 298 if [ $isok -eq 1 ] 299 then 300 echo "Leader sampling [Failed inconsistent cycles count]" 301 err=1 302 else 303 echo "Basic leader sampling test [Success]" 304 fi 305} 306 307test_topdown_leader_sampling() { 308 echo "Topdown leader sampling test" 309 if ! perf stat -e "{slots,topdown-retiring}" true 2> /dev/null 310 then 311 echo "Topdown leader sampling [Skipped event parsing failed]" 312 return 313 fi 314 if ! perf record -o "${perfdata}" -e "{instructions,slots,topdown-retiring}:S" true 2> /dev/null 315 then 316 echo "Topdown leader sampling [Failed topdown events not reordered correctly]" 317 err=1 318 return 319 fi 320 echo "Topdown leader sampling test [Success]" 321} 322 323test_precise_max() { 324 local -i skipped=0 325 326 echo "precise_max attribute test" 327 # Just to make sure event cycles is supported for sampling 328 if perf record -o "${perfdata}" -e "cycles" true 2> /dev/null 329 then 330 if ! perf record -o "${perfdata}" -e "cycles:P" true 2> /dev/null 331 then 332 echo "precise_max attribute [Failed cycles:P event]" 333 err=1 334 return 335 fi 336 else 337 echo "precise_max attribute [Skipped no cycles:P event]" 338 ((skipped+=1)) 339 fi 340 # On s390 event instructions is not supported for perf record 341 if perf record -o "${perfdata}" -e "instructions" true 2> /dev/null 342 then 343 # On AMD, cycles and instructions events are treated differently 344 if ! perf record -o "${perfdata}" -e "instructions:P" true 2> /dev/null 345 then 346 echo "precise_max attribute [Failed instructions:P event]" 347 err=1 348 return 349 fi 350 else 351 echo "precise_max attribute [Skipped no instructions:P event]" 352 ((skipped+=1)) 353 fi 354 if [ $skipped -eq 2 ] 355 then 356 echo "precise_max attribute [Skipped no hardware events]" 357 else 358 echo "precise_max attribute test [Success]" 359 fi 360} 361 362# raise the limit of file descriptors to minimum 363if [[ $default_fd_limit -lt $min_fd_limit ]]; then 364 ulimit -Sn $min_fd_limit 365fi 366 367test_per_thread 368test_register_capture 369test_system_wide 370test_workload 371test_branch_counter 372test_cgroup 373test_uid 374test_leader_sampling 375test_topdown_leader_sampling 376test_precise_max 377 378# restore the default value 379ulimit -Sn $default_fd_limit 380 381cleanup 382exit $err 383