lock_contention.sh 9.2 KB

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