1# Copyright 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 19class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend): 20 def __init__(self): 21 super(MacPlatformBackend, self).__init__() 22 self.libproc = None 23 self.power_monitor_ = powermetrics_power_monitor.PowerMetricsPowerMonitor( 24 self) 25 26 def StartRawDisplayFrameRateMeasurement(self): 27 raise NotImplementedError() 28 29 def StopRawDisplayFrameRateMeasurement(self): 30 raise NotImplementedError() 31 32 def GetRawDisplayFrameRateMeasurements(self): 33 raise NotImplementedError() 34 35 def IsThermallyThrottled(self): 36 raise NotImplementedError() 37 38 def HasBeenThermallyThrottled(self): 39 raise NotImplementedError() 40 41 def _GetIdleWakeupCount(self, pid): 42 top_output = self._GetTopOutput(pid, ['idlew']) 43 44 # Sometimes top won't return anything here, just ignore such cases - 45 # crbug.com/354812 . 46 if top_output[-2] != 'IDLEW': 47 return 0 48 # Numbers reported by top may have a '+' appended. 49 wakeup_count = int(top_output[-1].strip('+ ')) 50 return wakeup_count 51 52 def GetCpuStats(self, pid): 53 """Return current cpu processing time of pid in seconds.""" 54 class ProcTaskInfo(ctypes.Structure): 55 """Struct for proc_pidinfo() call.""" 56 _fields_ = [("pti_virtual_size", ctypes.c_uint64), 57 ("pti_resident_size", ctypes.c_uint64), 58 ("pti_total_user", ctypes.c_uint64), 59 ("pti_total_system", ctypes.c_uint64), 60 ("pti_threads_user", ctypes.c_uint64), 61 ("pti_threads_system", ctypes.c_uint64), 62 ("pti_policy", ctypes.c_int32), 63 ("pti_faults", ctypes.c_int32), 64 ("pti_pageins", ctypes.c_int32), 65 ("pti_cow_faults", ctypes.c_int32), 66 ("pti_messages_sent", ctypes.c_int32), 67 ("pti_messages_received", ctypes.c_int32), 68 ("pti_syscalls_mach", ctypes.c_int32), 69 ("pti_syscalls_unix", ctypes.c_int32), 70 ("pti_csw", ctypes.c_int32), 71 ("pti_threadnum", ctypes.c_int32), 72 ("pti_numrunning", ctypes.c_int32), 73 ("pti_priority", ctypes.c_int32)] 74 PROC_PIDTASKINFO = 4 75 def __init__(self): 76 self.size = ctypes.sizeof(self) 77 super(ProcTaskInfo, self).__init__() 78 79 proc_info = ProcTaskInfo() 80 if not self.libproc: 81 self.libproc = ctypes.CDLL(ctypes.util.find_library('libproc')) 82 self.libproc.proc_pidinfo(pid, proc_info.PROC_PIDTASKINFO, 0, 83 ctypes.byref(proc_info), proc_info.size) 84 85 # Convert nanoseconds to seconds. 86 cpu_time = (proc_info.pti_total_user / 1000000000.0 + 87 proc_info.pti_total_system / 1000000000.0) 88 results = {'CpuProcessTime': cpu_time, 89 'ContextSwitches': proc_info.pti_csw} 90 91 # top only reports idle wakeup count starting from OS X 10.9. 92 if self.GetOSVersionName() >= platform_backend.MAVERICKS: 93 results.update({'IdleWakeupCount': self._GetIdleWakeupCount(pid)}) 94 return results 95 96 def GetCpuTimestamp(self): 97 """Return current timestamp in seconds.""" 98 return {'TotalTime': time.time()} 99 100 def GetSystemCommitCharge(self): 101 vm_stat = self._RunCommand(['vm_stat']) 102 for stat in vm_stat.splitlines(): 103 key, value = stat.split(':') 104 if key == 'Pages active': 105 pages_active = int(value.strip()[:-1]) # Strip trailing '.' 106 return pages_active * resource.getpagesize() / 1024 107 return 0 108 109 @decorators.Cache 110 def GetSystemTotalPhysicalMemory(self): 111 return int(self._RunCommand(['sysctl', '-n', 'hw.memsize'])) 112 113 def PurgeUnpinnedMemory(self): 114 # TODO(pliard): Implement this. 115 pass 116 117 def GetMemoryStats(self, pid): 118 rss_vsz = self._GetPsOutput(['rss', 'vsz'], pid) 119 if rss_vsz: 120 rss, vsz = rss_vsz[0].split() 121 return {'VM': 1024 * int(vsz), 122 'WorkingSetSize': 1024 * int(rss)} 123 return {} 124 125 def GetOSName(self): 126 return 'mac' 127 128 @decorators.Cache 129 def GetOSVersionName(self): 130 os_version = os.uname()[2] 131 132 if os_version.startswith('9.'): 133 return platform_backend.LEOPARD 134 if os_version.startswith('10.'): 135 return platform_backend.SNOWLEOPARD 136 if os_version.startswith('11.'): 137 return platform_backend.LION 138 if os_version.startswith('12.'): 139 return platform_backend.MOUNTAINLION 140 if os_version.startswith('13.'): 141 return platform_backend.MAVERICKS 142 143 raise NotImplementedError('Unknown mac version %s.' % os_version) 144 145 def CanFlushIndividualFilesFromSystemCache(self): 146 return False 147 148 def FlushEntireSystemCache(self): 149 mavericks_or_later = self.GetOSVersionName() >= platform_backend.MAVERICKS 150 p = self.LaunchApplication('purge', elevate_privilege=mavericks_or_later) 151 p.communicate() 152 assert p.returncode == 0, 'Failed to flush system cache' 153 154 def CanMonitorPower(self): 155 return self.power_monitor_.CanMonitorPower() 156 157 def StartMonitoringPower(self, browser): 158 self.power_monitor_.StartMonitoringPower(browser) 159 160 def StopMonitoringPower(self): 161 return self.power_monitor_.StopMonitoringPower() 162