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

Side by Side Diff: tools/run_perf.py

Issue 1681283004: [tools] add --pretty switch to run_perf.py Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: fix output concatenation on android Created 4 years, 10 months 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 387 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 self.timeout = suite.get("timeout_%s" % arch, self.timeout) 398 self.timeout = suite.get("timeout_%s" % arch, self.timeout)
399 self.units = suite.get("units", parent.units) 399 self.units = suite.get("units", parent.units)
400 self.total = suite.get("total", parent.total) 400 self.total = suite.get("total", parent.total)
401 401
402 # A regular expression for results. If the parent graph provides a 402 # A regular expression for results. If the parent graph provides a
403 # regexp and the current suite has none, a string place holder for the 403 # regexp and the current suite has none, a string place holder for the
404 # suite name is expected. 404 # suite name is expected.
405 # TODO(machenbach): Currently that makes only sense for the leaf level. 405 # TODO(machenbach): Currently that makes only sense for the leaf level.
406 # Multiple place holders for multiple levels are not supported. 406 # Multiple place holders for multiple levels are not supported.
407 if parent.results_regexp: 407 if parent.results_regexp:
408 regexp_default = parent.results_regexp % re.escape(suite["name"]) 408 try:
409 regexp_default = parent.results_regexp % re.escape(suite["name"])
410 except TypeError:
411 regexp_default = parent.results_regexp
409 else: 412 else:
410 regexp_default = None 413 regexp_default = None
411 self.results_regexp = suite.get("results_regexp", regexp_default) 414 self.results_regexp = suite.get("results_regexp", regexp_default)
412 415
413 # A similar regular expression for the standard deviation (optional). 416 # A similar regular expression for the standard deviation (optional).
414 if parent.stddev_regexp: 417 if parent.stddev_regexp:
415 stddev_default = parent.stddev_regexp % re.escape(suite["name"]) 418 stddev_default = parent.stddev_regexp % re.escape(suite["name"])
416 else: 419 else:
417 stddev_default = None 420 stddev_default = None
418 self.stddev_regexp = suite.get("stddev_regexp", stddev_default) 421 self.stddev_regexp = suite.get("stddev_regexp", stddev_default)
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
580 self.shell_dir_no_patch = options.shell_dir_no_patch 583 self.shell_dir_no_patch = options.shell_dir_no_patch
581 self.extra_flags = options.extra_flags.split() 584 self.extra_flags = options.extra_flags.split()
582 585
583 @staticmethod 586 @staticmethod
584 def GetPlatform(options): 587 def GetPlatform(options):
585 if options.android_build_tools: 588 if options.android_build_tools:
586 return AndroidPlatform(options) 589 return AndroidPlatform(options)
587 else: 590 else:
588 return DesktopPlatform(options) 591 return DesktopPlatform(options)
589 592
593 def GetPrettyFormatted(self, options):
594 return self
595
596 def PreExecution(self):
597 pass
598
599 def PostExecution(self):
600 pass
601
602 def PreTests(self, node, path):
603 pass
604
605 def PrintResult(self, result):
606 pass
607
608 def _PrintStdout(self, title, output):
609 print title % "Stdout"
610 print output.stdout
611
590 def _Run(self, runnable, count, no_patch=False): 612 def _Run(self, runnable, count, no_patch=False):
591 raise NotImplementedError() # pragma: no cover 613 raise NotImplementedError() # pragma: no cover
592 614
593 def Run(self, runnable, count): 615 def Run(self, runnable, count):
594 """Execute the benchmark's main file. 616 """Execute the benchmark's main file.
595 617
596 If options.shell_dir_no_patch is specified, the benchmark is run once with 618 If options.shell_dir_no_patch is specified, the benchmark is run once with
597 and once without patch. 619 and once without patch.
598 Args: 620 Args:
599 runnable: A Runnable benchmark instance. 621 runnable: A Runnable benchmark instance.
600 count: The number of this (repeated) run. 622 count: The number of this (repeated) run.
601 Returns: A tuple with the benchmark outputs with and without patch. The 623 Returns: A tuple with the benchmark outputs with and without patch. The
602 latter will be None if options.shell_dir_no_patch was not 624 latter will be None if options.shell_dir_no_patch was not
603 specified. 625 specified.
604 """ 626 """
605 stdout = self._Run(runnable, count, no_patch=False) 627 stdout = self._Run(runnable, count, no_patch=False)
606 if self.shell_dir_no_patch: 628 if self.shell_dir_no_patch:
607 return stdout, self._Run(runnable, count, no_patch=True) 629 return stdout, self._Run(runnable, count, no_patch=True)
608 else: 630 else:
609 return stdout, None 631 return stdout, None
610 632
611 633
634 class PlatformFormattedMixin(object):
635 """
636 Helper mixin that adds formatted output used when running benchmarks
637 with the --pretty flag.
638 """
639
640 def _PrintStdout(self, title, output):
641 sys.stdout.write("\r")
642 if output.exit_code != 0:
643 print output.stdout
644 return
645 # Assume the time is on the last line
646 result_line = output.stdout.splitlines()[-1].strip()
647 sys.stdout.write(result_line)
648 # Fill with spaces up to 80 characters.
649 sys.stdout.write(' '*max(0, 80-len(result_line)))
650 sys.stdout.flush()
651
652 def _GetMean(self, trace):
653 results = trace['results']
654 if len(results) == 0:
655 return 0
656 # If the tests provided a stddev the results consists of one single average
657 # value, so return that instead.
658 if trace['stddev']:
659 return results[0]
660 # For a non-zero length results list calculate the average here.
661 return sum([float(x) for x in results]) / len(results)
662
663 def _GetDeviation(self, trace):
664 # If the benchmark provided a stddev use that directly.
665 stddev = trace['stddev']
666 if stddev:
667 return stddev
668 # If no stddev was provided calculate it from the results.
669 results = trace['results']
670 if len(results) == 0:
671 return 0
672 mean = self._GetMean(trace)
673 square_deviation = sum((float(x)-mean)**2 for x in results)
674 return (square_deviation / len(results)) ** 0.5
675
676 def PrintResult(self, result):
677 if result.errors:
678 print "\r:Errors:"
679 print "\n".join(set(result.errors))
680 else:
681 trace = result.traces[0]
682 average = self._GetMean(trace)
683 stdev = self._GetDeviation(trace)
684 stdev_percentage = 100 * stdev / average if average != 0 else 0
685 result_string = "\r %s +/- %3.2f%% %s" % (
686 average, stdev_percentage, trace['units'])
687 sys.stdout.write(result_string)
688 # Fill with spaces up to 80 characters.
689 sys.stdout.write(' '*max(0, 80-len(result_string)))
690 sys.stdout.write("\n")
691 sys.stdout.flush()
692
693
612 class DesktopPlatform(Platform): 694 class DesktopPlatform(Platform):
613 def __init__(self, options): 695 def __init__(self, options):
614 super(DesktopPlatform, self).__init__(options) 696 super(DesktopPlatform, self).__init__(options)
615 697
616 def PreExecution(self): 698 def GetPrettyFormatted(self, options):
617 pass 699 return PrettyFormattedDesktopPlatform(options)
618
619 def PostExecution(self):
620 pass
621 700
622 def PreTests(self, node, path): 701 def PreTests(self, node, path):
623 if isinstance(node, RunnableConfig): 702 if isinstance(node, RunnableConfig):
624 node.ChangeCWD(path) 703 node.ChangeCWD(path)
625 704
626 def _Run(self, runnable, count, no_patch=False): 705 def _Run(self, runnable, count, no_patch=False):
627 suffix = ' - without patch' if no_patch else '' 706 suffix = ' - without patch' if no_patch else ''
628 shell_dir = self.shell_dir_no_patch if no_patch else self.shell_dir 707 shell_dir = self.shell_dir_no_patch if no_patch else self.shell_dir
629 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix) 708 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix)
630 try: 709 try:
631 output = commands.Execute( 710 output = commands.Execute(
632 runnable.GetCommand(shell_dir, self.extra_flags), 711 runnable.GetCommand(shell_dir, self.extra_flags),
633 timeout=runnable.timeout, 712 timeout=runnable.timeout,
634 ) 713 )
635 except OSError as e: # pragma: no cover 714 except OSError as e: # pragma: no cover
636 print title % "OSError" 715 print title % "OSError"
637 print e 716 print e
638 return "" 717 return ""
639 print title % "Stdout" 718 self._PrintStdout(title, output)
640 print output.stdout
641 if output.stderr: # pragma: no cover 719 if output.stderr: # pragma: no cover
642 # Print stderr for debugging. 720 # Print stderr for debugging.
643 print title % "Stderr" 721 print title % "Stderr"
644 print output.stderr 722 print output.stderr
645 if output.timed_out: 723 if output.timed_out:
646 print ">>> Test timed out after %ss." % runnable.timeout 724 print ">>> Test timed out after %ss." % runnable.timeout
647 if '--prof' in self.extra_flags: 725 if '--prof' in self.extra_flags:
648 os_prefix = {"linux": "linux", "macos": "mac"}.get(utils.GuessOS()) 726 os_prefix = {"linux": "linux", "macos": "mac"}.get(utils.GuessOS())
649 if os_prefix: 727 if os_prefix:
650 tick_tools = os.path.join(TOOLS_BASE, "%s-tick-processor" % os_prefix) 728 tick_tools = os.path.join(TOOLS_BASE, "%s-tick-processor" % os_prefix)
651 subprocess.check_call(tick_tools + " --only-summary", shell=True) 729 subprocess.check_call(tick_tools + " --only-summary", shell=True)
652 else: # pragma: no cover 730 else: # pragma: no cover
653 print "Profiler option currently supported on Linux and Mac OS." 731 print "Profiler option currently supported on Linux and Mac OS."
654 return output.stdout 732 return output.stdout
655 733
656 734
735 class PrettyFormattedDesktopPlatform(PlatformFormattedMixin, DesktopPlatform):
736 pass
737
738
657 class AndroidPlatform(Platform): # pragma: no cover 739 class AndroidPlatform(Platform): # pragma: no cover
658 DEVICE_DIR = "/data/local/tmp/v8/" 740 DEVICE_DIR = "/data/local/tmp/v8/"
659 741
660 def __init__(self, options): 742 def __init__(self, options):
661 super(AndroidPlatform, self).__init__(options) 743 super(AndroidPlatform, self).__init__(options)
662 LoadAndroidBuildTools(options.android_build_tools) 744 LoadAndroidBuildTools(options.android_build_tools)
663 745
664 if not options.device: 746 if not options.device:
665 # Detect attached device if not specified. 747 # Detect attached device if not specified.
666 devices = adb_wrapper.AdbWrapper.Devices() 748 devices = adb_wrapper.AdbWrapper.Devices()
667 assert devices and len(devices) == 1, ( 749 assert devices and len(devices) == 1, (
668 "None or multiple devices detected. Please specify the device on " 750 "None or multiple devices detected. Please specify the device on "
669 "the command-line with --device") 751 "the command-line with --device")
670 options.device = str(devices[0]) 752 options.device = str(devices[0])
671 self.adb_wrapper = adb_wrapper.AdbWrapper(options.device) 753 self.adb_wrapper = adb_wrapper.AdbWrapper(options.device)
672 self.device = device_utils.DeviceUtils(self.adb_wrapper) 754 self.device = device_utils.DeviceUtils(self.adb_wrapper)
673 755
756 def GetPrettyFormatted(self, options):
757 return PrettyFormattedAndroidPlatform(options)
758
674 def PreExecution(self): 759 def PreExecution(self):
675 perf = perf_control.PerfControl(self.device) 760 perf = perf_control.PerfControl(self.device)
676 perf.SetHighPerfMode() 761 perf.SetHighPerfMode()
677 762
678 # Remember what we have already pushed to the device. 763 # Remember what we have already pushed to the device.
679 self.pushed = set() 764 self.pushed = set()
680 765
681 def PostExecution(self): 766 def PostExecution(self):
682 perf = perf_control.PerfControl(self.device) 767 perf = perf_control.PerfControl(self.device)
683 perf.SetDefaultPerfMode() 768 perf.SetDefaultPerfMode()
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
750 self._PushExecutable(self.shell_dir, "bin", node.binary) 835 self._PushExecutable(self.shell_dir, "bin", node.binary)
751 if self.shell_dir_no_patch: 836 if self.shell_dir_no_patch:
752 self._PushExecutable( 837 self._PushExecutable(
753 self.shell_dir_no_patch, "bin_no_patch", node.binary) 838 self.shell_dir_no_patch, "bin_no_patch", node.binary)
754 839
755 if isinstance(node, RunnableConfig): 840 if isinstance(node, RunnableConfig):
756 self._PushFile(bench_abs, node.main, bench_rel) 841 self._PushFile(bench_abs, node.main, bench_rel)
757 for resource in node.resources: 842 for resource in node.resources:
758 self._PushFile(bench_abs, resource, bench_rel) 843 self._PushFile(bench_abs, resource, bench_rel)
759 844
845 def _PrintStdout(self, title, output):
846 print title % "Stdout"
847 print "\n".join(output)
848
760 def _Run(self, runnable, count, no_patch=False): 849 def _Run(self, runnable, count, no_patch=False):
761 suffix = ' - without patch' if no_patch else '' 850 suffix = ' - without patch' if no_patch else ''
762 target_dir = "bin_no_patch" if no_patch else "bin" 851 target_dir = "bin_no_patch" if no_patch else "bin"
763 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix) 852 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix)
764 cache = cache_control.CacheControl(self.device) 853 cache = cache_control.CacheControl(self.device)
765 cache.DropRamCaches() 854 cache.DropRamCaches()
766 binary_on_device = os.path.join( 855 binary_on_device = os.path.join(
767 AndroidPlatform.DEVICE_DIR, target_dir, runnable.binary) 856 AndroidPlatform.DEVICE_DIR, target_dir, runnable.binary)
768 cmd = [binary_on_device] + runnable.GetCommandFlags(self.extra_flags) 857 cmd = [binary_on_device] + runnable.GetCommandFlags(self.extra_flags)
769 858
770 # Relative path to benchmark directory. 859 # Relative path to benchmark directory.
771 if runnable.path: 860 if runnable.path:
772 bench_rel = os.path.normpath(os.path.join(*runnable.path)) 861 bench_rel = os.path.normpath(os.path.join(*runnable.path))
773 else: 862 else:
774 bench_rel = "." 863 bench_rel = "."
775 864
776 try: 865 try:
777 output = self.device.RunShellCommand( 866 output = self.device.RunShellCommand(
778 cmd, 867 cmd,
779 cwd=os.path.join(AndroidPlatform.DEVICE_DIR, bench_rel), 868 cwd=os.path.join(AndroidPlatform.DEVICE_DIR, bench_rel),
780 timeout=runnable.timeout, 869 timeout=runnable.timeout,
781 retries=0, 870 retries=0,
782 ) 871 )
783 stdout = "\n".join(output) 872 self._PrintStdout(title, output)
784 print title % "Stdout"
785 print stdout
786 except device_errors.CommandTimeoutError: 873 except device_errors.CommandTimeoutError:
787 print ">>> Test timed out after %ss." % runnable.timeout 874 print ">>> Test timed out after %ss." % runnable.timeout
788 stdout = "" 875 stdout = ""
789 return stdout 876 return stdout
Michael Achenbach 2016/02/16 13:01:49 Meh. This needs to contain the stdout from above.
790 877
791 878
879 class PrettyFormattedAndroidPlatform(PlatformFormattedMixin, AndroidPlatform):
880 pass
881
882
792 # TODO: Implement results_processor. 883 # TODO: Implement results_processor.
793 def Main(args): 884 def Main(args):
794 logging.getLogger().setLevel(logging.INFO) 885 logging.getLogger().setLevel(logging.INFO)
795 parser = optparse.OptionParser() 886 parser = optparse.OptionParser()
796 parser.add_option("--android-build-tools", 887 parser.add_option("--android-build-tools",
797 help="Path to chromium's build/android. Specifying this " 888 help="Path to chromium's build/android. Specifying this "
798 "option will run tests using android platform.") 889 "option will run tests using android platform.")
799 parser.add_option("--arch", 890 parser.add_option("--arch",
800 help=("The architecture to run tests for, " 891 help=("The architecture to run tests for, "
801 "'auto' or 'native' for auto-detect"), 892 "'auto' or 'native' for auto-detect"),
802 default="x64") 893 default="x64")
803 parser.add_option("--buildbot", 894 parser.add_option("--buildbot",
804 help="Adapt to path structure used on buildbots", 895 help="Adapt to path structure used on buildbots",
805 default=False, action="store_true") 896 default=False, action="store_true")
806 parser.add_option("--device", 897 parser.add_option("--device",
807 help="The device ID to run Android tests on. If not given " 898 help="The device ID to run Android tests on. If not given "
808 "it will be autodetected.") 899 "it will be autodetected.")
809 parser.add_option("--extra-flags", 900 parser.add_option("--extra-flags",
810 help="Additional flags to pass to the test executable", 901 help="Additional flags to pass to the test executable",
811 default="") 902 default="")
812 parser.add_option("--json-test-results", 903 parser.add_option("--json-test-results",
813 help="Path to a file for storing json results.") 904 help="Path to a file for storing json results.")
814 parser.add_option("--json-test-results-no-patch", 905 parser.add_option("--json-test-results-no-patch",
815 help="Path to a file for storing json results from run " 906 help="Path to a file for storing json results from run "
816 "without patch.") 907 "without patch.")
817 parser.add_option("--outdir", help="Base directory with compile output", 908 parser.add_option("--outdir", help="Base directory with compile output",
818 default="out") 909 default="out")
819 parser.add_option("--outdir-no-patch", 910 parser.add_option("--outdir-no-patch",
820 help="Base directory with compile output without patch") 911 help="Base directory with compile output without patch")
912 parser.add_option("--pretty",
913 help="Print human readable output",
914 default=False, action="store_true")
821 parser.add_option("--binary-override-path", 915 parser.add_option("--binary-override-path",
822 help="JavaScript engine binary. By default, d8 under " 916 help="JavaScript engine binary. By default, d8 under "
823 "architecture-specific build dir. " 917 "architecture-specific build dir. "
824 "Not supported in conjunction with outdir-no-patch.") 918 "Not supported in conjunction with outdir-no-patch.")
825 919
826 (options, args) = parser.parse_args(args) 920 (options, args) = parser.parse_args(args)
827 921
828 if len(args) == 0: # pragma: no cover 922 if len(args) == 0: # pragma: no cover
829 parser.print_help() 923 parser.print_help()
830 return 1 924 return 1
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
866 options.shell_dir = os.path.dirname(options.binary_override_path) 960 options.shell_dir = os.path.dirname(options.binary_override_path)
867 default_binary_name = os.path.basename(options.binary_override_path) 961 default_binary_name = os.path.basename(options.binary_override_path)
868 962
869 if options.outdir_no_patch: 963 if options.outdir_no_patch:
870 options.shell_dir_no_patch = os.path.join( 964 options.shell_dir_no_patch = os.path.join(
871 workspace, options.outdir_no_patch, build_config) 965 workspace, options.outdir_no_patch, build_config)
872 else: 966 else:
873 options.shell_dir_no_patch = None 967 options.shell_dir_no_patch = None
874 968
875 platform = Platform.GetPlatform(options) 969 platform = Platform.GetPlatform(options)
970 if options.pretty:
971 platform = platform.GetPrettyFormatted(options)
876 972
877 results = Results() 973 results = Results()
878 results_no_patch = Results() 974 results_no_patch = Results()
879 for path in args: 975 for path in args:
880 path = os.path.abspath(path) 976 path = os.path.abspath(path)
881 977
882 if not os.path.exists(path): # pragma: no cover 978 if not os.path.exists(path): # pragma: no cover
883 results.errors.append("Configuration file %s does not exist." % path) 979 results.errors.append("Configuration file %s does not exist." % path)
884 continue 980 continue
885 981
(...skipping 21 matching lines...) Expand all
907 def Runner(): 1003 def Runner():
908 """Output generator that reruns several times.""" 1004 """Output generator that reruns several times."""
909 for i in xrange(0, max(1, runnable.run_count)): 1005 for i in xrange(0, max(1, runnable.run_count)):
910 # TODO(machenbach): Allow timeout per arch like with run_count per 1006 # TODO(machenbach): Allow timeout per arch like with run_count per
911 # arch. 1007 # arch.
912 yield platform.Run(runnable, i) 1008 yield platform.Run(runnable, i)
913 1009
914 # Let runnable iterate over all runs and handle output. 1010 # Let runnable iterate over all runs and handle output.
915 result, result_no_patch = runnable.Run( 1011 result, result_no_patch = runnable.Run(
916 Runner, trybot=options.shell_dir_no_patch) 1012 Runner, trybot=options.shell_dir_no_patch)
1013 platform.PrintResult(result)
917 results += result 1014 results += result
918 results_no_patch += result_no_patch 1015 results_no_patch += result_no_patch
919 platform.PostExecution() 1016 platform.PostExecution()
920 1017
921 if options.json_test_results: 1018 if options.json_test_results:
922 results.WriteToFile(options.json_test_results) 1019 results.WriteToFile(options.json_test_results)
923 else: # pragma: no cover 1020 else: # pragma: no cover
924 print results 1021 if not options.pretty:
1022 print results
925 1023
926 if options.json_test_results_no_patch: 1024 if options.json_test_results_no_patch:
927 results_no_patch.WriteToFile(options.json_test_results_no_patch) 1025 results_no_patch.WriteToFile(options.json_test_results_no_patch)
928 else: # pragma: no cover 1026 else: # pragma: no cover
929 print results_no_patch 1027 if not options.pretty:
1028 print results_no_patch
930 1029
931 return min(1, len(results.errors)) 1030 return min(1, len(results.errors))
932 1031
933 if __name__ == "__main__": # pragma: no cover 1032 if __name__ == "__main__": # pragma: no cover
934 sys.exit(Main(sys.argv[1:])) 1033 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