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