kvm.sh 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #!/bin/bash
  2. # perf kvm tests
  3. # SPDX-License-Identifier: GPL-2.0
  4. set -e
  5. err=0
  6. perfdata=$(mktemp /tmp/__perf_kvm_test.perf.data.XXXXX)
  7. qemu_pid_file=$(mktemp /tmp/__perf_kvm_test.qemu.pid.XXXXX)
  8. log_file=$(mktemp /tmp/__perf_kvm_test.live_log.XXXXX)
  9. cleanup() {
  10. rm -f "${perfdata}" "${log_file}"
  11. if [ -f "${qemu_pid_file}" ]; then
  12. if [ -s "${qemu_pid_file}" ]; then
  13. qemu_pid=$(cat "${qemu_pid_file}")
  14. if [ -n "${qemu_pid}" ]; then
  15. kill "${qemu_pid}" 2>/dev/null || true
  16. fi
  17. fi
  18. rm -f "${qemu_pid_file}"
  19. fi
  20. trap - EXIT TERM INT
  21. }
  22. trap_cleanup() {
  23. echo "Unexpected signal in ${FUNCNAME[1]}"
  24. cleanup
  25. exit 1
  26. }
  27. trap trap_cleanup EXIT TERM INT
  28. skip() {
  29. echo "Skip: $1"
  30. cleanup
  31. exit 2
  32. }
  33. test_kvm_stat() {
  34. echo "Testing perf kvm stat"
  35. echo "Recording kvm events for pid ${qemu_pid}..."
  36. if ! perf kvm stat record -p "${qemu_pid}" -o "${perfdata}" sleep 1; then
  37. echo "Failed to record kvm events"
  38. err=1
  39. return
  40. fi
  41. echo "Reporting kvm events..."
  42. if ! perf kvm -i "${perfdata}" stat report 2>&1 | grep -q "VM-EXIT"; then
  43. echo "Failed to find VM-EXIT in report"
  44. perf kvm -i "${perfdata}" stat report 2>&1
  45. err=1
  46. return
  47. fi
  48. echo "perf kvm stat test [Success]"
  49. }
  50. test_kvm_record_report() {
  51. echo "Testing perf kvm record/report"
  52. echo "Recording kvm profile for pid ${qemu_pid}..."
  53. # Use --host to avoid needing guest symbols/mounts for this simple test
  54. # We just want to verify the command runs and produces data
  55. # We run in background and kill it because 'perf kvm record' appends options
  56. # after the command, which breaks 'sleep' (e.g. it gets '-e cycles').
  57. perf kvm --host record -p "${qemu_pid}" -o "${perfdata}" &
  58. rec_pid=$!
  59. sleep 1
  60. kill -INT "${rec_pid}"
  61. wait "${rec_pid}" || true
  62. echo "Reporting kvm profile..."
  63. # Check for some standard output from report
  64. if ! perf kvm -i "${perfdata}" report --stdio 2>&1 | grep -q "Event count"; then
  65. echo "Failed to report kvm profile"
  66. perf kvm -i "${perfdata}" report --stdio 2>&1
  67. err=1
  68. return
  69. fi
  70. echo "perf kvm record/report test [Success]"
  71. }
  72. test_kvm_buildid_list() {
  73. echo "Testing perf kvm buildid-list"
  74. # We reuse the perf.data from the previous record test
  75. if ! perf kvm --host -i "${perfdata}" buildid-list 2>&1 | grep -q "."; then
  76. echo "Failed to list buildids"
  77. perf kvm --host -i "${perfdata}" buildid-list 2>&1
  78. err=1
  79. return
  80. fi
  81. echo "perf kvm buildid-list test [Success]"
  82. }
  83. test_kvm_stat_live() {
  84. echo "Testing perf kvm stat live"
  85. # Run perf kvm live for 5 seconds, monitoring that PID
  86. # Use sleep to keep stdin open but silent, preventing EOF loop or interactive spam
  87. if ! sleep 10 | timeout 5s perf kvm stat live -p "${qemu_pid}" > "${log_file}" 2>&1; then
  88. retval=$?
  89. if [ $retval -ne 124 ] && [ $retval -ne 0 ]; then
  90. echo "perf kvm stat live [Failed: perf kvm stat live failed to start or run (ret=$retval)]"
  91. head -n 50 "${log_file}"
  92. err=1
  93. return
  94. fi
  95. fi
  96. # Check for some sample data (percentage)
  97. if ! grep -E -q "[0-9]+\.[0-9]+%" "${log_file}"; then
  98. echo "perf kvm stat live [Failed: no sample percentage found]"
  99. head -n 50 "${log_file}"
  100. err=1
  101. return
  102. fi
  103. echo "perf kvm stat live test [Success]"
  104. }
  105. setup_qemu() {
  106. # Find qemu
  107. if [ "$(uname -m)" = "x86_64" ]; then
  108. qemu="qemu-system-x86_64"
  109. elif [ "$(uname -m)" = "aarch64" ]; then
  110. qemu="qemu-system-aarch64"
  111. elif [ "$(uname -m)" = "s390x" ]; then
  112. qemu="qemu-system-s390x"
  113. elif [ "$(uname -m)" = "ppc64le" ]; then
  114. qemu="qemu-system-ppc64"
  115. else
  116. qemu="qemu-system-$(uname -m)"
  117. fi
  118. if ! which -s "$qemu"; then
  119. skip "$qemu not found"
  120. fi
  121. if [ ! -r /dev/kvm ] || [ ! -w /dev/kvm ]; then
  122. skip "/dev/kvm not accessible"
  123. fi
  124. if ! perf kvm stat record -o /dev/null -a sleep 0.01 >/dev/null 2>&1; then
  125. skip "No permission to record kvm events"
  126. fi
  127. echo "Starting $qemu..."
  128. # Start qemu in background, detached, with pidfile
  129. # We use -display none -daemonize and a monitor to keep it alive/controllable if needed
  130. # We don't need a real kernel, just KVM active.
  131. if ! $qemu -enable-kvm -display none -daemonize -pidfile "${qemu_pid_file}" -monitor none; then
  132. echo "Failed to start qemu"
  133. err=1
  134. return
  135. fi
  136. # Wait a bit for qemu to start
  137. sleep 1
  138. qemu_pid=$(cat "${qemu_pid_file}")
  139. if ! kill -0 "${qemu_pid}" 2>/dev/null; then
  140. echo "Qemu process failed to stay alive"
  141. err=1
  142. return
  143. fi
  144. }
  145. setup_qemu
  146. if [ $err -eq 0 ]; then
  147. test_kvm_stat
  148. test_kvm_record_report
  149. test_kvm_buildid_list
  150. test_kvm_stat_live
  151. fi
  152. cleanup
  153. exit $err