xref: /linux/tools/perf/tests/shell/lock_contention.sh (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1#!/bin/bash
2# kernel lock contention analysis test
3# SPDX-License-Identifier: GPL-2.0
4
5set -e
6
7err=0
8perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX)
9result=$(mktemp /tmp/__perf_test.result.XXXXX)
10errout=$(mktemp /tmp/__perf_test.errout.XXXXX)
11
12# Workload to generate lock contention.
13# Using 1 group (-g 1) keeps runtime low while generating sufficient lock events.
14# We include -p (pipes) because socketpairs don't generate enough lock events on s390.
15msg_workload="perf bench sched messaging -g 1 -p"
16
17cleanup() {
18	rm -f ${perfdata}
19	rm -f ${result}
20	rm -f ${errout}
21	trap - EXIT TERM INT ERR
22}
23
24trap_cleanup() {
25	if (( $? == 139 )); then #SIGSEGV
26		err=1
27	fi
28	echo "Unexpected signal in ${FUNCNAME[1]}"
29	cleanup
30	exit ${err}
31}
32trap trap_cleanup EXIT TERM INT ERR
33
34check() {
35	if [ "$(id -u)" != 0 ]; then
36		echo "[Skip] No root permission"
37		err=2
38		exit
39	fi
40
41	if ! perf list tracepoint | grep -q lock:contention_begin; then
42		echo "[Skip] No lock contention tracepoints"
43		err=2
44		exit
45	fi
46
47	# shellcheck disable=SC2046
48	if [ `nproc` -lt 4 ]; then
49		echo "[Skip] Low number of CPUs (`nproc`), lock event cannot be triggered certainly"
50		err=2
51		exit
52	fi
53}
54
55test_record()
56{
57	echo "Testing perf lock record and perf lock contention"
58	perf lock record -o ${perfdata} -- ${msg_workload} > /dev/null 2>&1
59	# the output goes to the stderr and we expect only 1 output (-E 1)
60	perf lock contention -i ${perfdata} -E 1 -q 2> ${result}
61	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
62		echo "[Fail] Recorded result count is not 1:" "$(cat "${result}" | wc -l)"
63		err=1
64		exit
65	fi
66}
67
68test_bpf()
69{
70	echo "Testing perf lock contention --use-bpf"
71
72	if ! perf lock con -b true > /dev/null 2>&1 ; then
73		echo "[Skip] No BPF support"
74		return
75	fi
76
77	# the perf lock contention output goes to the stderr
78	perf lock con -a -b -E 1 -q -- ${msg_workload} > /dev/null 2> ${result}
79	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
80		echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)"
81		err=1
82		exit
83	fi
84}
85
86test_record_concurrent()
87{
88	echo "Testing perf lock record and perf lock contention at the same time"
89	perf lock record -o- -- ${msg_workload} 2> ${errout} | \
90	perf lock contention -i- -E 1 -q 2> ${result}
91	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
92		echo "[Fail] Recorded result count is not 1:" "$(cat "${result}" | wc -l)"
93		cat ${errout}
94		cat ${result}
95		err=1
96		exit
97	fi
98}
99
100test_aggr_task()
101{
102	echo "Testing perf lock contention --threads"
103	perf lock contention -i ${perfdata} -t -E 1 -q 2> ${result}
104	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
105		echo "[Fail] Recorded result count is not 1:" "$(cat "${result}" | wc -l)"
106		err=1
107		exit
108	fi
109
110	if ! perf lock con -b true > /dev/null 2>&1 ; then
111		return
112	fi
113
114	# the perf lock contention output goes to the stderr
115	perf lock con -a -b -t -E 1 -q -- ${msg_workload} > /dev/null 2> ${result}
116	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
117		echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)"
118		err=1
119		exit
120	fi
121}
122
123test_aggr_addr()
124{
125	echo "Testing perf lock contention --lock-addr"
126	perf lock contention -i ${perfdata} -l -E 1 -q 2> ${result}
127	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
128		echo "[Fail] Recorded result count is not 1:" "$(cat "${result}" | wc -l)"
129		err=1
130		exit
131	fi
132
133	if ! perf lock con -b true > /dev/null 2>&1 ; then
134		return
135	fi
136
137	# the perf lock contention output goes to the stderr
138	perf lock con -a -b -l -E 1 -q -- ${msg_workload} > /dev/null 2> ${result}
139	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
140		echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)"
141		err=1
142		exit
143	fi
144}
145
146test_aggr_cgroup()
147{
148	echo "Testing perf lock contention --lock-cgroup"
149
150	if ! perf lock con -b true > /dev/null 2>&1 ; then
151		echo "[Skip] No BPF support"
152		return
153	fi
154
155	# the perf lock contention output goes to the stderr
156	perf lock con -a -b --lock-cgroup -E 1 -q -- ${msg_workload} > /dev/null 2> ${result}
157	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
158		echo "[Fail] BPF result count is not 1:" "$(cat "${result}" | wc -l)"
159		err=1
160		exit
161	fi
162}
163
164test_type_filter()
165{
166	echo "Testing perf lock contention --type-filter (w/ spinlock)"
167	perf lock contention -i ${perfdata} -Y spinlock -q 2> ${result}
168	if [ "$(grep -c -v spinlock "${result}")" != "0" ]; then
169		echo "[Fail] Recorded result should not have non-spinlocks:" "$(cat "${result}")"
170		err=1
171		exit
172	fi
173
174	if ! perf lock con -b true > /dev/null 2>&1 ; then
175		return
176	fi
177
178	perf lock con -a -b -Y spinlock -q -- ${msg_workload} > /dev/null 2> ${result}
179	if [ "$(grep -c -v spinlock "${result}")" != "0" ]; then
180		echo "[Fail] BPF result should not have non-spinlocks:" "$(cat "${result}")"
181		err=1
182		exit
183	fi
184}
185
186test_lock_filter()
187{
188	echo "Testing perf lock contention --lock-filter (w/ tasklist_lock)"
189	perf lock contention -i ${perfdata} -l -q 2> ${result}
190	if [ "$(grep -c tasklist_lock "${result}")" != "1" ]; then
191		echo "[Skip] Could not find 'tasklist_lock'"
192		return
193	fi
194
195	perf lock contention -i ${perfdata} -L tasklist_lock -q 2> ${result}
196
197	# find out the type of tasklist_lock
198	test_lock_filter_type=$(head -1 "${result}" | awk '{ print $8 }' | sed -e 's/:.*//')
199
200	if [ "$(grep -c -v "${test_lock_filter_type}" "${result}")" != "0" ]; then
201		echo "[Fail] Recorded result should not have non-${test_lock_filter_type} locks:" "$(cat "${result}")"
202		err=1
203		exit
204	fi
205
206	if ! perf lock con -b true > /dev/null 2>&1 ; then
207		return
208	fi
209
210	perf lock con -a -b -L tasklist_lock -q -- ${msg_workload} > /dev/null 2> ${result}
211	if [ "$(grep -c -v "${test_lock_filter_type}" "${result}")" != "0" ]; then
212		echo "[Fail] BPF result should not have non-${test_lock_filter_type} locks:" "$(cat "${result}")"
213		err=1
214		exit
215	fi
216
217	perf lock con -b -L mmap_lock -q -- perf bench mem mmap -t 2 -l 10 > /dev/null 2> ${result}
218
219	# find out the type of mmap_lock
220	test_lock_filter_type=$(head -1 "${result}" | awk '{ print $8 }' | sed -e 's/:.*//')
221
222	if [ "$(grep -c -v "${test_lock_filter_type}" "${result}")" != "0" ]; then
223		echo "[Fail] BPF result should not have non-${test_lock_filter_type} locks:" "$(cat "${result}")"
224		err=1
225		exit
226	fi
227}
228
229test_stack_filter()
230{
231	echo "Testing perf lock contention --callstack-filter (w/ unix_stream)"
232	perf lock contention -i ${perfdata} -v -q 2> ${result}
233	if [ "$(grep -c unix_stream "${result}")" = "0" ]; then
234		echo "[Skip] Could not find 'unix_stream'"
235		return
236	fi
237
238	perf lock contention -i ${perfdata} -E 1 -S unix_stream -q 2> ${result}
239	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
240		echo "[Fail] Recorded result should have a lock from unix_stream:" "$(cat "${result}")"
241		err=1
242		exit
243	fi
244
245	if ! perf lock con -b true > /dev/null 2>&1 ; then
246		return
247	fi
248
249	perf lock con -a -b -S unix_stream -E 1 -q -- ${msg_workload} > /dev/null 2> ${result}
250	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
251		echo "[Fail] BPF result should have a lock from unix_stream:" "$(cat "${result}")"
252		err=1
253		exit
254	fi
255}
256
257test_aggr_task_stack_filter()
258{
259	echo "Testing perf lock contention --callstack-filter with task aggregation"
260	perf lock contention -i ${perfdata} -v -q 2> ${result}
261	if [ "$(grep -c unix_stream "${result}")" = "0" ]; then
262		echo "[Skip] Could not find 'unix_stream'"
263		return
264	fi
265
266	perf lock contention -i ${perfdata} -t -E 1 -S unix_stream -q 2> ${result}
267	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
268		echo "[Fail] Recorded result should have a task from unix_stream:" "$(cat "${result}")"
269		err=1
270		exit
271	fi
272
273	if ! perf lock con -b true > /dev/null 2>&1 ; then
274		return
275	fi
276
277	perf lock con -a -b -t -S unix_stream -E 1 -q -- ${msg_workload} > /dev/null 2> ${result}
278	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
279		echo "[Fail] BPF result should have a task from unix_stream:" "$(cat "${result}")"
280		err=1
281		exit
282	fi
283}
284test_cgroup_filter()
285{
286	echo "Testing perf lock contention --cgroup-filter"
287
288	if ! perf lock con -b true > /dev/null 2>&1 ; then
289		echo "[Skip] No BPF support"
290		return
291	fi
292
293	perf lock con -a -b --lock-cgroup -E 1 -F wait_total -q \
294		-- ${msg_workload} > /dev/null 2> ${result}
295	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
296		echo "[Fail] BPF result should have a cgroup result:" "$(cat "${result}")"
297		err=1
298		exit
299	fi
300
301	cgroup=$(cat "${result}" | awk '{ print $3 }')
302	perf lock con -a -b --lock-cgroup -E 1 -G "${cgroup}" -q \
303		-- ${msg_workload} > /dev/null 2> ${result}
304	if [ "$(cat "${result}" | wc -l)" != "1" ]; then
305		echo "[Fail] BPF result should have a result with cgroup filter:" "$(cat "${cgroup}")"
306		err=1
307		exit
308	fi
309}
310
311
312test_csv_output()
313{
314	echo "Testing perf lock contention CSV output"
315	perf lock contention -i ${perfdata} -E 1 -x , --output ${result}
316	# count the number of commas in the header
317	# it should have 5: contended, total-wait, max-wait, avg-wait, type, caller
318	header=$(grep "# output:" ${result} | tr -d -c , | wc -c)
319	if [ "${header}" != "5" ]; then
320		echo "[Fail] Recorded result does not have enough output columns: ${header} != 5"
321		err=1
322		exit
323	fi
324	# count the number of commas in the output
325	output=$(grep -v "^#" ${result} | tr -d -c , | wc -c)
326	if [ "${header}" != "${output}" ]; then
327		echo "[Fail] Recorded result does not match the number of commas: ${header} != ${output}"
328		err=1
329		exit
330	fi
331
332	if ! perf lock con -b true > /dev/null 2>&1 ; then
333		echo "[Skip] No BPF support"
334		return
335	fi
336
337	# the perf lock contention output goes to the stderr
338	perf lock con -a -b -E 1 -x , --output ${result} -- ${msg_workload} > /dev/null 2>&1
339	output=$(grep -v "^#" ${result} | tr -d -c , | wc -c)
340	if [ "${header}" != "${output}" ]; then
341		echo "[Fail] BPF result does not match the number of commas: ${header} != ${output}"
342		err=1
343		exit
344	fi
345}
346
347check
348
349test_record
350test_bpf
351test_record_concurrent
352test_aggr_task
353test_aggr_addr
354test_aggr_cgroup
355test_type_filter
356test_lock_filter
357test_stack_filter
358test_aggr_task_stack_filter
359test_cgroup_filter
360test_csv_output
361
362cleanup
363exit ${err}
364