 Chromium Code Reviews
 Chromium Code Reviews Issue 1915303002:
  [tools] Specify affinity and raise priority when benchmarking  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@master
    
  
    Issue 1915303002:
  [tools] Specify affinity and raise priority when benchmarking  (Closed) 
  Base URL: https://chromium.googlesource.com/v8/v8.git@master| 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 = None, governor = None): | |
| 811 self.aslr_backup = None | |
| 812 self.governor_backup = None | |
| 813 self.disable_aslr = disable_aslr | |
| 814 self.governor = governor | |
| 815 | |
| 
Michael Achenbach
2016/04/29 07:40:32
I like this!
 | |
| 816 def __enter__(self): | |
| 817 if self.disable_aslr != None: | |
| 818 self.BackupASLR() | |
| 819 self.DisableASLR() | |
| 820 if self.governor != None: | |
| 821 self.BackupGovernor() | |
| 822 self.UpdateGovernor() | |
| 823 return self | |
| 824 | |
| 825 def __exit__(self, type, value, traceback): | |
| 826 if self.aslr_backup != None: | |
| 827 self.RestoreASLR() | |
| 828 if self.governor_backup != None: | |
| 829 self.RestoreGovernor() | |
| 830 | |
| 831 def BackupASLR(self): | |
| 
Michael Achenbach
2016/04/29 07:40:32
The next 6 methods are each called only once and h
 
Mircea Trofin
2016/04/29 15:25:10
Done.
 | |
| 832 self.aslr_backup = CustomMachineConfiguration.GetASLR() | |
| 833 | |
| 834 def DisableASLR(self): | |
| 835 CustomMachineConfiguration.SetASLR(0) | |
| 836 | |
| 837 def RestoreASLR(self): | |
| 838 CustomMachineConfiguration.SetASLR(self.aslr_backup) | |
| 839 | |
| 840 def BackupGovernor(self): | |
| 841 self.governor_backup = CustomMachineConfiguration.GetCPUGovernor() | |
| 842 | |
| 843 def UpdateGovernor(self): | |
| 844 CustomMachineConfiguration.SetCPUGovernor(self.governor) | |
| 845 | |
| 846 def RestoreGovernor(self): | |
| 847 CustomMachineConfiguration.SetCPUGovernor(self.governor_backup) | |
| 848 | |
| 849 @staticmethod | |
| 850 def GetASLR(): | |
| 851 try: | |
| 852 with open("/proc/sys/kernel/randomize_va_space", "r") as f: | |
| 853 return int(f.readline().strip()) | |
| 854 except Exception as e: | |
| 855 print "Failed to get current ASLR settings." | |
| 856 raise e | |
| 857 | |
| 858 @staticmethod | |
| 859 def SetASLR(value): | |
| 860 try: | |
| 861 with open("/proc/sys/kernel/randomize_va_space", "w") as f: | |
| 862 f.write(str(value)) | |
| 863 except Exception as e: | |
| 864 print "Failed to update ASLR to %s." % value | |
| 865 print "Are we running under sudo?" | |
| 866 raise e | |
| 867 | |
| 868 new_value = CustomMachineConfiguration.GetASLR() | |
| 869 if value != new_value: | |
| 870 raise Exception("Present value is %s" % new_value) | |
| 871 | |
| 872 @staticmethod | |
| 873 def GetCPUCoresRange(): | |
| 874 try: | |
| 875 with open("/sys/devices/system/cpu/present", "r") as f: | |
| 876 indexes = f.readline() | |
| 877 first, last = map(int, indexes.split("-")) | |
| 878 return range(first, last + 1) | |
| 879 except Exception as e: | |
| 880 print "Failed to retrieve number of CPUs." | |
| 881 raise e | |
| 882 | |
| 883 @staticmethod | |
| 884 def GetCPUPathForId(cpu_index): | |
| 885 ret = "/sys/devices/system/cpu/cpu" | |
| 886 ret += str(cpu_index) | |
| 887 ret += "/cpufreq/scaling_governor" | |
| 888 return ret | |
| 889 | |
| 890 @staticmethod | |
| 891 def GetCPUGovernor(): | |
| 892 try: | |
| 893 cpu_indices = CustomMachineConfiguration.GetCPUCoresRange() | |
| 894 ret = None | |
| 895 for cpu_index in cpu_indices: | |
| 896 cpu_device = CustomMachineConfiguration.GetCPUPathForId(cpu_index) | |
| 897 with open(cpu_device, "r") as f: | |
| 898 # We assume the governors of all CPUs are set to the same value | |
| 899 val = f.readline().strip() | |
| 900 if ret == None: | |
| 901 ret = val | |
| 902 elif ret != val: | |
| 903 raise Exception("CPU cores have differing governor settings") | |
| 904 return ret | |
| 905 except Exception as e: | |
| 906 print "Failed to get the current CPU governor." | |
| 907 print "Is the CPU governor disabled? Check BIOS." | |
| 908 raise e | |
| 909 | |
| 910 @staticmethod | |
| 911 def SetCPUGovernor(value): | |
| 912 try: | |
| 913 cpu_indices = CustomMachineConfiguration.GetCPUCoresRange() | |
| 914 for cpu_index in cpu_indices: | |
| 915 cpu_device = CustomMachineConfiguration.GetCPUPathForId(cpu_index) | |
| 916 with open(cpu_device, "w") as f: | |
| 917 f.write(value) | |
| 918 | |
| 919 except Exception as e: | |
| 920 print "Failed to change CPU governor to %s." % value | |
| 921 print "Are we running under sudo?" | |
| 922 raise e | |
| 923 | |
| 924 cur_value = CustomMachineConfiguration.GetCPUGovernor() | |
| 925 if cur_value != value: | |
| 926 raise Exception("Could not set CPU governor. Present value is %s" | |
| 927 % cur_value ) | |
| 791 | 928 | 
| 792 # TODO: Implement results_processor. | 929 # TODO: Implement results_processor. | 
| 793 def Main(args): | 930 def Main(args): | 
| 794 logging.getLogger().setLevel(logging.INFO) | 931 logging.getLogger().setLevel(logging.INFO) | 
| 795 parser = optparse.OptionParser() | 932 parser = optparse.OptionParser() | 
| 796 parser.add_option("--android-build-tools", | 933 parser.add_option("--android-build-tools", | 
| 797 help="Path to chromium's build/android. Specifying this " | 934 help="Path to chromium's build/android. Specifying this " | 
| 798 "option will run tests using android platform.") | 935 "option will run tests using android platform.") | 
| 799 parser.add_option("--arch", | 936 parser.add_option("--arch", | 
| 800 help=("The architecture to run tests for, " | 937 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 " | 952 help="Path to a file for storing json results from run " | 
| 816 "without patch.") | 953 "without patch.") | 
| 817 parser.add_option("--outdir", help="Base directory with compile output", | 954 parser.add_option("--outdir", help="Base directory with compile output", | 
| 818 default="out") | 955 default="out") | 
| 819 parser.add_option("--outdir-no-patch", | 956 parser.add_option("--outdir-no-patch", | 
| 820 help="Base directory with compile output without patch") | 957 help="Base directory with compile output without patch") | 
| 821 parser.add_option("--binary-override-path", | 958 parser.add_option("--binary-override-path", | 
| 822 help="JavaScript engine binary. By default, d8 under " | 959 help="JavaScript engine binary. By default, d8 under " | 
| 823 "architecture-specific build dir. " | 960 "architecture-specific build dir. " | 
| 824 "Not supported in conjunction with outdir-no-patch.") | 961 "Not supported in conjunction with outdir-no-patch.") | 
| 962 parser.add_option("--prioritize", | |
| 963 help="Raise the priority to nice -20 for the benchmarking " | |
| 964 "process.Requires Linux, schedtool, and sudo privileges.", | |
| 965 default=False, action="store_true") | |
| 966 parser.add_option("--affinitize", | |
| 967 help="Run benchmarking process on the specified core. " | |
| 968 "For example: " | |
| 969 "--affinitize=0 will run the benchmark process on core 0. " | |
| 970 "--affinitize=3 will run the benchmark process on core 3. " | |
| 971 "Requires Linux, schedtool, and sudo privileges.", | |
| 972 default=None) | |
| 973 parser.add_option("--noaslr", | |
| 974 help="Disable ASLR for the duration of the benchmarked " | |
| 975 "process. Requires Linux and sudo privileges.", | |
| 976 default=False, action="store_true") | |
| 977 parser.add_option("--cpu-governor", | |
| 978 help="Set cpu governor to specified policy for the " | |
| 979 "duration of the benchmarked process. Typical options: " | |
| 980 "'powersave' for more stable results, or 'performance' " | |
| 981 "for shorter completion time of suite, with potentially " | |
| 982 "more noise in results.") | |
| 825 | 983 | 
| 826 (options, args) = parser.parse_args(args) | 984 (options, args) = parser.parse_args(args) | 
| 827 | 985 | 
| 828 if len(args) == 0: # pragma: no cover | 986 if len(args) == 0: # pragma: no cover | 
| 829 parser.print_help() | 987 parser.print_help() | 
| 830 return 1 | 988 return 1 | 
| 831 | 989 | 
| 832 if options.arch in ["auto", "native"]: # pragma: no cover | 990 if options.arch in ["auto", "native"]: # pragma: no cover | 
| 833 options.arch = ARCH_GUESS | 991 options.arch = ARCH_GUESS | 
| 834 | 992 | 
| (...skipping 30 matching lines...) Expand all Loading... | |
| 865 return 1 | 1023 return 1 | 
| 866 options.shell_dir = os.path.dirname(options.binary_override_path) | 1024 options.shell_dir = os.path.dirname(options.binary_override_path) | 
| 867 default_binary_name = os.path.basename(options.binary_override_path) | 1025 default_binary_name = os.path.basename(options.binary_override_path) | 
| 868 | 1026 | 
| 869 if options.outdir_no_patch: | 1027 if options.outdir_no_patch: | 
| 870 options.shell_dir_no_patch = os.path.join( | 1028 options.shell_dir_no_patch = os.path.join( | 
| 871 workspace, options.outdir_no_patch, build_config) | 1029 workspace, options.outdir_no_patch, build_config) | 
| 872 else: | 1030 else: | 
| 873 options.shell_dir_no_patch = None | 1031 options.shell_dir_no_patch = None | 
| 874 | 1032 | 
| 1033 prev_aslr = None | |
| 1034 prev_cpu_gov = None | |
| 875 platform = Platform.GetPlatform(options) | 1035 platform = Platform.GetPlatform(options) | 
| 876 | 1036 | 
| 877 results = Results() | 1037 results = Results() | 
| 878 results_no_patch = Results() | 1038 results_no_patch = Results() | 
| 879 for path in args: | 1039 with CustomMachineConfiguration(governor = options.cpu_governor, | 
| 880 path = os.path.abspath(path) | 1040 disable_aslr = options.noaslr) as conf: | 
| 1041 for path in args: | |
| 1042 path = os.path.abspath(path) | |
| 881 | 1043 | 
| 882 if not os.path.exists(path): # pragma: no cover | 1044 if not os.path.exists(path): # pragma: no cover | 
| 883 results.errors.append("Configuration file %s does not exist." % path) | 1045 results.errors.append("Configuration file %s does not exist." % path) | 
| 884 continue | 1046 continue | 
| 885 | 1047 | 
| 886 with open(path) as f: | 1048 with open(path) as f: | 
| 887 suite = json.loads(f.read()) | 1049 suite = json.loads(f.read()) | 
| 888 | 1050 | 
| 889 # If no name is given, default to the file name without .json. | 1051 # If no name is given, default to the file name without .json. | 
| 890 suite.setdefault("name", os.path.splitext(os.path.basename(path))[0]) | 1052 suite.setdefault("name", os.path.splitext(os.path.basename(path))[0]) | 
| 891 | 1053 | 
| 892 # Setup things common to one test suite. | 1054 # Setup things common to one test suite. | 
| 893 platform.PreExecution() | 1055 platform.PreExecution() | 
| 894 | 1056 | 
| 895 # Build the graph/trace tree structure. | 1057 # Build the graph/trace tree structure. | 
| 896 default_parent = DefaultSentinel(default_binary_name) | 1058 default_parent = DefaultSentinel(default_binary_name) | 
| 897 root = BuildGraphConfigs(suite, options.arch, default_parent) | 1059 root = BuildGraphConfigs(suite, options.arch, default_parent) | 
| 898 | 1060 | 
| 899 # Callback to be called on each node on traversal. | 1061 # Callback to be called on each node on traversal. | 
| 900 def NodeCB(node): | 1062 def NodeCB(node): | 
| 901 platform.PreTests(node, path) | 1063 platform.PreTests(node, path) | 
| 902 | 1064 | 
| 903 # Traverse graph/trace tree and interate over all runnables. | 1065 # Traverse graph/trace tree and interate over all runnables. | 
| 904 for runnable in FlattenRunnables(root, NodeCB): | 1066 for runnable in FlattenRunnables(root, NodeCB): | 
| 905 print ">>> Running suite: %s" % "/".join(runnable.graphs) | 1067 print ">>> Running suite: %s" % "/".join(runnable.graphs) | 
| 906 | 1068 | 
| 907 def Runner(): | 1069 def Runner(): | 
| 908 """Output generator that reruns several times.""" | 1070 """Output generator that reruns several times.""" | 
| 909 for i in xrange(0, max(1, runnable.run_count)): | 1071 for i in xrange(0, max(1, runnable.run_count)): | 
| 910 # TODO(machenbach): Allow timeout per arch like with run_count per | 1072 # TODO(machenbach): Allow timeout per arch like with run_count per | 
| 911 # arch. | 1073 # arch. | 
| 912 yield platform.Run(runnable, i) | 1074 yield platform.Run(runnable, i) | 
| 913 | 1075 | 
| 914 # Let runnable iterate over all runs and handle output. | 1076 # Let runnable iterate over all runs and handle output. | 
| 915 result, result_no_patch = runnable.Run( | 1077 result, result_no_patch = runnable.Run( | 
| 916 Runner, trybot=options.shell_dir_no_patch) | 1078 Runner, trybot=options.shell_dir_no_patch) | 
| 917 results += result | 1079 results += result | 
| 918 results_no_patch += result_no_patch | 1080 results_no_patch += result_no_patch | 
| 919 platform.PostExecution() | 1081 platform.PostExecution() | 
| 920 | 1082 | 
| 921 if options.json_test_results: | 1083 if options.json_test_results: | 
| 922 results.WriteToFile(options.json_test_results) | 1084 results.WriteToFile(options.json_test_results) | 
| 923 else: # pragma: no cover | 1085 else: # pragma: no cover | 
| 924 print results | 1086 print results | 
| 925 | 1087 | 
| 926 if options.json_test_results_no_patch: | 1088 if options.json_test_results_no_patch: | 
| 927 results_no_patch.WriteToFile(options.json_test_results_no_patch) | 1089 results_no_patch.WriteToFile(options.json_test_results_no_patch) | 
| 928 else: # pragma: no cover | 1090 else: # pragma: no cover | 
| 929 print results_no_patch | 1091 print results_no_patch | 
| 930 | 1092 | 
| 931 return min(1, len(results.errors)) | 1093 return min(1, len(results.errors)) | 
| 932 | 1094 | 
| 933 if __name__ == "__main__": # pragma: no cover | 1095 if __name__ == "__main__": # pragma: no cover | 
| 934 sys.exit(Main(sys.argv[1:])) | 1096 sys.exit(Main(sys.argv[1:])) | 
| OLD | NEW |