• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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