projects/linux·blob master · refs

510 lines · 14.0 KiB

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