aboutsummaryrefslogtreecommitdiff
path: root/catapult/telemetry/telemetry/internal/platform/mac_platform_backend.py
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/telemetry/telemetry/internal/platform/mac_platform_backend.py')
-rw-r--r--catapult/telemetry/telemetry/internal/platform/mac_platform_backend.py194
1 files changed, 194 insertions, 0 deletions
diff --git a/catapult/telemetry/telemetry/internal/platform/mac_platform_backend.py b/catapult/telemetry/telemetry/internal/platform/mac_platform_backend.py
new file mode 100644
index 00000000..c271825b
--- /dev/null
+++ b/catapult/telemetry/telemetry/internal/platform/mac_platform_backend.py
@@ -0,0 +1,194 @@
+# 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 os
+import platform
+import subprocess
+import sys
+import time
+
+from telemetry.core import os_version as os_version_module
+from telemetry import decorators
+from telemetry.internal.platform import posix_platform_backend
+from telemetry.internal.platform.power_monitor import powermetrics_power_monitor
+from telemetry.util import process_statistic_timeline_data
+
+try:
+ import resource # pylint: disable=import-error
+except ImportError:
+ resource = None # Not available on all platforms
+
+
+
+class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend):
+ def __init__(self):
+ super(MacPlatformBackend, self).__init__()
+ self.libproc = None
+ self._power_monitor = powermetrics_power_monitor.PowerMetricsPowerMonitor(
+ self)
+
+ def GetSystemLog(self):
+ # Since the log file can be very large, only show the last 1000 lines.
+ return subprocess.check_output(
+ ['tail', '-n', '1000', '/var/log/system.log'])
+
+ @classmethod
+ def IsPlatformBackendForHost(cls):
+ return sys.platform == 'darwin'
+
+ def IsThermallyThrottled(self):
+ raise NotImplementedError()
+
+ 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):
+ """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),
+ ("pti_resident_size", ctypes.c_uint64),
+ ("pti_total_user", ctypes.c_uint64),
+ ("pti_total_system", ctypes.c_uint64),
+ ("pti_threads_user", ctypes.c_uint64),
+ ("pti_threads_system", ctypes.c_uint64),
+ ("pti_policy", ctypes.c_int32),
+ ("pti_faults", ctypes.c_int32),
+ ("pti_pageins", ctypes.c_int32),
+ ("pti_cow_faults", ctypes.c_int32),
+ ("pti_messages_sent", ctypes.c_int32),
+ ("pti_messages_received", ctypes.c_int32),
+ ("pti_syscalls_mach", ctypes.c_int32),
+ ("pti_syscalls_unix", ctypes.c_int32),
+ ("pti_csw", ctypes.c_int32),
+ ("pti_threadnum", ctypes.c_int32),
+ ("pti_numrunning", ctypes.c_int32),
+ ("pti_priority", ctypes.c_int32)]
+ PROC_PIDTASKINFO = 4
+ def __init__(self):
+ self.size = ctypes.sizeof(self)
+ super(ProcTaskInfo, self).__init__() # pylint: disable=bad-super-call
+
+ proc_info = ProcTaskInfo()
+ if not self.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.
+ cpu_time = (proc_info.pti_total_user / 1000000000.0 +
+ proc_info.pti_total_system / 1000000000.0)
+ results = {'CpuProcessTime': cpu_time,
+ 'ContextSwitches': proc_info.pti_csw}
+
+ # top only reports idle wakeup count starting from OS X 10.9.
+ if self.GetOSVersionName() >= os_version_module.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'])
+ for stat in vm_stat.splitlines():
+ key, value = stat.split(':')
+ if key == 'Pages active':
+ pages_active = int(value.strip()[:-1]) # Strip trailing '.'
+ 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
+
+ @decorators.Deprecated(
+ 2017, 11, 4,
+ 'Clients should use tracing and memory-infra in new Telemetry '
+ 'benchmarks. See for context: https://crbug.com/632021')
+ def GetMemoryStats(self, 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 os_version_module.LEOPARD
+ if os_version.startswith('10.'):
+ return os_version_module.SNOWLEOPARD
+ if os_version.startswith('11.'):
+ return os_version_module.LION
+ if os_version.startswith('12.'):
+ return os_version_module.MOUNTAINLION
+ if os_version.startswith('13.'):
+ return os_version_module.MAVERICKS
+ if os_version.startswith('14.'):
+ return os_version_module.YOSEMITE
+ if os_version.startswith('15.'):
+ return os_version_module.ELCAPITAN
+ if os_version.startswith('16.'):
+ return os_version_module.SIERRA
+
+ raise NotImplementedError('Unknown mac version %s.' % os_version)
+
+ def CanTakeScreenshot(self):
+ return True
+
+ def TakeScreenshot(self, file_path):
+ return subprocess.call(['screencapture', file_path])
+
+ def CanFlushIndividualFilesFromSystemCache(self):
+ return False
+
+ def SupportFlushEntireSystemCache(self):
+ return self.HasRootAccess()
+
+ def FlushEntireSystemCache(self):
+ mavericks_or_later = self.GetOSVersionName() >= os_version_module.MAVERICKS
+ p = self.LaunchApplication('purge', elevate_privilege=mavericks_or_later)
+ p.communicate()
+ assert p.returncode == 0, 'Failed to flush system cache'
+
+ def CanMonitorPower(self):
+ return self._power_monitor.CanMonitorPower()
+
+ def CanMeasurePerApplicationPower(self):
+ return self._power_monitor.CanMeasurePerApplicationPower()
+
+ def StartMonitoringPower(self, browser):
+ self._power_monitor.StartMonitoringPower(browser)
+
+ def StopMonitoringPower(self):
+ return self._power_monitor.StopMonitoringPower()