xref: /linux/tools/perf/tests/shell/record.sh (revision 74dba58222f0d34cf8bd3eba1a6926e9654d4b6b)
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_ratio_to_prev() {
407  echo "ratio-to-prev test"
408  if ! perf record -o /dev/null -e "{instructions, cycles/period=100000,ratio-to-prev=0.5/}" \
409     true 2> /dev/null
410  then
411    echo "ratio-to-prev [Skipped not supported]"
412    return
413  fi
414  if ! perf record -o /dev/null -e "instructions, cycles/period=100000,ratio-to-prev=0.5/" \
415     true |& grep -q 'Invalid use of ratio-to-prev term without preceding element in group'
416  then
417    echo "ratio-to-prev test [Failed elements must be in same group]"
418    err=1
419    return
420  fi
421  if ! perf record -o /dev/null -e "{instructions,dummy,cycles/period=100000,ratio-to-prev=0.5/}" \
422     true |& grep -q 'must have same PMU'
423  then
424    echo "ratio-to-prev test [Failed elements must have same PMU]"
425    err=1
426    return
427  fi
428  if ! perf record -o /dev/null -e "{instructions,cycles/ratio-to-prev=0.5/}" \
429     true |& grep -q 'Event period term or count (-c) must be set when using ratio-to-prev term.'
430  then
431    echo "ratio-to-prev test [Failed period must be set]"
432    err=1
433    return
434  fi
435  if ! perf record -o /dev/null -e "{cycles/ratio-to-prev=0.5/}" \
436     true |& grep -q 'Invalid use of ratio-to-prev term without preceding element in group'
437  then
438    echo "ratio-to-prev test [Failed need 2+ events]"
439    err=1
440    return
441  fi
442  echo "Basic ratio-to-prev record test [Success]"
443}
444
445# raise the limit of file descriptors to minimum
446if [[ $default_fd_limit -lt $min_fd_limit ]]; then
447       ulimit -Sn $min_fd_limit
448fi
449
450test_per_thread
451test_register_capture
452test_system_wide
453test_workload
454test_branch_counter
455test_cgroup
456test_uid
457test_leader_sampling
458test_topdown_leader_sampling
459test_precise_max
460test_callgraph
461test_ratio_to_prev
462
463# restore the default value
464ulimit -Sn $default_fd_limit
465
466cleanup
467exit $err
468