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.
19 from telemetry import decorators
20 from telemetry.core import exceptions
21 from telemetry.core import util
22 from telemetry.core.platform import desktop_platform_backend
23 from telemetry.core.platform import platform_backend
24 from telemetry.core.platform.power_monitor import msr_power_monitor
25 from telemetry.util import cloud_storage
26 from telemetry.util import path
29 import pywintypes # pylint: disable=F0401
30 import win32api # pylint: disable=F0401
31 from win32com.shell import shell # pylint: disable=F0401
32 from win32com.shell import shellcon # pylint: disable=F0401
33 import win32con # pylint: disable=F0401
34 import win32process # pylint: disable=F0401
35 import win32security # pylint: disable=F0401
46 def _InstallWinRing0():
47 """WinRing0 is used for reading MSRs."""
48 executable_dir = os.path.dirname(sys.executable)
50 python_is_64_bit = sys.maxsize > 2 ** 32
51 dll_file_name = 'WinRing0x64.dll' if python_is_64_bit else 'WinRing0.dll'
52 dll_path = os.path.join(executable_dir, dll_file_name)
54 os_is_64_bit = platform.machine().endswith('64')
55 driver_file_name = 'WinRing0x64.sys' if os_is_64_bit else 'WinRing0.sys'
56 driver_path = os.path.join(executable_dir, driver_file_name)
58 # Check for WinRing0 and download if needed.
59 if not (os.path.exists(dll_path) and os.path.exists(driver_path)):
60 win_binary_dir = os.path.join(path.GetTelemetryDir(), 'bin', 'win')
61 zip_path = os.path.join(win_binary_dir, 'winring0.zip')
62 cloud_storage.GetIfChanged(zip_path, bucket=cloud_storage.PUBLIC_BUCKET)
64 with zipfile.ZipFile(zip_path, 'r') as zip_file:
66 if not os.path.exists(dll_path):
67 zip_file.extract(dll_file_name, executable_dir)
68 # Install kernel driver.
69 if not os.path.exists(driver_path):
70 zip_file.extract(driver_file_name, executable_dir)
75 def TerminateProcess(process_handle):
76 if not process_handle:
78 if win32process.GetExitCodeProcess(process_handle) == win32con.STILL_ACTIVE:
79 win32process.TerminateProcess(process_handle, 0)
80 process_handle.close()
83 class WinPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
85 super(WinPlatformBackend, self).__init__()
86 self._msr_server_handle = None
87 self._msr_server_port = None
88 self._power_monitor = msr_power_monitor.MsrPowerMonitor(self)
96 def CloseMsrServer(self):
97 if not self._msr_server_handle:
100 TerminateProcess(self._msr_server_handle)
101 self._msr_server_handle = None
102 self._msr_server_port = None
104 # pylint: disable=W0613
105 def StartRawDisplayFrameRateMeasurement(self):
106 raise NotImplementedError()
108 def StopRawDisplayFrameRateMeasurement(self):
109 raise NotImplementedError()
111 def GetRawDisplayFrameRateMeasurements(self):
112 raise NotImplementedError()
114 def IsThermallyThrottled(self):
115 raise NotImplementedError()
117 def HasBeenThermallyThrottled(self):
118 raise NotImplementedError()
120 def GetSystemCommitCharge(self):
121 performance_info = self._GetPerformanceInfo()
122 return performance_info.CommitTotal * performance_info.PageSize / 1024
125 def GetSystemTotalPhysicalMemory(self):
126 performance_info = self._GetPerformanceInfo()
127 return performance_info.PhysicalTotal * performance_info.PageSize / 1024
129 def GetCpuStats(self, pid):
130 cpu_info = self._GetWin32ProcessInfo(win32process.GetProcessTimes, pid)
131 # Convert 100 nanosecond units to seconds
132 cpu_time = (cpu_info['UserTime'] / 1e7 +
133 cpu_info['KernelTime'] / 1e7)
134 return {'CpuProcessTime': cpu_time}
136 def GetCpuTimestamp(self):
137 """Return current timestamp in seconds."""
138 return {'TotalTime': time.time()}
140 def GetMemoryStats(self, pid):
141 memory_info = self._GetWin32ProcessInfo(
142 win32process.GetProcessMemoryInfo, pid)
143 return {'VM': memory_info['PagefileUsage'],
144 'VMPeak': memory_info['PeakPagefileUsage'],
145 'WorkingSetSize': memory_info['WorkingSetSize'],
146 'WorkingSetSizePeak': memory_info['PeakWorkingSetSize']}
148 def GetIOStats(self, pid):
149 io_stats = self._GetWin32ProcessInfo(win32process.GetProcessIoCounters, pid)
150 return {'ReadOperationCount': io_stats['ReadOperationCount'],
151 'WriteOperationCount': io_stats['WriteOperationCount'],
152 'ReadTransferCount': io_stats['ReadTransferCount'],
153 'WriteTransferCount': io_stats['WriteTransferCount']}
155 def KillProcess(self, pid, kill_process_tree=False):
156 # os.kill for Windows is Python 2.7.
157 cmd = ['taskkill', '/F', '/PID', str(pid)]
158 if kill_process_tree:
160 subprocess.Popen(cmd, stdout=subprocess.PIPE,
161 stderr=subprocess.STDOUT).communicate()
163 def GetSystemProcessInfo(self):
164 # [3:] To skip 2 blank lines and header.
165 lines = subprocess.Popen(
166 ['wmic', 'process', 'get',
167 'CommandLine,CreationDate,Name,ParentProcessId,ProcessId',
169 stdout=subprocess.PIPE).communicate()[0].splitlines()[3:]
174 parts = line.split(',')
176 pi['ProcessId'] = int(parts[-1])
177 pi['ParentProcessId'] = int(parts[-2])
178 pi['Name'] = parts[-3]
181 creation_date = float(re.split('[+-]', parts[-4])[0])
182 pi['CreationDate'] = creation_date
183 pi['CommandLine'] = ','.join(parts[1:-4])
184 process_info.append(pi)
187 def GetChildPids(self, pid):
188 """Retunds a list of child pids of |pid|."""
189 ppid_map = collections.defaultdict(list)
191 for pi in self.GetSystemProcessInfo():
192 ppid_map[pi['ParentProcessId']].append(pi['ProcessId'])
193 if pi['CreationDate']:
194 creation_map[pi['ProcessId']] = pi['CreationDate']
196 def _InnerGetChildPids(pid):
197 if not pid or pid not in ppid_map:
199 ret = [p for p in ppid_map[pid] if creation_map[p] >= creation_map[pid]]
203 ret.extend(_InnerGetChildPids(child))
206 return _InnerGetChildPids(pid)
208 def GetCommandLine(self, pid):
209 for pi in self.GetSystemProcessInfo():
210 if pid == pi['ProcessId']:
211 return pi['CommandLine']
212 raise exceptions.ProcessGoneException()
215 def GetArchName(self):
216 return platform.machine()
222 def GetOSVersionName(self):
223 os_version = platform.uname()[3]
225 if os_version.startswith('5.1.'):
226 return platform_backend.XP
227 if os_version.startswith('6.0.'):
228 return platform_backend.VISTA
229 if os_version.startswith('6.1.'):
230 return platform_backend.WIN7
231 if os_version.startswith('6.2.'):
232 return platform_backend.WIN8
234 raise NotImplementedError('Unknown win version %s.' % os_version)
236 def CanFlushIndividualFilesFromSystemCache(self):
239 def _GetWin32ProcessInfo(self, func, pid):
240 mask = (win32con.PROCESS_QUERY_INFORMATION |
241 win32con.PROCESS_VM_READ)
244 handle = win32api.OpenProcess(mask, False, pid)
246 except pywintypes.error, e:
249 raise exceptions.ProcessGoneException()
253 win32api.CloseHandle(handle)
255 def _GetPerformanceInfo(self):
256 class PerformanceInfo(ctypes.Structure):
257 """Struct for GetPerformanceInfo() call
258 http://msdn.microsoft.com/en-us/library/ms683210
260 _fields_ = [('size', ctypes.c_ulong),
261 ('CommitTotal', ctypes.c_size_t),
262 ('CommitLimit', ctypes.c_size_t),
263 ('CommitPeak', ctypes.c_size_t),
264 ('PhysicalTotal', ctypes.c_size_t),
265 ('PhysicalAvailable', ctypes.c_size_t),
266 ('SystemCache', ctypes.c_size_t),
267 ('KernelTotal', ctypes.c_size_t),
268 ('KernelPaged', ctypes.c_size_t),
269 ('KernelNonpaged', ctypes.c_size_t),
270 ('PageSize', ctypes.c_size_t),
271 ('HandleCount', ctypes.c_ulong),
272 ('ProcessCount', ctypes.c_ulong),
273 ('ThreadCount', ctypes.c_ulong)]
276 self.size = ctypes.sizeof(self)
277 super(PerformanceInfo, self).__init__()
279 performance_info = PerformanceInfo()
280 ctypes.windll.psapi.GetPerformanceInfo(
281 ctypes.byref(performance_info), performance_info.size)
282 return performance_info
284 def IsCurrentProcessElevated(self):
285 if self.GetOSVersionName() < platform_backend.VISTA:
286 # TOKEN_QUERY is not defined before Vista. All processes are elevated.
289 handle = win32process.GetCurrentProcess()
290 with contextlib.closing(
291 win32security.OpenProcessToken(handle, win32con.TOKEN_QUERY)) as token:
292 return bool(win32security.GetTokenInformation(
293 token, win32security.TokenElevation))
295 def LaunchApplication(
296 self, application, parameters=None, elevate_privilege=False):
297 """Launch an application. Returns a PyHANDLE object."""
299 parameters = ' '.join(parameters) if parameters else ''
300 if elevate_privilege and not self.IsCurrentProcessElevated():
301 # Use ShellExecuteEx() instead of subprocess.Popen()/CreateProcess() to
302 # elevate privileges. A new console will be created if the new process has
303 # different permissions than this process.
304 proc_info = shell.ShellExecuteEx(
305 fMask=shellcon.SEE_MASK_NOCLOSEPROCESS | shellcon.SEE_MASK_NO_CONSOLE,
306 lpVerb='runas' if elevate_privilege else '',
308 lpParameters=parameters,
309 nShow=win32con.SW_HIDE)
310 if proc_info['hInstApp'] <= 32:
311 raise Exception('Unable to launch %s' % application)
312 return proc_info['hProcess']
314 handle, _, _, _ = win32process.CreateProcess(
315 None, application + ' ' + parameters, None, None, False,
316 win32process.CREATE_NO_WINDOW, None, None, win32process.STARTUPINFO())
319 def CanMonitorPower(self):
320 return self._power_monitor.CanMonitorPower()
322 def CanMeasurePerApplicationPower(self):
323 return self._power_monitor.CanMeasurePerApplicationPower()
325 def StartMonitoringPower(self, browser):
326 self._power_monitor.StartMonitoringPower(browser)
328 def StopMonitoringPower(self):
329 return self._power_monitor.StopMonitoringPower()
331 def _StartMsrServerIfNeeded(self):
332 if self._msr_server_handle:
336 self._msr_server_port = util.GetUnreservedAvailableLocalPort()
337 # It might be flaky to get a port number without reserving it atomically,
338 # but if the server process chooses a port, we have no way of getting it.
339 # The stdout of the elevated process isn't accessible.
341 os.path.join(os.path.dirname(__file__), 'msr_server_win.py'),
342 str(self._msr_server_port),
344 self._msr_server_handle = self.LaunchApplication(
345 sys.executable, parameters, elevate_privilege=True)
346 # Wait for server to start.
348 socket.create_connection(('127.0.0.1', self._msr_server_port), 5).close()
350 self.CloseMsrServer()
351 atexit.register(TerminateProcess, self._msr_server_handle)
353 def ReadMsr(self, msr_number, start=0, length=64):
354 self._StartMsrServerIfNeeded()
355 if not self._msr_server_handle:
356 raise OSError('Unable to start MSR server.')
358 sock = socket.create_connection(('127.0.0.1', self._msr_server_port), 0.1)
360 sock.sendall(struct.pack('I', msr_number))
361 response = sock.recv(8)
364 return struct.unpack('Q', response)[0] >> start & ((1 << length) - 1)