From 710c03170939c74e2b53b0609354b84113788f53 Mon Sep 17 00:00:00 2001 From: Vaibhav Agrawal Date: Wed, 26 Mar 2014 06:14:51 -0400 Subject: [PATCH] Bug 908945 - Fix automation.py's exit code handling; r=jmaher --- build/automation.py.in | 16 +++++++++--- build/automationutils.py | 47 ++++++++++++++++++++++++++++++++--- testing/mochitest/runtests.py | 17 +++++++++---- 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/build/automation.py.in b/build/automation.py.in index 404baaa3cccf..9c7ba475b4d2 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -449,6 +449,7 @@ class Automation(object): pk12util = os.path.join(utilityPath, "pk12util" + self.BIN_SUFFIX) status = self.Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait() + automationutils.printstatus(status, "certutil") if status != 0: return status @@ -460,13 +461,15 @@ class Automation(object): trustBits = "CT,," if root.endswith("-object"): trustBits = "CT,,CT" - self.Process([certutil, "-A", "-i", os.path.join(certPath, item), + status = self.Process([certutil, "-A", "-i", os.path.join(certPath, item), "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits], env = env).wait() + automationutils.printstatus(status, "certutil") if ext == ".client": - self.Process([pk12util, "-i", os.path.join(certPath, item), "-w", + status = self.Process([pk12util, "-i", os.path.join(certPath, item), "-w", pwfilePath, "-d", profileDir], env = env).wait() + automationutils.printstatus(status, "pk12util") os.unlink(pwfilePath) return 0 @@ -654,8 +657,11 @@ class Automation(object): else: # We should have a "crashinject" program in our utility path crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe")) - if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(processPID)]).wait() == 0: - return + if os.path.exists(crashinject): + status = subprocess.Popen([crashinject, str(processPID)]).wait() + automationutils.printstatus(status, "crashinject") + if status == 0: + return self.log.info("Can't trigger Breakpad, just killing process") self.killPid(processPID) @@ -728,12 +734,14 @@ class Automation(object): self.killAndGetStack(browserProcessId, utilityPath, debuggerInfo) status = proc.wait() + automationutils.printstatus(status, "Main app process") if status == 0: self.lastTestSeen = "Main app process exited normally" if status != 0 and not didTimeout and not hitMaxTime: self.log.info("TEST-UNEXPECTED-FAIL | %s | Exited with code %d during test run", self.lastTestSeen, status) if stackFixerProcess is not None: fixerStatus = stackFixerProcess.wait() + automationutils.printstatus(status, "stackFixerProcess") if fixerStatus != 0 and not didTimeout and not hitMaxTime: self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", fixerStatus) return status diff --git a/build/automationutils.py b/build/automationutils.py index 0a79b2f8bbea..7684ecc9a5c9 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -7,8 +7,10 @@ from __future__ import with_statement import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2, zipfile import base64 import re +import os from urlparse import urlparse from operator import itemgetter +import signal try: import mozinfo @@ -155,6 +157,43 @@ def isURL(thing): # We want to download URLs like http://... but not Windows paths like c:\... return len(urlparse(thing).scheme) >= 2 +# Python does not provide strsignal() even in the very latest 3.x. +# This is a reasonable fake. +def strsig(n): + # Signal numbers run 0 through NSIG-1; an array with NSIG members + # has exactly that many slots + _sigtbl = [None]*signal.NSIG + for k in dir(signal): + if k.startswith("SIG") and not k.startswith("SIG_") and k != "SIGCLD" and k != "SIGPOLL": + _sigtbl[getattr(signal, k)] = k + # Realtime signals mostly have no names + if hasattr(signal, "SIGRTMIN") and hasattr(signal, "SIGRTMAX"): + for r in range(signal.SIGRTMIN+1, signal.SIGRTMAX+1): + _sigtbl[r] = "SIGRTMIN+" + str(r - signal.SIGRTMIN) + # Fill in any remaining gaps + for i in range(signal.NSIG): + if _sigtbl[i] is None: + _sigtbl[i] = "unrecognized signal, number " + str(i) + if n < 0 or n >= signal.NSIG: + return "out-of-range signal, number "+str(n) + return _sigtbl[n] + +def printstatus(status, name = ""): + # 'status' is the exit status + if os.name != 'posix': + # Windows error codes are easier to look up if printed in hexadecimal + if status < 0: + status += 2**32 + print "TEST-INFO | %s: exit status %x\n" % (name, status) + elif os.WIFEXITED(status): + print "TEST-INFO | %s: exit %d\n" % (name, os.WEXITSTATUS(status)) + elif os.WIFSIGNALED(status): + # The python stdlib doesn't appear to have strsignal(), alas + print "TEST-INFO | {}: killed by {}".format(name,strsig(os.WTERMSIG(status))) + else: + # This is probably a can't-happen condition on Unix, but let's be defensive + print "TEST-INFO | %s: undecodable exit status %04x\n" % (name, status) + def addCommonOptions(parser, defaults={}): parser.add_option("--xre-path", action = "store", type = "string", dest = "xrePath", @@ -499,10 +538,13 @@ def dumpScreen(utilityPath): # Need to figure out which OS-dependent tool to use if mozinfo.isUnix: utility = [os.path.join(utilityPath, "screentopng")] + utilityname = "screentopng" elif mozinfo.isMac: utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png'] + utilityname = "screencapture" elif mozinfo.isWin: utility = [os.path.join(utilityPath, "screenshot.exe")] + utilityname = "screenshot" # Get dir where to write the screenshot file parent_dir = os.environ.get('MOZ_UPLOAD_DIR', None) @@ -515,15 +557,12 @@ def dumpScreen(utilityPath): tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail-screenshot_', suffix='.png', dir=parent_dir) os.close(tmpfd) returncode = subprocess.call(utility + [imgfilename]) + printstatus(returncode, utilityname) except OSError, err: log.info("Failed to start %s for screenshot: %s", utility[0], err.strerror) return - # Check whether the capture utility ran successfully - if returncode != 0: - log.info("%s exited with code %d", utility, returncode) - class ShutdownLeaks(object): """ Parses the mochitest run log when running a debug build, assigns all leaked diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index eff5ea258ded..ed6b1078306e 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -29,7 +29,7 @@ import traceback import urllib2 import zipfile -from automationutils import environment, getDebuggerInfo, isURL, KeyValueParseError, parseKeyValue, processLeakLog, systemMemory, dumpScreen, ShutdownLeaks +from automationutils import environment, getDebuggerInfo, isURL, KeyValueParseError, parseKeyValue, processLeakLog, systemMemory, dumpScreen, ShutdownLeaks, printstatus from datetime import datetime from manifestparser import TestManifest from mochitest_options import MochitestOptions @@ -790,8 +790,11 @@ class Mochitest(MochitestUtilsMixin): if mozinfo.isWin: # We should have a "crashinject" program in our utility path crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe")) - if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(processPID)]).wait() == 0: - return + if os.path.exists(crashinject): + status = subprocess.Popen([crashinject, str(processPID)]).wait() + printstatus(status, "crashinject") + if status == 0: + return else: try: os.kill(processPID, signal.SIGABRT) @@ -1019,6 +1022,7 @@ class Mochitest(MochitestUtilsMixin): # until bug 913970 is fixed regarding mozrunner `wait` not returning status # see https://bugzilla.mozilla.org/show_bug.cgi?id=913970 status = proc.wait() + printstatus(status, "Main app process") runner.process_handler = None if timeout is None: @@ -1497,6 +1501,7 @@ class Mochitest(MochitestUtilsMixin): pk12util = os.path.join(utilityPath, "pk12util" + bin_suffix) status = call([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env=env) + printstatus(status, "certutil") if status: return status @@ -1508,13 +1513,15 @@ class Mochitest(MochitestUtilsMixin): trustBits = "CT,," if root.endswith("-object"): trustBits = "CT,,CT" - call([certutil, "-A", "-i", os.path.join(certPath, item), + status = call([certutil, "-A", "-i", os.path.join(certPath, item), "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits], env=env) + printstatus(status, "certutil") elif ext == ".client": - call([pk12util, "-i", os.path.join(certPath, item), "-w", + status = call([pk12util, "-i", os.path.join(certPath, item), "-w", pwfilePath, "-d", profileDir], env=env) + printstatus(status, "pk2util") os.unlink(pwfilePath) return 0