summaryrefslogtreecommitdiffstats
path: root/recipes-test/virt-test/files/qemu-tests-cyclictest.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-test/virt-test/files/qemu-tests-cyclictest.patch')
-rw-r--r--recipes-test/virt-test/files/qemu-tests-cyclictest.patch217
1 files changed, 217 insertions, 0 deletions
diff --git a/recipes-test/virt-test/files/qemu-tests-cyclictest.patch b/recipes-test/virt-test/files/qemu-tests-cyclictest.patch
new file mode 100644
index 0000000..f9ee831
--- /dev/null
+++ b/recipes-test/virt-test/files/qemu-tests-cyclictest.patch
@@ -0,0 +1,217 @@
1commit 61c087a0386ad04fd5579ad170b9d7933ed90c8c
2Author: Jonas Eriksson <jonas.eriksson@enea.com>
3Date: Fri Apr 25 16:00:12 2014 +0200
4
5 Add cyclictest test case
6
7 Cyclictest is a part of rt-tests used to test the responsiveness of a
8 system. While not developed for non-realtime systems, it can be used to
9 detect unexpected spikes in latencies within virtual machines.
10
11 Signed-off-by: Jonas Eriksson <jonas.eriksson@enea.com>
12 Upstream-Status: Pending
13
14diff --git a/qemu/tests/cfg/cyclictest.cfg b/qemu/tests/cfg/cyclictest.cfg
15new file mode 100644
16index 0000000..d142b7d
17--- /dev/null
18+++ b/qemu/tests/cfg/cyclictest.cfg
19@@ -0,0 +1,25 @@
20+- cyclictest:
21+ virt_test_type = qemu
22+ only Linux
23+ type = cyclictest
24+ kill_vm = yes
25+ kill_vm_gracefully = no
26+ backup_image_before_testing = yes
27+
28+ # It is possible to tune the arguments to cyclictest using the
29+ # cyclictest_args variable.
30+ #cyclictest_args = "-D 30m --smp"
31+ # It is formatted using a dictionary where a number of properties of the
32+ # target is available, for example 'num_cpus' which is the number of cpus,
33+ # and 'mask_all_cpus', which is the string "0-[num_cpus - 1]". See
34+ # cyclictest.py for all available values. The following is for example
35+ # equivalent to "-D 30m --smp":
36+ #cyclictest_args = "-D 30m -t %(num_cpus)s -a %(mask_all_cpus)s"
37+ # Default timeout is 35 min
38+ #cyclictest_timeout_s = 2100
39+
40+ # After the run, the test case will check all values against the below
41+ # allowed maximum values of each result.
42+ #allowed_max = 50
43+ #allowed_avg = 30
44+ #allowed_act =
45diff --git a/qemu/tests/cyclictest.py b/qemu/tests/cyclictest.py
46new file mode 100644
47index 0000000..0ad447f
48--- /dev/null
49+++ b/qemu/tests/cyclictest.py
50@@ -0,0 +1,167 @@
51+import logging
52+import re
53+from autotest.client import utils
54+from autotest.client.shared import error
55+from virttest import remote, utils_misc, utils_test
56+
57+
58+# Information about cyclictest is available here:
59+# https://rt.wiki.kernel.org/index.php/Cyclictest
60+@error.context_aware
61+def run(test, params, env):
62+ """
63+ Test Steps:
64+
65+ 1. Check availability of cyclictest
66+ 2. Get information about the target
67+ 3. Run cyclictest
68+ 4. Parse and check result
69+
70+ Params:
71+ :param test: QEMU test object.
72+ :param params: Dictionary with the test parameters.
73+ :param env: Dictionary with test environment.
74+ """
75+
76+ def parse_cyclictest(output_string):
77+ """
78+ Parses the output from a cyclictest run into a nested dictionary.
79+
80+ Example input:
81+ # /dev/cpu_dma_latency set to 0us
82+ T: 0 ( 7064) P: 0 I:1000 C: 2000 Min: 1 Act: 2 Avg: 3 Max: 751
83+ T: 1 ( 7065) P: 0 I:1500 C: 1334 Min: 1 Act: 3 Avg: 2 Max: 167
84+ Example output:
85+ { '0': { 'Act': '2',
86+ 'Avg': '3',
87+ 'C': '2000',
88+ 'I': '1000',
89+ 'Max': '751',
90+ 'Min': '1',
91+ 'P': '0',
92+ 'T': '0 ( 7064)'},
93+ '1': { 'Act': '3',
94+ 'Avg': '2',
95+ 'C': '1334',
96+ 'I': '1500',
97+ 'Max': '167',
98+ 'Min': '1',
99+ 'P': '0',
100+ 'T': '1 ( 7065)'}
101+ }
102+
103+ Params:
104+ :param output_string: Output data from cyclictest
105+ :returns: Nested dict of parsed output
106+ """
107+ output = output_string.split("\n")
108+ result = {}
109+ for row_string in output:
110+ # Only process lines beginning with "T:"
111+ if re.match("^T:", row_string):
112+ # Since the different segments of a line are not comma
113+ # separated (or similar), it is tricky to split them right
114+ # away. Instead, match the "header" of an entry (e.g. "P:"),
115+ # and add a line break before them. Then split the string using
116+ # the added line breaks.
117+ row_list = re.sub("\S+:", "\n\g<0>", row_string).split("\n")
118+ sub_result = {}
119+ for part in row_list:
120+ part = re.sub("\s+$", "", part)
121+ if not part == "":
122+ # The strings are now on the form "P: 0" (for example)
123+ kv = re.split(":\s*", part)
124+ sub_result[kv[0]] = kv[1]
125+ # Use the thread as an index in the outer result dict. Avoid
126+ # using an array to be able to process results in any order and
127+ # with gaps.
128+ thread = (sub_result['T'].split(" "))[0]
129+ result[thread] = sub_result
130+ return result
131+
132+ def check_val_ceiling(allowed_ceil, val, name, cpu):
133+ """
134+ Check that the value :val: is not larger than :allowed_ceil:, and if so
135+ log the error using :name: as the name of the value.
136+
137+ Params:
138+ :allowed_ceil: Maximum allowed vaule of :val:. If None, "" or 0,
139+ just return 0.
140+ :val: The value to be tested
141+ :name: The name of the value, for logging
142+ :cpu: CPU number for error reporting
143+ :returns: Number of errors; 0 if successful.
144+ """
145+ if allowed_ceil > 0 and val > allowed_ceil:
146+ logging.error("%s value too large: %d for CPU %d" % (name,
147+ val,
148+ cpu))
149+ return 1
150+ return 0
151+
152+ # Get/setup VM and session
153+ vm_name = params.get("vm_name", "main_vm")
154+ vm = env.get_vm(params[vm_name])
155+ vm.verify_alive()
156+ timeout = int(params.get("timeout", 2400))
157+ session = vm.wait_for_login(timeout=timeout)
158+
159+ try:
160+ error.context("Check availability of cyclictest")
161+ if session.cmd_status("which cyclictest"):
162+ raise error.TestFail("Test application not available")
163+
164+ error.context("Get information about the target")
165+ # Get the number of CPUs on target
166+ cmd = "grep -c '^processor[[:space:]]*:' /proc/cpuinfo"
167+ output = session.cmd_output(cmd)
168+ num_cpus = int(output)
169+ if num_cpus == 0:
170+ raise error.TestFail("Unable to get number of CPUs on target")
171+
172+ # Get arguments from the configuration
173+ default_args = "-D 30m --smp"
174+ cyclictest_args = params.get("cyclictest_args", default_args)
175+ fmt_dict = {
176+ 'num_cpus': num_cpus,
177+ 'last_cpu': num_cpus - 1,
178+ 'mask_all_cpus': "0-%d" % (num_cpus - 1),
179+ }
180+ formatted_args = cyclictest_args % fmt_dict
181+
182+ error.context("Run cyclictest")
183+ timeout_s = params.get("cyclictest_timeout_s", 60*35)
184+ cmd = "cyclictest -q %s" % formatted_args
185+ status, ct_output = session.cmd_status_output(cmd,
186+ timeout=timeout_s)
187+ if status:
188+ logging.error("Cyclic test output: %s" % ct_output)
189+ raise error.TestFail("Cyclictest returned %d" % status)
190+
191+ error.context("Parse and check result")
192+ results = parse_cyclictest(ct_output)
193+ if not results:
194+ logging.error("Cyclic test output: %s" % ct_output)
195+ raise error.TestFail("Parsing of cyclictest output failed")
196+
197+ # Get check values. By default, don't check Act.
198+ allowed_max = int(params.get("allowed_max", 50))
199+ allowed_avg = int(params.get("allowed_avg", 30))
200+ allowed_act = int(params.get("allowed_act", 0))
201+
202+ fails = 0
203+ for key, result in results.iteritems():
204+ cpu = int(key)
205+ max_val = int(result['Max'])
206+ avg_val = int(result['Avg'])
207+ act_val = int(result['Act'])
208+ fails += check_val_ceiling(allowed_max, max_val, "Max", cpu)
209+ fails += check_val_ceiling(allowed_avg, avg_val, "Avg", cpu)
210+ fails += check_val_ceiling(allowed_act, act_val, "Act", cpu)
211+
212+ if fails > 0:
213+ logging.error("Cyclic test output: %s" % ct_output)
214+ raise error.TestFail("Values out of bounds, see log")
215+
216+ finally:
217+ session.close()