13d004a37SPhilippe Mathieu-Daudé#!/usr/bin/env python3 223ea4f30SEduardo Habkost# 323ea4f30SEduardo Habkost# Copyright (c) 2017 Red Hat Inc 423ea4f30SEduardo Habkost# 523ea4f30SEduardo Habkost# Author: 623ea4f30SEduardo Habkost# Eduardo Habkost <ehabkost@redhat.com> 723ea4f30SEduardo Habkost# 823ea4f30SEduardo Habkost# This program is free software; you can redistribute it and/or modify 923ea4f30SEduardo Habkost# it under the terms of the GNU General Public License as published by 1023ea4f30SEduardo Habkost# the Free Software Foundation; either version 2 of the License, or 1123ea4f30SEduardo Habkost# (at your option) any later version. 1223ea4f30SEduardo Habkost# 1323ea4f30SEduardo Habkost# This program is distributed in the hope that it will be useful, 1423ea4f30SEduardo Habkost# but WITHOUT ANY WARRANTY; without even the implied warranty of 1523ea4f30SEduardo Habkost# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1623ea4f30SEduardo Habkost# GNU General Public License for more details. 1723ea4f30SEduardo Habkost# 1823ea4f30SEduardo Habkost# You should have received a copy of the GNU General Public License along 1923ea4f30SEduardo Habkost# with this program; if not, write to the Free Software Foundation, Inc., 2023ea4f30SEduardo Habkost# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 2123ea4f30SEduardo Habkost 2223ea4f30SEduardo Habkost""" 2323ea4f30SEduardo HabkostRun QEMU with all combinations of -machine and -device types, 2423ea4f30SEduardo Habkostcheck for crashes and unexpected errors. 2523ea4f30SEduardo Habkost""" 2623ea4f30SEduardo Habkost 278f8fd9edSCleber Rosaimport os 2823ea4f30SEduardo Habkostimport sys 2923ea4f30SEduardo Habkostimport glob 3023ea4f30SEduardo Habkostimport logging 3123ea4f30SEduardo Habkostimport traceback 3223ea4f30SEduardo Habkostimport re 3323ea4f30SEduardo Habkostimport random 3423ea4f30SEduardo Habkostimport argparse 3523ea4f30SEduardo Habkostfrom itertools import chain 361d8cf47eSJohn Snowfrom pathlib import Path 3723ea4f30SEduardo Habkost 381d8cf47eSJohn Snowtry: 39abf0bf99SJohn Snow from qemu.machine import QEMUMachine 4037094b6dSJohn Snow from qemu.qmp import ConnectError 411d8cf47eSJohn Snowexcept ModuleNotFoundError as exc: 421d8cf47eSJohn Snow path = Path(__file__).resolve() 431d8cf47eSJohn Snow print(f"Module '{exc.name}' not found.") 441d8cf47eSJohn Snow print(" Try 'make check-venv' from your build directory,") 451d8cf47eSJohn Snow print(" and then one way to run this script is like so:") 46c03f57fdSPaolo Bonzini print(f' > $builddir/pyvenv/bin/python3 "{path}"') 471d8cf47eSJohn Snow sys.exit(1) 4823ea4f30SEduardo Habkost 4923ea4f30SEduardo Habkostlogger = logging.getLogger('device-crash-test') 5023ea4f30SEduardo Habkostdbg = logger.debug 5123ea4f30SEduardo Habkost 5223ea4f30SEduardo Habkost 531a14d4e1SEduardo Habkost# Purposes of the following rule list: 5423ea4f30SEduardo Habkost# * Avoiding verbose log messages when we find known non-fatal 5523ea4f30SEduardo Habkost# (exitcode=1) errors 5623ea4f30SEduardo Habkost# * Avoiding fatal errors when we find known crashes 5723ea4f30SEduardo Habkost# * Skipping machines/devices that are known not to work out of 5823ea4f30SEduardo Habkost# the box, when running in --quick mode 5923ea4f30SEduardo Habkost# 601a14d4e1SEduardo Habkost# Keeping the rule list updated is desirable, but not required, 6123ea4f30SEduardo Habkost# because unexpected cases where QEMU exits with exitcode=1 will 6223ea4f30SEduardo Habkost# just trigger a INFO message. 6323ea4f30SEduardo Habkost 641a14d4e1SEduardo Habkost# Valid error rule keys: 6523ea4f30SEduardo Habkost# * accel: regexp, full match only 6623ea4f30SEduardo Habkost# * machine: regexp, full match only 6723ea4f30SEduardo Habkost# * device: regexp, full match only 6823ea4f30SEduardo Habkost# * log: regexp, partial match allowed 6923ea4f30SEduardo Habkost# * exitcode: if not present, defaults to 1. If None, matches any exitcode 7023ea4f30SEduardo Habkost# * warn: if True, matching failures will be logged as warnings 7123ea4f30SEduardo Habkost# * expected: if True, QEMU is expected to always fail every time 7223ea4f30SEduardo Habkost# when testing the corresponding test case 7323ea4f30SEduardo Habkost# * loglevel: log level of log output when there's a match. 741a14d4e1SEduardo HabkostERROR_RULE_LIST = [ 7523ea4f30SEduardo Habkost # Machines that won't work out of the box: 7623ea4f30SEduardo Habkost # MACHINE | ERROR MESSAGE 7723ea4f30SEduardo Habkost {'machine':'niagara', 'expected':True}, # Unable to load a firmware for -M niagara 7823ea4f30SEduardo Habkost {'machine':'boston', 'expected':True}, # Please provide either a -kernel or -bios argument 7923ea4f30SEduardo Habkost {'machine':'leon3_generic', 'expected':True}, # Can't read bios image (null) 8023ea4f30SEduardo Habkost 8123ea4f30SEduardo Habkost # devices that don't work out of the box because they require extra options to "-device DEV": 8223ea4f30SEduardo Habkost # DEVICE | ERROR MESSAGE 8323ea4f30SEduardo Habkost {'device':'.*-(i386|x86_64)-cpu', 'expected':True}, # CPU socket-id is not set 8423ea4f30SEduardo Habkost {'device':'icp', 'expected':True}, # icp_realize: required link 'xics' not found: Property '.xics' not found 8523ea4f30SEduardo Habkost {'device':'ics', 'expected':True}, # ics_base_realize: required link 'xics' not found: Property '.xics' not found 8623ea4f30SEduardo Habkost # "-device ide-cd" does work on more recent QEMU versions, so it doesn't have expected=True 8723ea4f30SEduardo Habkost {'device':'ide-cd'}, # No drive specified 8823ea4f30SEduardo Habkost {'device':'ide-hd', 'expected':True}, # No drive specified 8923ea4f30SEduardo Habkost {'device':'ipmi-bmc-extern', 'expected':True}, # IPMI external bmc requires chardev attribute 9023ea4f30SEduardo Habkost {'device':'isa-debugcon', 'expected':True}, # Can't create serial device, empty char device 9123ea4f30SEduardo Habkost {'device':'isa-ipmi-bt', 'expected':True}, # IPMI device requires a bmc attribute to be set 9223ea4f30SEduardo Habkost {'device':'isa-ipmi-kcs', 'expected':True}, # IPMI device requires a bmc attribute to be set 9323ea4f30SEduardo Habkost {'device':'isa-parallel', 'expected':True}, # Can't create serial device, empty char device 9423ea4f30SEduardo Habkost {'device':'ivshmem-doorbell', 'expected':True}, # You must specify a 'chardev' 9523ea4f30SEduardo Habkost {'device':'ivshmem-plain', 'expected':True}, # You must specify a 'memdev' 9623ea4f30SEduardo Habkost {'device':'loader', 'expected':True}, # please include valid arguments 9723ea4f30SEduardo Habkost {'device':'nand', 'expected':True}, # Unsupported NAND block size 0x1 9823ea4f30SEduardo Habkost {'device':'nvdimm', 'expected':True}, # 'memdev' property is not set 9923ea4f30SEduardo Habkost {'device':'nvme', 'expected':True}, # Device initialization failed 10023ea4f30SEduardo Habkost {'device':'pc-dimm', 'expected':True}, # 'memdev' property is not set 10123ea4f30SEduardo Habkost {'device':'pci-bridge', 'expected':True}, # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0. 10223ea4f30SEduardo Habkost {'device':'pci-bridge-seat', 'expected':True}, # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0. 10323ea4f30SEduardo Habkost {'device':'pxb', 'expected':True}, # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0. 1044f8db871SBen Widawsky {'device':'pxb-cxl', 'expected':True}, # pxb-cxl devices cannot reside on a PCI bus. 10523ea4f30SEduardo Habkost {'device':'scsi-block', 'expected':True}, # drive property not set 10623ea4f30SEduardo Habkost {'device':'scsi-generic', 'expected':True}, # drive property not set 10723ea4f30SEduardo Habkost {'device':'scsi-hd', 'expected':True}, # drive property not set 10823ea4f30SEduardo Habkost {'device':'spapr-pci-host-bridge', 'expected':True}, # BUID not specified for PHB 10923ea4f30SEduardo Habkost {'device':'spapr-rng', 'expected':True}, # spapr-rng needs an RNG backend! 11023ea4f30SEduardo Habkost {'device':'spapr-vty', 'expected':True}, # chardev property not set 11123ea4f30SEduardo Habkost {'device':'tpm-tis', 'expected':True}, # tpm_tis: backend driver with id (null) could not be found 11223ea4f30SEduardo Habkost {'device':'unimplemented-device', 'expected':True}, # property 'size' not specified or zero 11323ea4f30SEduardo Habkost {'device':'usb-braille', 'expected':True}, # Property chardev is required 1141ee53067SBandan {'device':'usb-mtp', 'expected':True}, # rootdir property must be configured 11523ea4f30SEduardo Habkost {'device':'usb-redir', 'expected':True}, # Parameter 'chardev' is missing 11623ea4f30SEduardo Habkost {'device':'usb-serial', 'expected':True}, # Property chardev is required 11723ea4f30SEduardo Habkost {'device':'usb-storage', 'expected':True}, # drive property not set 11823ea4f30SEduardo Habkost {'device':'vfio-amd-xgbe', 'expected':True}, # -device vfio-amd-xgbe: vfio error: wrong host device name 11923ea4f30SEduardo Habkost {'device':'vfio-calxeda-xgmac', 'expected':True}, # -device vfio-calxeda-xgmac: vfio error: wrong host device name 12023ea4f30SEduardo Habkost {'device':'vfio-pci', 'expected':True}, # No provided host device 12123ea4f30SEduardo Habkost {'device':'vfio-pci-igd-lpc-bridge', 'expected':True}, # VFIO dummy ISA/LPC bridge must have address 1f.0 12223ea4f30SEduardo Habkost {'device':'vhost-scsi.*', 'expected':True}, # vhost-scsi: missing wwpn 12323ea4f30SEduardo Habkost {'device':'vhost-vsock-device', 'expected':True}, # guest-cid property must be greater than 2 12423ea4f30SEduardo Habkost {'device':'vhost-vsock-pci', 'expected':True}, # guest-cid property must be greater than 2 12523ea4f30SEduardo Habkost {'device':'virtio-9p-ccw', 'expected':True}, # 9pfs device couldn't find fsdev with the id = NULL 12623ea4f30SEduardo Habkost {'device':'virtio-9p-device', 'expected':True}, # 9pfs device couldn't find fsdev with the id = NULL 12723ea4f30SEduardo Habkost {'device':'virtio-9p-pci', 'expected':True}, # 9pfs device couldn't find fsdev with the id = NULL 12823ea4f30SEduardo Habkost {'device':'virtio-blk-ccw', 'expected':True}, # drive property not set 12923ea4f30SEduardo Habkost {'device':'virtio-blk-device', 'expected':True}, # drive property not set 13023ea4f30SEduardo Habkost {'device':'virtio-blk-device', 'expected':True}, # drive property not set 13123ea4f30SEduardo Habkost {'device':'virtio-blk-pci', 'expected':True}, # drive property not set 13223ea4f30SEduardo Habkost {'device':'virtio-crypto-ccw', 'expected':True}, # 'cryptodev' parameter expects a valid object 13323ea4f30SEduardo Habkost {'device':'virtio-crypto-device', 'expected':True}, # 'cryptodev' parameter expects a valid object 13423ea4f30SEduardo Habkost {'device':'virtio-crypto-pci', 'expected':True}, # 'cryptodev' parameter expects a valid object 13523ea4f30SEduardo Habkost {'device':'virtio-input-host-device', 'expected':True}, # evdev property is required 13623ea4f30SEduardo Habkost {'device':'virtio-input-host-pci', 'expected':True}, # evdev property is required 13723ea4f30SEduardo Habkost {'device':'xen-pvdevice', 'expected':True}, # Device ID invalid, it must always be supplied 13823ea4f30SEduardo Habkost {'device':'vhost-vsock-ccw', 'expected':True}, # guest-cid property must be greater than 2 13923ea4f30SEduardo Habkost {'device':'zpci', 'expected':True}, # target must be defined 14023ea4f30SEduardo Habkost {'device':'pnv-(occ|icp|lpc)', 'expected':True}, # required link 'xics' not found: Property '.xics' not found 14123ea4f30SEduardo Habkost {'device':'powernv-cpu-.*', 'expected':True}, # pnv_core_realize: required link 'xics' not found: Property '.xics' not found 14223ea4f30SEduardo Habkost 14323ea4f30SEduardo Habkost # ioapic devices are already created by pc and will fail: 14423ea4f30SEduardo Habkost {'machine':'q35|pc.*', 'device':'kvm-ioapic', 'expected':True}, # Only 1 ioapics allowed 14523ea4f30SEduardo Habkost {'machine':'q35|pc.*', 'device':'ioapic', 'expected':True}, # Only 1 ioapics allowed 14623ea4f30SEduardo Habkost 1472363d5eeSThomas Huth # "spapr-cpu-core needs a pseries machine" 1482363d5eeSThomas Huth {'machine':'(?!pseries).*', 'device':'.*-spapr-cpu-core', 'expected':True}, 1492363d5eeSThomas Huth 15023ea4f30SEduardo Habkost # KVM-specific devices shouldn't be tried without accel=kvm: 15123ea4f30SEduardo Habkost {'accel':'(?!kvm).*', 'device':'kvmclock', 'expected':True}, 15223ea4f30SEduardo Habkost 15323ea4f30SEduardo Habkost # xen-specific machines and devices: 15423ea4f30SEduardo Habkost {'accel':'(?!xen).*', 'machine':'xen.*', 'expected':True}, 15523ea4f30SEduardo Habkost {'accel':'(?!xen).*', 'device':'xen-.*', 'expected':True}, 15623ea4f30SEduardo Habkost 15723ea4f30SEduardo Habkost # this fails on some machine-types, but not all, so they don't have expected=True: 15823ea4f30SEduardo Habkost {'device':'vmgenid'}, # vmgenid requires DMA write support in fw_cfg, which this machine type does not provide 15923ea4f30SEduardo Habkost 16023ea4f30SEduardo Habkost # Silence INFO messages for errors that are common on multiple 16123ea4f30SEduardo Habkost # devices/machines: 16223ea4f30SEduardo Habkost {'log':r"No '[\w-]+' bus found for device '[\w-]+'"}, 16323ea4f30SEduardo Habkost {'log':r"images* must be given with the 'pflash' parameter"}, 16423ea4f30SEduardo Habkost {'log':r"(Guest|ROM|Flash|Kernel) image must be specified"}, 16523ea4f30SEduardo Habkost {'log':r"[cC]ould not load [\w ]+ (BIOS|bios) '[\w-]+\.bin'"}, 16623ea4f30SEduardo Habkost {'log':r"Couldn't find rom image '[\w-]+\.bin'"}, 16723ea4f30SEduardo Habkost {'log':r"speed mismatch trying to attach usb device"}, 16823ea4f30SEduardo Habkost {'log':r"Can't create a second ISA bus"}, 16923ea4f30SEduardo Habkost {'log':r"duplicate fw_cfg file name"}, 17023ea4f30SEduardo Habkost # sysbus-related error messages: most machines reject most dynamic sysbus devices: 17123ea4f30SEduardo Habkost {'log':r"Option '-device [\w.,-]+' cannot be handled by this machine"}, 17223ea4f30SEduardo Habkost {'log':r"Device [\w.,-]+ is not supported by this machine yet"}, 17323ea4f30SEduardo Habkost {'log':r"Device [\w.,-]+ can not be dynamically instantiated"}, 17423ea4f30SEduardo Habkost {'log':r"Platform Bus: Can not fit MMIO region of size "}, 17523ea4f30SEduardo Habkost # other more specific errors we will ignore: 17623ea4f30SEduardo Habkost {'device':'.*-spapr-cpu-core', 'log':r"CPU core type should be"}, 17723ea4f30SEduardo Habkost {'log':r"MSI(-X)? is not supported by interrupt controller"}, 17823ea4f30SEduardo Habkost {'log':r"pxb-pcie? devices cannot reside on a PCIe? bus"}, 17923ea4f30SEduardo Habkost {'log':r"Ignoring smp_cpus value"}, 18023ea4f30SEduardo Habkost {'log':r"sd_init failed: Drive 'sd0' is already in use because it has been automatically connected to another device"}, 18123ea4f30SEduardo Habkost {'log':r"This CPU requires a smaller page size than the system is using"}, 18223ea4f30SEduardo Habkost {'log':r"MSI-X support is mandatory in the S390 architecture"}, 18323ea4f30SEduardo Habkost {'log':r"rom check and register reset failed"}, 18423ea4f30SEduardo Habkost {'log':r"Unable to initialize GIC, CPUState for CPU#0 not valid"}, 18523ea4f30SEduardo Habkost {'log':r"Multiple VT220 operator consoles are not supported"}, 18623ea4f30SEduardo Habkost {'log':r"core 0 already populated"}, 18723ea4f30SEduardo Habkost {'log':r"could not find stage1 bootloader"}, 18823ea4f30SEduardo Habkost 18923ea4f30SEduardo Habkost # other exitcode=1 failures not listed above will just generate INFO messages: 19023ea4f30SEduardo Habkost {'exitcode':1, 'loglevel':logging.INFO}, 19123ea4f30SEduardo Habkost 19223ea4f30SEduardo Habkost # everything else (including SIGABRT and SIGSEGV) will be a fatal error: 19323ea4f30SEduardo Habkost {'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL}, 19423ea4f30SEduardo Habkost] 19523ea4f30SEduardo Habkost 19623ea4f30SEduardo Habkost 1971a14d4e1SEduardo Habkostdef errorRuleTestCaseMatch(rule, t): 1981a14d4e1SEduardo Habkost """Check if a test case specification can match a error rule 19923ea4f30SEduardo Habkost 2001a14d4e1SEduardo Habkost This only checks if a error rule is a candidate match 20123ea4f30SEduardo Habkost for a given test case, it won't check if the test case 2021a14d4e1SEduardo Habkost results/output match the rule. See ruleListResultMatch(). 20323ea4f30SEduardo Habkost """ 2041a14d4e1SEduardo Habkost return (('machine' not in rule or 20523ea4f30SEduardo Habkost 'machine' not in t or 2061a14d4e1SEduardo Habkost re.match(rule['machine'] + '$', t['machine'])) and 2071a14d4e1SEduardo Habkost ('accel' not in rule or 20823ea4f30SEduardo Habkost 'accel' not in t or 2091a14d4e1SEduardo Habkost re.match(rule['accel'] + '$', t['accel'])) and 2101a14d4e1SEduardo Habkost ('device' not in rule or 21123ea4f30SEduardo Habkost 'device' not in t or 2121a14d4e1SEduardo Habkost re.match(rule['device'] + '$', t['device']))) 21323ea4f30SEduardo Habkost 21423ea4f30SEduardo Habkost 2151a14d4e1SEduardo Habkostdef ruleListCandidates(t): 21623ea4f30SEduardo Habkost """Generate the list of candidates that can match a test case""" 2171a14d4e1SEduardo Habkost for i, rule in enumerate(ERROR_RULE_LIST): 2181a14d4e1SEduardo Habkost if errorRuleTestCaseMatch(rule, t): 2191a14d4e1SEduardo Habkost yield (i, rule) 22023ea4f30SEduardo Habkost 22123ea4f30SEduardo Habkost 22223ea4f30SEduardo Habkostdef findExpectedResult(t): 2231a14d4e1SEduardo Habkost """Check if there's an expected=True error rule for a test case 22423ea4f30SEduardo Habkost 2251a14d4e1SEduardo Habkost Returns (i, rule) tuple, where i is the index in 2261a14d4e1SEduardo Habkost ERROR_RULE_LIST and rule is the error rule itself. 22723ea4f30SEduardo Habkost """ 2281a14d4e1SEduardo Habkost for i, rule in ruleListCandidates(t): 2291a14d4e1SEduardo Habkost if rule.get('expected'): 2301a14d4e1SEduardo Habkost return (i, rule) 23123ea4f30SEduardo Habkost 23223ea4f30SEduardo Habkost 2331a14d4e1SEduardo Habkostdef ruleListResultMatch(rule, r): 2341a14d4e1SEduardo Habkost """Check if test case results/output match a error rule 23523ea4f30SEduardo Habkost 23623ea4f30SEduardo Habkost It is valid to call this function only if 2371a14d4e1SEduardo Habkost errorRuleTestCaseMatch() is True for the rule (e.g. on 2381a14d4e1SEduardo Habkost rules returned by ruleListCandidates()) 23923ea4f30SEduardo Habkost """ 2401a14d4e1SEduardo Habkost assert errorRuleTestCaseMatch(rule, r['testcase']) 2411a14d4e1SEduardo Habkost return ((rule.get('exitcode', 1) is None or 2421a14d4e1SEduardo Habkost r['exitcode'] == rule.get('exitcode', 1)) and 2431a14d4e1SEduardo Habkost ('log' not in rule or 2441a14d4e1SEduardo Habkost re.search(rule['log'], r['log'], re.MULTILINE))) 24523ea4f30SEduardo Habkost 24623ea4f30SEduardo Habkost 2471a14d4e1SEduardo Habkostdef checkResultRuleList(r): 2481a14d4e1SEduardo Habkost """Look up error rule for a given test case result 24923ea4f30SEduardo Habkost 2501a14d4e1SEduardo Habkost Returns (i, rule) tuple, where i is the index in 2511a14d4e1SEduardo Habkost ERROR_RULE_LIST and rule is the error rule itself. 25223ea4f30SEduardo Habkost """ 2531a14d4e1SEduardo Habkost for i, rule in ruleListCandidates(r['testcase']): 2541a14d4e1SEduardo Habkost if ruleListResultMatch(rule, r): 2551a14d4e1SEduardo Habkost return i, rule 25623ea4f30SEduardo Habkost 25723ea4f30SEduardo Habkost raise Exception("this should never happen") 25823ea4f30SEduardo Habkost 25923ea4f30SEduardo Habkost 26023ea4f30SEduardo Habkostdef qemuOptsEscape(s): 26123ea4f30SEduardo Habkost """Escape option value QemuOpts""" 26223ea4f30SEduardo Habkost return s.replace(",", ",,") 26323ea4f30SEduardo Habkost 26423ea4f30SEduardo Habkost 26523ea4f30SEduardo Habkostdef formatTestCase(t): 26623ea4f30SEduardo Habkost """Format test case info as "key=value key=value" for prettier logging output""" 26723ea4f30SEduardo Habkost return ' '.join('%s=%s' % (k, v) for k, v in t.items()) 26823ea4f30SEduardo Habkost 26923ea4f30SEduardo Habkost 27023ea4f30SEduardo Habkostdef qomListTypeNames(vm, **kwargs): 27123ea4f30SEduardo Habkost """Run qom-list-types QMP command, return type names""" 272*684750abSVladimir Sementsov-Ogievskiy types = vm.cmd('qom-list-types', **kwargs) 27323ea4f30SEduardo Habkost return [t['name'] for t in types] 27423ea4f30SEduardo Habkost 27523ea4f30SEduardo Habkost 27623ea4f30SEduardo Habkostdef infoQDM(vm): 27723ea4f30SEduardo Habkost """Parse 'info qdm' output""" 27823ea4f30SEduardo Habkost args = {'command-line': 'info qdm'} 279*684750abSVladimir Sementsov-Ogievskiy devhelp = vm.cmd('human-monitor-command', **args) 28023ea4f30SEduardo Habkost for l in devhelp.split('\n'): 28123ea4f30SEduardo Habkost l = l.strip() 28223ea4f30SEduardo Habkost if l == '' or l.endswith(':'): 28323ea4f30SEduardo Habkost continue 28423ea4f30SEduardo Habkost d = {'name': re.search(r'name "([^"]+)"', l).group(1), 28523ea4f30SEduardo Habkost 'no-user': (re.search(', no-user', l) is not None)} 28623ea4f30SEduardo Habkost yield d 28723ea4f30SEduardo Habkost 28823ea4f30SEduardo Habkost 28923ea4f30SEduardo Habkostclass QemuBinaryInfo(object): 29023ea4f30SEduardo Habkost def __init__(self, binary, devtype): 29123ea4f30SEduardo Habkost if devtype is None: 29223ea4f30SEduardo Habkost devtype = 'device' 29323ea4f30SEduardo Habkost 29423ea4f30SEduardo Habkost self.binary = binary 29523ea4f30SEduardo Habkost self._machine_info = {} 29623ea4f30SEduardo Habkost 29723ea4f30SEduardo Habkost dbg("devtype: %r", devtype) 29823ea4f30SEduardo Habkost args = ['-S', '-machine', 'none,accel=kvm:tcg'] 29923ea4f30SEduardo Habkost dbg("querying info for QEMU binary: %s", binary) 30023ea4f30SEduardo Habkost vm = QEMUMachine(binary=binary, args=args) 30123ea4f30SEduardo Habkost vm.launch() 30223ea4f30SEduardo Habkost try: 30323ea4f30SEduardo Habkost self.alldevs = set(qomListTypeNames(vm, implements=devtype, abstract=False)) 30423ea4f30SEduardo Habkost # there's no way to query DeviceClass::user_creatable using QMP, 30523ea4f30SEduardo Habkost # so use 'info qdm': 30623ea4f30SEduardo Habkost self.no_user_devs = set([d['name'] for d in infoQDM(vm, ) if d['no-user']]) 307*684750abSVladimir Sementsov-Ogievskiy self.machines = list(m['name'] for m in vm.cmd('query-machines')) 30823ea4f30SEduardo Habkost self.user_devs = self.alldevs.difference(self.no_user_devs) 309*684750abSVladimir Sementsov-Ogievskiy self.kvm_available = vm.cmd('query-kvm')['enabled'] 31023ea4f30SEduardo Habkost finally: 31123ea4f30SEduardo Habkost vm.shutdown() 31223ea4f30SEduardo Habkost 31323ea4f30SEduardo Habkost def machineInfo(self, machine): 31423ea4f30SEduardo Habkost """Query for information on a specific machine-type 31523ea4f30SEduardo Habkost 31623ea4f30SEduardo Habkost Results are cached internally, in case the same machine- 31723ea4f30SEduardo Habkost type is queried multiple times. 31823ea4f30SEduardo Habkost """ 31923ea4f30SEduardo Habkost if machine in self._machine_info: 32023ea4f30SEduardo Habkost return self._machine_info[machine] 32123ea4f30SEduardo Habkost 32223ea4f30SEduardo Habkost mi = {} 32323ea4f30SEduardo Habkost args = ['-S', '-machine', '%s' % (machine)] 32423ea4f30SEduardo Habkost dbg("querying machine info for binary=%s machine=%s", self.binary, machine) 32523ea4f30SEduardo Habkost vm = QEMUMachine(binary=self.binary, args=args) 32623ea4f30SEduardo Habkost try: 32723ea4f30SEduardo Habkost vm.launch() 32823ea4f30SEduardo Habkost mi['runnable'] = True 32947b43acdSJohn Snow except Exception: 33023ea4f30SEduardo Habkost dbg("exception trying to run binary=%s machine=%s", self.binary, machine, exc_info=sys.exc_info()) 33123ea4f30SEduardo Habkost dbg("log: %r", vm.get_log()) 33223ea4f30SEduardo Habkost mi['runnable'] = False 33323ea4f30SEduardo Habkost 33423ea4f30SEduardo Habkost vm.shutdown() 33523ea4f30SEduardo Habkost self._machine_info[machine] = mi 33623ea4f30SEduardo Habkost return mi 33723ea4f30SEduardo Habkost 33823ea4f30SEduardo Habkost 33923ea4f30SEduardo HabkostBINARY_INFO = {} 34023ea4f30SEduardo Habkost 34123ea4f30SEduardo Habkost 34223ea4f30SEduardo Habkostdef getBinaryInfo(args, binary): 34323ea4f30SEduardo Habkost if binary not in BINARY_INFO: 34423ea4f30SEduardo Habkost BINARY_INFO[binary] = QemuBinaryInfo(binary, args.devtype) 34523ea4f30SEduardo Habkost return BINARY_INFO[binary] 34623ea4f30SEduardo Habkost 34723ea4f30SEduardo Habkost 34823ea4f30SEduardo Habkostdef checkOneCase(args, testcase): 34923ea4f30SEduardo Habkost """Check one specific case 35023ea4f30SEduardo Habkost 35123ea4f30SEduardo Habkost Returns a dictionary containing failure information on error, 35223ea4f30SEduardo Habkost or None on success 35323ea4f30SEduardo Habkost """ 35423ea4f30SEduardo Habkost binary = testcase['binary'] 35523ea4f30SEduardo Habkost accel = testcase['accel'] 35623ea4f30SEduardo Habkost machine = testcase['machine'] 35723ea4f30SEduardo Habkost device = testcase['device'] 35823ea4f30SEduardo Habkost 35923ea4f30SEduardo Habkost dbg("will test: %r", testcase) 36023ea4f30SEduardo Habkost 36123ea4f30SEduardo Habkost args = ['-S', '-machine', '%s,accel=%s' % (machine, accel), 36223ea4f30SEduardo Habkost '-device', qemuOptsEscape(device)] 36323ea4f30SEduardo Habkost cmdline = ' '.join([binary] + args) 36423ea4f30SEduardo Habkost dbg("will launch QEMU: %s", cmdline) 365206439cdSJohn Snow vm = QEMUMachine(binary=binary, args=args, qmp_timer=15) 36623ea4f30SEduardo Habkost 367c398a241SJohn Snow exc = None 36823ea4f30SEduardo Habkost exc_traceback = None 36923ea4f30SEduardo Habkost try: 37023ea4f30SEduardo Habkost vm.launch() 371c398a241SJohn Snow except Exception as this_exc: 372c398a241SJohn Snow exc = this_exc 37323ea4f30SEduardo Habkost exc_traceback = traceback.format_exc() 37423ea4f30SEduardo Habkost dbg("Exception while running test case") 37523ea4f30SEduardo Habkost finally: 37623ea4f30SEduardo Habkost vm.shutdown() 37723ea4f30SEduardo Habkost ec = vm.exitcode() 37823ea4f30SEduardo Habkost log = vm.get_log() 37923ea4f30SEduardo Habkost 380c398a241SJohn Snow if exc is not None or ec != 0: 381c398a241SJohn Snow return {'exc': exc, 382c398a241SJohn Snow 'exc_traceback':exc_traceback, 38323ea4f30SEduardo Habkost 'exitcode':ec, 38423ea4f30SEduardo Habkost 'log':log, 38523ea4f30SEduardo Habkost 'testcase':testcase, 38623ea4f30SEduardo Habkost 'cmdline':cmdline} 38723ea4f30SEduardo Habkost 38823ea4f30SEduardo Habkost 38923ea4f30SEduardo Habkostdef binariesToTest(args, testcase): 39023ea4f30SEduardo Habkost if args.qemu: 39123ea4f30SEduardo Habkost r = args.qemu 39223ea4f30SEduardo Habkost else: 3938a478365SEduardo Habkost r = [f.path for f in os.scandir('.') 3948a478365SEduardo Habkost if f.name.startswith('qemu-system-') and 3958a478365SEduardo Habkost f.is_file() and os.access(f, os.X_OK)] 39623ea4f30SEduardo Habkost return r 39723ea4f30SEduardo Habkost 39823ea4f30SEduardo Habkost 39923ea4f30SEduardo Habkostdef accelsToTest(args, testcase): 4008b869aa5SThomas Huth if getBinaryInfo(args, testcase['binary']).kvm_available and not args.tcg_only: 40123ea4f30SEduardo Habkost yield 'kvm' 40223ea4f30SEduardo Habkost yield 'tcg' 40323ea4f30SEduardo Habkost 40423ea4f30SEduardo Habkost 40523ea4f30SEduardo Habkostdef machinesToTest(args, testcase): 40623ea4f30SEduardo Habkost return getBinaryInfo(args, testcase['binary']).machines 40723ea4f30SEduardo Habkost 40823ea4f30SEduardo Habkost 40923ea4f30SEduardo Habkostdef devicesToTest(args, testcase): 41023ea4f30SEduardo Habkost return getBinaryInfo(args, testcase['binary']).user_devs 41123ea4f30SEduardo Habkost 41223ea4f30SEduardo Habkost 41323ea4f30SEduardo HabkostTESTCASE_VARIABLES = [ 41423ea4f30SEduardo Habkost ('binary', binariesToTest), 41523ea4f30SEduardo Habkost ('accel', accelsToTest), 41623ea4f30SEduardo Habkost ('machine', machinesToTest), 41723ea4f30SEduardo Habkost ('device', devicesToTest), 41823ea4f30SEduardo Habkost] 41923ea4f30SEduardo Habkost 42023ea4f30SEduardo Habkost 42123ea4f30SEduardo Habkostdef genCases1(args, testcases, var, fn): 42223ea4f30SEduardo Habkost """Generate new testcases for one variable 42323ea4f30SEduardo Habkost 42423ea4f30SEduardo Habkost If an existing item already has a variable set, don't 42523ea4f30SEduardo Habkost generate new items and just return it directly. This 42623ea4f30SEduardo Habkost allows the "-t" command-line option to be used to choose 42723ea4f30SEduardo Habkost a specific test case. 42823ea4f30SEduardo Habkost """ 42923ea4f30SEduardo Habkost for testcase in testcases: 43023ea4f30SEduardo Habkost if var in testcase: 43123ea4f30SEduardo Habkost yield testcase.copy() 43223ea4f30SEduardo Habkost else: 43323ea4f30SEduardo Habkost for i in fn(args, testcase): 43423ea4f30SEduardo Habkost t = testcase.copy() 43523ea4f30SEduardo Habkost t[var] = i 43623ea4f30SEduardo Habkost yield t 43723ea4f30SEduardo Habkost 43823ea4f30SEduardo Habkost 43923ea4f30SEduardo Habkostdef genCases(args, testcase): 44023ea4f30SEduardo Habkost """Generate test cases for all variables 44123ea4f30SEduardo Habkost """ 44223ea4f30SEduardo Habkost cases = [testcase.copy()] 44323ea4f30SEduardo Habkost for var, fn in TESTCASE_VARIABLES: 44423ea4f30SEduardo Habkost dbg("var: %r, fn: %r", var, fn) 44523ea4f30SEduardo Habkost cases = genCases1(args, cases, var, fn) 44623ea4f30SEduardo Habkost return cases 44723ea4f30SEduardo Habkost 44823ea4f30SEduardo Habkost 44923ea4f30SEduardo Habkostdef casesToTest(args, testcase): 45023ea4f30SEduardo Habkost cases = genCases(args, testcase) 45123ea4f30SEduardo Habkost if args.random: 45223ea4f30SEduardo Habkost cases = list(cases) 45323ea4f30SEduardo Habkost cases = random.sample(cases, min(args.random, len(cases))) 45423ea4f30SEduardo Habkost if args.debug: 45523ea4f30SEduardo Habkost cases = list(cases) 45623ea4f30SEduardo Habkost dbg("%d test cases to test", len(cases)) 45723ea4f30SEduardo Habkost if args.shuffle: 45823ea4f30SEduardo Habkost cases = list(cases) 45923ea4f30SEduardo Habkost random.shuffle(cases) 46023ea4f30SEduardo Habkost return cases 46123ea4f30SEduardo Habkost 46223ea4f30SEduardo Habkost 46323ea4f30SEduardo Habkostdef logFailure(f, level): 46423ea4f30SEduardo Habkost t = f['testcase'] 46523ea4f30SEduardo Habkost logger.log(level, "failed: %s", formatTestCase(t)) 46623ea4f30SEduardo Habkost logger.log(level, "cmdline: %s", f['cmdline']) 46723ea4f30SEduardo Habkost for l in f['log'].strip().split('\n'): 46823ea4f30SEduardo Habkost logger.log(level, "log: %s", l) 46923ea4f30SEduardo Habkost logger.log(level, "exit code: %r", f['exitcode']) 470c398a241SJohn Snow 471c398a241SJohn Snow # If the Exception is merely a QMP connect error, 472c398a241SJohn Snow # reduce the logging level for its traceback to 473c398a241SJohn Snow # improve visual clarity. 474c398a241SJohn Snow if isinstance(f.get('exc'), ConnectError): 475c398a241SJohn Snow logger.log(level, "%s.%s: %s", 476c398a241SJohn Snow type(f['exc']).__module__, 477c398a241SJohn Snow type(f['exc']).__qualname__, 478c398a241SJohn Snow str(f['exc'])) 479c398a241SJohn Snow level = logging.DEBUG 480c398a241SJohn Snow 48123ea4f30SEduardo Habkost if f['exc_traceback']: 48223ea4f30SEduardo Habkost logger.log(level, "exception:") 48323ea4f30SEduardo Habkost for l in f['exc_traceback'].split('\n'): 48423ea4f30SEduardo Habkost logger.log(level, " %s", l.rstrip('\n')) 48523ea4f30SEduardo Habkost 48623ea4f30SEduardo Habkost 48723ea4f30SEduardo Habkostdef main(): 48823ea4f30SEduardo Habkost parser = argparse.ArgumentParser(description="QEMU -device crash test") 48923ea4f30SEduardo Habkost parser.add_argument('-t', metavar='KEY=VALUE', nargs='*', 49023ea4f30SEduardo Habkost help="Limit test cases to KEY=VALUE", 49123ea4f30SEduardo Habkost action='append', dest='testcases', default=[]) 49223ea4f30SEduardo Habkost parser.add_argument('-d', '--debug', action='store_true', 49323ea4f30SEduardo Habkost help='debug output') 49423ea4f30SEduardo Habkost parser.add_argument('-v', '--verbose', action='store_true', default=True, 49523ea4f30SEduardo Habkost help='verbose output') 49623ea4f30SEduardo Habkost parser.add_argument('-q', '--quiet', dest='verbose', action='store_false', 49723ea4f30SEduardo Habkost help='non-verbose output') 49823ea4f30SEduardo Habkost parser.add_argument('-r', '--random', type=int, metavar='COUNT', 49923ea4f30SEduardo Habkost help='run a random sample of COUNT test cases', 50023ea4f30SEduardo Habkost default=0) 50123ea4f30SEduardo Habkost parser.add_argument('--shuffle', action='store_true', 50223ea4f30SEduardo Habkost help='Run test cases in random order') 50323ea4f30SEduardo Habkost parser.add_argument('--dry-run', action='store_true', 50423ea4f30SEduardo Habkost help="Don't run any tests, just generate list") 50523ea4f30SEduardo Habkost parser.add_argument('-D', '--devtype', metavar='TYPE', 50623ea4f30SEduardo Habkost help="Test only device types that implement TYPE") 50723ea4f30SEduardo Habkost parser.add_argument('-Q', '--quick', action='store_true', default=True, 50823ea4f30SEduardo Habkost help="Quick mode: skip test cases that are expected to fail") 50923ea4f30SEduardo Habkost parser.add_argument('-F', '--full', action='store_false', dest='quick', 51023ea4f30SEduardo Habkost help="Full mode: test cases that are expected to fail") 51123ea4f30SEduardo Habkost parser.add_argument('--strict', action='store_true', dest='strict', 51223ea4f30SEduardo Habkost help="Treat all warnings as fatal") 5138b869aa5SThomas Huth parser.add_argument('--tcg-only', action='store_true', dest='tcg_only', 5148b869aa5SThomas Huth help="Only test with TCG accelerator") 51523ea4f30SEduardo Habkost parser.add_argument('qemu', nargs='*', metavar='QEMU', 51623ea4f30SEduardo Habkost help='QEMU binary to run') 51723ea4f30SEduardo Habkost args = parser.parse_args() 51823ea4f30SEduardo Habkost 51923ea4f30SEduardo Habkost if args.debug: 52023ea4f30SEduardo Habkost lvl = logging.DEBUG 52123ea4f30SEduardo Habkost elif args.verbose: 52223ea4f30SEduardo Habkost lvl = logging.INFO 52323ea4f30SEduardo Habkost else: 52423ea4f30SEduardo Habkost lvl = logging.WARN 52523ea4f30SEduardo Habkost logging.basicConfig(stream=sys.stdout, level=lvl, format='%(levelname)s: %(message)s') 52623ea4f30SEduardo Habkost 52776f86e78SJohn Snow if not args.debug: 52876f86e78SJohn Snow # Async QMP, when in use, is chatty about connection failures. 52976f86e78SJohn Snow # This script knowingly generates a ton of connection errors. 53076f86e78SJohn Snow # Silence this logger. 53137094b6dSJohn Snow logging.getLogger('qemu.qmp.qmp_client').setLevel(logging.CRITICAL) 53276f86e78SJohn Snow 53323ea4f30SEduardo Habkost fatal_failures = [] 53423ea4f30SEduardo Habkost wl_stats = {} 53523ea4f30SEduardo Habkost skipped = 0 53623ea4f30SEduardo Habkost total = 0 53723ea4f30SEduardo Habkost 53823ea4f30SEduardo Habkost tc = {} 53923ea4f30SEduardo Habkost dbg("testcases: %r", args.testcases) 54023ea4f30SEduardo Habkost if args.testcases: 54123ea4f30SEduardo Habkost for t in chain(*args.testcases): 54223ea4f30SEduardo Habkost for kv in t.split(): 54323ea4f30SEduardo Habkost k, v = kv.split('=', 1) 54423ea4f30SEduardo Habkost tc[k] = v 54523ea4f30SEduardo Habkost 54623ea4f30SEduardo Habkost if len(binariesToTest(args, tc)) == 0: 547f03868bdSEduardo Habkost print("No QEMU binary found", file=sys.stderr) 54823ea4f30SEduardo Habkost parser.print_usage(sys.stderr) 54923ea4f30SEduardo Habkost return 1 55023ea4f30SEduardo Habkost 55123ea4f30SEduardo Habkost for t in casesToTest(args, tc): 55223ea4f30SEduardo Habkost logger.info("running test case: %s", formatTestCase(t)) 55323ea4f30SEduardo Habkost total += 1 55423ea4f30SEduardo Habkost 55523ea4f30SEduardo Habkost expected_match = findExpectedResult(t) 55623ea4f30SEduardo Habkost if (args.quick and 55723ea4f30SEduardo Habkost (expected_match or 55823ea4f30SEduardo Habkost not getBinaryInfo(args, t['binary']).machineInfo(t['machine'])['runnable'])): 55923ea4f30SEduardo Habkost dbg("skipped: %s", formatTestCase(t)) 56023ea4f30SEduardo Habkost skipped += 1 56123ea4f30SEduardo Habkost continue 56223ea4f30SEduardo Habkost 56323ea4f30SEduardo Habkost if args.dry_run: 56423ea4f30SEduardo Habkost continue 56523ea4f30SEduardo Habkost 56623ea4f30SEduardo Habkost try: 56723ea4f30SEduardo Habkost f = checkOneCase(args, t) 56823ea4f30SEduardo Habkost except KeyboardInterrupt: 56923ea4f30SEduardo Habkost break 57023ea4f30SEduardo Habkost 57123ea4f30SEduardo Habkost if f: 5721a14d4e1SEduardo Habkost i, rule = checkResultRuleList(f) 5731a14d4e1SEduardo Habkost dbg("testcase: %r, rule list match: %r", t, rule) 57423ea4f30SEduardo Habkost wl_stats.setdefault(i, []).append(f) 5751a14d4e1SEduardo Habkost level = rule.get('loglevel', logging.DEBUG) 57623ea4f30SEduardo Habkost logFailure(f, level) 5771a14d4e1SEduardo Habkost if rule.get('fatal') or (args.strict and level >= logging.WARN): 57823ea4f30SEduardo Habkost fatal_failures.append(f) 57923ea4f30SEduardo Habkost else: 58023ea4f30SEduardo Habkost dbg("success: %s", formatTestCase(t)) 58123ea4f30SEduardo Habkost if expected_match: 58223ea4f30SEduardo Habkost logger.warn("Didn't fail as expected: %s", formatTestCase(t)) 58323ea4f30SEduardo Habkost 58423ea4f30SEduardo Habkost logger.info("Total: %d test cases", total) 58523ea4f30SEduardo Habkost if skipped: 58623ea4f30SEduardo Habkost logger.info("Skipped %d test cases", skipped) 58723ea4f30SEduardo Habkost 58823ea4f30SEduardo Habkost if args.debug: 5891a14d4e1SEduardo Habkost stats = sorted([(len(wl_stats.get(i, [])), rule) for i, rule in 5901a14d4e1SEduardo Habkost enumerate(ERROR_RULE_LIST)], key=lambda x: x[0]) 5911a14d4e1SEduardo Habkost for count, rule in stats: 5921a14d4e1SEduardo Habkost dbg("error rule stats: %d: %r", count, rule) 59323ea4f30SEduardo Habkost 59423ea4f30SEduardo Habkost if fatal_failures: 59523ea4f30SEduardo Habkost for f in fatal_failures: 59623ea4f30SEduardo Habkost t = f['testcase'] 59723ea4f30SEduardo Habkost logger.error("Fatal failure: %s", formatTestCase(t)) 59823ea4f30SEduardo Habkost logger.error("Fatal failures on some machine/device combinations") 59923ea4f30SEduardo Habkost return 1 60023ea4f30SEduardo Habkost 60123ea4f30SEduardo Habkostif __name__ == '__main__': 60223ea4f30SEduardo Habkost sys.exit(main()) 603