xref: /linux/tools/perf/tests/shell/stat.sh (revision c7decec2f2d2ab0366567f9e30c0e1418cece43f)
1#!/bin/bash
2# perf stat tests
3# SPDX-License-Identifier: GPL-2.0
4
5set -e
6
7err=0
8stat_output=$(mktemp /tmp/perf-stat-test-output.XXXXX)
9
10cleanup() {
11  rm -f "${stat_output}"
12  trap - EXIT TERM INT
13}
14
15trap_cleanup() {
16  echo "Unexpected signal in ${FUNCNAME[1]}"
17  cleanup
18  exit 1
19}
20
21trap trap_cleanup EXIT TERM INT
22
23test_default_stat() {
24  echo "Basic stat command test"
25  if ! perf stat true 2>&1 | grep -E -q "Performance counter stats for 'true':"
26  then
27    echo "Basic stat command test [Failed]"
28    err=1
29    return
30  fi
31  echo "Basic stat command test [Success]"
32}
33
34test_null_stat() {
35  echo "Null stat command test"
36  if ! perf stat --null true 2>&1 | grep -E -q "Performance counter stats for 'true':"
37  then
38    echo "Null stat command test [Failed]"
39    err=1
40    return
41  fi
42  echo "Null stat command test [Success]"
43}
44
45find_offline_cpu() {
46  for i in $(seq 1 4096)
47  do
48    if [[ ! -f /sys/devices/system/cpu/cpu$i/online || \
49          $(cat /sys/devices/system/cpu/cpu$i/online) == "0" ]]
50    then
51      echo $i
52      return
53    fi
54  done
55  echo "Failed to find offline CPU"
56  exit 1
57}
58
59test_offline_cpu_stat() {
60  cpu=$(find_offline_cpu)
61  echo "Offline CPU stat command test (cpu $cpu)"
62  if ! perf stat "-C$cpu" -e cycles true 2>&1 | grep -E -q "No supported events found."
63  then
64    echo "Offline CPU stat command test [Failed]"
65    err=1
66    return
67  fi
68  echo "Offline CPU stat command test [Success]"
69}
70
71test_stat_record_report() {
72  echo "stat record and report test"
73  if ! perf stat record -e task-clock -o - true | perf stat report -i - 2>&1 | \
74    grep -E -q "Performance counter stats for 'pipe':"
75  then
76    echo "stat record and report test [Failed]"
77    err=1
78    return
79  fi
80  echo "stat record and report test [Success]"
81}
82
83test_stat_record_script() {
84  echo "stat record and script test"
85  if ! perf stat record -e task-clock -o - true | perf script -i - 2>&1 | \
86    grep -E -q "CPU[[:space:]]+THREAD[[:space:]]+VAL[[:space:]]+ENA[[:space:]]+RUN[[:space:]]+TIME[[:space:]]+EVENT"
87  then
88    echo "stat record and script test [Failed]"
89    err=1
90    return
91  fi
92  echo "stat record and script test [Success]"
93}
94
95test_stat_repeat_weak_groups() {
96  echo "stat repeat weak groups test"
97  if ! perf stat -e '{cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles}' \
98     true 2>&1 | grep -q 'seconds time elapsed'
99  then
100    echo "stat repeat weak groups test [Skipped event parsing failed]"
101    return
102  fi
103  if ! perf stat -r2 -e '{cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles}:W' \
104    true > /dev/null 2>&1
105  then
106    echo "stat repeat weak groups test [Failed]"
107    err=1
108    return
109  fi
110  echo "stat repeat weak groups test [Success]"
111}
112
113test_topdown_groups() {
114  # Topdown events must be grouped with the slots event first. Test that
115  # parse-events reorders this.
116  echo "Topdown event group test"
117  if ! perf stat -e '{slots,topdown-retiring}' true > /dev/null 2>&1
118  then
119    echo "Topdown event group test [Skipped event parsing failed]"
120    return
121  fi
122  td_err=0
123  do_topdown_group_test() {
124    events=$1
125    failure=$2
126    if perf stat -e "$events" true 2>&1 | grep -E -q "<not supported>"
127    then
128      echo "Topdown event group test [Failed $failure for '$events']"
129      td_err=1
130      return
131    fi
132  }
133  do_topdown_group_test "{slots,topdown-retiring}" "events not supported"
134  do_topdown_group_test "{instructions,r400,r8000}" "raw format slots not reordered first"
135  filler_events=("instructions" "cycles"
136                 "context-switches" "faults")
137  for ((i = 0; i < ${#filler_events[@]}; i+=2))
138  do
139    filler1=${filler_events[i]}
140    filler2=${filler_events[i+1]}
141    do_topdown_group_test "$filler1,topdown-retiring,slots" \
142      "slots not reordered first in no-group case"
143    do_topdown_group_test "slots,$filler1,topdown-retiring" \
144      "topdown metrics event not reordered in no-group case"
145    do_topdown_group_test "{$filler1,topdown-retiring,slots}" \
146      "slots not reordered first in single group case"
147    do_topdown_group_test "{$filler1,slots},topdown-retiring" \
148      "topdown metrics event not move into slots group"
149    do_topdown_group_test "topdown-retiring,{$filler1,slots}" \
150      "topdown metrics event not move into slots group last"
151    do_topdown_group_test "{$filler1,slots},{topdown-retiring}" \
152      "topdown metrics group not merge into slots group"
153    do_topdown_group_test "{topdown-retiring},{$filler1,slots}" \
154      "topdown metrics group not merge into slots group last"
155    do_topdown_group_test "{$filler1,slots},$filler2,topdown-retiring" \
156      "non-adjacent topdown metrics group not move into slots group"
157    do_topdown_group_test "$filler2,topdown-retiring,{$filler1,slots}" \
158      "non-adjacent topdown metrics group not move into slots group last"
159    do_topdown_group_test "{$filler1,slots},{$filler2,topdown-retiring}" \
160      "metrics group not merge into slots group"
161    do_topdown_group_test "{$filler1,topdown-retiring},{$filler2,slots}" \
162      "metrics group not merge into slots group last"
163  done
164  if test "$td_err" -eq 0
165  then
166    echo "Topdown event group test [Success]"
167  else
168    err="$td_err"
169  fi
170}
171
172test_topdown_weak_groups() {
173  # Weak groups break if the perf_event_open of multiple grouped events
174  # fails. Breaking a topdown group causes the events to fail. Test a very large
175  # grouping to see that the topdown events aren't broken out.
176  echo "Topdown weak groups test"
177  ok_grouping="{slots,topdown-bad-spec,topdown-be-bound,topdown-fe-bound,topdown-retiring},branch-instructions,branch-misses,bus-cycles,cache-misses,cache-references,cpu-cycles,instructions,mem-loads,mem-stores,ref-cycles,cache-misses,cache-references"
178  if ! perf stat --no-merge -e "$ok_grouping" true > /dev/null 2>&1
179  then
180    echo "Topdown weak groups test [Skipped event parsing failed]"
181    return
182  fi
183  group_needs_break="{slots,topdown-bad-spec,topdown-be-bound,topdown-fe-bound,topdown-retiring,branch-instructions,branch-misses,bus-cycles,cache-misses,cache-references,cpu-cycles,instructions,mem-loads,mem-stores,ref-cycles,cache-misses,cache-references}:W"
184  if perf stat --no-merge -e "$group_needs_break" true 2>&1 | grep -E -q "<not supported>"
185  then
186    echo "Topdown weak groups test [Failed events not supported]"
187    err=1
188    return
189  fi
190  echo "Topdown weak groups test [Success]"
191}
192
193test_cputype() {
194  # Test --cputype argument.
195  echo "cputype test"
196
197  # Bogus PMU should fail.
198  if perf stat --cputype="123" -e instructions true > /dev/null 2>&1
199  then
200    echo "cputype test [Bogus PMU didn't fail]"
201    err=1
202    return
203  fi
204
205  # Find a known PMU for cputype.
206  pmu=""
207  devs="/sys/bus/event_source/devices"
208  for i in $devs/cpu $devs/cpu_atom $devs/armv8_pmuv3_0 $devs/armv8_cortex_*
209  do
210    i_base=$(basename "$i")
211    if test -d "$i"
212    then
213      pmu="$i_base"
214      break
215    fi
216    if perf stat -e "$i_base/instructions/" true > /dev/null 2>&1
217    then
218      pmu="$i_base"
219      break
220    fi
221  done
222  if test "x$pmu" = "x"
223  then
224    echo "cputype test [Skipped known PMU not found]"
225    return
226  fi
227
228  # Test running with cputype produces output.
229  if ! perf stat --cputype="$pmu" -e instructions true 2>&1 | grep -E -q "instructions"
230  then
231    echo "cputype test [Failed count missed with given filter]"
232    err=1
233    return
234  fi
235  echo "cputype test [Success]"
236}
237
238test_hybrid() {
239  # Test the default stat command on hybrid devices opens one cycles event for
240  # each CPU type.
241  echo "hybrid test"
242
243  # Count the number of core PMUs, assume minimum of 1
244  pmus=$(ls /sys/bus/event_source/devices/*/cpus 2>/dev/null | wc -l)
245  if [ "$pmus" -lt 1 ]
246  then
247    pmus=1
248  fi
249
250  # Run default Perf stat
251  cycles_events=$(perf stat -a -- sleep 0.1 2>&1 | grep -E "/cpu-cycles/[uH]*|  cpu-cycles[:uH]*  "  | wc -l)
252
253  # The expectation is that default output will have a cycles events on each
254  # hybrid PMU. In situations with no cycles PMU events, like virtualized, this
255  # can fall back to task-clock and so the end count may be 0. Fail if neither
256  # condition holds.
257  if [ "$pmus" -ne "$cycles_events" ] && [ "0" -ne "$cycles_events" ]
258  then
259    echo "hybrid test [Found $pmus PMUs but $cycles_events cycles events. Failed]"
260    err=1
261    return
262  fi
263  echo "hybrid test [Success]"
264}
265
266test_stat_cpu() {
267  echo "stat -C <cpu> test"
268  # Test the full online CPU list (ranges and lists)
269  online_cpus=$(cat /sys/devices/system/cpu/online)
270  if ! perf stat -C "$online_cpus" -a true > "${stat_output}" 2>&1
271  then
272    echo "stat -C <cpu> test [Failed - command failed for cpus $online_cpus]"
273    cat "${stat_output}"
274    err=1
275    return
276  fi
277
278  if ! grep -E -q "Performance counter stats for" "${stat_output}"
279  then
280    echo "stat -C <cpu> test [Failed - missing output for cpus $online_cpus]"
281    cat "${stat_output}"
282    err=1
283    return
284  fi
285
286  # Test each individual online CPU
287  for cpu_dir in /sys/devices/system/cpu/cpu[0-9]*; do
288    cpu=${cpu_dir##*/cpu}
289    # Check if online
290    if [ -f "$cpu_dir/online" ] && [ "$(cat "$cpu_dir/online")" -eq 0 ]
291    then
292      continue
293    fi
294
295    if ! perf stat -C "$cpu" -a true > "${stat_output}" 2>&1
296    then
297      echo "stat -C <cpu> test [Failed - command failed for cpu $cpu]"
298      cat "${stat_output}"
299      err=1
300      return
301    fi
302    if ! grep -E -q "Performance counter stats for" "${stat_output}"
303    then
304      echo "stat -C <cpu> test [Failed - missing output for cpu $cpu]"
305      cat "${stat_output}"
306      err=1
307      return
308    fi
309  done
310
311  # Test synthetic list and range if cpu0 and cpu1 are online
312  c0_online=0
313  c1_online=0
314  if [ -d "/sys/devices/system/cpu/cpu0" ]
315  then
316    if [ ! -f "/sys/devices/system/cpu/cpu0/online" ] || [ "$(cat /sys/devices/system/cpu/cpu0/online)" -eq 1 ]
317    then
318      c0_online=1
319    fi
320  fi
321  if [ -d "/sys/devices/system/cpu/cpu1" ]
322  then
323    if [ ! -f "/sys/devices/system/cpu/cpu1/online" ] || [ "$(cat /sys/devices/system/cpu/cpu1/online)" -eq 1 ]
324    then
325      c1_online=1
326    fi
327  fi
328
329  if [ $c0_online -eq 1 ] && [ $c1_online -eq 1 ]
330  then
331    # Test list "0,1"
332    if ! perf stat -C "0,1" -a true > "${stat_output}" 2>&1
333    then
334      echo "stat -C <cpu> test [Failed - command failed for cpus 0,1]"
335      cat "${stat_output}"
336      err=1
337      return
338    fi
339    if ! grep -E -q "Performance counter stats for" "${stat_output}"
340    then
341      echo "stat -C <cpu> test [Failed - missing output for cpus 0,1]"
342      cat "${stat_output}"
343      err=1
344      return
345    fi
346
347    # Test range "0-1"
348    if ! perf stat -C "0-1" -a true > "${stat_output}" 2>&1
349    then
350      echo "stat -C <cpu> test [Failed - command failed for cpus 0-1]"
351      cat "${stat_output}"
352      err=1
353      return
354    fi
355    if ! grep -E -q "Performance counter stats for" "${stat_output}"
356    then
357      echo "stat -C <cpu> test [Failed - missing output for cpus 0-1]"
358      cat "${stat_output}"
359      err=1
360      return
361    fi
362  fi
363
364  echo "stat -C <cpu> test [Success]"
365}
366
367test_stat_no_aggr() {
368  echo "stat -A test"
369  if ! perf stat -A -a true > "${stat_output}" 2>&1
370  then
371    echo "stat -A test [Failed - command failed]"
372    cat "${stat_output}"
373    err=1
374    return
375  fi
376
377  if ! grep -E -q "CPU" "${stat_output}"
378  then
379    echo "stat -A test [Failed - missing CPU column]"
380    cat "${stat_output}"
381    err=1
382    return
383  fi
384  echo "stat -A test [Success]"
385}
386
387test_stat_detailed() {
388  echo "stat -d test"
389  if ! perf stat -d true > "${stat_output}" 2>&1
390  then
391    echo "stat -d test [Failed - command failed]"
392    cat "${stat_output}"
393    err=1
394    return
395  fi
396
397  if ! grep -E -q "Performance counter stats" "${stat_output}"
398  then
399    echo "stat -d test [Failed - missing output]"
400    cat "${stat_output}"
401    err=1
402    return
403  fi
404
405  if ! perf stat -dd true > "${stat_output}" 2>&1
406  then
407    echo "stat -dd test [Failed - command failed]"
408    cat "${stat_output}"
409    err=1
410    return
411  fi
412
413  if ! grep -E -q "Performance counter stats" "${stat_output}"
414  then
415    echo "stat -dd test [Failed - missing output]"
416    cat "${stat_output}"
417    err=1
418    return
419  fi
420
421  if ! perf stat -ddd true > "${stat_output}" 2>&1
422  then
423    echo "stat -ddd test [Failed - command failed]"
424    cat "${stat_output}"
425    err=1
426    return
427  fi
428
429  if ! grep -E -q "Performance counter stats" "${stat_output}"
430  then
431    echo "stat -ddd test [Failed - missing output]"
432    cat "${stat_output}"
433    err=1
434    return
435  fi
436
437  echo "stat -d test [Success]"
438}
439
440test_stat_repeat() {
441  echo "stat -r test"
442  if ! perf stat -r 2 true > "${stat_output}" 2>&1
443  then
444    echo "stat -r test [Failed - command failed]"
445    cat "${stat_output}"
446    err=1
447    return
448  fi
449
450  if ! grep -E -q "\([[:space:]]*\+-.*%[[:space:]]*\)" "${stat_output}"
451  then
452    echo "stat -r test [Failed - missing variance]"
453    cat "${stat_output}"
454    err=1
455    return
456  fi
457  echo "stat -r test [Success]"
458}
459
460test_stat_pid() {
461  echo "stat -p test"
462  sleep 1 &
463  pid=$!
464  if ! perf stat -p $pid > "${stat_output}" 2>&1
465  then
466    echo "stat -p test [Failed - command failed]"
467    cat "${stat_output}"
468    err=1
469    kill $pid 2>/dev/null || true
470    wait $pid 2>/dev/null || true
471    return
472  fi
473
474  if ! grep -E -q "Performance counter stats" "${stat_output}"
475  then
476    echo "stat -p test [Failed - missing output]"
477    cat "${stat_output}"
478    err=1
479  else
480    echo "stat -p test [Success]"
481  fi
482  kill $pid 2>/dev/null || true
483  wait $pid 2>/dev/null || true
484}
485
486test_default_stat
487test_null_stat
488test_offline_cpu_stat
489test_stat_record_report
490test_stat_record_script
491test_stat_repeat_weak_groups
492test_topdown_groups
493test_topdown_weak_groups
494test_cputype
495test_hybrid
496test_stat_cpu
497test_stat_no_aggr
498test_stat_detailed
499test_stat_repeat
500test_stat_pid
501
502cleanup
503exit $err
504