summaryrefslogtreecommitdiff
path: root/tools/perf/tests/shell/kvm.sh
blob: 7b26368051ffdbad46617c0506696672845745bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#!/bin/bash
# perf kvm tests
# SPDX-License-Identifier: GPL-2.0

set -e

err=0
perfdata=$(mktemp /tmp/__perf_kvm_test.perf.data.XXXXX)
qemu_pid_file=$(mktemp /tmp/__perf_kvm_test.qemu.pid.XXXXX)
log_file=$(mktemp /tmp/__perf_kvm_test.live_log.XXXXX)

cleanup() {
	rm -f "${perfdata}" "${log_file}"
	if [ -f "${qemu_pid_file}" ]; then
		if [ -s "${qemu_pid_file}" ]; then
			qemu_pid=$(cat "${qemu_pid_file}")
			if [ -n "${qemu_pid}" ]; then
				kill "${qemu_pid}" 2>/dev/null || true
			fi
		fi
		rm -f "${qemu_pid_file}"
	fi
	trap - EXIT TERM INT
}

trap_cleanup() {
	echo "Unexpected signal in ${FUNCNAME[1]}"
	cleanup
	exit 1
}
trap trap_cleanup EXIT TERM INT

skip() {
	echo "Skip: $1"
	cleanup
	exit 2
}

test_kvm_stat() {
	echo "Testing perf kvm stat"

	local duration
	local success=false
	for duration in 1 2 4 8; do
		echo "Recording kvm events for pid ${qemu_pid} (duration ${duration}s)..."
		rm -f "${perfdata}" "${perfdata}".old
		if ! perf kvm stat record -p "${qemu_pid}" -o "${perfdata}" \
			sleep ${duration} >/dev/null 2>&1; then
			echo "perf kvm stat record failed, retrying..."
			continue
		fi

		if [ -e "${perfdata}" ] && \
			perf kvm -i "${perfdata}" stat report 2>&1 | grep -q "VM-EXIT"; then
			success=true
			break
		fi
		echo "No VM-EXIT events found, retrying..."
	done

	if [ "$success" = false ]; then
		echo "Failed to find VM-EXIT in report"
		perf kvm -i "${perfdata}" stat report 2>&1 || true
		err=1
		return
	fi

	echo "perf kvm stat test [Success]"
}

test_kvm_record_report() {
	echo "Testing perf kvm record/report"

	local duration
	local success=false
	for duration in 1 2 4 8; do
		echo "Recording kvm profile for pid ${qemu_pid} (duration ${duration}s)..."
		rm -f "${perfdata}" "${perfdata}".old

		perf kvm --host record -p "${qemu_pid}" -o "${perfdata}" \
			-e cpu-clock sleep ${duration}

		if [ -e "${perfdata}" ] && \
			perf kvm -i "${perfdata}" report --stdio 2>&1 | grep -q "Event count"; then
			success=true
			break
		fi
		echo "No samples or report failed, retrying..."
	done

	if [ "$success" = false ]; then
		echo "Failed to report kvm profile"
		perf kvm -i "${perfdata}" report --stdio 2>&1 || true
		err=1
		return
	fi

	echo "perf kvm record/report test [Success]"
}

test_kvm_buildid_list() {
	echo "Testing perf kvm buildid-list"

	# We reuse the perf.data from the previous record test
	if ! perf kvm --host -i "${perfdata}" buildid-list 2>&1 | grep -q "."; then
		echo "Failed to list buildids"
		perf kvm --host -i "${perfdata}" buildid-list 2>&1
		err=1
		return
	fi

	echo "perf kvm buildid-list test [Success]"
}

test_kvm_stat_live() {
	echo "Testing perf kvm stat live"

        # Run perf kvm live for 5 seconds, monitoring that PID
	# Use sleep to keep stdin open but silent, preventing EOF loop or interactive spam
	if ! sleep 10 | timeout 5s perf kvm stat live -p "${qemu_pid}" > "${log_file}" 2>&1; then
		retval=$?
		if [ $retval -ne 124 ] && [ $retval -ne 0 ]; then
			echo "perf kvm stat live [Failed: perf kvm stat live failed to start or run (ret=$retval)]"
			head -n 50 "${log_file}"
			err=1
			return
		fi
	fi

	# Check for some sample data (percentage)
	if ! grep -E -q "[0-9]+\.[0-9]+%" "${log_file}"; then
		echo "perf kvm stat live [Failed: no sample percentage found]"
		head -n 50 "${log_file}"
		err=1
		return
	fi

	echo "perf kvm stat live test [Success]"
}

test_kvm_default_event() {
	echo "Testing perf kvm record default event with command line"

	# Check if kvm record with default events handle command line arguments
	perf kvm record -p "${qemu_pid}" -o /dev/null sleep 1

	echo "perf kvm record default event [Success]"
}

setup_qemu() {
	# Find qemu
	if [ "$(uname -m)" = "x86_64" ]; then
		qemu="qemu-system-x86_64"
	elif [ "$(uname -m)" = "aarch64" ]; then
		qemu="qemu-system-aarch64"
	elif [ "$(uname -m)" = "s390x" ]; then
		qemu="qemu-system-s390x"
	elif [ "$(uname -m)" = "ppc64le" ]; then
		qemu="qemu-system-ppc64"
	else
		qemu="qemu-system-$(uname -m)"
	fi

	if ! which -s "$qemu"; then
		skip "$qemu not found"
	fi

	if [ ! -r /dev/kvm ] || [ ! -w /dev/kvm ]; then
		skip "/dev/kvm not accessible"
	fi

	if ! perf kvm stat record -o /dev/null -a sleep 0.01 >/dev/null 2>&1; then
		skip "No permission to record kvm events"
	fi

	echo "Starting $qemu..."
	# Start qemu in background, detached, with pidfile
	# We use -display none -daemonize and a monitor to keep it alive/controllable if needed
	# We don't need a real kernel, just KVM active.
	if ! $qemu -enable-kvm -display none -daemonize -pidfile "${qemu_pid_file}" -monitor none; then
		echo "Failed to start qemu"
		err=1
		return
	fi

	# Wait a bit for qemu to start
	sleep 1
	qemu_pid=$(cat "${qemu_pid_file}")

	if ! kill -0 "${qemu_pid}" 2>/dev/null; then
		echo "Qemu process failed to stay alive"
		err=1
		return
	fi
}

setup_qemu
if [ $err -eq 0 ]; then
	test_kvm_stat
	test_kvm_record_report
	test_kvm_buildid_list
	test_kvm_stat_live
	test_kvm_default_event
fi

cleanup
exit $err