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 platform 8import subprocess 9import sys 10import time 11 12from telemetry.core import os_version as os_version_module 13from telemetry import decorators 14from telemetry.internal.platform import posix_platform_backend 15from telemetry.internal.platform.power_monitor import powermetrics_power_monitor 16from telemetry.util import process_statistic_timeline_data 17 18try: 19 import resource # pylint: disable=import-error 20except ImportError: 21 resource = None # Not available on all platforms 22 23 24 25class MacPlatformBackend(posix_platform_backend.PosixPlatformBackend): 26 def __init__(self): 27 super(MacPlatformBackend, self).__init__() 28 self.libproc = None 29 self._power_monitor = powermetrics_power_monitor.PowerMetricsPowerMonitor( 30 self) 31 32 def GetSystemLog(self): 33 # Since the log file can be very large, only show the last 200 lines. 34 return subprocess.check_output( 35 ['tail', '-n', '200', '/var/log/system.log']) 36 37 @classmethod 38 def IsPlatformBackendForHost(cls): 39 return sys.platform == 'darwin' 40 41 def IsThermallyThrottled(self): 42 raise NotImplementedError() 43 44 def HasBeenThermallyThrottled(self): 45 raise NotImplementedError() 46 47 def _GetIdleWakeupCount(self, pid): 48 top_output = self._GetTopOutput(pid, ['idlew']) 49 50 # Sometimes top won't return anything here, just ignore such cases - 51 # crbug.com/354812 . 52 if top_output[-2] != 'IDLEW': 53 return process_statistic_timeline_data.IdleWakeupTimelineData(pid, 0) 54 # Numbers reported by top may have a '+' appended. 55 wakeup_count = int(top_output[-1].strip('+ ')) 56 return process_statistic_timeline_data.IdleWakeupTimelineData(pid, 57 wakeup_count) 58 59 def GetCpuStats(self, pid): 60 """Returns a dict of cpu statistics for the process represented by |pid|.""" 61 class ProcTaskInfo(ctypes.Structure): 62 """Struct for proc_pidinfo() call.""" 63 _fields_ = [("pti_virtual_size", ctypes.c_uint64), 64 ("pti_resident_size", ctypes.c_uint64), 65 ("pti_total_user", ctypes.c_uint64), 66 ("pti_total_system", ctypes.c_uint64), 67 ("pti_threads_user", ctypes.c_uint64), 68 ("pti_threads_system", ctypes.c_uint64), 69 ("pti_policy", ctypes.c_int32), 70 ("pti_faults", ctypes.c_int32), 71 ("pti_pageins", ctypes.c_int32), 72 ("pti_cow_faults", ctypes.c_int32), 73 ("pti_messages_sent", ctypes.c_int32), 74 ("pti_messages_received", ctypes.c_int32), 75 ("pti_syscalls_mach", ctypes.c_int32), 76 ("pti_syscalls_unix", ctypes.c_int32), 77 ("pti_csw", ctypes.c_int32), 78 ("pti_threadnum", ctypes.c_int32), 79 ("pti_numrunning", ctypes.c_int32), 80 ("pti_priority", ctypes.c_int32)] 81 PROC_PIDTASKINFO = 4 82 def __init__(self): 83 self.size = ctypes.sizeof(self) 84 super(ProcTaskInfo, self).__init__() # pylint: disable=bad-super-call 85 86 proc_info = ProcTaskInfo() 87 if not self.libproc: 88 self.libproc = ctypes.CDLL(ctypes.util.find_library('libproc')) 89 self.libproc.proc_pidinfo(pid, proc_info.PROC_PIDTASKINFO, 0, 90 ctypes.byref(proc_info), proc_info.size) 91 92 # Convert nanoseconds to seconds. 93 cpu_time = (proc_info.pti_total_user / 1000000000.0 + 94 proc_info.pti_total_system / 1000000000.0) 95 results = {'CpuProcessTime': cpu_time, 96 'ContextSwitches': proc_info.pti_csw} 97 98 # top only reports idle wakeup count starting from OS X 10.9. 99 if self.GetOSVersionName() >= os_version_module.MAVERICKS: 100 results.update({'IdleWakeupCount': self._GetIdleWakeupCount(pid)}) 101 return results 102 103 def GetCpuTimestamp(self): 104 """Return current timestamp in seconds.""" 105 return {'TotalTime': time.time()} 106 107 def GetSystemCommitCharge(self): 108 vm_stat = self.RunCommand(['vm_stat']) 109 for stat in vm_stat.splitlines(): 110 key, value = stat.split(':') 111 if key == 'Pages active': 112 pages_active = int(value.strip()[:-1]) # Strip trailing '.' 113 return pages_active * resource.getpagesize() / 1024 114 return 0 115 116 @decorators.Cache 117 def GetSystemTotalPhysicalMemory(self): 118 return int(self.RunCommand(['sysctl', '-n', 'hw.memsize'])) 119 120 def PurgeUnpinnedMemory(self): 121 # TODO(pliard): Implement this. 122 pass 123 124 @decorators.Deprecated( 125 2017, 11, 4, 126 'Clients should use tracing and memory-infra in new Telemetry ' 127 'benchmarks. See for context: https://crbug.com/632021') 128 def GetMemoryStats(self, pid): 129 rss_vsz = self.GetPsOutput(['rss', 'vsz'], pid) 130 if rss_vsz: 131 rss, vsz = rss_vsz[0].split() 132 return {'VM': 1024 * int(vsz), 133 'WorkingSetSize': 1024 * int(rss)} 134 return {} 135 136 @decorators.Cache 137 def GetArchName(self): 138 return platform.machine() 139 140 def GetOSName(self): 141 return 'mac' 142 143 @decorators.Cache 144 def GetOSVersionName(self): 145 os_version = os.uname()[2] 146 147 if os_version.startswith('9.'): 148 return os_version_module.LEOPARD 149 if os_version.startswith('10.'): 150 return os_version_module.SNOWLEOPARD 151 if os_version.startswith('11.'): 152 return os_version_module.LION 153 if os_version.startswith('12.'): 154 return os_version_module.MOUNTAINLION 155 if os_version.startswith('13.'): 156 return os_version_module.MAVERICKS 157 if os_version.startswith('14.'): 158 return os_version_module.YOSEMITE 159 if os_version.startswith('15.'): 160 return os_version_module.ELCAPITAN 161 if os_version.startswith('16.'): 162 return os_version_module.SIERRA 163 164 raise NotImplementedError('Unknown mac version %s.' % os_version) 165 166 def CanTakeScreenshot(self): 167 return True 168 169 def TakeScreenshot(self, file_path): 170 return subprocess.call(['screencapture', file_path]) 171 172 def CanFlushIndividualFilesFromSystemCache(self): 173 return False 174 175 def SupportFlushEntireSystemCache(self): 176 return self.HasRootAccess() 177 178 def FlushEntireSystemCache(self): 179 mavericks_or_later = self.GetOSVersionName() >= os_version_module.MAVERICKS 180 p = self.LaunchApplication('purge', elevate_privilege=mavericks_or_later) 181 p.communicate() 182 assert p.returncode == 0, 'Failed to flush system cache' 183 184 def CanMonitorPower(self): 185 return self._power_monitor.CanMonitorPower() 186 187 def CanMeasurePerApplicationPower(self): 188 return self._power_monitor.CanMeasurePerApplicationPower() 189 190 def StartMonitoringPower(self, browser): 191 self._power_monitor.StartMonitoringPower(browser) 192 193 def StopMonitoringPower(self): 194 return self._power_monitor.StopMonitoringPower() 195