Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(181)

Unified Diff: tools/telemetry/telemetry/core/platform/win_platform_backend.py

Issue 578123002: [telemetry] Read MSRs from a separate privileged process. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add pre-Vista check and increase server launch timeout. Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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]
« no previous file with comments | « tools/telemetry/telemetry/core/platform/power_monitor/msr_power_monitor_unittest.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698