OLD | NEW |
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 594 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
605 stdout = self._Run(runnable, count, no_patch=False) | 605 stdout = self._Run(runnable, count, no_patch=False) |
606 if self.shell_dir_no_patch: | 606 if self.shell_dir_no_patch: |
607 return stdout, self._Run(runnable, count, no_patch=True) | 607 return stdout, self._Run(runnable, count, no_patch=True) |
608 else: | 608 else: |
609 return stdout, None | 609 return stdout, None |
610 | 610 |
611 | 611 |
612 class DesktopPlatform(Platform): | 612 class DesktopPlatform(Platform): |
613 def __init__(self, options): | 613 def __init__(self, options): |
614 super(DesktopPlatform, self).__init__(options) | 614 super(DesktopPlatform, self).__init__(options) |
| 615 self.command_prefix = [] |
| 616 |
| 617 if options.prioritize or options.affinitize != None: |
| 618 self.command_prefix = ["schedtool"] |
| 619 if options.prioritize: |
| 620 self.command_prefix += ["-n", "-20"] |
| 621 if options.affinitize != None: |
| 622 # schedtool expects a bit pattern when setting affinity, where each |
| 623 # bit set to '1' corresponds to a core where the process may run on. |
| 624 # First bit corresponds to CPU 0. Since the 'affinitize' parameter is |
| 625 # a core number, we need to map to said bit pattern. |
| 626 cpu = int(options.affinitize) |
| 627 core = 1 << cpu |
| 628 self.command_prefix += ["-a", ("0x%x" % core)] |
| 629 self.command_prefix += ["-e"] |
615 | 630 |
616 def PreExecution(self): | 631 def PreExecution(self): |
617 pass | 632 pass |
618 | 633 |
619 def PostExecution(self): | 634 def PostExecution(self): |
620 pass | 635 pass |
621 | 636 |
622 def PreTests(self, node, path): | 637 def PreTests(self, node, path): |
623 if isinstance(node, RunnableConfig): | 638 if isinstance(node, RunnableConfig): |
624 node.ChangeCWD(path) | 639 node.ChangeCWD(path) |
625 | 640 |
626 def _Run(self, runnable, count, no_patch=False): | 641 def _Run(self, runnable, count, no_patch=False): |
627 suffix = ' - without patch' if no_patch else '' | 642 suffix = ' - without patch' if no_patch else '' |
628 shell_dir = self.shell_dir_no_patch if no_patch else self.shell_dir | 643 shell_dir = self.shell_dir_no_patch if no_patch else self.shell_dir |
629 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix) | 644 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix) |
| 645 command = self.command_prefix + runnable.GetCommand(shell_dir, |
| 646 self.extra_flags) |
630 try: | 647 try: |
631 output = commands.Execute( | 648 output = commands.Execute( |
632 runnable.GetCommand(shell_dir, self.extra_flags), | 649 command, |
633 timeout=runnable.timeout, | 650 timeout=runnable.timeout, |
634 ) | 651 ) |
635 except OSError as e: # pragma: no cover | 652 except OSError as e: # pragma: no cover |
636 print title % "OSError" | 653 print title % "OSError" |
637 print e | 654 print e |
638 return "" | 655 return "" |
| 656 |
639 print title % "Stdout" | 657 print title % "Stdout" |
640 print output.stdout | 658 print output.stdout |
641 if output.stderr: # pragma: no cover | 659 if output.stderr: # pragma: no cover |
642 # Print stderr for debugging. | 660 # Print stderr for debugging. |
643 print title % "Stderr" | 661 print title % "Stderr" |
644 print output.stderr | 662 print output.stderr |
645 if output.timed_out: | 663 if output.timed_out: |
646 print ">>> Test timed out after %ss." % runnable.timeout | 664 print ">>> Test timed out after %ss." % runnable.timeout |
647 if '--prof' in self.extra_flags: | 665 if '--prof' in self.extra_flags: |
648 os_prefix = {"linux": "linux", "macos": "mac"}.get(utils.GuessOS()) | 666 os_prefix = {"linux": "linux", "macos": "mac"}.get(utils.GuessOS()) |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
781 retries=0, | 799 retries=0, |
782 ) | 800 ) |
783 stdout = "\n".join(output) | 801 stdout = "\n".join(output) |
784 print title % "Stdout" | 802 print title % "Stdout" |
785 print stdout | 803 print stdout |
786 except device_errors.CommandTimeoutError: | 804 except device_errors.CommandTimeoutError: |
787 print ">>> Test timed out after %ss." % runnable.timeout | 805 print ">>> Test timed out after %ss." % runnable.timeout |
788 stdout = "" | 806 stdout = "" |
789 return stdout | 807 return stdout |
790 | 808 |
| 809 class CustomMachineConfiguration: |
| 810 def __init__(self, disable_aslr = False, governor = None): |
| 811 self.aslr_backup = None |
| 812 self.governor_backup = None |
| 813 self.disable_aslr = disable_aslr |
| 814 self.governor = governor |
| 815 |
| 816 def __enter__(self): |
| 817 if self.disable_aslr: |
| 818 self.aslr_backup = CustomMachineConfiguration.GetASLR() |
| 819 CustomMachineConfiguration.SetASLR(0) |
| 820 if self.governor != None: |
| 821 self.governor_backup = CustomMachineConfiguration.GetCPUGovernor() |
| 822 CustomMachineConfiguration.SetCPUGovernor(self.governor) |
| 823 return self |
| 824 |
| 825 def __exit__(self, type, value, traceback): |
| 826 if self.aslr_backup != None: |
| 827 CustomMachineConfiguration.SetASLR(self.aslr_backup) |
| 828 if self.governor_backup != None: |
| 829 CustomMachineConfiguration.SetCPUGovernor(self.governor_backup) |
| 830 |
| 831 @staticmethod |
| 832 def GetASLR(): |
| 833 try: |
| 834 with open("/proc/sys/kernel/randomize_va_space", "r") as f: |
| 835 return int(f.readline().strip()) |
| 836 except Exception as e: |
| 837 print "Failed to get current ASLR settings." |
| 838 raise e |
| 839 |
| 840 @staticmethod |
| 841 def SetASLR(value): |
| 842 try: |
| 843 with open("/proc/sys/kernel/randomize_va_space", "w") as f: |
| 844 f.write(str(value)) |
| 845 except Exception as e: |
| 846 print "Failed to update ASLR to %s." % value |
| 847 print "Are we running under sudo?" |
| 848 raise e |
| 849 |
| 850 new_value = CustomMachineConfiguration.GetASLR() |
| 851 if value != new_value: |
| 852 raise Exception("Present value is %s" % new_value) |
| 853 |
| 854 @staticmethod |
| 855 def GetCPUCoresRange(): |
| 856 try: |
| 857 with open("/sys/devices/system/cpu/present", "r") as f: |
| 858 indexes = f.readline() |
| 859 first, last = map(int, indexes.split("-")) |
| 860 return range(first, last + 1) |
| 861 except Exception as e: |
| 862 print "Failed to retrieve number of CPUs." |
| 863 raise e |
| 864 |
| 865 @staticmethod |
| 866 def GetCPUPathForId(cpu_index): |
| 867 ret = "/sys/devices/system/cpu/cpu" |
| 868 ret += str(cpu_index) |
| 869 ret += "/cpufreq/scaling_governor" |
| 870 return ret |
| 871 |
| 872 @staticmethod |
| 873 def GetCPUGovernor(): |
| 874 try: |
| 875 cpu_indices = CustomMachineConfiguration.GetCPUCoresRange() |
| 876 ret = None |
| 877 for cpu_index in cpu_indices: |
| 878 cpu_device = CustomMachineConfiguration.GetCPUPathForId(cpu_index) |
| 879 with open(cpu_device, "r") as f: |
| 880 # We assume the governors of all CPUs are set to the same value |
| 881 val = f.readline().strip() |
| 882 if ret == None: |
| 883 ret = val |
| 884 elif ret != val: |
| 885 raise Exception("CPU cores have differing governor settings") |
| 886 return ret |
| 887 except Exception as e: |
| 888 print "Failed to get the current CPU governor." |
| 889 print "Is the CPU governor disabled? Check BIOS." |
| 890 raise e |
| 891 |
| 892 @staticmethod |
| 893 def SetCPUGovernor(value): |
| 894 try: |
| 895 cpu_indices = CustomMachineConfiguration.GetCPUCoresRange() |
| 896 for cpu_index in cpu_indices: |
| 897 cpu_device = CustomMachineConfiguration.GetCPUPathForId(cpu_index) |
| 898 with open(cpu_device, "w") as f: |
| 899 f.write(value) |
| 900 |
| 901 except Exception as e: |
| 902 print "Failed to change CPU governor to %s." % value |
| 903 print "Are we running under sudo?" |
| 904 raise e |
| 905 |
| 906 cur_value = CustomMachineConfiguration.GetCPUGovernor() |
| 907 if cur_value != value: |
| 908 raise Exception("Could not set CPU governor. Present value is %s" |
| 909 % cur_value ) |
791 | 910 |
792 # TODO: Implement results_processor. | 911 # TODO: Implement results_processor. |
793 def Main(args): | 912 def Main(args): |
794 logging.getLogger().setLevel(logging.INFO) | 913 logging.getLogger().setLevel(logging.INFO) |
795 parser = optparse.OptionParser() | 914 parser = optparse.OptionParser() |
796 parser.add_option("--android-build-tools", | 915 parser.add_option("--android-build-tools", |
797 help="Path to chromium's build/android. Specifying this " | 916 help="Path to chromium's build/android. Specifying this " |
798 "option will run tests using android platform.") | 917 "option will run tests using android platform.") |
799 parser.add_option("--arch", | 918 parser.add_option("--arch", |
800 help=("The architecture to run tests for, " | 919 help=("The architecture to run tests for, " |
(...skipping 14 matching lines...) Expand all Loading... |
815 help="Path to a file for storing json results from run " | 934 help="Path to a file for storing json results from run " |
816 "without patch.") | 935 "without patch.") |
817 parser.add_option("--outdir", help="Base directory with compile output", | 936 parser.add_option("--outdir", help="Base directory with compile output", |
818 default="out") | 937 default="out") |
819 parser.add_option("--outdir-no-patch", | 938 parser.add_option("--outdir-no-patch", |
820 help="Base directory with compile output without patch") | 939 help="Base directory with compile output without patch") |
821 parser.add_option("--binary-override-path", | 940 parser.add_option("--binary-override-path", |
822 help="JavaScript engine binary. By default, d8 under " | 941 help="JavaScript engine binary. By default, d8 under " |
823 "architecture-specific build dir. " | 942 "architecture-specific build dir. " |
824 "Not supported in conjunction with outdir-no-patch.") | 943 "Not supported in conjunction with outdir-no-patch.") |
| 944 parser.add_option("--prioritize", |
| 945 help="Raise the priority to nice -20 for the benchmarking " |
| 946 "process.Requires Linux, schedtool, and sudo privileges.", |
| 947 default=False, action="store_true") |
| 948 parser.add_option("--affinitize", |
| 949 help="Run benchmarking process on the specified core. " |
| 950 "For example: " |
| 951 "--affinitize=0 will run the benchmark process on core 0. " |
| 952 "--affinitize=3 will run the benchmark process on core 3. " |
| 953 "Requires Linux, schedtool, and sudo privileges.", |
| 954 default=None) |
| 955 parser.add_option("--noaslr", |
| 956 help="Disable ASLR for the duration of the benchmarked " |
| 957 "process. Requires Linux and sudo privileges.", |
| 958 default=False, action="store_true") |
| 959 parser.add_option("--cpu-governor", |
| 960 help="Set cpu governor to specified policy for the " |
| 961 "duration of the benchmarked process. Typical options: " |
| 962 "'powersave' for more stable results, or 'performance' " |
| 963 "for shorter completion time of suite, with potentially " |
| 964 "more noise in results.") |
825 | 965 |
826 (options, args) = parser.parse_args(args) | 966 (options, args) = parser.parse_args(args) |
827 | 967 |
828 if len(args) == 0: # pragma: no cover | 968 if len(args) == 0: # pragma: no cover |
829 parser.print_help() | 969 parser.print_help() |
830 return 1 | 970 return 1 |
831 | 971 |
832 if options.arch in ["auto", "native"]: # pragma: no cover | 972 if options.arch in ["auto", "native"]: # pragma: no cover |
833 options.arch = ARCH_GUESS | 973 options.arch = ARCH_GUESS |
834 | 974 |
(...skipping 30 matching lines...) Expand all Loading... |
865 return 1 | 1005 return 1 |
866 options.shell_dir = os.path.dirname(options.binary_override_path) | 1006 options.shell_dir = os.path.dirname(options.binary_override_path) |
867 default_binary_name = os.path.basename(options.binary_override_path) | 1007 default_binary_name = os.path.basename(options.binary_override_path) |
868 | 1008 |
869 if options.outdir_no_patch: | 1009 if options.outdir_no_patch: |
870 options.shell_dir_no_patch = os.path.join( | 1010 options.shell_dir_no_patch = os.path.join( |
871 workspace, options.outdir_no_patch, build_config) | 1011 workspace, options.outdir_no_patch, build_config) |
872 else: | 1012 else: |
873 options.shell_dir_no_patch = None | 1013 options.shell_dir_no_patch = None |
874 | 1014 |
| 1015 prev_aslr = None |
| 1016 prev_cpu_gov = None |
875 platform = Platform.GetPlatform(options) | 1017 platform = Platform.GetPlatform(options) |
876 | 1018 |
877 results = Results() | 1019 results = Results() |
878 results_no_patch = Results() | 1020 results_no_patch = Results() |
879 for path in args: | 1021 with CustomMachineConfiguration(governor = options.cpu_governor, |
880 path = os.path.abspath(path) | 1022 disable_aslr = options.noaslr) as conf: |
| 1023 for path in args: |
| 1024 path = os.path.abspath(path) |
881 | 1025 |
882 if not os.path.exists(path): # pragma: no cover | 1026 if not os.path.exists(path): # pragma: no cover |
883 results.errors.append("Configuration file %s does not exist." % path) | 1027 results.errors.append("Configuration file %s does not exist." % path) |
884 continue | 1028 continue |
885 | 1029 |
886 with open(path) as f: | 1030 with open(path) as f: |
887 suite = json.loads(f.read()) | 1031 suite = json.loads(f.read()) |
888 | 1032 |
889 # If no name is given, default to the file name without .json. | 1033 # If no name is given, default to the file name without .json. |
890 suite.setdefault("name", os.path.splitext(os.path.basename(path))[0]) | 1034 suite.setdefault("name", os.path.splitext(os.path.basename(path))[0]) |
891 | 1035 |
892 # Setup things common to one test suite. | 1036 # Setup things common to one test suite. |
893 platform.PreExecution() | 1037 platform.PreExecution() |
894 | 1038 |
895 # Build the graph/trace tree structure. | 1039 # Build the graph/trace tree structure. |
896 default_parent = DefaultSentinel(default_binary_name) | 1040 default_parent = DefaultSentinel(default_binary_name) |
897 root = BuildGraphConfigs(suite, options.arch, default_parent) | 1041 root = BuildGraphConfigs(suite, options.arch, default_parent) |
898 | 1042 |
899 # Callback to be called on each node on traversal. | 1043 # Callback to be called on each node on traversal. |
900 def NodeCB(node): | 1044 def NodeCB(node): |
901 platform.PreTests(node, path) | 1045 platform.PreTests(node, path) |
902 | 1046 |
903 # Traverse graph/trace tree and interate over all runnables. | 1047 # Traverse graph/trace tree and interate over all runnables. |
904 for runnable in FlattenRunnables(root, NodeCB): | 1048 for runnable in FlattenRunnables(root, NodeCB): |
905 print ">>> Running suite: %s" % "/".join(runnable.graphs) | 1049 print ">>> Running suite: %s" % "/".join(runnable.graphs) |
906 | 1050 |
907 def Runner(): | 1051 def Runner(): |
908 """Output generator that reruns several times.""" | 1052 """Output generator that reruns several times.""" |
909 for i in xrange(0, max(1, runnable.run_count)): | 1053 for i in xrange(0, max(1, runnable.run_count)): |
910 # TODO(machenbach): Allow timeout per arch like with run_count per | 1054 # TODO(machenbach): Allow timeout per arch like with run_count per |
911 # arch. | 1055 # arch. |
912 yield platform.Run(runnable, i) | 1056 yield platform.Run(runnable, i) |
913 | 1057 |
914 # Let runnable iterate over all runs and handle output. | 1058 # Let runnable iterate over all runs and handle output. |
915 result, result_no_patch = runnable.Run( | 1059 result, result_no_patch = runnable.Run( |
916 Runner, trybot=options.shell_dir_no_patch) | 1060 Runner, trybot=options.shell_dir_no_patch) |
917 results += result | 1061 results += result |
918 results_no_patch += result_no_patch | 1062 results_no_patch += result_no_patch |
919 platform.PostExecution() | 1063 platform.PostExecution() |
920 | 1064 |
921 if options.json_test_results: | 1065 if options.json_test_results: |
922 results.WriteToFile(options.json_test_results) | 1066 results.WriteToFile(options.json_test_results) |
923 else: # pragma: no cover | 1067 else: # pragma: no cover |
924 print results | 1068 print results |
925 | 1069 |
926 if options.json_test_results_no_patch: | 1070 if options.json_test_results_no_patch: |
927 results_no_patch.WriteToFile(options.json_test_results_no_patch) | 1071 results_no_patch.WriteToFile(options.json_test_results_no_patch) |
928 else: # pragma: no cover | 1072 else: # pragma: no cover |
929 print results_no_patch | 1073 print results_no_patch |
930 | 1074 |
931 return min(1, len(results.errors)) | 1075 return min(1, len(results.errors)) |
932 | 1076 |
933 if __name__ == "__main__": # pragma: no cover | 1077 if __name__ == "__main__": # pragma: no cover |
934 sys.exit(Main(sys.argv[1:])) | 1078 sys.exit(Main(sys.argv[1:])) |
OLD | NEW |