Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / build / android / pylib / perf / perf_control.py
index e289991..7038907 100644 (file)
@@ -2,34 +2,61 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-
+import atexit
 import logging
 
+from pylib import android_commands
+from pylib.device import device_utils
 
 class PerfControl(object):
   """Provides methods for setting the performance mode of a device."""
   _SCALING_GOVERNOR_FMT = (
       '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor')
+  _CPU_ONLINE_FMT = '/sys/devices/system/cpu/cpu%d/online'
   _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
 
-  def __init__(self, adb):
-    self._adb = adb
-    kernel_max = self._adb.GetFileContents(PerfControl._KERNEL_MAX,
-                                           log_result=False)
-    assert kernel_max, 'Unable to find %s' % PerfControl._KERNEL_MAX
-    self._kernel_max = int(kernel_max[0])
-    logging.info('Maximum CPU index: %d', self._kernel_max)
-    self._original_scaling_governor = self._adb.GetFileContents(
-        PerfControl._SCALING_GOVERNOR_FMT % 0,
-        log_result=False)[0]
+  def __init__(self, device):
+    # TODO(jbudorick) Remove once telemetry gets switched over.
+    if isinstance(device, android_commands.AndroidCommands):
+      device = device_utils.DeviceUtils(device)
+    self._device = device
+    cpu_files = self._device.RunShellCommand(
+      'ls -d /sys/devices/system/cpu/cpu[0-9]*')
+    self._num_cpu_cores = len(cpu_files)
+    assert self._num_cpu_cores > 0, 'Failed to detect CPUs.'
+    logging.info('Number of CPUs: %d', self._num_cpu_cores)
+    self._have_mpdecision = self._device.FileExists('/system/bin/mpdecision')
 
   def SetHighPerfMode(self):
     """Sets the highest possible performance mode for the device."""
+    if not self._device.old_interface.IsRootEnabled():
+      message = 'Need root for performance mode. Results may be NOISY!!'
+      logging.warning(message)
+      # Add an additional warning at exit, such that it's clear that any results
+      # may be different/noisy (due to the lack of intended performance mode).
+      atexit.register(logging.warning, message)
+      return
+    # TODO(epenner): Enable on all devices (http://crbug.com/383566)
+    if 'Nexus 4' == self._device.old_interface.GetProductModel():
+      self._ForceAllCpusOnline(True)
+      if not self._AllCpusAreOnline():
+        logging.warning('Failed to force CPUs online. Results may be NOISY!')
+    self._SetScalingGovernorInternal('performance')
+
+  def SetPerfProfilingMode(self):
+    """Enables all cores for reliable perf profiling."""
+    self._ForceAllCpusOnline(True)
     self._SetScalingGovernorInternal('performance')
+    if not self._AllCpusAreOnline():
+      if not self._device.old_interface.IsRootEnabled():
+        raise RuntimeError('Need root to force CPUs online.')
+      raise RuntimeError('Failed to force CPUs online.')
 
   def SetDefaultPerfMode(self):
     """Sets the performance mode for the device to its default mode."""
-    product_model = self._adb.GetProductModel()
+    if not self._device.old_interface.IsRootEnabled():
+      return
+    product_model = self._device.GetProp('ro.product.model')
     governor_mode = {
         'GT-I9300': 'pegasusq',
         'Galaxy Nexus': 'interactive',
@@ -38,15 +65,50 @@ class PerfControl(object):
         'Nexus 10': 'interactive'
     }.get(product_model, 'ondemand')
     self._SetScalingGovernorInternal(governor_mode)
-
-  def RestoreOriginalPerfMode(self):
-    """Resets the original performance mode of the device."""
-    self._SetScalingGovernorInternal(self._original_scaling_governor)
+    self._ForceAllCpusOnline(False)
 
   def _SetScalingGovernorInternal(self, value):
-    for cpu in range(self._kernel_max + 1):
+    for cpu in range(self._num_cpu_cores):
       scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu
-      if self._adb.FileExistsOnDevice(scaling_governor_file):
+      if self._device.FileExists(scaling_governor_file):
         logging.info('Writing scaling governor mode \'%s\' -> %s',
                      value, scaling_governor_file)
-        self._adb.SetProtectedFileContents(scaling_governor_file, value)
+        self._device.WriteFile(scaling_governor_file, value, as_root=True)
+
+  def _AllCpusAreOnline(self):
+    for cpu in range(self._num_cpu_cores):
+      online_path = PerfControl._CPU_ONLINE_FMT % cpu
+      # TODO(epenner): Investigate why file may be missing
+      # (http://crbug.com/397118)
+      if not self._device.FileExists(online_path) or \
+            self._device.ReadFile(online_path)[0] == '0':
+        return False
+    return True
+
+  def _ForceAllCpusOnline(self, force_online):
+    """Enable all CPUs on a device.
+
+    Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise
+    to measurements:
+    - In perf, samples are only taken for the CPUs that are online when the
+      measurement is started.
+    - The scaling governor can't be set for an offline CPU and frequency scaling
+      on newly enabled CPUs adds noise to both perf and tracing measurements.
+
+    It appears Qualcomm is the only vendor that hot-plugs CPUs, and on Qualcomm
+    this is done by "mpdecision".
+
+    """
+    if self._have_mpdecision:
+      script = 'stop mpdecision' if force_online else 'start mpdecision'
+      self._device.RunShellCommand(script, as_root=True)
+
+    if not self._have_mpdecision and not self._AllCpusAreOnline():
+      logging.warning('Unexpected cpu hot plugging detected.')
+
+    if not force_online:
+      return
+
+    for cpu in range(self._num_cpu_cores):
+      online_path = PerfControl._CPU_ONLINE_FMT % cpu
+      self._device.WriteFile(online_path, '1', as_root=True)