]> git.proxmox.com Git - mirror_qemu.git/blame - scripts/device-crash-test
ppc4xx: Export ECB and PLB emulation
[mirror_qemu.git] / scripts / device-crash-test
CommitLineData
23ea4f30
EH
1#!/usr/bin/env python2.7
2#
3# Copyright (c) 2017 Red Hat Inc
4#
5# Author:
6# Eduardo Habkost <ehabkost@redhat.com>
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License along
19# with this program; if not, write to the Free Software Foundation, Inc.,
20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22"""
23Run QEMU with all combinations of -machine and -device types,
24check for crashes and unexpected errors.
25"""
26
27import sys
28import os
29import glob
30import logging
31import traceback
32import re
33import random
34import argparse
35from itertools import chain
36
37sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'scripts'))
38from qemu import QEMUMachine
39
40logger = logging.getLogger('device-crash-test')
41dbg = logger.debug
42
43
44# Purposes of the following whitelist:
45# * Avoiding verbose log messages when we find known non-fatal
46# (exitcode=1) errors
47# * Avoiding fatal errors when we find known crashes
48# * Skipping machines/devices that are known not to work out of
49# the box, when running in --quick mode
50#
51# Keeping the whitelist updated is desirable, but not required,
52# because unexpected cases where QEMU exits with exitcode=1 will
53# just trigger a INFO message.
54
55# Valid whitelist entry keys:
56# * accel: regexp, full match only
57# * machine: regexp, full match only
58# * device: regexp, full match only
59# * log: regexp, partial match allowed
60# * exitcode: if not present, defaults to 1. If None, matches any exitcode
61# * warn: if True, matching failures will be logged as warnings
62# * expected: if True, QEMU is expected to always fail every time
63# when testing the corresponding test case
64# * loglevel: log level of log output when there's a match.
65ERROR_WHITELIST = [
66 # Machines that won't work out of the box:
67 # MACHINE | ERROR MESSAGE
68 {'machine':'niagara', 'expected':True}, # Unable to load a firmware for -M niagara
69 {'machine':'boston', 'expected':True}, # Please provide either a -kernel or -bios argument
70 {'machine':'leon3_generic', 'expected':True}, # Can't read bios image (null)
71
72 # devices that don't work out of the box because they require extra options to "-device DEV":
73 # DEVICE | ERROR MESSAGE
74 {'device':'.*-(i386|x86_64)-cpu', 'expected':True}, # CPU socket-id is not set
75 {'device':'ARM,bitband-memory', 'expected':True}, # source-memory property not set
76 {'device':'arm.cortex-a9-global-timer', 'expected':True}, # a9_gtimer_realize: num-cpu must be between 1 and 4
77 {'device':'arm_mptimer', 'expected':True}, # num-cpu must be between 1 and 4
78 {'device':'armv7m', 'expected':True}, # memory property was not set
79 {'device':'aspeed.scu', 'expected':True}, # Unknown silicon revision: 0x0
80 {'device':'aspeed.sdmc', 'expected':True}, # Unknown silicon revision: 0x0
81 {'device':'bcm2835-dma', 'expected':True}, # bcm2835_dma_realize: required dma-mr link not found: Property '.dma-mr' not found
82 {'device':'bcm2835-fb', 'expected':True}, # bcm2835_fb_realize: required vcram-base property not set
83 {'device':'bcm2835-mbox', 'expected':True}, # bcm2835_mbox_realize: required mbox-mr link not found: Property '.mbox-mr' not found
84 {'device':'bcm2835-peripherals', 'expected':True}, # bcm2835_peripherals_realize: required ram link not found: Property '.ram' not found
85 {'device':'bcm2835-property', 'expected':True}, # bcm2835_property_realize: required fb link not found: Property '.fb' not found
86 {'device':'bcm2835_gpio', 'expected':True}, # bcm2835_gpio_realize: required sdhci link not found: Property '.sdbus-sdhci' not found
87 {'device':'bcm2836', 'expected':True}, # bcm2836_realize: required ram link not found: Property '.ram' not found
88 {'device':'cfi.pflash01', 'expected':True}, # attribute "sector-length" not specified or zero.
89 {'device':'cfi.pflash02', 'expected':True}, # attribute "sector-length" not specified or zero.
90 {'device':'icp', 'expected':True}, # icp_realize: required link 'xics' not found: Property '.xics' not found
91 {'device':'ics', 'expected':True}, # ics_base_realize: required link 'xics' not found: Property '.xics' not found
92 # "-device ide-cd" does work on more recent QEMU versions, so it doesn't have expected=True
93 {'device':'ide-cd'}, # No drive specified
94 {'device':'ide-drive', 'expected':True}, # No drive specified
95 {'device':'ide-hd', 'expected':True}, # No drive specified
96 {'device':'ipmi-bmc-extern', 'expected':True}, # IPMI external bmc requires chardev attribute
97 {'device':'isa-debugcon', 'expected':True}, # Can't create serial device, empty char device
98 {'device':'isa-ipmi-bt', 'expected':True}, # IPMI device requires a bmc attribute to be set
99 {'device':'isa-ipmi-kcs', 'expected':True}, # IPMI device requires a bmc attribute to be set
100 {'device':'isa-parallel', 'expected':True}, # Can't create serial device, empty char device
101 {'device':'isa-serial', 'expected':True}, # Can't create serial device, empty char device
102 {'device':'ivshmem', 'expected':True}, # You must specify either 'shm' or 'chardev'
103 {'device':'ivshmem-doorbell', 'expected':True}, # You must specify a 'chardev'
104 {'device':'ivshmem-plain', 'expected':True}, # You must specify a 'memdev'
105 {'device':'kvm-pci-assign', 'expected':True}, # no host device specified
106 {'device':'loader', 'expected':True}, # please include valid arguments
107 {'device':'nand', 'expected':True}, # Unsupported NAND block size 0x1
108 {'device':'nvdimm', 'expected':True}, # 'memdev' property is not set
109 {'device':'nvme', 'expected':True}, # Device initialization failed
110 {'device':'pc-dimm', 'expected':True}, # 'memdev' property is not set
111 {'device':'pci-bridge', 'expected':True}, # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
112 {'device':'pci-bridge-seat', 'expected':True}, # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
113 {'device':'pci-serial', 'expected':True}, # Can't create serial device, empty char device
114 {'device':'pci-serial-2x', 'expected':True}, # Can't create serial device, empty char device
115 {'device':'pci-serial-4x', 'expected':True}, # Can't create serial device, empty char device
116 {'device':'pxa2xx-dma', 'expected':True}, # channels value invalid
117 {'device':'pxb', 'expected':True}, # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
118 {'device':'scsi-block', 'expected':True}, # drive property not set
119 {'device':'scsi-disk', 'expected':True}, # drive property not set
120 {'device':'scsi-generic', 'expected':True}, # drive property not set
121 {'device':'scsi-hd', 'expected':True}, # drive property not set
122 {'device':'spapr-pci-host-bridge', 'expected':True}, # BUID not specified for PHB
123 {'device':'spapr-pci-vfio-host-bridge', 'expected':True}, # BUID not specified for PHB
124 {'device':'spapr-rng', 'expected':True}, # spapr-rng needs an RNG backend!
125 {'device':'spapr-vty', 'expected':True}, # chardev property not set
126 {'device':'tpm-tis', 'expected':True}, # tpm_tis: backend driver with id (null) could not be found
127 {'device':'unimplemented-device', 'expected':True}, # property 'size' not specified or zero
128 {'device':'usb-braille', 'expected':True}, # Property chardev is required
129 {'device':'usb-mtp', 'expected':True}, # x-root property must be configured
130 {'device':'usb-redir', 'expected':True}, # Parameter 'chardev' is missing
131 {'device':'usb-serial', 'expected':True}, # Property chardev is required
132 {'device':'usb-storage', 'expected':True}, # drive property not set
133 {'device':'vfio-amd-xgbe', 'expected':True}, # -device vfio-amd-xgbe: vfio error: wrong host device name
134 {'device':'vfio-calxeda-xgmac', 'expected':True}, # -device vfio-calxeda-xgmac: vfio error: wrong host device name
135 {'device':'vfio-pci', 'expected':True}, # No provided host device
136 {'device':'vfio-pci-igd-lpc-bridge', 'expected':True}, # VFIO dummy ISA/LPC bridge must have address 1f.0
137 {'device':'vhost-scsi.*', 'expected':True}, # vhost-scsi: missing wwpn
138 {'device':'vhost-vsock-device', 'expected':True}, # guest-cid property must be greater than 2
139 {'device':'vhost-vsock-pci', 'expected':True}, # guest-cid property must be greater than 2
140 {'device':'virtio-9p-ccw', 'expected':True}, # 9pfs device couldn't find fsdev with the id = NULL
141 {'device':'virtio-9p-device', 'expected':True}, # 9pfs device couldn't find fsdev with the id = NULL
142 {'device':'virtio-9p-pci', 'expected':True}, # 9pfs device couldn't find fsdev with the id = NULL
143 {'device':'virtio-blk-ccw', 'expected':True}, # drive property not set
144 {'device':'virtio-blk-device', 'expected':True}, # drive property not set
145 {'device':'virtio-blk-device', 'expected':True}, # drive property not set
146 {'device':'virtio-blk-pci', 'expected':True}, # drive property not set
147 {'device':'virtio-crypto-ccw', 'expected':True}, # 'cryptodev' parameter expects a valid object
148 {'device':'virtio-crypto-device', 'expected':True}, # 'cryptodev' parameter expects a valid object
149 {'device':'virtio-crypto-pci', 'expected':True}, # 'cryptodev' parameter expects a valid object
150 {'device':'virtio-input-host-device', 'expected':True}, # evdev property is required
151 {'device':'virtio-input-host-pci', 'expected':True}, # evdev property is required
152 {'device':'xen-pvdevice', 'expected':True}, # Device ID invalid, it must always be supplied
153 {'device':'vhost-vsock-ccw', 'expected':True}, # guest-cid property must be greater than 2
154 {'device':'ALTR.timer', 'expected':True}, # "clock-frequency" property must be provided
155 {'device':'zpci', 'expected':True}, # target must be defined
156 {'device':'pnv-(occ|icp|lpc)', 'expected':True}, # required link 'xics' not found: Property '.xics' not found
157 {'device':'powernv-cpu-.*', 'expected':True}, # pnv_core_realize: required link 'xics' not found: Property '.xics' not found
158
159 # ioapic devices are already created by pc and will fail:
160 {'machine':'q35|pc.*', 'device':'kvm-ioapic', 'expected':True}, # Only 1 ioapics allowed
161 {'machine':'q35|pc.*', 'device':'ioapic', 'expected':True}, # Only 1 ioapics allowed
162
163 # KVM-specific devices shouldn't be tried without accel=kvm:
164 {'accel':'(?!kvm).*', 'device':'kvmclock', 'expected':True},
165 {'accel':'(?!kvm).*', 'device':'kvm-pci-assign', 'expected':True},
166
167 # xen-specific machines and devices:
168 {'accel':'(?!xen).*', 'machine':'xen.*', 'expected':True},
169 {'accel':'(?!xen).*', 'device':'xen-.*', 'expected':True},
170
171 # this fails on some machine-types, but not all, so they don't have expected=True:
172 {'device':'vmgenid'}, # vmgenid requires DMA write support in fw_cfg, which this machine type does not provide
173
174 # Silence INFO messages for errors that are common on multiple
175 # devices/machines:
176 {'log':r"No '[\w-]+' bus found for device '[\w-]+'"},
177 {'log':r"images* must be given with the 'pflash' parameter"},
178 {'log':r"(Guest|ROM|Flash|Kernel) image must be specified"},
179 {'log':r"[cC]ould not load [\w ]+ (BIOS|bios) '[\w-]+\.bin'"},
180 {'log':r"Couldn't find rom image '[\w-]+\.bin'"},
181 {'log':r"speed mismatch trying to attach usb device"},
182 {'log':r"Can't create a second ISA bus"},
183 {'log':r"duplicate fw_cfg file name"},
184 # sysbus-related error messages: most machines reject most dynamic sysbus devices:
185 {'log':r"Option '-device [\w.,-]+' cannot be handled by this machine"},
186 {'log':r"Device [\w.,-]+ is not supported by this machine yet"},
187 {'log':r"Device [\w.,-]+ can not be dynamically instantiated"},
188 {'log':r"Platform Bus: Can not fit MMIO region of size "},
189 # other more specific errors we will ignore:
23ea4f30
EH
190 {'device':'.*-spapr-cpu-core', 'log':r"CPU core type should be"},
191 {'log':r"MSI(-X)? is not supported by interrupt controller"},
192 {'log':r"pxb-pcie? devices cannot reside on a PCIe? bus"},
193 {'log':r"Ignoring smp_cpus value"},
194 {'log':r"sd_init failed: Drive 'sd0' is already in use because it has been automatically connected to another device"},
195 {'log':r"This CPU requires a smaller page size than the system is using"},
196 {'log':r"MSI-X support is mandatory in the S390 architecture"},
197 {'log':r"rom check and register reset failed"},
198 {'log':r"Unable to initialize GIC, CPUState for CPU#0 not valid"},
199 {'log':r"Multiple VT220 operator consoles are not supported"},
200 {'log':r"core 0 already populated"},
201 {'log':r"could not find stage1 bootloader"},
202
203 # other exitcode=1 failures not listed above will just generate INFO messages:
204 {'exitcode':1, 'loglevel':logging.INFO},
205
206 # KNOWN CRASHES:
207 # Known crashes will generate error messages, but won't be fatal.
208 # Those entries must be removed once we fix the crashes.
209 {'exitcode':-6, 'log':r"Device 'serial0' is in use", 'loglevel':logging.ERROR},
210 {'exitcode':-6, 'log':r"spapr_rtas_register: Assertion .*rtas_table\[token\]\.name.* failed", 'loglevel':logging.ERROR},
211 {'exitcode':-6, 'log':r"qemu_net_client_setup: Assertion `!peer->peer' failed", 'loglevel':logging.ERROR},
212 {'exitcode':-6, 'log':r'RAMBlock "[\w.-]+" already registered', 'loglevel':logging.ERROR},
213 {'exitcode':-6, 'log':r"find_ram_offset: Assertion `size != 0' failed.", 'loglevel':logging.ERROR},
214 {'exitcode':-6, 'log':r"puv3_load_kernel: Assertion `kernel_filename != NULL' failed", 'loglevel':logging.ERROR},
215 {'exitcode':-6, 'log':r"add_cpreg_to_hashtable: code should not be reached", 'loglevel':logging.ERROR},
216 {'exitcode':-6, 'log':r"qemu_alloc_display: Assertion `surface->image != NULL' failed", 'loglevel':logging.ERROR},
217 {'exitcode':-6, 'log':r"Unexpected error in error_set_from_qdev_prop_error", 'loglevel':logging.ERROR},
218 {'exitcode':-6, 'log':r"Object .* is not an instance of type spapr-machine", 'loglevel':logging.ERROR},
219 {'exitcode':-6, 'log':r"Object .* is not an instance of type generic-pc-machine", 'loglevel':logging.ERROR},
220 {'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR},
2a6f395b 221 {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR},
23ea4f30
EH
222 {'exitcode':-11, 'device':'stm32f205-soc', 'loglevel':logging.ERROR, 'expected':True},
223 {'exitcode':-11, 'device':'xlnx,zynqmp', 'loglevel':logging.ERROR, 'expected':True},
224 {'exitcode':-11, 'device':'mips-cps', 'loglevel':logging.ERROR, 'expected':True},
225 {'exitcode':-11, 'device':'gus', 'loglevel':logging.ERROR, 'expected':True},
226 {'exitcode':-11, 'device':'a9mpcore_priv', 'loglevel':logging.ERROR, 'expected':True},
227 {'exitcode':-11, 'device':'a15mpcore_priv', 'loglevel':logging.ERROR, 'expected':True},
228 {'exitcode':-11, 'device':'isa-serial', 'loglevel':logging.ERROR, 'expected':True},
229 {'exitcode':-11, 'device':'sb16', 'loglevel':logging.ERROR, 'expected':True},
230 {'exitcode':-11, 'device':'cs4231a', 'loglevel':logging.ERROR, 'expected':True},
231 {'exitcode':-11, 'device':'arm-gicv3', 'loglevel':logging.ERROR, 'expected':True},
232 {'exitcode':-11, 'machine':'isapc', 'device':'.*-iommu', 'loglevel':logging.ERROR, 'expected':True},
233
234 # everything else (including SIGABRT and SIGSEGV) will be a fatal error:
235 {'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL},
236]
237
238
239def whitelistTestCaseMatch(wl, t):
240 """Check if a test case specification can match a whitelist entry
241
242 This only checks if a whitelist entry is a candidate match
243 for a given test case, it won't check if the test case
244 results/output match the entry. See whitelistResultMatch().
245 """
246 return (('machine' not in wl or
247 'machine' not in t or
248 re.match(wl['machine'] + '$', t['machine'])) and
249 ('accel' not in wl or
250 'accel' not in t or
251 re.match(wl['accel'] + '$', t['accel'])) and
252 ('device' not in wl or
253 'device' not in t or
254 re.match(wl['device'] + '$', t['device'])))
255
256
257def whitelistCandidates(t):
258 """Generate the list of candidates that can match a test case"""
259 for i, wl in enumerate(ERROR_WHITELIST):
260 if whitelistTestCaseMatch(wl, t):
261 yield (i, wl)
262
263
264def findExpectedResult(t):
265 """Check if there's an expected=True whitelist entry for a test case
266
267 Returns (i, wl) tuple, where i is the index in
268 ERROR_WHITELIST and wl is the whitelist entry itself.
269 """
270 for i, wl in whitelistCandidates(t):
271 if wl.get('expected'):
272 return (i, wl)
273
274
275def whitelistResultMatch(wl, r):
276 """Check if test case results/output match a whitelist entry
277
278 It is valid to call this function only if
279 whitelistTestCaseMatch() is True for the entry (e.g. on
280 entries returned by whitelistCandidates())
281 """
282 assert whitelistTestCaseMatch(wl, r['testcase'])
283 return ((wl.get('exitcode', 1) is None or
284 r['exitcode'] == wl.get('exitcode', 1)) and
285 ('log' not in wl or
286 re.search(wl['log'], r['log'], re.MULTILINE)))
287
288
289def checkResultWhitelist(r):
290 """Look up whitelist entry for a given test case result
291
292 Returns (i, wl) tuple, where i is the index in
293 ERROR_WHITELIST and wl is the whitelist entry itself.
294 """
295 for i, wl in whitelistCandidates(r['testcase']):
296 if whitelistResultMatch(wl, r):
297 return i, wl
298
299 raise Exception("this should never happen")
300
301
302def qemuOptsEscape(s):
303 """Escape option value QemuOpts"""
304 return s.replace(",", ",,")
305
306
307def formatTestCase(t):
308 """Format test case info as "key=value key=value" for prettier logging output"""
309 return ' '.join('%s=%s' % (k, v) for k, v in t.items())
310
311
312def qomListTypeNames(vm, **kwargs):
313 """Run qom-list-types QMP command, return type names"""
314 types = vm.command('qom-list-types', **kwargs)
315 return [t['name'] for t in types]
316
317
318def infoQDM(vm):
319 """Parse 'info qdm' output"""
320 args = {'command-line': 'info qdm'}
321 devhelp = vm.command('human-monitor-command', **args)
322 for l in devhelp.split('\n'):
323 l = l.strip()
324 if l == '' or l.endswith(':'):
325 continue
326 d = {'name': re.search(r'name "([^"]+)"', l).group(1),
327 'no-user': (re.search(', no-user', l) is not None)}
328 yield d
329
330
331class QemuBinaryInfo(object):
332 def __init__(self, binary, devtype):
333 if devtype is None:
334 devtype = 'device'
335
336 self.binary = binary
337 self._machine_info = {}
338
339 dbg("devtype: %r", devtype)
340 args = ['-S', '-machine', 'none,accel=kvm:tcg']
341 dbg("querying info for QEMU binary: %s", binary)
342 vm = QEMUMachine(binary=binary, args=args)
343 vm.launch()
344 try:
345 self.alldevs = set(qomListTypeNames(vm, implements=devtype, abstract=False))
346 # there's no way to query DeviceClass::user_creatable using QMP,
347 # so use 'info qdm':
348 self.no_user_devs = set([d['name'] for d in infoQDM(vm, ) if d['no-user']])
349 self.machines = list(m['name'] for m in vm.command('query-machines'))
350 self.user_devs = self.alldevs.difference(self.no_user_devs)
351 self.kvm_available = vm.command('query-kvm')['enabled']
352 finally:
353 vm.shutdown()
354
355 def machineInfo(self, machine):
356 """Query for information on a specific machine-type
357
358 Results are cached internally, in case the same machine-
359 type is queried multiple times.
360 """
361 if machine in self._machine_info:
362 return self._machine_info[machine]
363
364 mi = {}
365 args = ['-S', '-machine', '%s' % (machine)]
366 dbg("querying machine info for binary=%s machine=%s", self.binary, machine)
367 vm = QEMUMachine(binary=self.binary, args=args)
368 try:
369 vm.launch()
370 mi['runnable'] = True
371 except KeyboardInterrupt:
372 raise
373 except:
374 dbg("exception trying to run binary=%s machine=%s", self.binary, machine, exc_info=sys.exc_info())
375 dbg("log: %r", vm.get_log())
376 mi['runnable'] = False
377
378 vm.shutdown()
379 self._machine_info[machine] = mi
380 return mi
381
382
383BINARY_INFO = {}
384
385
386def getBinaryInfo(args, binary):
387 if binary not in BINARY_INFO:
388 BINARY_INFO[binary] = QemuBinaryInfo(binary, args.devtype)
389 return BINARY_INFO[binary]
390
391
392def checkOneCase(args, testcase):
393 """Check one specific case
394
395 Returns a dictionary containing failure information on error,
396 or None on success
397 """
398 binary = testcase['binary']
399 accel = testcase['accel']
400 machine = testcase['machine']
401 device = testcase['device']
402
403 dbg("will test: %r", testcase)
404
405 args = ['-S', '-machine', '%s,accel=%s' % (machine, accel),
406 '-device', qemuOptsEscape(device)]
407 cmdline = ' '.join([binary] + args)
408 dbg("will launch QEMU: %s", cmdline)
409 vm = QEMUMachine(binary=binary, args=args)
410
411 exc_traceback = None
412 try:
413 vm.launch()
414 except KeyboardInterrupt:
415 raise
416 except:
417 exc_traceback = traceback.format_exc()
418 dbg("Exception while running test case")
419 finally:
420 vm.shutdown()
421 ec = vm.exitcode()
422 log = vm.get_log()
423
424 if exc_traceback is not None or ec != 0:
425 return {'exc_traceback':exc_traceback,
426 'exitcode':ec,
427 'log':log,
428 'testcase':testcase,
429 'cmdline':cmdline}
430
431
432def binariesToTest(args, testcase):
433 if args.qemu:
434 r = args.qemu
435 else:
436 r = glob.glob('./*-softmmu/qemu-system-*')
437 return r
438
439
440def accelsToTest(args, testcase):
441 if getBinaryInfo(args, testcase['binary']).kvm_available:
442 yield 'kvm'
443 yield 'tcg'
444
445
446def machinesToTest(args, testcase):
447 return getBinaryInfo(args, testcase['binary']).machines
448
449
450def devicesToTest(args, testcase):
451 return getBinaryInfo(args, testcase['binary']).user_devs
452
453
454TESTCASE_VARIABLES = [
455 ('binary', binariesToTest),
456 ('accel', accelsToTest),
457 ('machine', machinesToTest),
458 ('device', devicesToTest),
459]
460
461
462def genCases1(args, testcases, var, fn):
463 """Generate new testcases for one variable
464
465 If an existing item already has a variable set, don't
466 generate new items and just return it directly. This
467 allows the "-t" command-line option to be used to choose
468 a specific test case.
469 """
470 for testcase in testcases:
471 if var in testcase:
472 yield testcase.copy()
473 else:
474 for i in fn(args, testcase):
475 t = testcase.copy()
476 t[var] = i
477 yield t
478
479
480def genCases(args, testcase):
481 """Generate test cases for all variables
482 """
483 cases = [testcase.copy()]
484 for var, fn in TESTCASE_VARIABLES:
485 dbg("var: %r, fn: %r", var, fn)
486 cases = genCases1(args, cases, var, fn)
487 return cases
488
489
490def casesToTest(args, testcase):
491 cases = genCases(args, testcase)
492 if args.random:
493 cases = list(cases)
494 cases = random.sample(cases, min(args.random, len(cases)))
495 if args.debug:
496 cases = list(cases)
497 dbg("%d test cases to test", len(cases))
498 if args.shuffle:
499 cases = list(cases)
500 random.shuffle(cases)
501 return cases
502
503
504def logFailure(f, level):
505 t = f['testcase']
506 logger.log(level, "failed: %s", formatTestCase(t))
507 logger.log(level, "cmdline: %s", f['cmdline'])
508 for l in f['log'].strip().split('\n'):
509 logger.log(level, "log: %s", l)
510 logger.log(level, "exit code: %r", f['exitcode'])
511 if f['exc_traceback']:
512 logger.log(level, "exception:")
513 for l in f['exc_traceback'].split('\n'):
514 logger.log(level, " %s", l.rstrip('\n'))
515
516
517def main():
518 parser = argparse.ArgumentParser(description="QEMU -device crash test")
519 parser.add_argument('-t', metavar='KEY=VALUE', nargs='*',
520 help="Limit test cases to KEY=VALUE",
521 action='append', dest='testcases', default=[])
522 parser.add_argument('-d', '--debug', action='store_true',
523 help='debug output')
524 parser.add_argument('-v', '--verbose', action='store_true', default=True,
525 help='verbose output')
526 parser.add_argument('-q', '--quiet', dest='verbose', action='store_false',
527 help='non-verbose output')
528 parser.add_argument('-r', '--random', type=int, metavar='COUNT',
529 help='run a random sample of COUNT test cases',
530 default=0)
531 parser.add_argument('--shuffle', action='store_true',
532 help='Run test cases in random order')
533 parser.add_argument('--dry-run', action='store_true',
534 help="Don't run any tests, just generate list")
535 parser.add_argument('-D', '--devtype', metavar='TYPE',
536 help="Test only device types that implement TYPE")
537 parser.add_argument('-Q', '--quick', action='store_true', default=True,
538 help="Quick mode: skip test cases that are expected to fail")
539 parser.add_argument('-F', '--full', action='store_false', dest='quick',
540 help="Full mode: test cases that are expected to fail")
541 parser.add_argument('--strict', action='store_true', dest='strict',
542 help="Treat all warnings as fatal")
543 parser.add_argument('qemu', nargs='*', metavar='QEMU',
544 help='QEMU binary to run')
545 args = parser.parse_args()
546
547 if args.debug:
548 lvl = logging.DEBUG
549 elif args.verbose:
550 lvl = logging.INFO
551 else:
552 lvl = logging.WARN
553 logging.basicConfig(stream=sys.stdout, level=lvl, format='%(levelname)s: %(message)s')
554
555 fatal_failures = []
556 wl_stats = {}
557 skipped = 0
558 total = 0
559
560 tc = {}
561 dbg("testcases: %r", args.testcases)
562 if args.testcases:
563 for t in chain(*args.testcases):
564 for kv in t.split():
565 k, v = kv.split('=', 1)
566 tc[k] = v
567
568 if len(binariesToTest(args, tc)) == 0:
569 print >>sys.stderr, "No QEMU binary found"
570 parser.print_usage(sys.stderr)
571 return 1
572
573 for t in casesToTest(args, tc):
574 logger.info("running test case: %s", formatTestCase(t))
575 total += 1
576
577 expected_match = findExpectedResult(t)
578 if (args.quick and
579 (expected_match or
580 not getBinaryInfo(args, t['binary']).machineInfo(t['machine'])['runnable'])):
581 dbg("skipped: %s", formatTestCase(t))
582 skipped += 1
583 continue
584
585 if args.dry_run:
586 continue
587
588 try:
589 f = checkOneCase(args, t)
590 except KeyboardInterrupt:
591 break
592
593 if f:
594 i, wl = checkResultWhitelist(f)
595 dbg("testcase: %r, whitelist match: %r", t, wl)
596 wl_stats.setdefault(i, []).append(f)
597 level = wl.get('loglevel', logging.DEBUG)
598 logFailure(f, level)
599 if wl.get('fatal') or (args.strict and level >= logging.WARN):
600 fatal_failures.append(f)
601 else:
602 dbg("success: %s", formatTestCase(t))
603 if expected_match:
604 logger.warn("Didn't fail as expected: %s", formatTestCase(t))
605
606 logger.info("Total: %d test cases", total)
607 if skipped:
608 logger.info("Skipped %d test cases", skipped)
609
610 if args.debug:
611 stats = sorted([(len(wl_stats.get(i, [])), wl) for i, wl in enumerate(ERROR_WHITELIST)])
612 for count, wl in stats:
613 dbg("whitelist entry stats: %d: %r", count, wl)
614
615 if fatal_failures:
616 for f in fatal_failures:
617 t = f['testcase']
618 logger.error("Fatal failure: %s", formatTestCase(t))
619 logger.error("Fatal failures on some machine/device combinations")
620 return 1
621
622if __name__ == '__main__':
623 sys.exit(main())