perf test: test_intel_pt.sh: Tidy some perf record options
[platform/kernel/linux-rpi.git] / tools / perf / tests / shell / test_intel_pt.sh
1 #!/bin/sh
2 # Miscellaneous Intel PT testing
3 # SPDX-License-Identifier: GPL-2.0
4
5 set -e
6
7 # Skip if no Intel PT
8 perf list | grep -q 'intel_pt//' || exit 2
9
10 shelldir=$(dirname "$0")
11 . "${shelldir}"/lib/waiting.sh
12
13 skip_cnt=0
14 ok_cnt=0
15 err_cnt=0
16
17 temp_dir=$(mktemp -d /tmp/perf-test-intel-pt-sh.XXXXXXXXXX)
18
19 tmpfile="${temp_dir}/tmp-perf.data"
20 perfdatafile="${temp_dir}/test-perf.data"
21 outfile="${temp_dir}/test-out.txt"
22 errfile="${temp_dir}/test-err.txt"
23 workload="${temp_dir}/workload"
24 awkscript="${temp_dir}/awkscript"
25
26 cleanup()
27 {
28         trap - EXIT TERM INT
29         sane=$(echo "${temp_dir}" | cut -b 1-26)
30         if [ "${sane}" = "/tmp/perf-test-intel-pt-sh" ] ; then
31                 echo "--- Cleaning up ---"
32                 rm -f "${temp_dir}/"*
33                 rmdir "${temp_dir}"
34         fi
35 }
36
37 trap_cleanup()
38 {
39         cleanup
40         exit 1
41 }
42
43 trap trap_cleanup EXIT TERM INT
44
45 # perf record for testing without decoding
46 perf_record_no_decode()
47 {
48         # Options to speed up recording: no post-processing, no build-id cache update,
49         # and no BPF events.
50         perf record -B -N --no-bpf-event "$@"
51 }
52
53 have_workload=false
54 cat << _end_of_file_ | /usr/bin/cc -o "${workload}" -xc - -pthread && have_workload=true
55 #include <time.h>
56 #include <pthread.h>
57
58 void work(void) {
59         struct timespec tm = {
60                 .tv_nsec = 1000000,
61         };
62         int i;
63
64         /* Run for about 30 seconds */
65         for (i = 0; i < 30000; i++)
66                 nanosleep(&tm, NULL);
67 }
68
69 void *threadfunc(void *arg) {
70         work();
71         return NULL;
72 }
73
74 int main(void) {
75         pthread_t th;
76
77         pthread_create(&th, NULL, threadfunc, NULL);
78         work();
79         pthread_join(th, NULL);
80         return 0;
81 }
82 _end_of_file_
83
84 can_cpu_wide()
85 {
86         echo "Checking for CPU-wide recording on CPU $1"
87         if ! perf_record_no_decode -o "${tmpfile}" -e dummy:u -C "$1" true >/dev/null 2>&1 ; then
88                 echo "No so skipping"
89                 return 2
90         fi
91         echo OK
92         return 0
93 }
94
95 test_system_wide_side_band()
96 {
97         echo "--- Test system-wide sideband ---"
98
99         # Need CPU 0 and CPU 1
100         can_cpu_wide 0 || return $?
101         can_cpu_wide 1 || return $?
102
103         # Record on CPU 0 a task running on CPU 1
104         perf_record_no_decode -o "${perfdatafile}" -e intel_pt//u -C 0 -- taskset --cpu-list 1 uname
105
106         # Should get MMAP events from CPU 1 because they can be needed to decode
107         mmap_cnt=$(perf script -i "${perfdatafile}" --no-itrace --show-mmap-events -C 1 2>/dev/null | grep -c MMAP)
108
109         if [ "${mmap_cnt}" -gt 0 ] ; then
110                 echo OK
111                 return 0
112         fi
113
114         echo "Failed to record MMAP events on CPU 1 when tracing CPU 0"
115         return 1
116 }
117
118 can_kernel()
119 {
120         perf_record_no_decode -o "${tmpfile}" -e dummy:k true >/dev/null 2>&1 || return 2
121         return 0
122 }
123
124 test_per_thread()
125 {
126         k="$1"
127         desc="$2"
128
129         echo "--- Test per-thread ${desc}recording ---"
130
131         if ! $have_workload ; then
132                 echo "No workload, so skipping"
133                 return 2
134         fi
135
136         if [ "${k}" = "k" ] ; then
137                 can_kernel || return 2
138         fi
139
140         cat <<- "_end_of_file_" > "${awkscript}"
141         BEGIN {
142                 s = "[ ]*"
143                 u = s"[0-9]+"s
144                 d = s"[0-9-]+"s
145                 x = s"[0-9a-fA-FxX]+"s
146                 mmapping = "idx"u": mmapping fd"u
147                 set_output = "idx"u": set output fd"u"->"u
148                 perf_event_open = "sys_perf_event_open: pid"d"cpu"d"group_fd"d"flags"x"="u
149         }
150
151         /perf record opening and mmapping events/ {
152                 if (!done)
153                         active = 1
154         }
155
156         /perf record done opening and mmapping events/ {
157                 active = 0
158                 done = 1
159         }
160
161         $0 ~ perf_event_open && active {
162                 match($0, perf_event_open)
163                 $0 = substr($0, RSTART, RLENGTH)
164                 pid = $3
165                 cpu = $5
166                 fd = $11
167                 print "pid " pid " cpu " cpu " fd " fd " : " $0
168                 fd_array[fd] = fd
169                 pid_array[fd] = pid
170                 cpu_array[fd] = cpu
171         }
172
173         $0 ~ mmapping && active  {
174                 match($0, mmapping)
175                 $0 = substr($0, RSTART, RLENGTH)
176                 fd = $5
177                 print "fd " fd " : " $0
178                 if (fd in fd_array) {
179                         mmap_array[fd] = 1
180                 } else {
181                         print "Unknown fd " fd
182                         exit 1
183                 }
184         }
185
186         $0 ~ set_output && active {
187                 match($0, set_output)
188                 $0 = substr($0, RSTART, RLENGTH)
189                 fd = $6
190                 fd_to = $8
191                 print "fd " fd " fd_to " fd_to " : " $0
192                 if (fd in fd_array) {
193                         if (fd_to in fd_array) {
194                                 set_output_array[fd] = fd_to
195                         } else {
196                                 print "Unknown fd " fd_to
197                                 exit 1
198                         }
199                 } else {
200                         print "Unknown fd " fd
201                         exit 1
202                 }
203         }
204
205         END {
206                 print "Checking " length(fd_array) " fds"
207                 for (fd in fd_array) {
208                         if (fd in mmap_array) {
209                                 pid = pid_array[fd]
210                                 if (pid != -1) {
211                                         if (pid in pids) {
212                                                 print "More than 1 mmap for PID " pid
213                                                 exit 1
214                                         }
215                                         pids[pid] = 1
216                                 }
217                                 cpu = cpu_array[fd]
218                                 if (cpu != -1) {
219                                         if (cpu in cpus) {
220                                                 print "More than 1 mmap for CPU " cpu
221                                                 exit 1
222                                         }
223                                         cpus[cpu] = 1
224                                 }
225                         } else if (!(fd in set_output_array)) {
226                                 print "No mmap for fd " fd
227                                 exit 1
228                         }
229                 }
230                 n = length(pids)
231                 if (n != thread_cnt) {
232                         print "Expected " thread_cnt " per-thread mmaps - found " n
233                         exit 1
234                 }
235         }
236         _end_of_file_
237
238         $workload &
239         w1=$!
240         $workload &
241         w2=$!
242         echo "Workload PIDs are $w1 and $w2"
243         wait_for_threads ${w1} 2
244         wait_for_threads ${w2} 2
245
246         perf_record_no_decode -o "${perfdatafile}" -e intel_pt//u"${k}" -vvv --per-thread -p "${w1},${w2}" 2>"${errfile}" >"${outfile}" &
247         ppid=$!
248         echo "perf PID is $ppid"
249         wait_for_perf_to_start ${ppid} "${errfile}" || return 1
250
251         kill ${w1}
252         wait_for_process_to_exit ${w1} || return 1
253         is_running ${ppid} || return 1
254
255         kill ${w2}
256         wait_for_process_to_exit ${w2} || return 1
257         wait_for_process_to_exit ${ppid} || return 1
258
259         awk -v thread_cnt=4 -f "${awkscript}" "${errfile}" || return 1
260
261         echo OK
262         return 0
263 }
264
265 count_result()
266 {
267         if [ "$1" -eq 2 ] ; then
268                 skip_cnt=$((skip_cnt + 1))
269                 return
270         fi
271         if [ "$1" -eq 0 ] ; then
272                 ok_cnt=$((ok_cnt + 1))
273                 return
274         fi
275         err_cnt=$((err_cnt + 1))
276 }
277
278 ret=0
279 test_system_wide_side_band || ret=$? ; count_result $ret ; ret=0
280 test_per_thread "" "" || ret=$? ; count_result $ret ; ret=0
281 test_per_thread "k" "(incl. kernel) " || ret=$? ; count_result $ret ; ret=0
282
283 cleanup
284
285 echo "--- Done ---"
286
287 if [ ${err_cnt} -gt 0 ] ; then
288         exit 1
289 fi
290
291 if [ ${ok_cnt} -gt 0 ] ; then
292         exit 0
293 fi
294
295 exit 2