blob: 168986aa25a784ed02b632465c170d7c5f6eb327 [file] [log] [blame]
# 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 200 lines.
return subprocess.check_output(
['tail', '-n', '200', '/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()