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