Update V8 to version 4.1.0.21
This is a cherry-pick of all commits up to and including the
4.1.0.21 cherry-pick in Chromium.
Original commit message:
Version 4.1.0.21 (cherry-pick)
Merged 206e9136bde0f2b5ae8cb77afbb1e7833e5bd412
Unlink pages from the space page list after evacuation.
BUG=430201
LOG=N
R=jkummerow@chromium.org
Review URL: https://codereview.chromium.org/953813002
Cr-Commit-Position: refs/branch-heads/4.1@{#22}
Cr-Branched-From: 2e08d2a7aa9d65d269d8c57aba82eb38a8cb0a18-refs/heads/candidates@{#25353}
---
FPIIM-449
Change-Id: I8c23c7bbb70772b4858fe8a47b64fa97ee0d1f8c
diff --git a/tools/run_perf.py b/tools/run_perf.py
index 920c18d..63c9148 100755
--- a/tools/run_perf.py
+++ b/tools/run_perf.py
@@ -15,9 +15,10 @@
"archs": [<architecture name for which this suite is run>, ...],
"binary": <name of binary to run, default "d8">,
"flags": [<flag to d8>, ...],
+ "test_flags": [<flag to the test file>, ...],
"run_count": <how often will this suite run (optional)>,
"run_count_XXX": <how often will this suite run for arch XXX (optional)>,
- "resources": [<js file to be loaded before main>, ...]
+ "resources": [<js file to be moved to android device>, ...]
"main": <main js perf runner file>,
"results_regexp": <optional regexp>,
"results_processor": <optional python results processor script>,
@@ -54,6 +55,7 @@
{
"path": ["."],
"flags": ["--expose-gc"],
+ "test_flags": ["5"],
"archs": ["ia32", "x64"],
"run_count": 5,
"run_count_ia32": 3,
@@ -89,9 +91,13 @@
}
Path pieces are concatenated. D8 is always run with the suite's path as cwd.
+
+The test flags are passed to the js test file after '--'.
"""
+from collections import OrderedDict
import json
+import logging
import math
import optparse
import os
@@ -114,8 +120,25 @@
"x64",
"arm64"]
-GENERIC_RESULTS_RE = re.compile(
- r"^Trace\(([^\)]+)\), Result\(([^\)]+)\), StdDev\(([^\)]+)\)$")
+GENERIC_RESULTS_RE = re.compile(r"^RESULT ([^:]+): ([^=]+)= ([^ ]+) ([^ ]*)$")
+RESULT_STDDEV_RE = re.compile(r"^\{([^\}]+)\}$")
+RESULT_LIST_RE = re.compile(r"^\[([^\]]+)\]$")
+
+
+def LoadAndroidBuildTools(path): # pragma: no cover
+ assert os.path.exists(path)
+ sys.path.insert(0, path)
+
+ from pylib.device import device_utils # pylint: disable=F0401
+ from pylib.device import device_errors # pylint: disable=F0401
+ from pylib.perf import cache_control # pylint: disable=F0401
+ from pylib.perf import perf_control # pylint: disable=F0401
+ import pylib.android_commands # pylint: disable=F0401
+ global cache_control
+ global device_errors
+ global device_utils
+ global perf_control
+ global pylib
def GeometricMean(values):
@@ -168,6 +191,7 @@
self.path = []
self.graphs = []
self.flags = []
+ self.test_flags = []
self.resources = []
self.results_regexp = None
self.stddev_regexp = None
@@ -187,19 +211,24 @@
assert isinstance(suite.get("path", []), list)
assert isinstance(suite["name"], basestring)
assert isinstance(suite.get("flags", []), list)
+ assert isinstance(suite.get("test_flags", []), list)
assert isinstance(suite.get("resources", []), list)
# Accumulated values.
self.path = parent.path[:] + suite.get("path", [])
self.graphs = parent.graphs[:] + [suite["name"]]
self.flags = parent.flags[:] + suite.get("flags", [])
- self.resources = parent.resources[:] + suite.get("resources", [])
+ self.test_flags = parent.test_flags[:] + suite.get("test_flags", [])
+
+ # Values independent of parent node.
+ self.resources = suite.get("resources", [])
# Descrete values (with parent defaults).
self.binary = suite.get("binary", parent.binary)
self.run_count = suite.get("run_count", parent.run_count)
self.run_count = suite.get("run_count_%s" % arch, self.run_count)
self.timeout = suite.get("timeout", parent.timeout)
+ self.timeout = suite.get("timeout_%s" % arch, self.timeout)
self.units = suite.get("units", parent.units)
self.total = suite.get("total", parent.total)
@@ -236,8 +265,11 @@
def ConsumeOutput(self, stdout):
try:
- self.results.append(
- re.search(self.results_regexp, stdout, re.M).group(1))
+ result = re.search(self.results_regexp, stdout, re.M).group(1)
+ self.results.append(str(float(result)))
+ except ValueError:
+ self.errors.append("Regexp \"%s\" returned a non-numeric for test %s."
+ % (self.results_regexp, self.graphs[-1]))
except:
self.errors.append("Regexp \"%s\" didn't match for test %s."
% (self.results_regexp, self.graphs[-1]))
@@ -277,14 +309,13 @@
bench_dir = os.path.normpath(os.path.join(*self.path))
os.chdir(os.path.join(suite_dir, bench_dir))
+ def GetCommandFlags(self):
+ suffix = ["--"] + self.test_flags if self.test_flags else []
+ return self.flags + [self.main] + suffix
+
def GetCommand(self, shell_dir):
# TODO(machenbach): This requires +.exe if run on windows.
- return (
- [os.path.join(shell_dir, self.binary)] +
- self.flags +
- self.resources +
- [self.main]
- )
+ return [os.path.join(shell_dir, self.binary)] + self.GetCommandFlags()
def Run(self, runner):
"""Iterates over several runs and handles the output for all traces."""
@@ -334,21 +365,41 @@
def Run(self, runner):
"""Iterates over several runs and handles the output."""
- traces = {}
+ traces = OrderedDict()
for stdout in runner():
for line in stdout.strip().splitlines():
match = GENERIC_RESULTS_RE.match(line)
if match:
- trace = match.group(1)
- result = match.group(2)
- stddev = match.group(3)
+ stddev = ""
+ graph = match.group(1)
+ trace = match.group(2)
+ body = match.group(3)
+ units = match.group(4)
+ match_stddev = RESULT_STDDEV_RE.match(body)
+ match_list = RESULT_LIST_RE.match(body)
+ errors = []
+ if match_stddev:
+ result, stddev = map(str.strip, match_stddev.group(1).split(","))
+ results = [result]
+ elif match_list:
+ results = map(str.strip, match_list.group(1).split(","))
+ else:
+ results = [body.strip()]
+
+ try:
+ results = map(lambda r: str(float(r)), results)
+ except ValueError:
+ results = []
+ errors = ["Found non-numeric in %s" %
+ "/".join(self.graphs + [graph, trace])]
+
trace_result = traces.setdefault(trace, Results([{
- "graphs": self.graphs + [trace],
- "units": self.units,
+ "graphs": self.graphs + [graph, trace],
+ "units": (units or self.units).strip(),
"results": [],
"stddev": "",
- }], []))
- trace_result.traces[0]["results"].append(result)
+ }], errors))
+ trace_result.traces[0]["results"].extend(results)
trace_result.traces[0]["stddev"] = stddev
return reduce(lambda r, t: r + t, traces.itervalues(), Results())
@@ -385,7 +436,7 @@
parent = parent or DefaultSentinel()
# TODO(machenbach): Implement notion of cpu type?
- if arch not in suite.get("archs", ["ia32", "x64"]):
+ if arch not in suite.get("archs", SUPPORTED_ARCHS):
return None
graph = MakeGraph(suite, arch, parent)
@@ -395,23 +446,167 @@
return graph
-def FlattenRunnables(node):
+def FlattenRunnables(node, node_cb):
"""Generator that traverses the tree structure and iterates over all
runnables.
"""
+ node_cb(node)
if isinstance(node, Runnable):
yield node
elif isinstance(node, Node):
for child in node._children:
- for result in FlattenRunnables(child):
+ for result in FlattenRunnables(child, node_cb):
yield result
else: # pragma: no cover
raise Exception("Invalid suite configuration.")
+class Platform(object):
+ @staticmethod
+ def GetPlatform(options):
+ if options.arch.startswith("android"):
+ return AndroidPlatform(options)
+ else:
+ return DesktopPlatform(options)
+
+
+class DesktopPlatform(Platform):
+ def __init__(self, options):
+ self.shell_dir = options.shell_dir
+
+ def PreExecution(self):
+ pass
+
+ def PostExecution(self):
+ pass
+
+ def PreTests(self, node, path):
+ if isinstance(node, Runnable):
+ node.ChangeCWD(path)
+
+ def Run(self, runnable, count):
+ output = commands.Execute(runnable.GetCommand(self.shell_dir),
+ timeout=runnable.timeout)
+ print ">>> Stdout (#%d):" % (count + 1)
+ print output.stdout
+ if output.stderr: # pragma: no cover
+ # Print stderr for debugging.
+ print ">>> Stderr (#%d):" % (count + 1)
+ print output.stderr
+ if output.timed_out:
+ print ">>> Test timed out after %ss." % runnable.timeout
+ return output.stdout
+
+
+class AndroidPlatform(Platform): # pragma: no cover
+ DEVICE_DIR = "/data/local/tmp/v8/"
+
+ def __init__(self, options):
+ self.shell_dir = options.shell_dir
+ LoadAndroidBuildTools(options.android_build_tools)
+
+ if not options.device:
+ # Detect attached device if not specified.
+ devices = pylib.android_commands.GetAttachedDevices(
+ hardware=True, emulator=False, offline=False)
+ assert devices and len(devices) == 1, (
+ "None or multiple devices detected. Please specify the device on "
+ "the command-line with --device")
+ options.device = devices[0]
+ adb_wrapper = pylib.android_commands.AndroidCommands(options.device)
+ self.device = device_utils.DeviceUtils(adb_wrapper)
+ self.adb = adb_wrapper.Adb()
+
+ def PreExecution(self):
+ perf = perf_control.PerfControl(self.device)
+ perf.SetHighPerfMode()
+
+ # Remember what we have already pushed to the device.
+ self.pushed = set()
+
+ def PostExecution(self):
+ perf = perf_control.PerfControl(self.device)
+ perf.SetDefaultPerfMode()
+ self.device.RunShellCommand(["rm", "-rf", AndroidPlatform.DEVICE_DIR])
+
+ def _SendCommand(self, cmd):
+ logging.info("adb -s %s %s" % (str(self.device), cmd))
+ return self.adb.SendCommand(cmd, timeout_time=60)
+
+ def _PushFile(self, host_dir, file_name, target_rel="."):
+ file_on_host = os.path.join(host_dir, file_name)
+ file_on_device_tmp = os.path.join(
+ AndroidPlatform.DEVICE_DIR, "_tmp_", file_name)
+ file_on_device = os.path.join(
+ AndroidPlatform.DEVICE_DIR, target_rel, file_name)
+ folder_on_device = os.path.dirname(file_on_device)
+
+ # Only push files not yet pushed in one execution.
+ if file_on_host in self.pushed:
+ return
+ else:
+ self.pushed.add(file_on_host)
+
+ # Work-around for "text file busy" errors. Push the files to a temporary
+ # location and then copy them with a shell command.
+ output = self._SendCommand(
+ "push %s %s" % (file_on_host, file_on_device_tmp))
+ # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)".
+ # Errors look like this: "failed to copy ... ".
+ if output and not re.search('^[0-9]', output.splitlines()[-1]):
+ logging.critical('PUSH FAILED: ' + output)
+ self._SendCommand("shell mkdir -p %s" % folder_on_device)
+ self._SendCommand("shell cp %s %s" % (file_on_device_tmp, file_on_device))
+
+ def PreTests(self, node, path):
+ suite_dir = os.path.abspath(os.path.dirname(path))
+ if node.path:
+ bench_rel = os.path.normpath(os.path.join(*node.path))
+ bench_abs = os.path.join(suite_dir, bench_rel)
+ else:
+ bench_rel = "."
+ bench_abs = suite_dir
+
+ self._PushFile(self.shell_dir, node.binary)
+ if isinstance(node, Runnable):
+ self._PushFile(bench_abs, node.main, bench_rel)
+ for resource in node.resources:
+ self._PushFile(bench_abs, resource, bench_rel)
+
+ def Run(self, runnable, count):
+ cache = cache_control.CacheControl(self.device)
+ cache.DropRamCaches()
+ binary_on_device = AndroidPlatform.DEVICE_DIR + runnable.binary
+ cmd = [binary_on_device] + runnable.GetCommandFlags()
+
+ # Relative path to benchmark directory.
+ if runnable.path:
+ bench_rel = os.path.normpath(os.path.join(*runnable.path))
+ else:
+ bench_rel = "."
+
+ try:
+ output = self.device.RunShellCommand(
+ cmd,
+ cwd=os.path.join(AndroidPlatform.DEVICE_DIR, bench_rel),
+ timeout=runnable.timeout,
+ retries=0,
+ )
+ stdout = "\n".join(output)
+ print ">>> Stdout (#%d):" % (count + 1)
+ print stdout
+ except device_errors.CommandTimeoutError:
+ print ">>> Test timed out after %ss." % runnable.timeout
+ stdout = ""
+ return stdout
+
+
# TODO: Implement results_processor.
def Main(args):
+ logging.getLogger().setLevel(logging.INFO)
parser = optparse.OptionParser()
+ parser.add_option("--android-build-tools",
+ help="Path to chromium's build/android.")
parser.add_option("--arch",
help=("The architecture to run tests for, "
"'auto' or 'native' for auto-detect"),
@@ -419,6 +614,9 @@
parser.add_option("--buildbot",
help="Adapt to path structure used on buildbots",
default=False, action="store_true")
+ parser.add_option("--device",
+ help="The device ID to run Android tests on. If not given "
+ "it will be autodetected.")
parser.add_option("--json-test-results",
help="Path to a file for storing json results.")
parser.add_option("--outdir", help="Base directory with compile output",
@@ -436,13 +634,26 @@
print "Unknown architecture %s" % options.arch
return 1
+ if (bool(options.arch.startswith("android")) !=
+ bool(options.android_build_tools)): # pragma: no cover
+ print ("Android architectures imply setting --android-build-tools and the "
+ "other way around.")
+ return 1
+
+ if (options.device and not
+ options.arch.startswith("android")): # pragma: no cover
+ print "Specifying a device requires an Android architecture to be used."
+ return 1
+
workspace = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if options.buildbot:
- shell_dir = os.path.join(workspace, options.outdir, "Release")
+ options.shell_dir = os.path.join(workspace, options.outdir, "Release")
else:
- shell_dir = os.path.join(workspace, options.outdir,
- "%s.release" % options.arch)
+ options.shell_dir = os.path.join(workspace, options.outdir,
+ "%s.release" % options.arch)
+
+ platform = Platform.GetPlatform(options)
results = Results()
for path in args:
@@ -458,30 +669,32 @@
# If no name is given, default to the file name without .json.
suite.setdefault("name", os.path.splitext(os.path.basename(path))[0])
- for runnable in FlattenRunnables(BuildGraphs(suite, options.arch)):
+ # Setup things common to one test suite.
+ platform.PreExecution()
+
+ # Build the graph/trace tree structure.
+ root = BuildGraphs(suite, options.arch)
+
+ # Callback to be called on each node on traversal.
+ def NodeCB(node):
+ platform.PreTests(node, path)
+
+ # Traverse graph/trace tree and interate over all runnables.
+ for runnable in FlattenRunnables(root, NodeCB):
print ">>> Running suite: %s" % "/".join(runnable.graphs)
- runnable.ChangeCWD(path)
def Runner():
"""Output generator that reruns several times."""
for i in xrange(0, max(1, runnable.run_count)):
# TODO(machenbach): Allow timeout per arch like with run_count per
# arch.
- output = commands.Execute(runnable.GetCommand(shell_dir),
- timeout=runnable.timeout)
- print ">>> Stdout (#%d):" % (i + 1)
- print output.stdout
- if output.stderr: # pragma: no cover
- # Print stderr for debugging.
- print ">>> Stderr (#%d):" % (i + 1)
- print output.stderr
- if output.timed_out:
- print ">>> Test timed out after %ss." % runnable.timeout
- yield output.stdout
+ yield platform.Run(runnable, i)
# Let runnable iterate over all runs and handle output.
results += runnable.Run(Runner)
+ platform.PostExecution()
+
if options.json_test_results:
results.WriteToFile(options.json_test_results)
else: # pragma: no cover