Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / platform / mac_platform_backend.py
index eb99577..002d521 100644 (file)
@@ -1,71 +1,31 @@
-# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Copyright 2013 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 import ctypes
-import logging
 import os
-import plistlib
-import signal
-import subprocess
-import tempfile
+import platform
 import time
+
+from telemetry import decorators
+from telemetry.core.platform import platform_backend
+from telemetry.core.platform import posix_platform_backend
+from telemetry.core.platform import process_statistic_timeline_data
+from telemetry.core.platform.power_monitor import powermetrics_power_monitor
+
 try:
   import resource  # pylint: disable=F0401
 except ImportError:
   resource = None  # Not available on all platforms
 
-from ctypes import util
-from telemetry.core.platform import posix_platform_backend
-
-class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend):
 
-  class PowerMetricsUtility(object):
-    def __init__(self):
-      self._powermetrics_process = None
-      self._powermetrics_output_file = None
-
-    @property
-    def binary_path(self):
-      return '/usr/bin/powermetrics'
-
-    def StartMonitoringPowerAsync(self):
-      assert not self._powermetrics_process, (
-          "Must call StopMonitoringPowerAsync().")
-      SAMPLE_INTERVAL_MS = 1000 / 20 # 20 Hz, arbitrary.
-      self._powermetrics_output_file = tempfile.NamedTemporaryFile()
-      args = [self.binary_path, '-f', 'plist', '-i',
-          '%d' % SAMPLE_INTERVAL_MS, '-u', self._powermetrics_output_file.name]
-
-      # powermetrics writes lots of output to stderr, don't echo unless verbose
-      # logging enabled.
-      stderror_destination = subprocess.PIPE
-      if logging.getLogger().isEnabledFor(logging.DEBUG):
-        stderror_destination = None
-
-      self._powermetrics_process = subprocess.Popen(args,
-          stdout=subprocess.PIPE, stderr=stderror_destination)
-
-    def StopMonitoringPowerAsync(self):
-      assert self._powermetrics_process, (
-          "StartMonitoringPowerAsync() not called.")
-      # Tell powermetrics to take an immediate sample.
-      try:
-        self._powermetrics_process.send_signal(signal.SIGINFO)
-        self._powermetrics_process.send_signal(signal.SIGTERM)
-        returncode = self._powermetrics_process.wait()
-        assert returncode in [0, -15], (
-            "powermetrics return code: %d" % returncode)
-        return open(self._powermetrics_output_file.name, 'r').read()
-      finally:
-        self._powermetrics_output_file.close()
-        self._powermetrics_output_file = None
-        self._powermetrics_process = None
 
+class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend):
   def __init__(self):
     super(MacPlatformBackend, self).__init__()
     self.libproc = None
-    self.powermetrics_tool_ = MacPlatformBackend.PowerMetricsUtility()
+    self._power_monitor = powermetrics_power_monitor.PowerMetricsPowerMonitor(
+        self)
 
   def StartRawDisplayFrameRateMeasurement(self):
     raise NotImplementedError()
@@ -82,8 +42,20 @@ class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend):
   def HasBeenThermallyThrottled(self):
     raise NotImplementedError()
 
+  def _GetIdleWakeupCount(self, pid):
+    top_output = self._GetTopOutput(pid, ['idlew'])
+
+    # Sometimes top won't return anything here, just ignore such cases -
+    # crbug.com/354812 .
+    if top_output[-2] != 'IDLEW':
+      return process_statistic_timeline_data.IdleWakeupTimelineData(pid, 0)
+    # Numbers reported by top may have a '+' appended.
+    wakeup_count = int(top_output[-1].strip('+ '))
+    return process_statistic_timeline_data.IdleWakeupTimelineData(pid,
+        wakeup_count)
+
   def GetCpuStats(self, pid):
-    """Return current cpu processing time of pid in seconds."""
+    """Returns a dict of cpu statistics for the process represented by |pid|."""
     class ProcTaskInfo(ctypes.Structure):
       """Struct for proc_pidinfo() call."""
       _fields_ = [("pti_virtual_size", ctypes.c_uint64),
@@ -111,21 +83,27 @@ class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend):
 
     proc_info = ProcTaskInfo()
     if not self.libproc:
-      self.libproc = ctypes.CDLL(util.find_library('libproc'))
+      self.libproc = ctypes.CDLL(ctypes.util.find_library('libproc'))
     self.libproc.proc_pidinfo(pid, proc_info.PROC_PIDTASKINFO, 0,
                               ctypes.byref(proc_info), proc_info.size)
 
-    # Convert nanoseconds to seconds
+    # Convert nanoseconds to seconds.
     cpu_time = (proc_info.pti_total_user / 1000000000.0 +
                 proc_info.pti_total_system / 1000000000.0)
-    return {'CpuProcessTime': cpu_time}
+    results = {'CpuProcessTime': cpu_time,
+               'ContextSwitches': proc_info.pti_csw}
+
+    # top only reports idle wakeup count starting from OS X 10.9.
+    if self.GetOSVersionName() >= platform_backend.MAVERICKS:
+      results.update({'IdleWakeupCount': self._GetIdleWakeupCount(pid)})
+    return results
 
   def GetCpuTimestamp(self):
     """Return current timestamp in seconds."""
     return {'TotalTime': time.time()}
 
   def GetSystemCommitCharge(self):
-    vm_stat = self._RunCommand(['vm_stat'])
+    vm_stat = self.RunCommand(['vm_stat'])
     for stat in vm_stat.splitlines():
       key, value = stat.split(':')
       if key == 'Pages active':
@@ -133,94 +111,65 @@ class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend):
         return pages_active * resource.getpagesize() / 1024
     return 0
 
+  @decorators.Cache
+  def GetSystemTotalPhysicalMemory(self):
+    return int(self.RunCommand(['sysctl', '-n', 'hw.memsize']))
+
   def PurgeUnpinnedMemory(self):
     # TODO(pliard): Implement this.
     pass
 
   def GetMemoryStats(self, pid):
-    rss_vsz = self._GetPsOutput(['rss', 'vsz'], pid)
+    rss_vsz = self.GetPsOutput(['rss', 'vsz'], pid)
     if rss_vsz:
       rss, vsz = rss_vsz[0].split()
       return {'VM': 1024 * int(vsz),
               'WorkingSetSize': 1024 * int(rss)}
     return {}
 
+  @decorators.Cache
+  def GetArchName(self):
+    return platform.machine()
+
   def GetOSName(self):
     return 'mac'
 
+  @decorators.Cache
   def GetOSVersionName(self):
     os_version = os.uname()[2]
 
     if os_version.startswith('9.'):
-      return 'leopard'
+      return platform_backend.LEOPARD
     if os_version.startswith('10.'):
-      return 'snowleopard'
+      return platform_backend.SNOWLEOPARD
     if os_version.startswith('11.'):
-      return 'lion'
+      return platform_backend.LION
     if os_version.startswith('12.'):
-      return 'mountainlion'
+      return platform_backend.MOUNTAINLION
     if os_version.startswith('13.'):
-      return 'mavericks'
+      return platform_backend.MAVERICKS
+    if os_version.startswith('14.'):
+      return platform_backend.YOSEMITE
 
-    raise NotImplementedError("Unknown OS X version %s." % os_version)
+    raise NotImplementedError('Unknown mac version %s.' % os_version)
 
   def CanFlushIndividualFilesFromSystemCache(self):
     return False
 
   def FlushEntireSystemCache(self):
-    p = subprocess.Popen(['purge'])
-    p.wait()
+    mavericks_or_later = self.GetOSVersionName() >= platform_backend.MAVERICKS
+    p = self.LaunchApplication('purge', elevate_privilege=mavericks_or_later)
+    p.communicate()
     assert p.returncode == 0, 'Failed to flush system cache'
 
-  def CanMonitorPowerAsync(self):
-    # powermetrics only runs on OS X version >= 10.9 .
-    os_version = int(os.uname()[2].split('.')[0])
-    binary_path = self.powermetrics_tool_.binary_path
-    return os_version >= 13 and self.CanLaunchApplication(binary_path)
-
-  def SetPowerMetricsUtilityForTest(self, obj):
-    self.powermetrics_tool_ = obj
-
-  def StartMonitoringPowerAsync(self):
-    self.powermetrics_tool_.StartMonitoringPowerAsync()
-
-  def _ParsePowerMetricsOutput(self, powermetrics_output):
-    """Parse output of powermetrics command line utility.
-
-    Returns:
-        Dictionary in the format returned by StopMonitoringPowerAsync().
-    """
-    power_samples = []
-    total_energy_consumption_mwh = 0
-    # powermetrics outputs multiple PLists separated by null terminators.
-    raw_plists = powermetrics_output.split('\0')[:-1]
-    for raw_plist in raw_plists:
-      plist = plistlib.readPlistFromString(raw_plist)
-
-      # Duration of this sample.
-      sample_duration_ms = int(plist['elapsed_ns']) / 10**6
-
-      if 'processor' not in plist:
-        continue
-      processor = plist['processor']
-
-      energy_consumption_mw = int(processor.get('package_watts', 0)) * 10**3
-
-      total_energy_consumption_mwh += (energy_consumption_mw *
-          (sample_duration_ms / 3600000.))
-
-      power_samples.append(energy_consumption_mw)
+  def CanMonitorPower(self):
+    return self._power_monitor.CanMonitorPower()
 
-    # -------- Collect and Process Data -------------
-    out_dict = {}
-    # Raw power usage samples.
-    if power_samples:
-      out_dict['power_samples_mw'] = power_samples
-      out_dict['energy_consumption_mwh'] = total_energy_consumption_mwh
+  def CanMeasurePerApplicationPower(self):
+    return self._power_monitor.CanMeasurePerApplicationPower()
 
-    return out_dict
+  def StartMonitoringPower(self, browser):
+    self._power_monitor.StartMonitoringPower(browser)
 
-  def StopMonitoringPowerAsync(self):
-    powermetrics_output = self.powermetrics_tool_.StopMonitoringPowerAsync()
-    assert len(powermetrics_output) > 0
-    return self._ParsePowerMetricsOutput(powermetrics_output)
+  def StopMonitoringPower(self):
+    return self._power_monitor.StopMonitoringPower()