1#!/bin/bash
2# perf stat tests
3# SPDX-License-Identifier: GPL-2.0
4
5set -e
6
7err=0
8test_default_stat() {
9  echo "Basic stat command test"
10  if ! perf stat true 2>&1 | grep -E -q "Performance counter stats for 'true':"
11  then
12    echo "Basic stat command test [Failed]"
13    err=1
14    return
15  fi
16  echo "Basic stat command test [Success]"
17}
18
19test_stat_record_report() {
20  echo "stat record and report test"
21  if ! perf stat record -o - true | perf stat report -i - 2>&1 | \
22    grep -E -q "Performance counter stats for 'pipe':"
23  then
24    echo "stat record and report test [Failed]"
25    err=1
26    return
27  fi
28  echo "stat record and report test [Success]"
29}
30
31test_stat_record_script() {
32  echo "stat record and script test"
33  if ! perf stat record -o - true | perf script -i - 2>&1 | \
34    grep -E -q "CPU[[:space:]]+THREAD[[:space:]]+VAL[[:space:]]+ENA[[:space:]]+RUN[[:space:]]+TIME[[:space:]]+EVENT"
35  then
36    echo "stat record and script test [Failed]"
37    err=1
38    return
39  fi
40  echo "stat record and script test [Success]"
41}
42
43test_stat_repeat_weak_groups() {
44  echo "stat repeat weak groups test"
45  if ! perf stat -e '{cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles}' \
46     true 2>&1 | grep -q 'seconds time elapsed'
47  then
48    echo "stat repeat weak groups test [Skipped event parsing failed]"
49    return
50  fi
51  if ! perf stat -r2 -e '{cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles,cycles}:W' \
52    true > /dev/null 2>&1
53  then
54    echo "stat repeat weak groups test [Failed]"
55    err=1
56    return
57  fi
58  echo "stat repeat weak groups test [Success]"
59}
60
61test_topdown_groups() {
62  # Topdown events must be grouped with the slots event first. Test that
63  # parse-events reorders this.
64  echo "Topdown event group test"
65  if ! perf stat -e '{slots,topdown-retiring}' true > /dev/null 2>&1
66  then
67    echo "Topdown event group test [Skipped event parsing failed]"
68    return
69  fi
70  td_err=0
71  do_topdown_group_test() {
72    events=$1
73    failure=$2
74    if perf stat -e "$events" true 2>&1 | grep -E -q "<not supported>"
75    then
76      echo "Topdown event group test [Failed $failure for '$events']"
77      td_err=1
78      return
79    fi
80  }
81  do_topdown_group_test "{slots,topdown-retiring}" "events not supported"
82  do_topdown_group_test "{instructions,r400,r8000}" "raw format slots not reordered first"
83  filler_events=("instructions" "cycles"
84                 "context-switches" "faults")
85  for ((i = 0; i < ${#filler_events[@]}; i+=2))
86  do
87    filler1=${filler_events[i]}
88    filler2=${filler_events[i+1]}
89    do_topdown_group_test "$filler1,topdown-retiring,slots" \
90      "slots not reordered first in no-group case"
91    do_topdown_group_test "slots,$filler1,topdown-retiring" \
92      "topdown metrics event not reordered in no-group case"
93    do_topdown_group_test "{$filler1,topdown-retiring,slots}" \
94      "slots not reordered first in single group case"
95    do_topdown_group_test "{$filler1,slots},topdown-retiring" \
96      "topdown metrics event not move into slots group"
97    do_topdown_group_test "topdown-retiring,{$filler1,slots}" \
98      "topdown metrics event not move into slots group last"
99    do_topdown_group_test "{$filler1,slots},{topdown-retiring}" \
100      "topdown metrics group not merge into slots group"
101    do_topdown_group_test "{topdown-retiring},{$filler1,slots}" \
102      "topdown metrics group not merge into slots group last"
103    do_topdown_group_test "{$filler1,slots},$filler2,topdown-retiring" \
104      "non-adjacent topdown metrics group not move into slots group"
105    do_topdown_group_test "$filler2,topdown-retiring,{$filler1,slots}" \
106      "non-adjacent topdown metrics group not move into slots group last"
107    do_topdown_group_test "{$filler1,slots},{$filler2,topdown-retiring}" \
108      "metrics group not merge into slots group"
109    do_topdown_group_test "{$filler1,topdown-retiring},{$filler2,slots}" \
110      "metrics group not merge into slots group last"
111  done
112  if test "$td_err" -eq 0
113  then
114    echo "Topdown event group test [Success]"
115  else
116    err="$td_err"
117  fi
118}
119
120test_topdown_weak_groups() {
121  # Weak groups break if the perf_event_open of multiple grouped events
122  # fails. Breaking a topdown group causes the events to fail. Test a very large
123  # grouping to see that the topdown events aren't broken out.
124  echo "Topdown weak groups test"
125  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"
126  if ! perf stat --no-merge -e "$ok_grouping" true > /dev/null 2>&1
127  then
128    echo "Topdown weak groups test [Skipped event parsing failed]"
129    return
130  fi
131  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"
132  if perf stat --no-merge -e "$group_needs_break" true 2>&1 | grep -E -q "<not supported>"
133  then
134    echo "Topdown weak groups test [Failed events not supported]"
135    err=1
136    return
137  fi
138  echo "Topdown weak groups test [Success]"
139}
140
141test_cputype() {
142  # Test --cputype argument.
143  echo "cputype test"
144
145  # Bogus PMU should fail.
146  if perf stat --cputype="123" -e instructions true > /dev/null 2>&1
147  then
148    echo "cputype test [Bogus PMU didn't fail]"
149    err=1
150    return
151  fi
152
153  # Find a known PMU for cputype.
154  pmu=""
155  devs="/sys/bus/event_source/devices"
156  for i in $devs/cpu $devs/cpu_atom $devs/armv8_pmuv3_0 $devs/armv8_cortex_*
157  do
158    i_base=$(basename "$i")
159    if test -d "$i"
160    then
161      pmu="$i_base"
162      break
163    fi
164    if perf stat -e "$i_base/instructions/" true > /dev/null 2>&1
165    then
166      pmu="$i_base"
167      break
168    fi
169  done
170  if test "x$pmu" = "x"
171  then
172    echo "cputype test [Skipped known PMU not found]"
173    return
174  fi
175
176  # Test running with cputype produces output.
177  if ! perf stat --cputype="$pmu" -e instructions true 2>&1 | grep -E -q "instructions"
178  then
179    echo "cputype test [Failed count missed with given filter]"
180    err=1
181    return
182  fi
183  echo "cputype test [Success]"
184}
185
186test_hybrid() {
187  # Test the default stat command on hybrid devices opens one cycles event for
188  # each CPU type.
189  echo "hybrid test"
190
191  # Count the number of core PMUs, assume minimum of 1
192  pmus=$(ls /sys/bus/event_source/devices/*/cpus 2>/dev/null | wc -l)
193  if [ "$pmus" -lt 1 ]
194  then
195    pmus=1
196  fi
197
198  # Run default Perf stat
199  cycles_events=$(perf stat -- true 2>&1 | grep -E "/cycles/[uH]*|  cycles[:uH]*  " -c)
200
201  # The expectation is that default output will have a cycles events on each
202  # hybrid PMU. In situations with no cycles PMU events, like virtualized, this
203  # can fall back to task-clock and so the end count may be 0. Fail if neither
204  # condition holds.
205  if [ "$pmus" -ne "$cycles_events" ] && [ "0" -ne "$cycles_events" ]
206  then
207    echo "hybrid test [Found $pmus PMUs but $cycles_events cycles events. Failed]"
208    err=1
209    return
210  fi
211  echo "hybrid test [Success]"
212}
213
214test_default_stat
215test_stat_record_report
216test_stat_record_script
217test_stat_repeat_weak_groups
218test_topdown_groups
219test_topdown_weak_groups
220test_cputype
221test_hybrid
222exit $err
223