diff options
-rwxr-xr-x | tools/valgrind/chrome_tests.py | 12 | ||||
-rwxr-xr-x | tools/valgrind/chrome_tests.sh | 2 | ||||
-rwxr-xr-x | tools/valgrind/common.py | 112 | ||||
-rwxr-xr-x | tools/valgrind/valgrind_test.py | 3 |
4 files changed, 116 insertions, 13 deletions
diff --git a/tools/valgrind/chrome_tests.py b/tools/valgrind/chrome_tests.py index 86d4280..7d9fc1a 100755 --- a/tools/valgrind/chrome_tests.py +++ b/tools/valgrind/chrome_tests.py @@ -5,11 +5,7 @@ # chrome_tests.py -''' Runs various chrome tests through valgrind_test.py. - -This file is a copy of ../purify/chrome_tests.py. Eventually, it would be nice -to merge these two files. -''' +''' Runs various chrome tests through valgrind_test.py.''' import glob import logging @@ -30,7 +26,6 @@ import google.path_utils # package. http://crbug.com/6164 import layout_package.path_utils -import common import valgrind_test class TestNotFound(Exception): pass @@ -70,9 +65,6 @@ def FindDirContainingNewestFile(dirs, file): return newest_dir class ChromeTests: - '''This class is derived from the chrome_tests.py file in ../purify/. - ''' - def __init__(self, options, args, test): # The known list of tests. # Recognise the original abbreviations as well as full executable names. @@ -336,7 +328,7 @@ class ChromeTests: # runs a slice of the layout tests of size chunk_size that increments with # each run. Since tests can be added and removed from the layout tests at # any time, this is not going to give exact coverage, but it will allow us - # to continuously run small slices of the layout tests under purify rather + # to continuously run small slices of the layout tests under valgrind rather # than having to run all of them in one shot. chunk_size = self._options.num_tests if (chunk_size == 0): diff --git a/tools/valgrind/chrome_tests.sh b/tools/valgrind/chrome_tests.sh index 6fe4bc5..1d75e7f 100755 --- a/tools/valgrind/chrome_tests.sh +++ b/tools/valgrind/chrome_tests.sh @@ -29,4 +29,4 @@ echo "Using ${CHROME_VALGRIND_BIN}/valgrind" PATH="${CHROME_VALGRIND_BIN}:$PATH" export THISDIR=`dirname $0` -PYTHONPATH=$THISDIR/../../webkit/tools/layout_tests/:$THISDIR/../purify:$THISDIR/../python "$THISDIR/chrome_tests.py" "$@" +PYTHONPATH=$THISDIR/../../webkit/tools/layout_tests/:$THISDIR/../python "$THISDIR/chrome_tests.py" "$@" diff --git a/tools/valgrind/common.py b/tools/valgrind/common.py new file mode 100755 index 0000000..d273bf7 --- /dev/null +++ b/tools/valgrind/common.py @@ -0,0 +1,112 @@ +# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import logging +import os +import signal +import subprocess +import sys +import time + + +class TimeoutError(Exception): + pass + + +def _print_line(line, flush=True): + # Printing to a text file (including stdout) on Windows always winds up + # using \r\n automatically. On buildbot, this winds up being read by a master + # running on Linux, so we manually convert crlf to '\n' + print line.rstrip() + '\n', + if flush: + sys.stdout.flush() + + +def RunSubprocess(proc, timeout=0, detach=False): + """ Runs a subprocess, until it finishes or |timeout| is exceeded and the + process is killed with taskkill. A |timeout| <= 0 means no timeout. + + Args: + proc: list of process components (exe + args) + timeout: how long to wait before killing, <= 0 means wait forever + detach: Whether to pass the DETACHED_PROCESS argument to CreateProcess + on Windows. This is used by Purify subprocesses on buildbot which + seem to get confused by the parent console that buildbot sets up. + """ + + logging.info("running %s, timeout %d sec" % (" ".join(proc), timeout)) + if detach: + # see MSDN docs for "Process Creation Flags" + DETACHED_PROCESS = 0x8 + p = subprocess.Popen(proc, creationflags=DETACHED_PROCESS) + else: + # For non-detached processes, manually read and print out stdout and stderr. + # By default, the subprocess is supposed to inherit these from its parent, + # however when run under buildbot, it seems unable to read data from a + # grandchild process, so we have to read the child and print the data as if + # it came from us for buildbot to read it. We're not sure why this is + # necessary. + # TODO(erikkay): should we buffer stderr and stdout separately? + p = subprocess.Popen(proc, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + logging.info("started subprocess") + + # How long to wait (in seconds) before printing progress log messages. + progress_delay = 300 + progress_delay_time = time.time() + progress_delay + did_timeout = False + if timeout > 0: + wait_until = time.time() + timeout + while p.poll() is None and not did_timeout: + if not detach: + line = p.stdout.readline() + while line and not did_timeout: + _print_line(line) + line = p.stdout.readline() + if timeout > 0: + did_timeout = time.time() > wait_until + else: + # When we detach, blocking on reading stdout doesn't work, so we sleep + # a short time and poll. + time.sleep(0.5) + if time.time() >= progress_delay_time: + # Force output on a periodic basis to avoid getting killed off by the + # buildbot. + # TODO(erikkay): I'd prefer a less obtrusive 'print ".",' with a flush + # but because of how we're doing subprocesses, this doesn't appear to + # work reliably. + logging.info("%s still running..." % os.path.basename(proc[0])) + progress_delay_time = time.time() + progress_delay + if timeout > 0: + did_timeout = time.time() > wait_until + + if did_timeout: + logging.info("process timed out") + else: + logging.info("process ended, did not time out") + + if did_timeout: + if sys.platform == "win32": + subprocess.call(["taskkill", "/T", "/F", "/PID", str(p.pid)]) + else: + # Does this kill all children, too? + os.kill(p.pid, signal.SIGINT) + logging.error("KILLED %d" % p.pid) + # Give the process a chance to actually die before continuing + # so that cleanup can happen safely. + time.sleep(1.0) + logging.error("TIMEOUT waiting for %s" % proc[0]) + raise TimeoutError(proc[0]) + elif not detach: + for line in p.stdout.readlines(): + _print_line(line, False) + if sys.platform != 'darwin': # stdout flush fails on Mac + logging.info("flushing stdout") + p.stdout.flush() + + logging.info("collecting result code") + result = p.poll() + if result: + logging.error("%s exited with non-zero result code %d" % (proc[0], result)) + return result diff --git a/tools/valgrind/valgrind_test.py b/tools/valgrind/valgrind_test.py index 1c331ab..ecc504d 100755 --- a/tools/valgrind/valgrind_test.py +++ b/tools/valgrind/valgrind_test.py @@ -263,8 +263,7 @@ class ValgrindTool(object): common.RunSubprocess(proc, self._timeout) # Always return true, even if running the subprocess failed. We depend on - # Analyze to determine if the run was valid. (This behaviour copied from - # the purify_test.py script.) + # Analyze to determine if the run was valid. return True def Analyze(self): |