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