OLD | NEW |
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 from py_utils import atexit_with_log | |
6 import collections | 5 import collections |
7 import contextlib | 6 import contextlib |
8 import ctypes | 7 import ctypes |
9 import logging | 8 import logging |
10 import os | |
11 import platform | 9 import platform |
12 import re | 10 import re |
13 import socket | |
14 import struct | |
15 import subprocess | 11 import subprocess |
16 import sys | 12 import sys |
17 import time | 13 import time |
18 import zipfile | |
19 | |
20 from py_utils import cloud_storage # pylint: disable=import-error | |
21 | 14 |
22 from telemetry.core import exceptions | 15 from telemetry.core import exceptions |
23 from telemetry.core import os_version as os_version_module | 16 from telemetry.core import os_version as os_version_module |
24 from telemetry import decorators | 17 from telemetry import decorators |
25 from telemetry.internal.platform import desktop_platform_backend | 18 from telemetry.internal.platform import desktop_platform_backend |
26 from telemetry.internal.platform.power_monitor import msr_power_monitor | |
27 from telemetry.internal.util import path | |
28 | 19 |
29 try: | 20 try: |
30 import pywintypes # pylint: disable=import-error | 21 import pywintypes # pylint: disable=import-error |
31 import win32api # pylint: disable=import-error | 22 import win32api # pylint: disable=import-error |
32 from win32com.shell import shell # pylint: disable=no-name-in-module | 23 from win32com.shell import shell # pylint: disable=no-name-in-module |
33 from win32com.shell import shellcon # pylint: disable=no-name-in-module | 24 from win32com.shell import shellcon # pylint: disable=no-name-in-module |
34 import win32con # pylint: disable=import-error | 25 import win32con # pylint: disable=import-error |
35 import win32file # pylint: disable=import-error | |
36 import win32gui # pylint: disable=import-error | 26 import win32gui # pylint: disable=import-error |
37 import win32pipe # pylint: disable=import-error | |
38 import win32process # pylint: disable=import-error | 27 import win32process # pylint: disable=import-error |
39 import winerror # pylint: disable=import-error | 28 import winerror # pylint: disable=import-error |
40 try: | 29 try: |
41 import winreg # pylint: disable=import-error | 30 import winreg # pylint: disable=import-error |
42 except ImportError: | 31 except ImportError: |
43 import _winreg as winreg # pylint: disable=import-error | 32 import _winreg as winreg # pylint: disable=import-error |
44 import win32security # pylint: disable=import-error | 33 import win32security # pylint: disable=import-error |
45 except ImportError: | 34 except ImportError: |
46 pywintypes = None | 35 pywintypes = None |
47 shell = None | 36 shell = None |
48 shellcon = None | 37 shellcon = None |
49 win32api = None | 38 win32api = None |
50 win32con = None | 39 win32con = None |
51 win32file = None | 40 win32file = None |
52 win32gui = None | 41 win32gui = None |
53 win32pipe = None | 42 win32pipe = None |
54 win32process = None | 43 win32process = None |
55 win32security = None | 44 win32security = None |
56 winerror = None | 45 winerror = None |
57 winreg = None | 46 winreg = None |
58 | 47 |
59 | 48 |
60 def _InstallWinRing0(): | |
61 """WinRing0 is used for reading MSRs.""" | |
62 executable_dir = os.path.dirname(sys.executable) | |
63 | |
64 python_is_64_bit = sys.maxsize > 2 ** 32 | |
65 dll_file_name = 'WinRing0x64.dll' if python_is_64_bit else 'WinRing0.dll' | |
66 dll_path = os.path.join(executable_dir, dll_file_name) | |
67 | |
68 os_is_64_bit = platform.machine().endswith('64') | |
69 driver_file_name = 'WinRing0x64.sys' if os_is_64_bit else 'WinRing0.sys' | |
70 driver_path = os.path.join(executable_dir, driver_file_name) | |
71 | |
72 # Check for WinRing0 and download if needed. | |
73 if not (os.path.exists(dll_path) and os.path.exists(driver_path)): | |
74 win_binary_dir = os.path.join( | |
75 path.GetTelemetryDir(), 'bin', 'win', 'AMD64') | |
76 zip_path = os.path.join(win_binary_dir, 'winring0.zip') | |
77 cloud_storage.GetIfChanged(zip_path, bucket=cloud_storage.PUBLIC_BUCKET) | |
78 try: | |
79 with zipfile.ZipFile(zip_path, 'r') as zip_file: | |
80 error_message = ( | |
81 'Failed to extract %s into %s. If python claims that ' | |
82 'the zip file is locked, this may be a lie. The problem may be ' | |
83 'that python does not have write permissions to the destination ' | |
84 'directory.' | |
85 ) | |
86 # Install DLL. | |
87 if not os.path.exists(dll_path): | |
88 try: | |
89 zip_file.extract(dll_file_name, executable_dir) | |
90 except: | |
91 logging.error(error_message % (dll_file_name, executable_dir)) | |
92 raise | |
93 | |
94 # Install kernel driver. | |
95 if not os.path.exists(driver_path): | |
96 try: | |
97 zip_file.extract(driver_file_name, executable_dir) | |
98 except: | |
99 logging.error(error_message % (driver_file_name, executable_dir)) | |
100 raise | |
101 finally: | |
102 os.remove(zip_path) | |
103 | |
104 | |
105 def TerminateProcess(process_handle): | |
106 if not process_handle: | |
107 return | |
108 if win32process.GetExitCodeProcess(process_handle) == win32con.STILL_ACTIVE: | |
109 win32process.TerminateProcess(process_handle, 0) | |
110 process_handle.close() | |
111 | |
112 | |
113 class WinPlatformBackend(desktop_platform_backend.DesktopPlatformBackend): | 49 class WinPlatformBackend(desktop_platform_backend.DesktopPlatformBackend): |
114 def __init__(self): | 50 def __init__(self): |
115 super(WinPlatformBackend, self).__init__() | 51 super(WinPlatformBackend, self).__init__() |
116 self._msr_server_handle = None | |
117 self._msr_server_port = None | |
118 self._power_monitor = msr_power_monitor.MsrPowerMonitorWin(self) | |
119 | 52 |
120 @classmethod | 53 @classmethod |
121 def IsPlatformBackendForHost(cls): | 54 def IsPlatformBackendForHost(cls): |
122 return sys.platform == 'win32' | 55 return sys.platform == 'win32' |
123 | 56 |
124 def __del__(self): | |
125 self.close() | |
126 | |
127 def close(self): | |
128 self.CloseMsrServer() | |
129 | |
130 def CloseMsrServer(self): | |
131 if not self._msr_server_handle: | |
132 return | |
133 | |
134 TerminateProcess(self._msr_server_handle) | |
135 self._msr_server_handle = None | |
136 self._msr_server_port = None | |
137 | |
138 def IsThermallyThrottled(self): | 57 def IsThermallyThrottled(self): |
139 raise NotImplementedError() | 58 raise NotImplementedError() |
140 | 59 |
141 def HasBeenThermallyThrottled(self): | 60 def HasBeenThermallyThrottled(self): |
142 raise NotImplementedError() | 61 raise NotImplementedError() |
143 | 62 |
144 def GetSystemCommitCharge(self): | 63 def GetSystemCommitCharge(self): |
145 performance_info = self._GetPerformanceInfo() | 64 performance_info = self._GetPerformanceInfo() |
146 return performance_info.CommitTotal * performance_info.PageSize / 1024 | 65 return performance_info.CommitTotal * performance_info.PageSize / 1024 |
147 | 66 |
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
352 if proc_info['hInstApp'] <= 32: | 271 if proc_info['hInstApp'] <= 32: |
353 raise Exception('Unable to launch %s' % application) | 272 raise Exception('Unable to launch %s' % application) |
354 return proc_info['hProcess'] | 273 return proc_info['hProcess'] |
355 else: | 274 else: |
356 handle, _, _, _ = win32process.CreateProcess( | 275 handle, _, _, _ = win32process.CreateProcess( |
357 None, application + ' ' + parameters, None, None, False, | 276 None, application + ' ' + parameters, None, None, False, |
358 win32process.CREATE_NO_WINDOW, None, None, win32process.STARTUPINFO()) | 277 win32process.CREATE_NO_WINDOW, None, None, win32process.STARTUPINFO()) |
359 return handle | 278 return handle |
360 | 279 |
361 def CanMonitorPower(self): | 280 def CanMonitorPower(self): |
362 # TODO(charliea): This is a stopgap until all desktop power monitoring code | |
363 # can be removed. (crbug.com/763263) | |
364 return False | 281 return False |
365 | 282 |
366 def CanMeasurePerApplicationPower(self): | 283 def CanMeasurePerApplicationPower(self): |
367 return self._power_monitor.CanMeasurePerApplicationPower() | 284 return False |
368 | |
369 def StartMonitoringPower(self, browser): | |
370 self._power_monitor.StartMonitoringPower(browser) | |
371 | |
372 def StopMonitoringPower(self): | |
373 return self._power_monitor.StopMonitoringPower() | |
374 | |
375 def _StartMsrServerIfNeeded(self): | |
376 if self._msr_server_handle: | |
377 return | |
378 | |
379 _InstallWinRing0() | |
380 | |
381 pipe_name = r"\\.\pipe\msr_server_pipe_{}".format(os.getpid()) | |
382 # Try to open a named pipe to receive a msr port number from server process. | |
383 pipe = win32pipe.CreateNamedPipe( | |
384 pipe_name, | |
385 win32pipe.PIPE_ACCESS_INBOUND, | |
386 win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT, | |
387 1, 32, 32, 300, None) | |
388 parameters = ( | |
389 os.path.join(os.path.dirname(__file__), 'msr_server_win.py'), | |
390 pipe_name, | |
391 ) | |
392 self._msr_server_handle = self.LaunchApplication( | |
393 sys.executable, parameters, elevate_privilege=True) | |
394 if pipe != win32file.INVALID_HANDLE_VALUE: | |
395 if win32pipe.ConnectNamedPipe(pipe, None) == 0: | |
396 self._msr_server_port = int(win32file.ReadFile(pipe, 32)[1]) | |
397 win32api.CloseHandle(pipe) | |
398 # Wait for server to start. | |
399 try: | |
400 socket.create_connection(('127.0.0.1', self._msr_server_port), 5).close() | |
401 except socket.error: | |
402 self.CloseMsrServer() | |
403 atexit_with_log.Register(TerminateProcess, self._msr_server_handle) | |
404 | |
405 def ReadMsr(self, msr_number, start=0, length=64): | |
406 self._StartMsrServerIfNeeded() | |
407 if not self._msr_server_handle: | |
408 raise OSError('Unable to start MSR server.') | |
409 | |
410 sock = socket.create_connection(('127.0.0.1', self._msr_server_port), 5) | |
411 try: | |
412 sock.sendall(struct.pack('I', msr_number)) | |
413 response = sock.recv(8) | |
414 finally: | |
415 sock.close() | |
416 return struct.unpack('Q', response)[0] >> start & ((1 << length) - 1) | |
417 | 285 |
418 def IsCooperativeShutdownSupported(self): | 286 def IsCooperativeShutdownSupported(self): |
419 return True | 287 return True |
420 | 288 |
421 def CooperativelyShutdown(self, proc, app_name): | 289 def CooperativelyShutdown(self, proc, app_name): |
422 pid = proc.pid | 290 pid = proc.pid |
423 | 291 |
424 # http://timgolden.me.uk/python/win32_how_do_i/ | 292 # http://timgolden.me.uk/python/win32_how_do_i/ |
425 # find-the-window-for-my-subprocess.html | 293 # find-the-window-for-my-subprocess.html |
426 # | 294 # |
(...skipping 22 matching lines...) Expand all Loading... |
449 return True | 317 return True |
450 hwnds = [] | 318 hwnds = [] |
451 win32gui.EnumWindows(find_chrome_windows, hwnds) | 319 win32gui.EnumWindows(find_chrome_windows, hwnds) |
452 if hwnds: | 320 if hwnds: |
453 for hwnd in hwnds: | 321 for hwnd in hwnds: |
454 win32gui.SendMessage(hwnd, win32con.WM_CLOSE, 0, 0) | 322 win32gui.SendMessage(hwnd, win32con.WM_CLOSE, 0, 0) |
455 return True | 323 return True |
456 else: | 324 else: |
457 logging.info('Did not find any windows owned by target process') | 325 logging.info('Did not find any windows owned by target process') |
458 return False | 326 return False |
OLD | NEW |