| Index: tools/run_perf.py
|
| diff --git a/tools/run_perf.py b/tools/run_perf.py
|
| index db4245f499fc07c3a32f435a78ec08d5f06f58a3..1dd03bd5379aaff6dfeacf72092e5b7a4dba0f30 100755
|
| --- a/tools/run_perf.py
|
| +++ b/tools/run_perf.py
|
| @@ -612,6 +612,21 @@ class Platform(object):
|
| class DesktopPlatform(Platform):
|
| def __init__(self, options):
|
| super(DesktopPlatform, self).__init__(options)
|
| + self.command_prefix = []
|
| +
|
| + if options.prioritize or options.affinitize != None:
|
| + self.command_prefix = ["schedtool"]
|
| + if options.prioritize:
|
| + self.command_prefix += ["-n", "-20"]
|
| + if options.affinitize != None:
|
| + # schedtool expects a bit pattern when setting affinity, where each
|
| + # bit set to '1' corresponds to a core where the process may run on.
|
| + # First bit corresponds to CPU 0. Since the 'affinitize' parameter is
|
| + # a core number, we need to map to said bit pattern.
|
| + cpu = int(options.affinitize)
|
| + core = 1 << cpu
|
| + self.command_prefix += ["-a", ("0x%x" % core)]
|
| + self.command_prefix += ["-e"]
|
|
|
| def PreExecution(self):
|
| pass
|
| @@ -627,15 +642,18 @@ class DesktopPlatform(Platform):
|
| suffix = ' - without patch' if no_patch else ''
|
| shell_dir = self.shell_dir_no_patch if no_patch else self.shell_dir
|
| title = ">>> %%s (#%d)%s:" % ((count + 1), suffix)
|
| + command = self.command_prefix + runnable.GetCommand(shell_dir,
|
| + self.extra_flags)
|
| try:
|
| output = commands.Execute(
|
| - runnable.GetCommand(shell_dir, self.extra_flags),
|
| - timeout=runnable.timeout,
|
| + command,
|
| + timeout=runnable.timeout,
|
| )
|
| except OSError as e: # pragma: no cover
|
| print title % "OSError"
|
| print e
|
| return ""
|
| +
|
| print title % "Stdout"
|
| print output.stdout
|
| if output.stderr: # pragma: no cover
|
| @@ -788,6 +806,107 @@ class AndroidPlatform(Platform): # pragma: no cover
|
| stdout = ""
|
| return stdout
|
|
|
| +class CustomMachineConfiguration:
|
| + def __init__(self, disable_aslr = False, governor = None):
|
| + self.aslr_backup = None
|
| + self.governor_backup = None
|
| + self.disable_aslr = disable_aslr
|
| + self.governor = governor
|
| +
|
| + def __enter__(self):
|
| + if self.disable_aslr:
|
| + self.aslr_backup = CustomMachineConfiguration.GetASLR()
|
| + CustomMachineConfiguration.SetASLR(0)
|
| + if self.governor != None:
|
| + self.governor_backup = CustomMachineConfiguration.GetCPUGovernor()
|
| + CustomMachineConfiguration.SetCPUGovernor(self.governor)
|
| + return self
|
| +
|
| + def __exit__(self, type, value, traceback):
|
| + if self.aslr_backup != None:
|
| + CustomMachineConfiguration.SetASLR(self.aslr_backup)
|
| + if self.governor_backup != None:
|
| + CustomMachineConfiguration.SetCPUGovernor(self.governor_backup)
|
| +
|
| + @staticmethod
|
| + def GetASLR():
|
| + try:
|
| + with open("/proc/sys/kernel/randomize_va_space", "r") as f:
|
| + return int(f.readline().strip())
|
| + except Exception as e:
|
| + print "Failed to get current ASLR settings."
|
| + raise e
|
| +
|
| + @staticmethod
|
| + def SetASLR(value):
|
| + try:
|
| + with open("/proc/sys/kernel/randomize_va_space", "w") as f:
|
| + f.write(str(value))
|
| + except Exception as e:
|
| + print "Failed to update ASLR to %s." % value
|
| + print "Are we running under sudo?"
|
| + raise e
|
| +
|
| + new_value = CustomMachineConfiguration.GetASLR()
|
| + if value != new_value:
|
| + raise Exception("Present value is %s" % new_value)
|
| +
|
| + @staticmethod
|
| + def GetCPUCoresRange():
|
| + try:
|
| + with open("/sys/devices/system/cpu/present", "r") as f:
|
| + indexes = f.readline()
|
| + first, last = map(int, indexes.split("-"))
|
| + return range(first, last + 1)
|
| + except Exception as e:
|
| + print "Failed to retrieve number of CPUs."
|
| + raise e
|
| +
|
| + @staticmethod
|
| + def GetCPUPathForId(cpu_index):
|
| + ret = "/sys/devices/system/cpu/cpu"
|
| + ret += str(cpu_index)
|
| + ret += "/cpufreq/scaling_governor"
|
| + return ret
|
| +
|
| + @staticmethod
|
| + def GetCPUGovernor():
|
| + try:
|
| + cpu_indices = CustomMachineConfiguration.GetCPUCoresRange()
|
| + ret = None
|
| + for cpu_index in cpu_indices:
|
| + cpu_device = CustomMachineConfiguration.GetCPUPathForId(cpu_index)
|
| + with open(cpu_device, "r") as f:
|
| + # We assume the governors of all CPUs are set to the same value
|
| + val = f.readline().strip()
|
| + if ret == None:
|
| + ret = val
|
| + elif ret != val:
|
| + raise Exception("CPU cores have differing governor settings")
|
| + return ret
|
| + except Exception as e:
|
| + print "Failed to get the current CPU governor."
|
| + print "Is the CPU governor disabled? Check BIOS."
|
| + raise e
|
| +
|
| + @staticmethod
|
| + def SetCPUGovernor(value):
|
| + try:
|
| + cpu_indices = CustomMachineConfiguration.GetCPUCoresRange()
|
| + for cpu_index in cpu_indices:
|
| + cpu_device = CustomMachineConfiguration.GetCPUPathForId(cpu_index)
|
| + with open(cpu_device, "w") as f:
|
| + f.write(value)
|
| +
|
| + except Exception as e:
|
| + print "Failed to change CPU governor to %s." % value
|
| + print "Are we running under sudo?"
|
| + raise e
|
| +
|
| + cur_value = CustomMachineConfiguration.GetCPUGovernor()
|
| + if cur_value != value:
|
| + raise Exception("Could not set CPU governor. Present value is %s"
|
| + % cur_value )
|
|
|
| # TODO: Implement results_processor.
|
| def Main(args):
|
| @@ -822,6 +941,27 @@ def Main(args):
|
| help="JavaScript engine binary. By default, d8 under "
|
| "architecture-specific build dir. "
|
| "Not supported in conjunction with outdir-no-patch.")
|
| + parser.add_option("--prioritize",
|
| + help="Raise the priority to nice -20 for the benchmarking "
|
| + "process.Requires Linux, schedtool, and sudo privileges.",
|
| + default=False, action="store_true")
|
| + parser.add_option("--affinitize",
|
| + help="Run benchmarking process on the specified core. "
|
| + "For example: "
|
| + "--affinitize=0 will run the benchmark process on core 0. "
|
| + "--affinitize=3 will run the benchmark process on core 3. "
|
| + "Requires Linux, schedtool, and sudo privileges.",
|
| + default=None)
|
| + parser.add_option("--noaslr",
|
| + help="Disable ASLR for the duration of the benchmarked "
|
| + "process. Requires Linux and sudo privileges.",
|
| + default=False, action="store_true")
|
| + parser.add_option("--cpu-governor",
|
| + help="Set cpu governor to specified policy for the "
|
| + "duration of the benchmarked process. Typical options: "
|
| + "'powersave' for more stable results, or 'performance' "
|
| + "for shorter completion time of suite, with potentially "
|
| + "more noise in results.")
|
|
|
| (options, args) = parser.parse_args(args)
|
|
|
| @@ -872,56 +1012,60 @@ def Main(args):
|
| else:
|
| options.shell_dir_no_patch = None
|
|
|
| + prev_aslr = None
|
| + prev_cpu_gov = None
|
| platform = Platform.GetPlatform(options)
|
|
|
| results = Results()
|
| results_no_patch = Results()
|
| - for path in args:
|
| - path = os.path.abspath(path)
|
| + with CustomMachineConfiguration(governor = options.cpu_governor,
|
| + disable_aslr = options.noaslr) as conf:
|
| + for path in args:
|
| + path = os.path.abspath(path)
|
|
|
| - if not os.path.exists(path): # pragma: no cover
|
| - results.errors.append("Configuration file %s does not exist." % path)
|
| - continue
|
| + if not os.path.exists(path): # pragma: no cover
|
| + results.errors.append("Configuration file %s does not exist." % path)
|
| + continue
|
|
|
| - with open(path) as f:
|
| - suite = json.loads(f.read())
|
| + with open(path) as f:
|
| + suite = json.loads(f.read())
|
|
|
| - # If no name is given, default to the file name without .json.
|
| - suite.setdefault("name", os.path.splitext(os.path.basename(path))[0])
|
| + # If no name is given, default to the file name without .json.
|
| + suite.setdefault("name", os.path.splitext(os.path.basename(path))[0])
|
|
|
| - # Setup things common to one test suite.
|
| - platform.PreExecution()
|
| + # Setup things common to one test suite.
|
| + platform.PreExecution()
|
|
|
| - # Build the graph/trace tree structure.
|
| - default_parent = DefaultSentinel(default_binary_name)
|
| - root = BuildGraphConfigs(suite, options.arch, default_parent)
|
| + # Build the graph/trace tree structure.
|
| + default_parent = DefaultSentinel(default_binary_name)
|
| + root = BuildGraphConfigs(suite, options.arch, default_parent)
|
|
|
| - # Callback to be called on each node on traversal.
|
| - def NodeCB(node):
|
| - platform.PreTests(node, path)
|
| + # 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)
|
| + # Traverse graph/trace tree and interate over all runnables.
|
| + for runnable in FlattenRunnables(root, NodeCB):
|
| + print ">>> Running suite: %s" % "/".join(runnable.graphs)
|
|
|
| - 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.
|
| - yield platform.Run(runnable, i)
|
| + 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.
|
| + yield platform.Run(runnable, i)
|
|
|
| - # Let runnable iterate over all runs and handle output.
|
| - result, result_no_patch = runnable.Run(
|
| + # Let runnable iterate over all runs and handle output.
|
| + result, result_no_patch = runnable.Run(
|
| Runner, trybot=options.shell_dir_no_patch)
|
| - results += result
|
| - results_no_patch += result_no_patch
|
| - platform.PostExecution()
|
| -
|
| - if options.json_test_results:
|
| - results.WriteToFile(options.json_test_results)
|
| - else: # pragma: no cover
|
| - print results
|
| + results += result
|
| + results_no_patch += result_no_patch
|
| + platform.PostExecution()
|
| +
|
| + if options.json_test_results:
|
| + results.WriteToFile(options.json_test_results)
|
| + else: # pragma: no cover
|
| + print results
|
|
|
| if options.json_test_results_no_patch:
|
| results_no_patch.WriteToFile(options.json_test_results_no_patch)
|
|
|