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