]> git.proxmox.com Git - mirror_qemu.git/blobdiff - scripts/device-crash-test
Merge tag 'pull-tcg-20211111' of https://gitlab.com/rth7680/qemu into staging
[mirror_qemu.git] / scripts / device-crash-test
index e93a7c0c84119ae7970942a9bdede48d1848655a..8331c057b80f1234df0275ba02c763478e74bf97 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 #  Copyright (c) 2017 Red Hat Inc
 #
 Run QEMU with all combinations of -machine and -device types,
 check for crashes and unexpected errors.
 """
-from __future__ import print_function
 
-import sys
 import os
+import sys
 import glob
 import logging
 import traceback
@@ -35,24 +34,25 @@ import random
 import argparse
 from itertools import chain
 
-from qemu import QEMUMachine
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
+from qemu.machine import QEMUMachine
 
 logger = logging.getLogger('device-crash-test')
 dbg = logger.debug
 
 
-# Purposes of the following whitelist:
+# Purposes of the following rule list:
 # * Avoiding verbose log messages when we find known non-fatal
 #   (exitcode=1) errors
 # * Avoiding fatal errors when we find known crashes
 # * Skipping machines/devices that are known not to work out of
 #   the box, when running in --quick mode
 #
-# Keeping the whitelist updated is desirable, but not required,
+# Keeping the rule list updated is desirable, but not required,
 # because unexpected cases where QEMU exits with exitcode=1 will
 # just trigger a INFO message.
 
-# Valid whitelist entry keys:
+# Valid error rule keys:
 # * accel: regexp, full match only
 # * machine: regexp, full match only
 # * device: regexp, full match only
@@ -62,7 +62,7 @@ dbg = logger.debug
 # * expected: if True, QEMU is expected to always fail every time
 #   when testing the corresponding test case
 # * loglevel: log level of log output when there's a match.
-ERROR_WHITELIST = [
+ERROR_RULE_LIST = [
     # Machines that won't work out of the box:
     #             MACHINE                         | ERROR MESSAGE
     {'machine':'niagara', 'expected':True},       # Unable to load a firmware for -M niagara
@@ -76,14 +76,12 @@ ERROR_WHITELIST = [
     {'device':'ics', 'expected':True},                     # ics_base_realize: required link 'xics' not found: Property '.xics' not found
     # "-device ide-cd" does work on more recent QEMU versions, so it doesn't have expected=True
     {'device':'ide-cd'},                                 # No drive specified
-    {'device':'ide-drive', 'expected':True},               # No drive specified
     {'device':'ide-hd', 'expected':True},                  # No drive specified
     {'device':'ipmi-bmc-extern', 'expected':True},         # IPMI external bmc requires chardev attribute
     {'device':'isa-debugcon', 'expected':True},            # Can't create serial device, empty char device
     {'device':'isa-ipmi-bt', 'expected':True},             # IPMI device requires a bmc attribute to be set
     {'device':'isa-ipmi-kcs', 'expected':True},            # IPMI device requires a bmc attribute to be set
     {'device':'isa-parallel', 'expected':True},            # Can't create serial device, empty char device
-    {'device':'ivshmem', 'expected':True},                 # You must specify either 'shm' or 'chardev'
     {'device':'ivshmem-doorbell', 'expected':True},        # You must specify a 'chardev'
     {'device':'ivshmem-plain', 'expected':True},           # You must specify a 'memdev'
     {'device':'loader', 'expected':True},                  # please include valid arguments
@@ -95,7 +93,6 @@ ERROR_WHITELIST = [
     {'device':'pci-bridge-seat', 'expected':True},         # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
     {'device':'pxb', 'expected':True},                     # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
     {'device':'scsi-block', 'expected':True},              # drive property not set
-    {'device':'scsi-disk', 'expected':True},               # drive property not set
     {'device':'scsi-generic', 'expected':True},            # drive property not set
     {'device':'scsi-hd', 'expected':True},                 # drive property not set
     {'device':'spapr-pci-host-bridge', 'expected':True},   # BUID not specified for PHB
@@ -178,89 +175,75 @@ ERROR_WHITELIST = [
     {'log':r"Multiple VT220 operator consoles are not supported"},
     {'log':r"core 0 already populated"},
     {'log':r"could not find stage1 bootloader"},
+    {'log':r"No '.*' bus found for device"},
 
     # other exitcode=1 failures not listed above will just generate INFO messages:
     {'exitcode':1, 'loglevel':logging.INFO},
 
-    # KNOWN CRASHES:
-    # Known crashes will generate error messages, but won't be fatal.
-    # Those entries must be removed once we fix the crashes.
-    {'exitcode':-6, 'log':r"Device 'serial0' is in use", 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r"qemu_net_client_setup: Assertion `!peer->peer' failed", 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r'RAMBlock "[\w.-]+" already registered', 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r"find_ram_offset: Assertion `size != 0' failed.", 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r"add_cpreg_to_hashtable: code should not be reached", 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r"qemu_alloc_display: Assertion `surface->image != NULL' failed", 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r"Unexpected error in error_set_from_qdev_prop_error", 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r"Object .* is not an instance of type spapr-machine", 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r"Object .* is not an instance of type generic-pc-machine", 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR},
-    {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR},
-
     # everything else (including SIGABRT and SIGSEGV) will be a fatal error:
     {'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL},
 ]
 
 
-def whitelistTestCaseMatch(wl, t):
-    """Check if a test case specification can match a whitelist entry
+def errorRuleTestCaseMatch(rule, t):
+    """Check if a test case specification can match a error rule
 
-    This only checks if a whitelist entry is a candidate match
+    This only checks if a error rule is a candidate match
     for a given test case, it won't check if the test case
-    results/output match the entry.  See whitelistResultMatch().
+    results/output match the rule.  See ruleListResultMatch().
     """
-    return (('machine' not in wl or
+    return (('machine' not in rule or
              'machine' not in t or
-             re.match(wl['machine'] + '$', t['machine'])) and
-            ('accel' not in wl or
+             re.match(rule['machine'] + '$', t['machine'])) and
+            ('accel' not in rule or
              'accel' not in t or
-             re.match(wl['accel'] + '$', t['accel'])) and
-            ('device' not in wl or
+             re.match(rule['accel'] + '$', t['accel'])) and
+            ('device' not in rule or
              'device' not in t or
-             re.match(wl['device'] + '$', t['device'])))
+             re.match(rule['device'] + '$', t['device'])))
 
 
-def whitelistCandidates(t):
+def ruleListCandidates(t):
     """Generate the list of candidates that can match a test case"""
-    for i, wl in enumerate(ERROR_WHITELIST):
-        if whitelistTestCaseMatch(wl, t):
-            yield (i, wl)
+    for i, rule in enumerate(ERROR_RULE_LIST):
+        if errorRuleTestCaseMatch(rule, t):
+            yield (i, rule)
 
 
 def findExpectedResult(t):
-    """Check if there's an expected=True whitelist entry for a test case
+    """Check if there's an expected=True error rule for a test case
 
-    Returns (i, wl) tuple, where i is the index in
-    ERROR_WHITELIST and wl is the whitelist entry itself.
+    Returns (i, rule) tuple, where i is the index in
+    ERROR_RULE_LIST and rule is the error rule itself.
     """
-    for i, wl in whitelistCandidates(t):
-        if wl.get('expected'):
-            return (i, wl)
+    for i, rule in ruleListCandidates(t):
+        if rule.get('expected'):
+            return (i, rule)
 
 
-def whitelistResultMatch(wl, r):
-    """Check if test case results/output match a whitelist entry
+def ruleListResultMatch(rule, r):
+    """Check if test case results/output match a error rule
 
     It is valid to call this function only if
-    whitelistTestCaseMatch() is True for the entry (e.g. on
-    entries returned by whitelistCandidates())
+    errorRuleTestCaseMatch() is True for the rule (e.g. on
+    rules returned by ruleListCandidates())
     """
-    assert whitelistTestCaseMatch(wl, r['testcase'])
-    return ((wl.get('exitcode', 1) is None or
-             r['exitcode'] == wl.get('exitcode', 1)) and
-            ('log' not in wl or
-             re.search(wl['log'], r['log'], re.MULTILINE)))
+    assert errorRuleTestCaseMatch(rule, r['testcase'])
+    return ((rule.get('exitcode', 1) is None or
+             r['exitcode'] == rule.get('exitcode', 1)) and
+            ('log' not in rule or
+             re.search(rule['log'], r['log'], re.MULTILINE)))
 
 
-def checkResultWhitelist(r):
-    """Look up whitelist entry for a given test case result
+def checkResultRuleList(r):
+    """Look up error rule for a given test case result
 
-    Returns (i, wl) tuple, where i is the index in
-    ERROR_WHITELIST and wl is the whitelist entry itself.
+    Returns (i, rule) tuple, where i is the index in
+    ERROR_RULE_LIST and rule is the error rule itself.
     """
-    for i, wl in whitelistCandidates(r['testcase']):
-        if whitelistResultMatch(wl, r):
-            return i, wl
+    for i, rule in ruleListCandidates(r['testcase']):
+        if ruleListResultMatch(rule, r):
+            return i, rule
 
     raise Exception("this should never happen")
 
@@ -399,7 +382,9 @@ def binariesToTest(args, testcase):
     if args.qemu:
         r = args.qemu
     else:
-        r = glob.glob('./*-softmmu/qemu-system-*')
+        r = [f.path for f in os.scandir('.')
+             if f.name.startswith('qemu-system-') and
+                f.is_file() and os.access(f, os.X_OK)]
     return r
 
 
@@ -557,12 +542,12 @@ def main():
             break
 
         if f:
-            i, wl = checkResultWhitelist(f)
-            dbg("testcase: %r, whitelist match: %r", t, wl)
+            i, rule = checkResultRuleList(f)
+            dbg("testcase: %r, rule list match: %r", t, rule)
             wl_stats.setdefault(i, []).append(f)
-            level = wl.get('loglevel', logging.DEBUG)
+            level = rule.get('loglevel', logging.DEBUG)
             logFailure(f, level)
-            if wl.get('fatal') or (args.strict and level >= logging.WARN):
+            if rule.get('fatal') or (args.strict and level >= logging.WARN):
                 fatal_failures.append(f)
         else:
             dbg("success: %s", formatTestCase(t))
@@ -574,9 +559,10 @@ def main():
         logger.info("Skipped %d test cases", skipped)
 
     if args.debug:
-        stats = sorted([(len(wl_stats.get(i, [])), wl) for i, wl in enumerate(ERROR_WHITELIST)])
-        for count, wl in stats:
-            dbg("whitelist entry stats: %d: %r", count, wl)
+        stats = sorted([(len(wl_stats.get(i, [])), rule) for i, rule in
+                         enumerate(ERROR_RULE_LIST)], key=lambda x: x[0])
+        for count, rule in stats:
+            dbg("error rule stats: %d: %r", count, rule)
 
     if fatal_failures:
         for f in fatal_failures: