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.
13 from telemetry import decorators
14 from telemetry.core import exceptions
15 from telemetry.core.platform import desktop_platform_backend
16 from telemetry.core.platform import platform_backend
17 from telemetry.core.platform.power_monitor import ippet_power_monitor
20 import pywintypes # pylint: disable=F0401
21 import win32api # pylint: disable=F0401
22 from win32com.shell import shell # pylint: disable=F0401
23 from win32com.shell import shellcon # pylint: disable=F0401
24 import win32con # pylint: disable=F0401
25 import win32process # pylint: disable=F0401
26 import win32security # pylint: disable=F0401
38 def IsCurrentProcessElevated():
39 handle = win32process.GetCurrentProcess()
40 with contextlib.closing(
41 win32security.OpenProcessToken(handle, win32con.TOKEN_QUERY)) as token:
43 win32security.GetTokenInformation(token, win32security.TokenElevation))
46 class WinPlatformBackend(desktop_platform_backend.DesktopPlatformBackend):
48 super(WinPlatformBackend, self).__init__()
49 self._power_monitor = ippet_power_monitor.IppetPowerMonitor(self)
51 # pylint: disable=W0613
52 def StartRawDisplayFrameRateMeasurement(self):
53 raise NotImplementedError()
55 def StopRawDisplayFrameRateMeasurement(self):
56 raise NotImplementedError()
58 def GetRawDisplayFrameRateMeasurements(self):
59 raise NotImplementedError()
61 def IsThermallyThrottled(self):
62 raise NotImplementedError()
64 def HasBeenThermallyThrottled(self):
65 raise NotImplementedError()
67 def GetSystemCommitCharge(self):
68 performance_info = self._GetPerformanceInfo()
69 return performance_info.CommitTotal * performance_info.PageSize / 1024
72 def GetSystemTotalPhysicalMemory(self):
73 performance_info = self._GetPerformanceInfo()
74 return performance_info.PhysicalTotal * performance_info.PageSize / 1024
76 def GetCpuStats(self, pid):
77 cpu_info = self._GetWin32ProcessInfo(win32process.GetProcessTimes, pid)
78 # Convert 100 nanosecond units to seconds
79 cpu_time = (cpu_info['UserTime'] / 1e7 +
80 cpu_info['KernelTime'] / 1e7)
81 return {'CpuProcessTime': cpu_time}
83 def GetCpuTimestamp(self):
84 """Return current timestamp in seconds."""
85 return {'TotalTime': time.time()}
87 def GetMemoryStats(self, pid):
88 memory_info = self._GetWin32ProcessInfo(
89 win32process.GetProcessMemoryInfo, pid)
90 return {'VM': memory_info['PagefileUsage'],
91 'VMPeak': memory_info['PeakPagefileUsage'],
92 'WorkingSetSize': memory_info['WorkingSetSize'],
93 'WorkingSetSizePeak': memory_info['PeakWorkingSetSize']}
95 def GetIOStats(self, pid):
96 io_stats = self._GetWin32ProcessInfo(win32process.GetProcessIoCounters, pid)
97 return {'ReadOperationCount': io_stats['ReadOperationCount'],
98 'WriteOperationCount': io_stats['WriteOperationCount'],
99 'ReadTransferCount': io_stats['ReadTransferCount'],
100 'WriteTransferCount': io_stats['WriteTransferCount']}
102 def KillProcess(self, pid, kill_process_tree=False):
103 # os.kill for Windows is Python 2.7.
104 cmd = ['taskkill', '/F', '/PID', str(pid)]
105 if kill_process_tree:
107 subprocess.Popen(cmd, stdout=subprocess.PIPE,
108 stderr=subprocess.STDOUT).communicate()
110 def GetSystemProcessInfo(self):
111 # [3:] To skip 2 blank lines and header.
112 lines = subprocess.Popen(
113 ['wmic', 'process', 'get',
114 'CommandLine,CreationDate,Name,ParentProcessId,ProcessId',
116 stdout=subprocess.PIPE).communicate()[0].splitlines()[3:]
121 parts = line.split(',')
123 pi['ProcessId'] = int(parts[-1])
124 pi['ParentProcessId'] = int(parts[-2])
125 pi['Name'] = parts[-3]
128 creation_date = float(re.split('[+-]', parts[-4])[0])
129 pi['CreationDate'] = creation_date
130 pi['CommandLine'] = ','.join(parts[1:-4])
131 process_info.append(pi)
134 def GetChildPids(self, pid):
135 """Retunds a list of child pids of |pid|."""
136 ppid_map = collections.defaultdict(list)
138 for pi in self.GetSystemProcessInfo():
139 ppid_map[pi['ParentProcessId']].append(pi['ProcessId'])
140 if pi['CreationDate']:
141 creation_map[pi['ProcessId']] = pi['CreationDate']
143 def _InnerGetChildPids(pid):
144 if not pid or pid not in ppid_map:
146 ret = [p for p in ppid_map[pid] if creation_map[p] >= creation_map[pid]]
150 ret.extend(_InnerGetChildPids(child))
153 return _InnerGetChildPids(pid)
155 def GetCommandLine(self, pid):
156 for pi in self.GetSystemProcessInfo():
157 if pid == pi['ProcessId']:
158 return pi['CommandLine']
159 raise exceptions.ProcessGoneException()
165 def GetOSVersionName(self):
166 os_version = platform.uname()[3]
168 if os_version.startswith('5.1.'):
169 return platform_backend.XP
170 if os_version.startswith('6.0.'):
171 return platform_backend.VISTA
172 if os_version.startswith('6.1.'):
173 return platform_backend.WIN7
174 if os_version.startswith('6.2.'):
175 return platform_backend.WIN8
177 raise NotImplementedError('Unknown win version %s.' % os_version)
179 def CanFlushIndividualFilesFromSystemCache(self):
182 def _GetWin32ProcessInfo(self, func, pid):
183 mask = (win32con.PROCESS_QUERY_INFORMATION |
184 win32con.PROCESS_VM_READ)
187 handle = win32api.OpenProcess(mask, False, pid)
189 except pywintypes.error, e:
192 raise exceptions.ProcessGoneException()
196 win32api.CloseHandle(handle)
198 def _GetPerformanceInfo(self):
199 class PerformanceInfo(ctypes.Structure):
200 """Struct for GetPerformanceInfo() call
201 http://msdn.microsoft.com/en-us/library/ms683210
203 _fields_ = [('size', ctypes.c_ulong),
204 ('CommitTotal', ctypes.c_size_t),
205 ('CommitLimit', ctypes.c_size_t),
206 ('CommitPeak', ctypes.c_size_t),
207 ('PhysicalTotal', ctypes.c_size_t),
208 ('PhysicalAvailable', ctypes.c_size_t),
209 ('SystemCache', ctypes.c_size_t),
210 ('KernelTotal', ctypes.c_size_t),
211 ('KernelPaged', ctypes.c_size_t),
212 ('KernelNonpaged', ctypes.c_size_t),
213 ('PageSize', ctypes.c_size_t),
214 ('HandleCount', ctypes.c_ulong),
215 ('ProcessCount', ctypes.c_ulong),
216 ('ThreadCount', ctypes.c_ulong)]
219 self.size = ctypes.sizeof(self)
220 super(PerformanceInfo, self).__init__()
222 performance_info = PerformanceInfo()
223 ctypes.windll.psapi.GetPerformanceInfo(
224 ctypes.byref(performance_info), performance_info.size)
225 return performance_info
227 def LaunchApplication(
228 self, application, parameters=None, elevate_privilege=False):
229 """Launch an application. Returns a PyHANDLE object."""
231 parameters = ' '.join(parameters) if parameters else ''
232 if elevate_privilege and not IsCurrentProcessElevated():
233 # Use ShellExecuteEx() instead of subprocess.Popen()/CreateProcess() to
234 # elevate privileges. A new console will be created if the new process has
235 # different permissions than this process.
236 proc_info = shell.ShellExecuteEx(
237 fMask=shellcon.SEE_MASK_NOCLOSEPROCESS | shellcon.SEE_MASK_NO_CONSOLE,
238 lpVerb='runas' if elevate_privilege else '',
240 lpParameters=parameters,
241 nShow=win32con.SW_HIDE)
242 if proc_info['hInstApp'] <= 32:
243 raise Exception('Unable to launch %s' % application)
244 return proc_info['hProcess']
246 handle, _, _, _ = win32process.CreateProcess(
247 None, application + ' ' + parameters, None, None, False,
248 win32process.CREATE_NO_WINDOW, None, None, win32process.STARTUPINFO())
251 def CanMonitorPower(self):
252 return self._power_monitor.CanMonitorPower()
254 def CanMeasurePerApplicationPower(self):
255 return self._power_monitor.CanMeasurePerApplicationPower()
257 def StartMonitoringPower(self, browser):
258 self._power_monitor.StartMonitoringPower(browser)
260 def StopMonitoringPower(self):
261 return self._power_monitor.StopMonitoringPower()