| Index: tools/telemetry/telemetry/core/platform/win_platform_backend.py
|
| diff --git a/tools/telemetry/telemetry/core/platform/win_platform_backend.py b/tools/telemetry/telemetry/core/platform/win_platform_backend.py
|
| index 6e91b3a91beae84d4966a64eca143a4c9afeb465..c81e3e756a51550f6416dcbd0a5bf36f58bcd1d6 100644
|
| --- a/tools/telemetry/telemetry/core/platform/win_platform_backend.py
|
| +++ b/tools/telemetry/telemetry/core/platform/win_platform_backend.py
|
| @@ -2,19 +2,28 @@
|
| # Use of this source code is governed by a BSD-style license that can be
|
| # found in the LICENSE file.
|
|
|
| +import atexit
|
| import collections
|
| import contextlib
|
| import ctypes
|
| +import os
|
| import platform
|
| import re
|
| +import socket
|
| +import struct
|
| import subprocess
|
| +import sys
|
| import time
|
| +import zipfile
|
|
|
| from telemetry import decorators
|
| from telemetry.core import exceptions
|
| +from telemetry.core import util
|
| from telemetry.core.platform import desktop_platform_backend
|
| from telemetry.core.platform import platform_backend
|
| from telemetry.core.platform.power_monitor import msr_power_monitor
|
| +from telemetry.util import cloud_storage
|
| +from telemetry.util import path
|
|
|
| try:
|
| import pywintypes # pylint: disable=F0401
|
| @@ -34,20 +43,64 @@ except ImportError:
|
| win32security = None
|
|
|
|
|
| +def _InstallWinRing0():
|
| + """WinRing0 is used for reading MSRs."""
|
| + executable_dir = os.path.dirname(sys.executable)
|
|
|
| -def IsCurrentProcessElevated():
|
| - handle = win32process.GetCurrentProcess()
|
| - with contextlib.closing(
|
| - win32security.OpenProcessToken(handle, win32con.TOKEN_QUERY)) as token:
|
| - return bool(
|
| - win32security.GetTokenInformation(token, win32security.TokenElevation))
|
| + python_is_64_bit = sys.maxsize > 2 ** 32
|
| + dll_file_name = 'WinRing0x64.dll' if python_is_64_bit else 'WinRing0.dll'
|
| + dll_path = os.path.join(executable_dir, dll_file_name)
|
| +
|
| + os_is_64_bit = 'PROGRAMFILES(X86)' in os.environ
|
| + driver_file_name = 'WinRing0x64.sys' if os_is_64_bit else 'WinRing0.sys'
|
| + driver_path = os.path.join(executable_dir, driver_file_name)
|
| +
|
| + # Check for WinRing0 and download if needed.
|
| + if not (os.path.exists(dll_path) and os.path.exists(driver_path)):
|
| + win_binary_dir = os.path.join(path.GetTelemetryDir(), 'bin', 'win')
|
| + zip_path = os.path.join(win_binary_dir, 'winring0.zip')
|
| + cloud_storage.GetIfChanged(zip_path, bucket=cloud_storage.PUBLIC_BUCKET)
|
| + try:
|
| + with zipfile.ZipFile(zip_path, 'r') as zip_file:
|
| + # Install DLL.
|
| + if not os.path.exists(dll_path):
|
| + zip_file.extract(dll_file_name, executable_dir)
|
| + # Install kernel driver.
|
| + if not os.path.exists(driver_path):
|
| + zip_file.extract(driver_file_name, executable_dir)
|
| + finally:
|
| + os.remove(zip_path)
|
| +
|
| +
|
| +def TerminateProcess(process_handle):
|
| + if not process_handle:
|
| + return
|
| + if win32process.GetExitCodeProcess(process_handle) == win32con.STILL_ACTIVE:
|
| + win32process.TerminateProcess(process_handle, 0)
|
| + process_handle.close()
|
|
|
|
|
| class WinPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
|
| def __init__(self):
|
| super(WinPlatformBackend, self).__init__()
|
| + self._msr_server_handle = None
|
| + self._msr_server_port = None
|
| self._power_monitor = msr_power_monitor.MsrPowerMonitor(self)
|
|
|
| + def __del__(self):
|
| + self.close()
|
| +
|
| + def close(self):
|
| + self.CloseMsrServer()
|
| +
|
| + def CloseMsrServer(self):
|
| + if not self._msr_server_handle:
|
| + return
|
| +
|
| + TerminateProcess(self._msr_server_handle)
|
| + self._msr_server_handle = None
|
| + self._msr_server_port = None
|
| +
|
| # pylint: disable=W0613
|
| def StartRawDisplayFrameRateMeasurement(self):
|
| raise NotImplementedError()
|
| @@ -224,12 +277,23 @@ class WinPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
|
| ctypes.byref(performance_info), performance_info.size)
|
| return performance_info
|
|
|
| + def IsCurrentProcessElevated(self):
|
| + if self.GetOSVersionName() < platform_backend.VISTA:
|
| + # TOKEN_QUERY is not defined before Vista. All processes are elevated.
|
| + return True
|
| +
|
| + handle = win32process.GetCurrentProcess()
|
| + with contextlib.closing(
|
| + win32security.OpenProcessToken(handle, win32con.TOKEN_QUERY)) as token:
|
| + return bool(win32security.GetTokenInformation(
|
| + token, win32security.TokenElevation))
|
| +
|
| def LaunchApplication(
|
| self, application, parameters=None, elevate_privilege=False):
|
| """Launch an application. Returns a PyHANDLE object."""
|
|
|
| parameters = ' '.join(parameters) if parameters else ''
|
| - if elevate_privilege and not IsCurrentProcessElevated():
|
| + if elevate_privilege and not self.IsCurrentProcessElevated():
|
| # Use ShellExecuteEx() instead of subprocess.Popen()/CreateProcess() to
|
| # elevate privileges. A new console will be created if the new process has
|
| # different permissions than this process.
|
| @@ -259,3 +323,38 @@ class WinPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
|
|
|
| def StopMonitoringPower(self):
|
| return self._power_monitor.StopMonitoringPower()
|
| +
|
| + def _StartMsrServerIfNeeded(self):
|
| + if self._msr_server_handle:
|
| + return
|
| +
|
| + _InstallWinRing0()
|
| + self._msr_server_port = util.GetUnreservedAvailableLocalPort()
|
| + # It might be flaky to get a port number without reserving it atomically,
|
| + # but if the server process chooses a port, we have no way of getting it.
|
| + # The stdout of the elevated process isn't accessible.
|
| + parameters = (
|
| + os.path.join(os.path.dirname(__file__), 'msr_server_win.py'),
|
| + str(self._msr_server_port),
|
| + )
|
| + self._msr_server_handle = self.LaunchApplication(
|
| + sys.executable, parameters, elevate_privilege=True)
|
| + # Wait for server to start.
|
| + try:
|
| + socket.create_connection(('127.0.0.1', self._msr_server_port), 5).close()
|
| + except socket.error:
|
| + self.CloseMsrServer()
|
| + atexit.register(TerminateProcess, self._msr_server_handle)
|
| +
|
| + def ReadMsr(self, msr_number):
|
| + self._StartMsrServerIfNeeded()
|
| + if not self._msr_server_handle:
|
| + raise OSError('Unable to start MSR server.')
|
| +
|
| + sock = socket.create_connection(('127.0.0.1', self._msr_server_port), 0.1)
|
| + try:
|
| + sock.sendall(struct.pack('I', msr_number))
|
| + response = sock.recv(8)
|
| + finally:
|
| + sock.close()
|
| + return struct.unpack('Q', response)[0]
|
|
|