From nobody Thu May 2 16:39:21 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1494646505901845.5243079049314; Fri, 12 May 2017 20:35:05 -0700 (PDT) Received: from localhost ([::1]:56153 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d9Npf-0000Kf-LB for importer@patchew.org; Fri, 12 May 2017 23:35:03 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41835) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d9No7-0007ux-Nj for qemu-devel@nongnu.org; Fri, 12 May 2017 23:33:28 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d9No6-0005KG-SQ for qemu-devel@nongnu.org; Fri, 12 May 2017 23:33:27 -0400 Received: from mx1.redhat.com ([209.132.183.28]:57970) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1d9No6-0005If-Mf for qemu-devel@nongnu.org; Fri, 12 May 2017 23:33:26 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id B882081241 for ; Sat, 13 May 2017 03:33:24 +0000 (UTC) Received: from localhost (ovpn-116-7.gru2.redhat.com [10.97.116.7]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7C14277D7D; Sat, 13 May 2017 03:33:22 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com B882081241 Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=ehabkost@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com B882081241 From: Eduardo Habkost To: qemu-devel@nongnu.org Date: Sat, 13 May 2017 00:33:14 -0300 Message-Id: <20170513033316.22395-2-ehabkost@redhat.com> In-Reply-To: <20170513033316.22395-1-ehabkost@redhat.com> References: <20170513033316.22395-1-ehabkost@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.25]); Sat, 13 May 2017 03:33:25 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v2 1/3] qemu.py: Don't set _popen=None on error/shutdown X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Marcel Apfelbaum , Thomas Huth , Markus Armbruster Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Keep the Popen object around to we can query its exit code later. To keep the existing 'self._popen is None' checks working, add a is_running() method, that will check if the process is still running. Signed-off-by: Eduardo Habkost --- scripts/qemu.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/qemu.py b/scripts/qemu.py index 6d1b6230b7..16934f1e02 100644 --- a/scripts/qemu.py +++ b/scripts/qemu.py @@ -85,8 +85,11 @@ class QEMUMachine(object): return raise =20 + def is_running(self): + return self._popen and (self._popen.returncode is None) + def get_pid(self): - if not self._popen: + if not self.is_running(): return None return self._popen.pid =20 @@ -128,16 +131,16 @@ class QEMUMachine(object): stderr=3Dsubprocess.STDOUT, she= ll=3DFalse) self._post_launch() except: - if self._popen: + if self.is_running(): self._popen.kill() + self._popen.wait() self._load_io_log() self._post_shutdown() - self._popen =3D None raise =20 def shutdown(self): '''Terminate the VM and clean up''' - if not self._popen is None: + if self.is_running(): try: self._qmp.cmd('quit') self._qmp.close() @@ -149,7 +152,6 @@ class QEMUMachine(object): sys.stderr.write('qemu received signal %i: %s\n' % (-exitc= ode, ' '.join(self._args))) self._load_io_log() self._post_shutdown() - self._popen =3D None =20 underscore_to_dash =3D string.maketrans('_', '-') def qmp(self, cmd, conv_keys=3DTrue, **args): --=20 2.11.0.259.g40922b1 From nobody Thu May 2 16:39:21 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1494646509320824.5650357347504; Fri, 12 May 2017 20:35:09 -0700 (PDT) Received: from localhost ([::1]:56154 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d9Npj-0000Oy-TJ for importer@patchew.org; Fri, 12 May 2017 23:35:07 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41848) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d9No8-0007uz-HA for qemu-devel@nongnu.org; Fri, 12 May 2017 23:33:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d9No7-0005Ll-So for qemu-devel@nongnu.org; Fri, 12 May 2017 23:33:28 -0400 Received: from mx1.redhat.com ([209.132.183.28]:58444) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1d9No7-0005Kf-MF for qemu-devel@nongnu.org; Fri, 12 May 2017 23:33:27 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id AB3A2811A9 for ; Sat, 13 May 2017 03:33:26 +0000 (UTC) Received: from localhost (ovpn-116-7.gru2.redhat.com [10.97.116.7]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3A28677DD9; Sat, 13 May 2017 03:33:26 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com AB3A2811A9 Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx03.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=ehabkost@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com AB3A2811A9 From: Eduardo Habkost To: qemu-devel@nongnu.org Date: Sat, 13 May 2017 00:33:15 -0300 Message-Id: <20170513033316.22395-3-ehabkost@redhat.com> In-Reply-To: <20170513033316.22395-1-ehabkost@redhat.com> References: <20170513033316.22395-1-ehabkost@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Sat, 13 May 2017 03:33:26 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v2 2/3] qemu.py: Add QEMUMachine.exitcode() method X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Marcel Apfelbaum , Thomas Huth , Markus Armbruster Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Allow the exit code of QEMU to be queried by scripts. Signed-off-by: Eduardo Habkost --- scripts/qemu.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/qemu.py b/scripts/qemu.py index 16934f1e02..ebe1c4b919 100644 --- a/scripts/qemu.py +++ b/scripts/qemu.py @@ -88,6 +88,10 @@ class QEMUMachine(object): def is_running(self): return self._popen and (self._popen.returncode is None) =20 + def exitcode(self): + if self._popen: + return self._popen.returncode + def get_pid(self): if not self.is_running(): return None --=20 2.11.0.259.g40922b1 From nobody Thu May 2 16:39:21 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1494646597871766.8312401231316; Fri, 12 May 2017 20:36:37 -0700 (PDT) Received: from localhost ([::1]:56163 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d9NrA-0001cv-J1 for importer@patchew.org; Fri, 12 May 2017 23:36:36 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41889) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d9NoD-00080I-KJ for qemu-devel@nongnu.org; Fri, 12 May 2017 23:33:39 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d9NoA-0005PU-Dd for qemu-devel@nongnu.org; Fri, 12 May 2017 23:33:33 -0400 Received: from mx1.redhat.com ([209.132.183.28]:57838) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1d9No9-0005Nm-Uh for qemu-devel@nongnu.org; Fri, 12 May 2017 23:33:30 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E4A9FC057FA8 for ; Sat, 13 May 2017 03:33:28 +0000 (UTC) Received: from localhost (ovpn-116-7.gru2.redhat.com [10.97.116.7]) by smtp.corp.redhat.com (Postfix) with ESMTP id 12F617EF61; Sat, 13 May 2017 03:33:27 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com E4A9FC057FA8 Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=ehabkost@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com E4A9FC057FA8 From: Eduardo Habkost To: qemu-devel@nongnu.org Date: Sat, 13 May 2017 00:33:16 -0300 Message-Id: <20170513033316.22395-4-ehabkost@redhat.com> In-Reply-To: <20170513033316.22395-1-ehabkost@redhat.com> References: <20170513033316.22395-1-ehabkost@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Sat, 13 May 2017 03:33:29 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v2 3/3] scripts: Test script to look for -device crashes X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Marcel Apfelbaum , Thomas Huth , Markus Armbruster Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Test code to check if we can crash QEMU using -device. It will test all accel/machine/device combinations by default, which may take a few hours (it's more than 90k test cases). There's a "-r" option that makes it test a random sample of combinations. The scripts contains a whitelist for: 1) known error messages that make QEMU exit cleanly; 2) known QEMU crashes. This is the behavior when the script finds a failure: * Known clean (exitcode=3D1) error messages generate INFO messages (visible only in verbose mode), to make script output shorter * Unknown clean error messages generate warnings (visible by default) * Known crashes generate error messages, but are not fatal * Unknown crashes generate fatal error messages I'm unsure about the need to maintain a list of known clean error messages, but I wanted to at least document all existing failure cases to use as base to build more comprehensive test code. Signed-off-by: Eduardo Habkost --- Changes v1 -> v2: * New whitelist entries: * "could not find stage1 bootloader" * Segfaults when using devices: a15mpcore_priv, sb16, cs4231a, arm-gicv3 * Format "success" line using formatTestCase(), and using DEBUg loglevel * Reword "test case:" line with "running test case:", for clarity * Fix "pc-.*" whitelist to include "q35" too * Add --devtype option to test only a specific device type * Send all log messages to stdout instead of stderr * Avoid printing "obsolete whitelist entry?" messages if we know we are not testing every single accel/machine/device combination * --quick mode, to skip cases where failures are always expected, and to print a warning in case we don't get an expected failure * Use qemu.QEMUMachine instead of qtest.QEMUQtestMachine, as we don't use any of the QEMUQtestMachine features * Fix handling of multiple '-t' options * Simplify code that generate random sample of test cases --- scripts/device-crash-test.py | 520 +++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 520 insertions(+) create mode 100755 scripts/device-crash-test.py diff --git a/scripts/device-crash-test.py b/scripts/device-crash-test.py new file mode 100755 index 0000000000..550da70ec7 --- /dev/null +++ b/scripts/device-crash-test.py @@ -0,0 +1,520 @@ +#!/usr/bin/env python2.7 +# +# Run QEMU with all combinations of -machine and -device types, +# check for crashes and unexpected errors. +# +# Copyright (c) 2017 Red Hat Inc +# +# Author: +# Eduardo Habkost +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + +import sys, os, glob +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'scripts')) + +from itertools import chain +from qemu import QEMUMachine +import logging, traceback, re, random, argparse + +logger =3D logging.getLogger('device-crash-test') +dbg =3D logger.debug + +# Valid whitelist entry keys: +# - accel: regexp, full match only +# - machine: regexp, full match only +# - device: regexp, full match only +# - log: regexp, partial match allowed +# - exitcode: if not present, defaults to 1. If None, matches any exitcode +# - warn: if True, matching failures will be logged as warnings +# - expected: if True, QEMU is expected to always fail every time +# when testing the corresponding test case +ERROR_WHITELIST =3D [ + # Machines that won't work out of the box: + # MACHINE | ERROR MESSAGE + dict(machine=3D'niagara', expected=3DTrue), # Unable to load a fir= mware for -M niagara + dict(machine=3D'boston', expected=3DTrue), # Please provide eithe= r a -kernel or -bios argument + dict(machine=3D'leon3_generic', expected=3DTrue), # Can't read bios imag= e (null) + + # devices that don't work out of the box because they require extra opti= ons to "-device DEV": + # DEVICE | ERROR MESSAGE + dict(device=3D'.*-(i386|x86_64)-cpu', expected=3DTrue), # CPU socket-= id is not set + dict(device=3D'ARM,bitband-memory', expected=3DTrue), # source-memo= ry property not set + dict(device=3D'arm.cortex-a9-global-timer', expected=3DTrue), # a9_gtime= r_realize: num-cpu must be between 1 and 4 + dict(device=3D'arm_mptimer', expected=3DTrue), # num-cpu mus= t be between 1 and 4 + dict(device=3D'armv7m', expected=3DTrue), # memory prop= erty was not set + dict(device=3D'aspeed.scu', expected=3DTrue), # Unknown sil= icon revision: 0x0 + dict(device=3D'aspeed.sdmc', expected=3DTrue), # Unknown sil= icon revision: 0x0 + dict(device=3D'bcm2835-dma', expected=3DTrue), # bcm2835_dma= _realize: required dma-mr link not found: Property '.dma-mr' not found + dict(device=3D'bcm2835-fb', expected=3DTrue), # bcm2835_fb_= realize: required vcram-base property not set + dict(device=3D'bcm2835-mbox', expected=3DTrue), # bcm2835_mbo= x_realize: required mbox-mr link not found: Property '.mbox-mr' not found + dict(device=3D'bcm2835-peripherals', expected=3DTrue), # bcm2835_per= ipherals_realize: required ram link not found: Property '.ram' not found + dict(device=3D'bcm2835-property', expected=3DTrue), # bcm2835_pro= perty_realize: required fb link not found: Property '.fb' not found + dict(device=3D'bcm2835_gpio', expected=3DTrue), # bcm2835_gpi= o_realize: required sdhci link not found: Property '.sdbus-sdhci' not found + dict(device=3D'bcm2836', expected=3DTrue), # bcm2836_rea= lize: required ram link not found: Property '.ram' not found + dict(device=3D'cfi.pflash01', expected=3DTrue), # attribute "= sector-length" not specified or zero. + dict(device=3D'cfi.pflash02', expected=3DTrue), # attribute "= sector-length" not specified or zero. + dict(device=3D'icp', expected=3DTrue), # icp_realize= : required link 'xics' not found: Property '.xics' not found + dict(device=3D'ics', expected=3DTrue), # ics_base_re= alize: required link 'xics' not found: Property '.xics' not found + dict(device=3D'ide-drive', expected=3DTrue), # No drive sp= ecified + dict(device=3D'ide-hd', expected=3DTrue), # No drive sp= ecified + dict(device=3D'ipmi-bmc-extern', expected=3DTrue), # IPMI extern= al bmc requires chardev attribute + dict(device=3D'isa-debugcon', expected=3DTrue), # Can't creat= e serial device, empty char device + dict(device=3D'isa-ipmi-bt', expected=3DTrue), # IPMI device= requires a bmc attribute to be set + dict(device=3D'isa-ipmi-kcs', expected=3DTrue), # IPMI device= requires a bmc attribute to be set + dict(device=3D'isa-parallel', expected=3DTrue), # Can't creat= e serial device, empty char device + dict(device=3D'isa-serial', expected=3DTrue), # Can't creat= e serial device, empty char device + dict(device=3D'ivshmem', expected=3DTrue), # You must sp= ecify either 'shm' or 'chardev' + dict(device=3D'ivshmem-doorbell', expected=3DTrue), # You must sp= ecify a 'chardev' + dict(device=3D'ivshmem-plain', expected=3DTrue), # You must sp= ecify a 'memdev' + dict(device=3D'kvm-pci-assign', expected=3DTrue), # no host dev= ice specified + dict(device=3D'loader', expected=3DTrue), # please incl= ude valid arguments + dict(device=3D'nand', expected=3DTrue), #' Unsupporte= d NAND block size 0x1 + dict(device=3D'nvdimm', expected=3DTrue), # 'memdev' pr= operty is not set + dict(device=3D'nvme', expected=3DTrue), # Device init= ialization failed + dict(device=3D'pc-dimm', expected=3DTrue), # 'memdev' pr= operty is not set + dict(device=3D'pci-bridge', expected=3DTrue), # Bridge chas= sis not specified. Each bridge is required to be assigned a unique chassis = id > 0. + dict(device=3D'pci-bridge-seat', expected=3DTrue), # Bridge chas= sis not specified. Each bridge is required to be assigned a unique chassis = id > 0. + dict(device=3D'pci-serial', expected=3DTrue), # Can't creat= e serial device, empty char device + dict(device=3D'pci-serial-2x', expected=3DTrue), # Can't creat= e serial device, empty char device + dict(device=3D'pci-serial-4x', expected=3DTrue), # Can't creat= e serial device, empty char device + dict(device=3D'pxa2xx-dma', expected=3DTrue), # channels va= lue invalid + dict(device=3D'pxb', expected=3DTrue), # Bridge chas= sis not specified. Each bridge is required to be assigned a unique chassis = id > 0. + dict(device=3D'scsi-block', expected=3DTrue), # drive prope= rty not set + dict(device=3D'scsi-disk', expected=3DTrue), # drive prope= rty not set + dict(device=3D'scsi-generic', expected=3DTrue), # drive prope= rty not set + dict(device=3D'scsi-hd', expected=3DTrue), # drive prope= rty not set + dict(device=3D'spapr-pci-host-bridge', expected=3DTrue), # BUID not sp= ecified for PHB + dict(device=3D'spapr-pci-vfio-host-bridge', expected=3DTrue), # BUID not= specified for PHB + dict(device=3D'spapr-rng', expected=3DTrue), #' spapr-rng = needs an RNG backend! + dict(device=3D'spapr-vty', expected=3DTrue), # chardev pro= perty not set + dict(device=3D'tpm-tis', expected=3DTrue), # tpm_tis: ba= ckend driver with id (null) could not be found + dict(device=3D'unimplemented-device', expected=3DTrue), # property 's= ize' not specified or zero + dict(device=3D'usb-braille', expected=3DTrue), # Property ch= ardev is required + dict(device=3D'usb-mtp', expected=3DTrue), # x-root prop= erty must be configured + dict(device=3D'usb-redir', expected=3DTrue), # Parameter '= chardev' is missing + dict(device=3D'usb-serial', expected=3DTrue), # Property ch= ardev is required + dict(device=3D'usb-storage', expected=3DTrue), # drive prope= rty not set + dict(device=3D'vfio-amd-xgbe', expected=3DTrue), # -device vfi= o-amd-xgbe: vfio error: wrong host device name + dict(device=3D'vfio-calxeda-xgmac', expected=3DTrue), # -device vfi= o-calxeda-xgmac: vfio error: wrong host device name + dict(device=3D'vfio-pci', expected=3DTrue), # No provided= host device + dict(device=3D'vfio-pci-igd-lpc-bridge', expected=3DTrue), # VFIO dummy = ISA/LPC bridge must have address 1f.0 + dict(device=3D'vhost-scsi.*', expected=3DTrue), # vhost-scsi:= missing wwpn + dict(device=3D'vhost-vsock-device', expected=3DTrue), # guest-cid p= roperty must be greater than 2 + dict(device=3D'vhost-vsock-pci', expected=3DTrue), # guest-cid p= roperty must be greater than 2 + dict(device=3D'virtio-9p-ccw', expected=3DTrue), # 9pfs device= couldn't find fsdev with the id =3D NULL + dict(device=3D'virtio-9p-device', expected=3DTrue), # 9pfs device= couldn't find fsdev with the id =3D NULL + dict(device=3D'virtio-9p-pci', expected=3DTrue), # 9pfs device= couldn't find fsdev with the id =3D NULL + dict(device=3D'virtio-blk-ccw', expected=3DTrue), # drive prope= rty not set + dict(device=3D'virtio-blk-device', expected=3DTrue), # drive prope= rty not set + dict(device=3D'virtio-blk-device', expected=3DTrue), # drive prope= rty not set + dict(device=3D'virtio-blk-pci', expected=3DTrue), # drive prope= rty not set + dict(device=3D'virtio-crypto-ccw', expected=3DTrue), # 'cryptodev'= parameter expects a valid object + dict(device=3D'virtio-crypto-device', expected=3DTrue), # 'cryptodev'= parameter expects a valid object + dict(device=3D'virtio-crypto-pci', expected=3DTrue), # 'cryptodev'= parameter expects a valid object + dict(device=3D'virtio-input-host-device', expected=3DTrue), # evdev prop= erty is required + dict(device=3D'virtio-input-host-pci', expected=3DTrue), # evdev prope= rty is required + dict(device=3D'xen-pvdevice', expected=3DTrue), # Device ID i= nvalid, it must always be supplied + dict(device=3D'vhost-vsock-ccw', expected=3DTrue), # guest-cid p= roperty must be greater than 2 + dict(device=3D'ALTR.timer', expected=3DTrue), # "clock-freq= uency" property must be provided + dict(device=3D'zpci', expected=3DTrue), # target must= be defined + + # ioapic devices are already created by pc and will fail: + dict(machine=3D'q35|pc.*', device=3D'kvm-ioapic', expected=3DTrue), # On= ly 1 ioapics allowed + dict(machine=3D'q35|pc.*', device=3D'ioapic', expected=3DTrue), # On= ly 1 ioapics allowed + + # KVM-specific devices shouldn't be tried without accel=3Dkvm: + dict(accel=3D'(?!kvm).*', device=3D'kvmclock', expected=3DTrue), + dict(accel=3D'(?!kvm).*', device=3D'kvm-pci-assign', expected=3DTrue), + + # xen-specific machines and devices: + dict(accel=3D'(?!xen).*', machine=3D'xen.*', expected=3DTrue), + dict(accel=3D'(?!xen).*', device=3D'xen-.*', expected=3DTrue), + + # KNOWN CRASHES: + # known crashes will generate error messages, but won't be fatal: + dict(exitcode=3D-6, log=3Dr"Device 'serial0' is in use", loglevel=3Dlogg= ing.ERROR), + dict(exitcode=3D-6, log=3Dr"spapr_rtas_register: Assertion .*rtas_table\= [token\]\.name.* failed", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"qemu_net_client_setup: Assertion `!peer->pee= r' failed", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr'RAMBlock "[\w.-]+" already registered', logl= evel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"find_ram_offset: Assertion `size !=3D 0' fai= led.", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"puv3_load_kernel: Assertion `kernel_filename= !=3D NULL' failed", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"add_cpreg_to_hashtable: code should not be r= eached", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"qemu_alloc_display: Assertion `surface->imag= e !=3D NULL' failed", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"Unexpected error in error_set_from_qdev_prop= _error", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"Object .* is not an instance of type spapr-m= achine", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"Object .* is not an instance of type generic= -pc-machine", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"Object .* is not an instance of type e500-cc= sr", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-6, log=3Dr"vmstate_register_with_alias_id: Assertion `!= se->compat || se->instance_id =3D=3D 0' failed", loglevel=3Dlogging.ERROR), + dict(exitcode=3D-11, device=3D'stm32f205-soc', loglevel=3Dlogging.ERROR,= expected=3DTrue), + dict(exitcode=3D-11, device=3D'xlnx,zynqmp', loglevel=3Dlogging.ERROR, e= xpected=3DTrue), + dict(exitcode=3D-11, device=3D'mips-cps', loglevel=3Dlogging.ERROR, expe= cted=3DTrue), + dict(exitcode=3D-11, device=3D'gus', loglevel=3Dlogging.ERROR, expected= =3DTrue), + dict(exitcode=3D-11, device=3D'a9mpcore_priv', loglevel=3Dlogging.ERROR,= expected=3DTrue), + dict(exitcode=3D-11, device=3D'a15mpcore_priv', loglevel=3Dlogging.ERROR= , expected=3DTrue), + dict(exitcode=3D-11, device=3D'isa-serial', loglevel=3Dlogging.ERROR, ex= pected=3DTrue), + dict(exitcode=3D-11, device=3D'sb16', loglevel=3Dlogging.ERROR, expected= =3DTrue), + dict(exitcode=3D-11, device=3D'cs4231a', loglevel=3Dlogging.ERROR, expec= ted=3DTrue), + dict(exitcode=3D-11, device=3D'arm-gicv3', loglevel=3Dlogging.ERROR, exp= ected=3DTrue), + dict(exitcode=3D-11, machine=3D'isapc', device=3D'.*-iommu', loglevel=3D= logging.ERROR, expected=3DTrue), + + # Some error messages that are common on multiple devices/machines: + dict(log=3Dr"No '[\w-]+' bus found for device '[\w-]+'"), + dict(log=3Dr"images* must be given with the 'pflash' parameter"), + dict(log=3Dr"(Guest|ROM|Flash|Kernel) image must be specified"), + dict(log=3Dr"[cC]ould not load [\w ]+ (BIOS|bios) '[\w-]+\.bin'"), + dict(log=3Dr"Couldn't find rom image '[\w-]+\.bin'"), + dict(log=3Dr"speed mismatch trying to attach usb device"), + dict(log=3Dr"Can't create a second ISA bus"), + dict(log=3Dr"duplicate fw_cfg file name"), + # sysbus-related error messages: most machines reject most dynamic sysbu= s devices: + #TODO: expected=3DTrue entries for unsupported sysbus devices + dict(log=3Dr"Option '-device [\w.,-]+' cannot be handled by this machine= "), + dict(log=3Dr"Device [\w.,-]+ is not supported by this machine yet"), + dict(log=3Dr"Device [\w.,-]+ can not be dynamically instantiated"), + dict(log=3Dr"Platform Bus: Can not fit MMIO region of size "), + # other more specific errors we will ignore: + dict(device=3D'allwinner-a10', log=3D"Unsupported NIC model:"), + dict(device=3D'.*-spapr-cpu-core', log=3Dr"CPU core type should be"), + dict(log=3Dr"MSI(-X)? is not supported by interrupt controller"), + dict(log=3Dr"pxb-pcie? devices cannot reside on a PCIe? bus"), + dict(log=3Dr"Ignoring smp_cpus value"), + dict(log=3Dr"sd_init failed: Drive 'sd0' is already in use because it ha= s been automatically connected to another device"), + dict(log=3Dr"This CPU requires a smaller page size than the system is us= ing"), + dict(log=3Dr"MSI-X support is mandatory in the S390 architecture"), + dict(log=3Dr"rom check and register reset failed"), + dict(log=3Dr"Unable to initialize GIC, CPUState for CPU#0 not valid"), + dict(log=3Dr"Multiple VT220 operator consoles are not supported"), + dict(log=3Dr"core 0 already populated"), + dict(log=3Dr"could not find stage1 bootloader"), + + # other exitcode=3D1 failures not listed above will generate warnings: + dict(exitcode=3D1, loglevel=3Dlogging.WARN), + + # everything else (including SIGABRT and SIGSEGV) will be a fatal error: + dict(exitcode=3DNone, fatal=3DTrue, loglevel=3Dlogging.FATAL), +] + +def whitelistMatch(wl, f): + t =3D f.get('testcase', {}) + return (not wl.has_key('machine') \ + or not t.has_key('machine') \ + or re.match(wl['machine'] +'$', t['machine'])) \ + and (not wl.has_key('accel') \ + or not t.has_key('accel') \ + or re.match(wl['accel'] +'$', t['accel'])) \ + and (not wl.has_key('device') \ + or not t.has_key('device') \ + or re.match(wl['device'] +'$', t['device'])) \ + and (wl.get('exitcode', 1) is None \ + or not f.has_key('exitcode') + or f['exitcode'] =3D=3D wl.get('exitcode', 1)) \ + and (not wl.has_key('log') \ + or not f.has_key('log') \ + or re.search(wl['log'], f['log'], re.MULTILINE)) + +def checkWhitelist(f): + """Look up whitelist entry for failure dictionary + + Returns index in ERROR_WHITELIST + """ + for i,wl in enumerate(ERROR_WHITELIST): + #dbg("whitelist entry: %r", wl) + if whitelistMatch(wl, f): + return i, wl + + raise Exception("this should never happen") + +def qemuOptsEscape(s): + return s.replace(",", ",,") + +def formatTestCase(t): + return ' '.join('%s=3D%s' % (k, v) for k,v in t.items()) + +def qomListTypeNames(vm, **kwargs): + """Run qom-list-types QMP command, return type names""" + types =3D vm.command('qom-list-types', **kwargs) + return [t['name'] for t in types] + +def infoQDM(vm): + """Parse 'info qdm' output""" + args =3D {'command-line': 'info qdm'} + devhelp =3D vm.command('human-monitor-command', **args) + for l in devhelp.split('\n'): + l =3D l.strip() + if l =3D=3D '' or l.endswith(':'): + continue + d =3D {'name': re.search(r'name "([^"]+)"', l).group(1), + 'no-user': (re.search(', no-user', l) is not None)} + #dbg('info qdm item: %r', d) + yield d + + +class QemuBinaryInfo: + def __init__(self, binary, devtype): + if devtype is None: + devtype =3D 'device' + + dbg("devtype: %r", devtype) + args =3D ['-S', '-machine', 'none,accel=3Dkvm:tcg'] + dbg("querying info for QEMU binary: %s", binary) + vm =3D QEMUMachine(binary=3Dbinary, args=3Dargs) + vm.launch() + try: + self.alldevs =3D set(qomListTypeNames(vm, implements=3Ddevtype= , abstract=3DFalse)) + # there's no way to query cannot_instantiate_with_device_add_y= et using QMP, + # so use 'info qdm': + self.no_user_devs =3D set([d['name'] for d in infoQDM(vm, ) if= d['no-user']]) + self.machines =3D list(m['name'] for m in vm.command('query-ma= chines')) + self.user_devs =3D self.alldevs.difference(self.no_user_devs) + self.kvm_available =3D vm.command('query-kvm')['enabled'] + finally: + vm.shutdown() + +BINARY_INFO =3D {} +def getBinaryInfo(args, binary): + if not BINARY_INFO.has_key(binary): + BINARY_INFO[binary] =3D QemuBinaryInfo(binary, args.devtype) + return BINARY_INFO[binary] + +def checkOneCase(args, testcase): + """Check one specific case + + Returns a dictionary containing failure information on error, + or None on success + """ + binary =3D testcase['binary'] + accel =3D testcase['accel'] + machine =3D testcase['machine'] + device =3D testcase['device'] + info =3D getBinaryInfo(args, binary) + + dbg("will test: %r", testcase) + + args =3D ['-S', '-machine', '%s,accel=3D%s' % (machine, accel), + '-device', qemuOptsEscape(device)] + cmdline =3D ' '.join([binary] + args) + dbg("will launch QEMU: %s", cmdline) + vm =3D QEMUMachine(binary=3Dbinary, args=3Dargs) + + exception =3D None + try: + vm.launch() + except KeyboardInterrupt: + raise + except Exception,e: + exception =3D e + finally: + vm.shutdown() + ec =3D vm.exitcode() + log =3D vm.get_log() + + if exception is not None or ec !=3D 0: + f =3D dict(exception =3D exception, + exitcode =3D ec, + log =3D log, + testcase =3D testcase, + cmdline=3Dcmdline) + return f + +def binariesToTest(args, testcase): + if args.qemu: + r =3D args.qemu + else: + r =3D glob.glob('./*-softmmu/qemu-system-*') + return r + +def accelsToTest(args, testcase): + if getBinaryInfo(args, testcase['binary']).kvm_available: + yield 'kvm' + yield 'tcg' + +def machinesToTest(args, testcase): + return getBinaryInfo(args, testcase['binary']).machines + +def devicesToTest(args, testcase): + return getBinaryInfo(args, testcase['binary']).user_devs + +TESTCASE_VARIABLES =3D [ + ('binary', binariesToTest), + ('accel', accelsToTest), + ('machine', machinesToTest), + ('device', devicesToTest), +] + +def genCases1(args, testcases, var, fn): + """Generate new testcases for one variable + + If an existing item already has a variable set, don't + generate new items and just return it directly. This + allows the "-t" command-line option to be used to choose + a specific test case. + """ + for testcase in testcases: + t =3D testcase.copy + if testcase.has_key(var): + yield testcase.copy() + else: + for i in fn(args, testcase): + t =3D testcase.copy() + t[var] =3D i + yield t + +def genCases(args, testcase): + """Generate test cases for all variables + """ + cases =3D [testcase.copy()] + for var,fn in TESTCASE_VARIABLES: + dbg("var: %r, fn: %r", var, fn) + cases =3D genCases1(args, cases, var, fn) + return cases + +def genAllCases(args, testcase): + return genCases(args, testcase) + +def pickRandomCase(args, testcase): + cases =3D list(genCases(args, testcase)) + if cases: + assert len(cases) =3D=3D 1 + return cases[0] + +def casesToTest(args, testcase): + cases =3D genAllCases(args, testcase) + if args.random: + cases =3D random.sample(list(cases), args.random) + if args.debug: + cases =3D list(cases) + dbg("%d test cases to test", len(cases)) + if args.shuffle: + cases =3D list(cases) + random.shuffle(cases) + return cases + +def logFailure(f, level): + t =3D f['testcase'] + logger.log(level, "failed: %s", formatTestCase(t)) + logger.log(level, "cmdline: %s", f['cmdline']) + for l in f['log'].strip().split('\n'): + logger.log(level, "log: %s", l) + logger.log(level, "exit code: %r", f['exitcode']) + +def main(): + parser =3D argparse.ArgumentParser(description=3D"QEMU -device crash t= est") + parser.add_argument('-t', metavar=3D'KEY=3DVALUE', nargs=3D'*', + help=3D"Limit test cases to KEY=3DVALUE", + action=3D'append', dest=3D'testcases', default=3D[= ]) + parser.add_argument('-d', '--debug', action=3D'store_true', + help=3D'debug output') + parser.add_argument('-v', '--verbose', action=3D'store_true', + help=3D'verbose output') + parser.add_argument('-r', '--random', type=3Dint, metavar=3D'COUNT', + help=3D'run a random sample of COUNT test cases', + default=3D0) + parser.add_argument('--shuffle', action=3D'store_true', + help=3D'Run test cases in random order') + parser.add_argument('--dry-run', action=3D'store_true', + help=3D"Don't run any tests, just generate list") + parser.add_argument('-D', '--devtype', metavar=3D'TYPE', + help=3D"Test only device types that implement TYPE= ") + parser.add_argument('-Q', '--quick', action=3D'store_true', + help=3D"Quick mode, skip test cases that are expec= ted to fail") + parser.add_argument('qemu', nargs=3D'*', metavar=3D'QEMU', + help=3D'QEMU binary to run') + args =3D parser.parse_args() + + if args.debug: + lvl =3D logging.DEBUG + elif args.verbose: + lvl =3D level=3Dlogging.INFO + else: + lvl =3D level=3Dlogging.WARN + logging.basicConfig(stream=3Dsys.stdout, level=3Dlvl, format=3D'%(leve= lname)s: %(message)s') + + + interrupted =3D False + fatal_failures =3D [] + wl_stats =3D {} + skipped =3D 0 + total =3D 0 + + tc =3D {} + dbg("testcases: %r", args.testcases) + if args.testcases: + for t in chain(*args.testcases): + for kv in t.split(): + k,v =3D kv.split('=3D', 1) + tc[k] =3D v + + if len(binariesToTest(args, tc)) =3D=3D 0: + print >>sys.stderr, "No QEMU binary found" + parser.print_usage(sys.stderr) + return 1 + + for t in casesToTest(args, tc): + logger.info("running test case: %s", formatTestCase(t)) + total +=3D 1 + + _,expected_match =3D checkWhitelist({'testcase': t}) + if args.quick and expected_match.get('expected'): + dbg("Skipped: %s", formatTestCase(t)) + skipped +=3D 1 + continue + + if args.dry_run: + continue + + try: + f =3D checkOneCase(args, t) + except KeyboardInterrupt: + interrupted =3D True + break + + if f: + i,wl =3D checkWhitelist(f) + dbg("testcase: %r, whitelist match: %r", t, wl) + wl_stats.setdefault(i, []).append(f) + logFailure(f, wl.get('loglevel', logging.DEBUG)) + if wl.get('fatal'): + fatal_failures.append(f) + else: + dbg("success: %s", formatTestCase(t)) + if expected_match.get('expected'): + logger.warn("Didn't fail as expected: %s", formatTestCase(= t)) + + logger.info("Total: %d test cases", total) + if skipped: + logger.info("Skipped %d test cases", skipped) + + # tell us about obsolete whitelist entries so we can clean it up after + # bugs are fixed: + if not (interrupted or args.random or args.dry_run \ + or args.testcases or args.qemu or args.devtype): + for i,wl in enumerate(ERROR_WHITELIST): + if not wl_stats.get(i) and not wl.get('fatal'): + logger.info("Obsolete whitelist entry? %r", wl) + + stats =3D sorted([(len(wl_stats[i]), i) for i in wl_stats]) + for count,i in stats: + dbg("whitelist entry stats: %d: %r", count, ERROR_WHITELIST[i]) + + if fatal_failures: + for f in fatal_failures: + t =3D f['testcase'] + logger.error("Fatal failure: %s", formatTestCase(t)) + logger.error("Fatal failures on some machine/device combinations") + return 1 + +if __name__ =3D=3D '__main__': + sys.exit(main()) --=20 2.11.0.259.g40922b1