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