Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(307)

Side by Side Diff: tools/run_perf.py

Issue 768143002: Add Android platform to performance runner. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/unittests/run_perf_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 the V8 project authors. All rights reserved. 2 # Copyright 2014 the V8 project authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """ 6 """
7 Performance runner for d8. 7 Performance runner for d8.
8 8
9 Call e.g. with tools/run-perf.py --arch ia32 some_suite.json 9 Call e.g. with tools/run-perf.py --arch ia32 some_suite.json
10 10
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
90 ] 90 ]
91 } 91 }
92 92
93 Path pieces are concatenated. D8 is always run with the suite's path as cwd. 93 Path pieces are concatenated. D8 is always run with the suite's path as cwd.
94 94
95 The test flags are passed to the js test file after '--'. 95 The test flags are passed to the js test file after '--'.
96 """ 96 """
97 97
98 from collections import OrderedDict 98 from collections import OrderedDict
99 import json 99 import json
100 import logging
100 import math 101 import math
101 import optparse 102 import optparse
102 import os 103 import os
103 import re 104 import re
104 import sys 105 import sys
105 106
106 from testrunner.local import commands 107 from testrunner.local import commands
107 from testrunner.local import utils 108 from testrunner.local import utils
108 109
109 ARCH_GUESS = utils.DefaultArch() 110 ARCH_GUESS = utils.DefaultArch()
110 SUPPORTED_ARCHS = ["android_arm", 111 SUPPORTED_ARCHS = ["android_arm",
111 "android_arm64", 112 "android_arm64",
112 "android_ia32", 113 "android_ia32",
113 "arm", 114 "arm",
114 "ia32", 115 "ia32",
115 "mips", 116 "mips",
116 "mipsel", 117 "mipsel",
117 "nacl_ia32", 118 "nacl_ia32",
118 "nacl_x64", 119 "nacl_x64",
119 "x64", 120 "x64",
120 "arm64"] 121 "arm64"]
121 122
122 GENERIC_RESULTS_RE = re.compile(r"^RESULT ([^:]+): ([^=]+)= ([^ ]+) ([^ ]*)$") 123 GENERIC_RESULTS_RE = re.compile(r"^RESULT ([^:]+): ([^=]+)= ([^ ]+) ([^ ]*)$")
123 RESULT_STDDEV_RE = re.compile(r"^\{([^\}]+)\}$") 124 RESULT_STDDEV_RE = re.compile(r"^\{([^\}]+)\}$")
124 RESULT_LIST_RE = re.compile(r"^\[([^\]]+)\]$") 125 RESULT_LIST_RE = re.compile(r"^\[([^\]]+)\]$")
125 126
126 127
128 def LoadAndroidBuildTools(path): # pragma: no cover
129 assert os.path.exists(path)
130 sys.path.insert(0, path)
131
132 from pylib.device import device_utils # pylint: disable=F0401
133 from pylib.device import device_errors # pylint: disable=F0401
134 from pylib.perf import cache_control # pylint: disable=F0401
135 from pylib.perf import perf_control # pylint: disable=F0401
136 import pylib.android_commands # pylint: disable=F0401
137 global cache_control
138 global device_errors
139 global device_utils
140 global perf_control
141 global pylib
142
127 143
128 def GeometricMean(values): 144 def GeometricMean(values):
129 """Returns the geometric mean of a list of values. 145 """Returns the geometric mean of a list of values.
130 146
131 The mean is calculated using log to avoid overflow. 147 The mean is calculated using log to avoid overflow.
132 """ 148 """
133 values = map(float, values) 149 values = map(float, values)
134 return str(math.exp(sum(map(math.log, values)) / len(values))) 150 return str(math.exp(sum(map(math.log, values)) / len(values)))
135 151
136 152
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
203 self.graphs = parent.graphs[:] + [suite["name"]] 219 self.graphs = parent.graphs[:] + [suite["name"]]
204 self.flags = parent.flags[:] + suite.get("flags", []) 220 self.flags = parent.flags[:] + suite.get("flags", [])
205 self.test_flags = parent.test_flags[:] + suite.get("test_flags", []) 221 self.test_flags = parent.test_flags[:] + suite.get("test_flags", [])
206 self.resources = parent.resources[:] + suite.get("resources", []) 222 self.resources = parent.resources[:] + suite.get("resources", [])
207 223
208 # Descrete values (with parent defaults). 224 # Descrete values (with parent defaults).
209 self.binary = suite.get("binary", parent.binary) 225 self.binary = suite.get("binary", parent.binary)
210 self.run_count = suite.get("run_count", parent.run_count) 226 self.run_count = suite.get("run_count", parent.run_count)
211 self.run_count = suite.get("run_count_%s" % arch, self.run_count) 227 self.run_count = suite.get("run_count_%s" % arch, self.run_count)
212 self.timeout = suite.get("timeout", parent.timeout) 228 self.timeout = suite.get("timeout", parent.timeout)
229 self.timeout = suite.get("timeout_%s" % arch, self.timeout)
213 self.units = suite.get("units", parent.units) 230 self.units = suite.get("units", parent.units)
214 self.total = suite.get("total", parent.total) 231 self.total = suite.get("total", parent.total)
215 232
216 # A regular expression for results. If the parent graph provides a 233 # A regular expression for results. If the parent graph provides a
217 # regexp and the current suite has none, a string place holder for the 234 # regexp and the current suite has none, a string place holder for the
218 # suite name is expected. 235 # suite name is expected.
219 # TODO(machenbach): Currently that makes only sense for the leaf level. 236 # TODO(machenbach): Currently that makes only sense for the leaf level.
220 # Multiple place holders for multiple levels are not supported. 237 # Multiple place holders for multiple levels are not supported.
221 if parent.results_regexp: 238 if parent.results_regexp:
222 regexp_default = parent.results_regexp % re.escape(suite["name"]) 239 regexp_default = parent.results_regexp % re.escape(suite["name"])
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 297
281 def ChangeCWD(self, suite_path): 298 def ChangeCWD(self, suite_path):
282 """Changes the cwd to to path defined in the current graph. 299 """Changes the cwd to to path defined in the current graph.
283 300
284 The tests are supposed to be relative to the suite configuration. 301 The tests are supposed to be relative to the suite configuration.
285 """ 302 """
286 suite_dir = os.path.abspath(os.path.dirname(suite_path)) 303 suite_dir = os.path.abspath(os.path.dirname(suite_path))
287 bench_dir = os.path.normpath(os.path.join(*self.path)) 304 bench_dir = os.path.normpath(os.path.join(*self.path))
288 os.chdir(os.path.join(suite_dir, bench_dir)) 305 os.chdir(os.path.join(suite_dir, bench_dir))
289 306
307 def GetCommandFlags(self):
308 suffix = ["--"] + self.test_flags if self.test_flags else []
309 return self.flags + [self.main] + suffix
310
290 def GetCommand(self, shell_dir): 311 def GetCommand(self, shell_dir):
291 # TODO(machenbach): This requires +.exe if run on windows. 312 # TODO(machenbach): This requires +.exe if run on windows.
292 suffix = ["--"] + self.test_flags if self.test_flags else [] 313 return [os.path.join(shell_dir, self.binary)] + self.GetCommandFlags()
293 return (
294 [os.path.join(shell_dir, self.binary)] +
295 self.flags +
296 [self.main] +
297 suffix
298 )
299 314
300 def Run(self, runner): 315 def Run(self, runner):
301 """Iterates over several runs and handles the output for all traces.""" 316 """Iterates over several runs and handles the output for all traces."""
302 for stdout in runner(): 317 for stdout in runner():
303 for trace in self._children: 318 for trace in self._children:
304 trace.ConsumeOutput(stdout) 319 trace.ConsumeOutput(stdout)
305 res = reduce(lambda r, t: r + t.GetResults(), self._children, Results()) 320 res = reduce(lambda r, t: r + t.GetResults(), self._children, Results())
306 321
307 if not res.traces or not self.total: 322 if not res.traces or not self.total:
308 return res 323 return res
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 raise Exception("Invalid suite configuration.") 416 raise Exception("Invalid suite configuration.")
402 417
403 418
404 def BuildGraphs(suite, arch, parent=None): 419 def BuildGraphs(suite, arch, parent=None):
405 """Builds a tree structure of graph objects that corresponds to the suite 420 """Builds a tree structure of graph objects that corresponds to the suite
406 configuration. 421 configuration.
407 """ 422 """
408 parent = parent or DefaultSentinel() 423 parent = parent or DefaultSentinel()
409 424
410 # TODO(machenbach): Implement notion of cpu type? 425 # TODO(machenbach): Implement notion of cpu type?
411 if arch not in suite.get("archs", ["ia32", "x64"]): 426 if arch not in suite.get("archs", SUPPORTED_ARCHS):
412 return None 427 return None
413 428
414 graph = MakeGraph(suite, arch, parent) 429 graph = MakeGraph(suite, arch, parent)
415 for subsuite in suite.get("tests", []): 430 for subsuite in suite.get("tests", []):
416 BuildGraphs(subsuite, arch, graph) 431 BuildGraphs(subsuite, arch, graph)
417 parent.AppendChild(graph) 432 parent.AppendChild(graph)
418 return graph 433 return graph
419 434
420 435
421 def FlattenRunnables(node): 436 def FlattenRunnables(node):
(...skipping 14 matching lines...) Expand all
436 @staticmethod 451 @staticmethod
437 def GetPlatform(options): 452 def GetPlatform(options):
438 if options.arch.startswith("android"): 453 if options.arch.startswith("android"):
439 return AndroidPlatform(options) 454 return AndroidPlatform(options)
440 else: 455 else:
441 return DesktopPlatform(options) 456 return DesktopPlatform(options)
442 457
443 458
444 class DesktopPlatform(Platform): 459 class DesktopPlatform(Platform):
445 def __init__(self, options): 460 def __init__(self, options):
446 workspace = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 461 self.shell_dir = options.shell_dir
447 462
448 if options.buildbot: 463 def PreExecution(self):
449 self.shell_dir = os.path.join(workspace, options.outdir, "Release")
450 else:
451 self.shell_dir = os.path.join(workspace, options.outdir,
452 "%s.release" % options.arch)
453
454 def PrepareExecution(self):
455 pass 464 pass
456 465
457 def PrepareTests(self, runnable, path): 466 def PostExecution(self):
467 pass
468
469 def PreTests(self, runnable, path):
458 runnable.ChangeCWD(path) 470 runnable.ChangeCWD(path)
459 471
460 def Run(self, runnable, count): 472 def Run(self, runnable, count):
461 output = commands.Execute(runnable.GetCommand(self.shell_dir), 473 output = commands.Execute(runnable.GetCommand(self.shell_dir),
462 timeout=runnable.timeout) 474 timeout=runnable.timeout)
463 print ">>> Stdout (#%d):" % (count + 1) 475 print ">>> Stdout (#%d):" % (count + 1)
464 print output.stdout 476 print output.stdout
465 if output.stderr: # pragma: no cover 477 if output.stderr: # pragma: no cover
466 # Print stderr for debugging. 478 # Print stderr for debugging.
467 print ">>> Stderr (#%d):" % (count + 1) 479 print ">>> Stderr (#%d):" % (count + 1)
468 print output.stderr 480 print output.stderr
469 if output.timed_out: 481 if output.timed_out:
470 print ">>> Test timed out after %ss." % runnable.timeout 482 print ">>> Test timed out after %ss." % runnable.timeout
471 return output.stdout 483 return output.stdout
472 484
473 485
474 # TODO(machenbach): Implement android platform. 486 class AndroidPlatform(Platform): # pragma: no cover
475 class AndroidPlatform(Platform): 487 DEVICE_DIR = "/data/local/tmp/v8/"
488
476 def __init__(self, options): 489 def __init__(self, options):
477 pass 490 self.shell_dir = options.shell_dir
491 LoadAndroidBuildTools(options.android_build_tools)
492
493 if not options.device:
494 # Detect attached device if not specified.
495 devices = pylib.android_commands.GetAttachedDevices(
496 hardware=True, emulator=False, offline=False)
497 assert devices and len(devices) == 1, (
498 "None or multiple devices detected. Please specify the device on "
499 "the command-line with --device")
500 options.device = devices[0]
501 adb_wrapper = pylib.android_commands.AndroidCommands(options.device)
502 self.device = device_utils.DeviceUtils(adb_wrapper)
503 self.adb = adb_wrapper.Adb()
504
505 def PreExecution(self):
506 perf = perf_control.PerfControl(self.device)
507 perf.SetHighPerfMode()
508
509 def PostExecution(self):
510 perf = perf_control.PerfControl(self.device)
511 perf.SetDefaultPerfMode()
512 self.device.RunShellCommand(
513 ["rm", "-rf", "*"],
514 cwd=AndroidPlatform.DEVICE_DIR,
515 )
516
517 def _PushFile(self, host_dir, file_name):
518 file_on_host = os.path.join(host_dir, file_name)
519 file_on_device = AndroidPlatform.DEVICE_DIR + file_name
520 logging.info("adb push %s %s" % (file_on_host, file_on_device))
521 self.adb.Push(file_on_host, file_on_device)
522
523 def PreTests(self, runnable, path):
524 suite_dir = os.path.abspath(os.path.dirname(path))
525 bench_dir = os.path.join(suite_dir,
526 os.path.normpath(os.path.join(*runnable.path)))
527
528 self._PushFile(self.shell_dir, runnable.binary)
529 self._PushFile(bench_dir, runnable.main)
530 for resource in runnable.resources:
531 self._PushFile(bench_dir, resource)
532
533 def Run(self, runnable, count):
534 cache = cache_control.CacheControl(self.device)
535 cache.DropRamCaches()
536 binary_on_device = AndroidPlatform.DEVICE_DIR + runnable.binary
537 cmd = [binary_on_device] + runnable.GetCommandFlags()
538 try:
539 output = self.device.RunShellCommand(
540 cmd,
541 cwd=AndroidPlatform.DEVICE_DIR,
542 timeout=runnable.timeout,
543 retries=0,
544 )
545 stdout = "\n".join(output)
546 print ">>> Stdout (#%d):" % (count + 1)
547 print stdout
548 except device_errors.CommandTimeoutError:
549 print ">>> Test timed out after %ss." % runnable.timeout
550 stdout = ""
551 return stdout
478 552
479 553
480 # TODO: Implement results_processor. 554 # TODO: Implement results_processor.
481 def Main(args): 555 def Main(args):
556 logging.getLogger().setLevel(logging.INFO)
482 parser = optparse.OptionParser() 557 parser = optparse.OptionParser()
483 parser.add_option("--android-build-tools", 558 parser.add_option("--android-build-tools",
484 help="Path to chromium's build/android.") 559 help="Path to chromium's build/android.")
485 parser.add_option("--arch", 560 parser.add_option("--arch",
486 help=("The architecture to run tests for, " 561 help=("The architecture to run tests for, "
487 "'auto' or 'native' for auto-detect"), 562 "'auto' or 'native' for auto-detect"),
488 default="x64") 563 default="x64")
489 parser.add_option("--buildbot", 564 parser.add_option("--buildbot",
490 help="Adapt to path structure used on buildbots", 565 help="Adapt to path structure used on buildbots",
491 default=False, action="store_true") 566 default=False, action="store_true")
(...skipping 21 matching lines...) Expand all
513 bool(options.android_build_tools)): # pragma: no cover 588 bool(options.android_build_tools)): # pragma: no cover
514 print ("Android architectures imply setting --android-build-tools and the " 589 print ("Android architectures imply setting --android-build-tools and the "
515 "other way around.") 590 "other way around.")
516 return 1 591 return 1
517 592
518 if (options.device and not 593 if (options.device and not
519 options.arch.startswith("android")): # pragma: no cover 594 options.arch.startswith("android")): # pragma: no cover
520 print "Specifying a device requires an Android architecture to be used." 595 print "Specifying a device requires an Android architecture to be used."
521 return 1 596 return 1
522 597
598 workspace = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
599
600 if options.buildbot:
601 options.shell_dir = os.path.join(workspace, options.outdir, "Release")
602 else:
603 options.shell_dir = os.path.join(workspace, options.outdir,
604 "%s.release" % options.arch)
605
523 platform = Platform.GetPlatform(options) 606 platform = Platform.GetPlatform(options)
524 platform.PrepareExecution() 607 platform.PreExecution()
525 608
526 results = Results() 609 results = Results()
527 for path in args: 610 for path in args:
528 path = os.path.abspath(path) 611 path = os.path.abspath(path)
529 612
530 if not os.path.exists(path): # pragma: no cover 613 if not os.path.exists(path): # pragma: no cover
531 results.errors.append("Configuration file %s does not exist." % path) 614 results.errors.append("Configuration file %s does not exist." % path)
532 continue 615 continue
533 616
534 with open(path) as f: 617 with open(path) as f:
535 suite = json.loads(f.read()) 618 suite = json.loads(f.read())
536 619
537 # If no name is given, default to the file name without .json. 620 # If no name is given, default to the file name without .json.
538 suite.setdefault("name", os.path.splitext(os.path.basename(path))[0]) 621 suite.setdefault("name", os.path.splitext(os.path.basename(path))[0])
539 622
540 for runnable in FlattenRunnables(BuildGraphs(suite, options.arch)): 623 for runnable in FlattenRunnables(BuildGraphs(suite, options.arch)):
541 print ">>> Running suite: %s" % "/".join(runnable.graphs) 624 print ">>> Running suite: %s" % "/".join(runnable.graphs)
542 platform.PrepareTests(runnable, path) 625 platform.PreTests(runnable, path)
543 626
544 def Runner(): 627 def Runner():
545 """Output generator that reruns several times.""" 628 """Output generator that reruns several times."""
546 for i in xrange(0, max(1, runnable.run_count)): 629 for i in xrange(0, max(1, runnable.run_count)):
547 # TODO(machenbach): Allow timeout per arch like with run_count per 630 # TODO(machenbach): Allow timeout per arch like with run_count per
548 # arch. 631 # arch.
549 yield platform.Run(runnable, i) 632 yield platform.Run(runnable, i)
550 633
551 # Let runnable iterate over all runs and handle output. 634 # Let runnable iterate over all runs and handle output.
552 results += runnable.Run(Runner) 635 results += runnable.Run(Runner)
553 636
637 platform.PostExecution()
638
554 if options.json_test_results: 639 if options.json_test_results:
555 results.WriteToFile(options.json_test_results) 640 results.WriteToFile(options.json_test_results)
556 else: # pragma: no cover 641 else: # pragma: no cover
557 print results 642 print results
558 643
559 return min(1, len(results.errors)) 644 return min(1, len(results.errors))
560 645
561 if __name__ == "__main__": # pragma: no cover 646 if __name__ == "__main__": # pragma: no cover
562 sys.exit(Main(sys.argv[1:])) 647 sys.exit(Main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | tools/unittests/run_perf_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698