mac_platform_backend.py revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
1# Copyright (c) 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import ctypes
6import os
7import time
8try:
9  import resource  # pylint: disable=F0401
10except ImportError:
11  resource = None  # Not available on all platforms
12
13from telemetry import decorators
14from telemetry.core.platform import platform_backend
15from telemetry.core.platform import posix_platform_backend
16from telemetry.core.platform.power_monitor import powermetrics_power_monitor
17
18
19LEOPARD =      platform_backend.OSVersion('leopard',      10.5)
20SNOWLEOPARD =  platform_backend.OSVersion('snowleopard',  10.6)
21LION =         platform_backend.OSVersion('lion',         10.7)
22MOUNTAINLION = platform_backend.OSVersion('mountainlion', 10.8)
23MAVERICKS =    platform_backend.OSVersion('mavericks',    10.9)
24
25
26class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend):
27  def __init__(self):
28    super(MacPlatformBackend, self).__init__()
29    self.libproc = None
30    self.power_monitor_ = powermetrics_power_monitor.PowerMetricsPowerMonitor(
31        self)
32
33  def StartRawDisplayFrameRateMeasurement(self):
34    raise NotImplementedError()
35
36  def StopRawDisplayFrameRateMeasurement(self):
37    raise NotImplementedError()
38
39  def GetRawDisplayFrameRateMeasurements(self):
40    raise NotImplementedError()
41
42  def IsThermallyThrottled(self):
43    raise NotImplementedError()
44
45  def HasBeenThermallyThrottled(self):
46    raise NotImplementedError()
47
48  def _GetIdleWakeupCount(self, pid):
49    top_output = self._GetTopOutput(pid, ['idlew'])
50    assert top_output[-2] == 'IDLEW'
51    # Numbers reported by top may have a '+' appended.
52    wakeup_count = int(top_output[-1].strip('+ '))
53    return wakeup_count
54
55  def GetCpuStats(self, pid):
56    """Return current cpu processing time of pid in seconds."""
57    class ProcTaskInfo(ctypes.Structure):
58      """Struct for proc_pidinfo() call."""
59      _fields_ = [("pti_virtual_size", ctypes.c_uint64),
60                  ("pti_resident_size", ctypes.c_uint64),
61                  ("pti_total_user", ctypes.c_uint64),
62                  ("pti_total_system", ctypes.c_uint64),
63                  ("pti_threads_user", ctypes.c_uint64),
64                  ("pti_threads_system", ctypes.c_uint64),
65                  ("pti_policy", ctypes.c_int32),
66                  ("pti_faults", ctypes.c_int32),
67                  ("pti_pageins", ctypes.c_int32),
68                  ("pti_cow_faults", ctypes.c_int32),
69                  ("pti_messages_sent", ctypes.c_int32),
70                  ("pti_messages_received", ctypes.c_int32),
71                  ("pti_syscalls_mach", ctypes.c_int32),
72                  ("pti_syscalls_unix", ctypes.c_int32),
73                  ("pti_csw", ctypes.c_int32),
74                  ("pti_threadnum", ctypes.c_int32),
75                  ("pti_numrunning", ctypes.c_int32),
76                  ("pti_priority", ctypes.c_int32)]
77      PROC_PIDTASKINFO = 4
78      def __init__(self):
79        self.size = ctypes.sizeof(self)
80        super(ProcTaskInfo, self).__init__()
81
82    proc_info = ProcTaskInfo()
83    if not self.libproc:
84      self.libproc = ctypes.CDLL(ctypes.util.find_library('libproc'))
85    self.libproc.proc_pidinfo(pid, proc_info.PROC_PIDTASKINFO, 0,
86                              ctypes.byref(proc_info), proc_info.size)
87
88    # Convert nanoseconds to seconds.
89    cpu_time = (proc_info.pti_total_user / 1000000000.0 +
90                proc_info.pti_total_system / 1000000000.0)
91    results = {'CpuProcessTime': cpu_time,
92               'ContextSwitches': proc_info.pti_csw}
93
94    # top only reports idle wakeup count starting from OS X 10.9.
95    if self.GetOSVersionName() >= MAVERICKS:
96      results.update({'IdleWakeupCount': self._GetIdleWakeupCount(pid)})
97    return results
98
99  def GetCpuTimestamp(self):
100    """Return current timestamp in seconds."""
101    return {'TotalTime': time.time()}
102
103  def GetSystemCommitCharge(self):
104    vm_stat = self._RunCommand(['vm_stat'])
105    for stat in vm_stat.splitlines():
106      key, value = stat.split(':')
107      if key == 'Pages active':
108        pages_active = int(value.strip()[:-1])  # Strip trailing '.'
109        return pages_active * resource.getpagesize() / 1024
110    return 0
111
112  @decorators.Cache
113  def GetSystemTotalPhysicalMemory(self):
114    return int(self._RunCommand(['sysctl', '-n', 'hw.memsize']))
115
116  def PurgeUnpinnedMemory(self):
117    # TODO(pliard): Implement this.
118    pass
119
120  def GetMemoryStats(self, pid):
121    rss_vsz = self._GetPsOutput(['rss', 'vsz'], pid)
122    if rss_vsz:
123      rss, vsz = rss_vsz[0].split()
124      return {'VM': 1024 * int(vsz),
125              'WorkingSetSize': 1024 * int(rss)}
126    return {}
127
128  def GetOSName(self):
129    return 'mac'
130
131  @decorators.Cache
132  def GetOSVersionName(self):
133    os_version = os.uname()[2]
134
135    if os_version.startswith('9.'):
136      return LEOPARD
137    if os_version.startswith('10.'):
138      return SNOWLEOPARD
139    if os_version.startswith('11.'):
140      return LION
141    if os_version.startswith('12.'):
142      return MOUNTAINLION
143    if os_version.startswith('13.'):
144      return MAVERICKS
145
146    raise NotImplementedError('Unknown mac version %s.' % os_version)
147
148  def CanFlushIndividualFilesFromSystemCache(self):
149    return False
150
151  def FlushEntireSystemCache(self):
152    mavericks_or_later = self.GetOSVersionName() >= MAVERICKS
153    p = self.LaunchApplication('purge', elevate_privilege=mavericks_or_later)
154    p.wait()
155    assert p.returncode == 0, 'Failed to flush system cache'
156
157  def CanMonitorPowerAsync(self):
158    return self.power_monitor_.CanMonitorPowerAsync()
159
160  def StartMonitoringPowerAsync(self):
161    self.power_monitor_.StartMonitoringPowerAsync()
162
163  def StopMonitoringPowerAsync(self):
164    return self.power_monitor_.StopMonitoringPowerAsync()
165