target/i386: move hflags update code to a function
[qemu/ericb.git] / scripts / device-crash-test
blob827d8ec2af5deed5a335967601e8e8a2058ba0f6
1 #!/usr/bin/env python2.7
3 #  Copyright (c) 2017 Red Hat Inc
5 # Author:
6 #  Eduardo Habkost <ehabkost@redhat.com>
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.
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.
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.
22 """
23 Run QEMU with all combinations of -machine and -device types,
24 check for crashes and unexpected errors.
25 """
27 import sys
28 import os
29 import glob
30 import logging
31 import traceback
32 import re
33 import random
34 import argparse
35 from itertools import chain
37 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'scripts'))
38 from qemu import QEMUMachine
40 logger = logging.getLogger('device-crash-test')
41 dbg = logger.debug
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
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.
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.
65 ERROR_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)
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':'loader', 'expected':True},                  # please include valid arguments
106     {'device':'nand', 'expected':True},                    # Unsupported NAND block size 0x1
107     {'device':'nvdimm', 'expected':True},                  # 'memdev' property is not set
108     {'device':'nvme', 'expected':True},                    # Device initialization failed
109     {'device':'pc-dimm', 'expected':True},                 # 'memdev' property is not set
110     {'device':'pci-bridge', 'expected':True},              # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
111     {'device':'pci-bridge-seat', 'expected':True},         # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
112     {'device':'pci-serial', 'expected':True},              # Can't create serial device, empty char device
113     {'device':'pci-serial-2x', 'expected':True},           # Can't create serial device, empty char device
114     {'device':'pci-serial-4x', 'expected':True},           # Can't create serial device, empty char device
115     {'device':'pxa2xx-dma', 'expected':True},              # channels value invalid
116     {'device':'pxb', 'expected':True},                     # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
117     {'device':'scsi-block', 'expected':True},              # drive property not set
118     {'device':'scsi-disk', 'expected':True},               # drive property not set
119     {'device':'scsi-generic', 'expected':True},            # drive property not set
120     {'device':'scsi-hd', 'expected':True},                 # drive property not set
121     {'device':'spapr-pci-host-bridge', 'expected':True},   # BUID not specified for PHB
122     {'device':'spapr-rng', 'expected':True},               # spapr-rng needs an RNG backend!
123     {'device':'spapr-vty', 'expected':True},               # chardev property not set
124     {'device':'tpm-tis', 'expected':True},                 # tpm_tis: backend driver with id (null) could not be found
125     {'device':'unimplemented-device', 'expected':True},    # property 'size' not specified or zero
126     {'device':'usb-braille', 'expected':True},             # Property chardev is required
127     {'device':'usb-mtp', 'expected':True},                 # x-root property must be configured
128     {'device':'usb-redir', 'expected':True},               # Parameter 'chardev' is missing
129     {'device':'usb-serial', 'expected':True},              # Property chardev is required
130     {'device':'usb-storage', 'expected':True},             # drive property not set
131     {'device':'vfio-amd-xgbe', 'expected':True},           # -device vfio-amd-xgbe: vfio error: wrong host device name
132     {'device':'vfio-calxeda-xgmac', 'expected':True},      # -device vfio-calxeda-xgmac: vfio error: wrong host device name
133     {'device':'vfio-pci', 'expected':True},                # No provided host device
134     {'device':'vfio-pci-igd-lpc-bridge', 'expected':True}, # VFIO dummy ISA/LPC bridge must have address 1f.0
135     {'device':'vhost-scsi.*', 'expected':True},            # vhost-scsi: missing wwpn
136     {'device':'vhost-vsock-device', 'expected':True},      # guest-cid property must be greater than 2
137     {'device':'vhost-vsock-pci', 'expected':True},         # guest-cid property must be greater than 2
138     {'device':'virtio-9p-ccw', 'expected':True},           # 9pfs device couldn't find fsdev with the id = NULL
139     {'device':'virtio-9p-device', 'expected':True},        # 9pfs device couldn't find fsdev with the id = NULL
140     {'device':'virtio-9p-pci', 'expected':True},           # 9pfs device couldn't find fsdev with the id = NULL
141     {'device':'virtio-blk-ccw', 'expected':True},          # drive property not set
142     {'device':'virtio-blk-device', 'expected':True},       # drive property not set
143     {'device':'virtio-blk-device', 'expected':True},       # drive property not set
144     {'device':'virtio-blk-pci', 'expected':True},          # drive property not set
145     {'device':'virtio-crypto-ccw', 'expected':True},       # 'cryptodev' parameter expects a valid object
146     {'device':'virtio-crypto-device', 'expected':True},    # 'cryptodev' parameter expects a valid object
147     {'device':'virtio-crypto-pci', 'expected':True},       # 'cryptodev' parameter expects a valid object
148     {'device':'virtio-input-host-device', 'expected':True}, # evdev property is required
149     {'device':'virtio-input-host-pci', 'expected':True},   # evdev property is required
150     {'device':'xen-pvdevice', 'expected':True},            # Device ID invalid, it must always be supplied
151     {'device':'vhost-vsock-ccw', 'expected':True},         # guest-cid property must be greater than 2
152     {'device':'ALTR.timer', 'expected':True},              # "clock-frequency" property must be provided
153     {'device':'zpci', 'expected':True},                    # target must be defined
154     {'device':'pnv-(occ|icp|lpc)', 'expected':True},       # required link 'xics' not found: Property '.xics' not found
155     {'device':'powernv-cpu-.*', 'expected':True},          # pnv_core_realize: required link 'xics' not found: Property '.xics' not found
157     # ioapic devices are already created by pc and will fail:
158     {'machine':'q35|pc.*', 'device':'kvm-ioapic', 'expected':True}, # Only 1 ioapics allowed
159     {'machine':'q35|pc.*', 'device':'ioapic', 'expected':True},     # Only 1 ioapics allowed
161     # "spapr-cpu-core needs a pseries machine"
162     {'machine':'(?!pseries).*', 'device':'.*-spapr-cpu-core', 'expected':True},
164     # KVM-specific devices shouldn't be tried without accel=kvm:
165     {'accel':'(?!kvm).*', 'device':'kvmclock', 'expected':True},
167     # xen-specific machines and devices:
168     {'accel':'(?!xen).*', 'machine':'xen.*', 'expected':True},
169     {'accel':'(?!xen).*', 'device':'xen-.*', 'expected':True},
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
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:
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"},
203     # other exitcode=1 failures not listed above will just generate INFO messages:
204     {'exitcode':1, 'loglevel':logging.INFO},
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},
221     {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR},
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},
234     # everything else (including SIGABRT and SIGSEGV) will be a fatal error:
235     {'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL},
239 def whitelistTestCaseMatch(wl, t):
240     """Check if a test case specification can match a whitelist entry
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'])))
257 def 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)
264 def findExpectedResult(t):
265     """Check if there's an expected=True whitelist entry for a test case
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)
275 def whitelistResultMatch(wl, r):
276     """Check if test case results/output match a whitelist entry
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)))
289 def checkResultWhitelist(r):
290     """Look up whitelist entry for a given test case result
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
299     raise Exception("this should never happen")
302 def qemuOptsEscape(s):
303     """Escape option value QemuOpts"""
304     return s.replace(",", ",,")
307 def 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())
312 def 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]
318 def 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
331 class QemuBinaryInfo(object):
332     def __init__(self, binary, devtype):
333         if devtype is None:
334             devtype = 'device'
336         self.binary = binary
337         self._machine_info = {}
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()
355     def machineInfo(self, machine):
356         """Query for information on a specific machine-type
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]
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
378         vm.shutdown()
379         self._machine_info[machine] = mi
380         return mi
383 BINARY_INFO = {}
386 def getBinaryInfo(args, binary):
387     if binary not in BINARY_INFO:
388         BINARY_INFO[binary] = QemuBinaryInfo(binary, args.devtype)
389     return BINARY_INFO[binary]
392 def checkOneCase(args, testcase):
393     """Check one specific case
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']
403     dbg("will test: %r", testcase)
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)
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()
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}
432 def binariesToTest(args, testcase):
433     if args.qemu:
434         r = args.qemu
435     else:
436         r = glob.glob('./*-softmmu/qemu-system-*')
437     return r
440 def accelsToTest(args, testcase):
441     if getBinaryInfo(args, testcase['binary']).kvm_available:
442         yield 'kvm'
443     yield 'tcg'
446 def machinesToTest(args, testcase):
447     return getBinaryInfo(args, testcase['binary']).machines
450 def devicesToTest(args, testcase):
451     return getBinaryInfo(args, testcase['binary']).user_devs
454 TESTCASE_VARIABLES = [
455     ('binary', binariesToTest),
456     ('accel', accelsToTest),
457     ('machine', machinesToTest),
458     ('device', devicesToTest),
462 def genCases1(args, testcases, var, fn):
463     """Generate new testcases for one variable
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
480 def 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
490 def 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
504 def 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'))
517 def 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()
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')
555     fatal_failures = []
556     wl_stats = {}
557     skipped = 0
558     total = 0
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
568     if len(binariesToTest(args, tc)) == 0:
569         print >>sys.stderr, "No QEMU binary found"
570         parser.print_usage(sys.stderr)
571         return 1
573     for t in casesToTest(args, tc):
574         logger.info("running test case: %s", formatTestCase(t))
575         total += 1
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
585         if args.dry_run:
586             continue
588         try:
589             f = checkOneCase(args, t)
590         except KeyboardInterrupt:
591             break
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))
606     logger.info("Total: %d test cases", total)
607     if skipped:
608         logger.info("Skipped %d test cases", skipped)
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)
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
622 if __name__ == '__main__':
623     sys.exit(main())