Upgrade to 3.29
Update V8 to 3.29.88.17 and update makefiles to support building on
all the relevant platforms.
Bug: 17370214
Change-Id: Ia3407c157fd8d72a93e23d8318ccaf6ecf77fa4e
diff --git a/tools/testrunner/local/execution.py b/tools/testrunner/local/execution.py
new file mode 100644
index 0000000..36ce7be
--- /dev/null
+++ b/tools/testrunner/local/execution.py
@@ -0,0 +1,269 @@
+# Copyright 2012 the V8 project authors. All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+import os
+import shutil
+import time
+
+from pool import Pool
+from . import commands
+from . import perfdata
+from . import utils
+
+
+class Job(object):
+ def __init__(self, command, dep_command, test_id, timeout, verbose):
+ self.command = command
+ self.dep_command = dep_command
+ self.id = test_id
+ self.timeout = timeout
+ self.verbose = verbose
+
+
+def RunTest(job):
+ start_time = time.time()
+ if job.dep_command is not None:
+ dep_output = commands.Execute(job.dep_command, job.verbose, job.timeout)
+ # TODO(jkummerow): We approximate the test suite specific function
+ # IsFailureOutput() by just checking the exit code here. Currently
+ # only cctests define dependencies, for which this simplification is
+ # correct.
+ if dep_output.exit_code != 0:
+ return (job.id, dep_output, time.time() - start_time)
+ output = commands.Execute(job.command, job.verbose, job.timeout)
+ return (job.id, output, time.time() - start_time)
+
+class Runner(object):
+
+ def __init__(self, suites, progress_indicator, context):
+ self.datapath = os.path.join("out", "testrunner_data")
+ self.perf_data_manager = perfdata.PerfDataManager(self.datapath)
+ self.perfdata = self.perf_data_manager.GetStore(context.arch, context.mode)
+ self.perf_failures = False
+ self.printed_allocations = False
+ self.tests = [ t for s in suites for t in s.tests ]
+ if not context.no_sorting:
+ for t in self.tests:
+ t.duration = self.perfdata.FetchPerfData(t) or 1.0
+ self.tests.sort(key=lambda t: t.duration, reverse=True)
+ self._CommonInit(len(self.tests), progress_indicator, context)
+
+ def _CommonInit(self, num_tests, progress_indicator, context):
+ self.indicator = progress_indicator
+ progress_indicator.runner = self
+ self.context = context
+ self.succeeded = 0
+ self.total = num_tests
+ self.remaining = num_tests
+ self.failed = []
+ self.crashed = 0
+ self.reran_tests = 0
+
+ def _RunPerfSafe(self, fun):
+ try:
+ fun()
+ except Exception, e:
+ print("PerfData exception: %s" % e)
+ self.perf_failures = True
+
+ def _GetJob(self, test):
+ command = self.GetCommand(test)
+ timeout = self.context.timeout
+ if ("--stress-opt" in test.flags or
+ "--stress-opt" in self.context.mode_flags or
+ "--stress-opt" in self.context.extra_flags):
+ timeout *= 4
+ if test.dependency is not None:
+ dep_command = [ c.replace(test.path, test.dependency) for c in command ]
+ else:
+ dep_command = None
+ return Job(command, dep_command, test.id, timeout, self.context.verbose)
+
+ def _MaybeRerun(self, pool, test):
+ if test.run <= self.context.rerun_failures_count:
+ # Possibly rerun this test if its run count is below the maximum per
+ # test. <= as the flag controls reruns not including the first run.
+ if test.run == 1:
+ # Count the overall number of reran tests on the first rerun.
+ if self.reran_tests < self.context.rerun_failures_max:
+ self.reran_tests += 1
+ else:
+ # Don't rerun this if the overall number of rerun tests has been
+ # reached.
+ return
+ if test.run >= 2 and test.duration > self.context.timeout / 20.0:
+ # Rerun slow tests at most once.
+ return
+
+ # Rerun this test.
+ test.duration = None
+ test.output = None
+ test.run += 1
+ pool.add([self._GetJob(test)])
+ self.remaining += 1
+
+ def _ProcessTestNormal(self, test, result, pool):
+ self.indicator.AboutToRun(test)
+ test.output = result[1]
+ test.duration = result[2]
+ has_unexpected_output = test.suite.HasUnexpectedOutput(test)
+ if has_unexpected_output:
+ self.failed.append(test)
+ if test.output.HasCrashed():
+ self.crashed += 1
+ else:
+ self.succeeded += 1
+ self.remaining -= 1
+ # For the indicator, everything that happens after the first run is treated
+ # as unexpected even if it flakily passes in order to include it in the
+ # output.
+ self.indicator.HasRun(test, has_unexpected_output or test.run > 1)
+ if has_unexpected_output:
+ # Rerun test failures after the indicator has processed the results.
+ self._MaybeRerun(pool, test)
+ # Update the perf database if the test succeeded.
+ return not has_unexpected_output
+
+ def _ProcessTestPredictable(self, test, result, pool):
+ def HasDifferentAllocations(output1, output2):
+ def AllocationStr(stdout):
+ for line in reversed((stdout or "").splitlines()):
+ if line.startswith("### Allocations = "):
+ self.printed_allocations = True
+ return line
+ return ""
+ return (AllocationStr(output1.stdout) != AllocationStr(output2.stdout))
+
+ # Always pass the test duration for the database update.
+ test.duration = result[2]
+ if test.run == 1 and result[1].HasTimedOut():
+ # If we get a timeout in the first run, we are already in an
+ # unpredictable state. Just report it as a failure and don't rerun.
+ self.indicator.AboutToRun(test)
+ test.output = result[1]
+ self.remaining -= 1
+ self.failed.append(test)
+ self.indicator.HasRun(test, True)
+ if test.run > 1 and HasDifferentAllocations(test.output, result[1]):
+ # From the second run on, check for different allocations. If a
+ # difference is found, call the indicator twice to report both tests.
+ # All runs of each test are counted as one for the statistic.
+ self.indicator.AboutToRun(test)
+ self.remaining -= 1
+ self.failed.append(test)
+ self.indicator.HasRun(test, True)
+ self.indicator.AboutToRun(test)
+ test.output = result[1]
+ self.indicator.HasRun(test, True)
+ elif test.run >= 3:
+ # No difference on the third run -> report a success.
+ self.indicator.AboutToRun(test)
+ self.remaining -= 1
+ self.succeeded += 1
+ test.output = result[1]
+ self.indicator.HasRun(test, False)
+ else:
+ # No difference yet and less than three runs -> add another run and
+ # remember the output for comparison.
+ test.run += 1
+ test.output = result[1]
+ pool.add([self._GetJob(test)])
+ # Always update the perf database.
+ return True
+
+ def Run(self, jobs):
+ self.indicator.Starting()
+ self._RunInternal(jobs)
+ self.indicator.Done()
+ if self.failed or self.remaining:
+ return 1
+ return 0
+
+ def _RunInternal(self, jobs):
+ pool = Pool(jobs)
+ test_map = {}
+ # TODO(machenbach): Instead of filling the queue completely before
+ # pool.imap_unordered, make this a generator that already starts testing
+ # while the queue is filled.
+ queue = []
+ queued_exception = None
+ for test in self.tests:
+ assert test.id >= 0
+ test_map[test.id] = test
+ try:
+ queue.append([self._GetJob(test)])
+ except Exception, e:
+ # If this failed, save the exception and re-raise it later (after
+ # all other tests have had a chance to run).
+ queued_exception = e
+ continue
+ try:
+ it = pool.imap_unordered(RunTest, queue)
+ for result in it:
+ test = test_map[result[0]]
+ if self.context.predictable:
+ update_perf = self._ProcessTestPredictable(test, result, pool)
+ else:
+ update_perf = self._ProcessTestNormal(test, result, pool)
+ if update_perf:
+ self._RunPerfSafe(lambda: self.perfdata.UpdatePerfData(test))
+ finally:
+ pool.terminate()
+ self._RunPerfSafe(lambda: self.perf_data_manager.close())
+ if self.perf_failures:
+ # Nuke perf data in case of failures. This might not work on windows as
+ # some files might still be open.
+ print "Deleting perf test data due to db corruption."
+ shutil.rmtree(self.datapath)
+ if queued_exception:
+ raise queued_exception
+
+ # Make sure that any allocations were printed in predictable mode.
+ assert not self.context.predictable or self.printed_allocations
+
+ def GetCommand(self, test):
+ d8testflag = []
+ shell = test.suite.shell()
+ if shell == "d8":
+ d8testflag = ["--test"]
+ if utils.IsWindows():
+ shell += ".exe"
+ cmd = (self.context.command_prefix +
+ [os.path.abspath(os.path.join(self.context.shell_dir, shell))] +
+ d8testflag +
+ ["--random-seed=%s" % self.context.random_seed] +
+ test.suite.GetFlagsForTestCase(test, self.context) +
+ self.context.extra_flags)
+ return cmd
+
+
+class BreakNowException(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)