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