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. 4 5import collections 6import ctypes 7import platform 8import re 9import subprocess 10import time 11try: 12 import pywintypes # pylint: disable=F0401 13 import win32api # pylint: disable=F0401 14 import win32con # pylint: disable=F0401 15 import win32process # pylint: disable=F0401 16except ImportError: 17 pywintypes = None 18 win32api = None 19 win32con = None 20 win32process = None 21 22from telemetry import decorators 23from telemetry.core import exceptions 24from telemetry.core.platform import desktop_platform_backend 25from telemetry.core.platform import platform_backend 26 27 28class WinPlatformBackend(desktop_platform_backend.DesktopPlatformBackend): 29 # pylint: disable=W0613 30 def StartRawDisplayFrameRateMeasurement(self): 31 raise NotImplementedError() 32 33 def StopRawDisplayFrameRateMeasurement(self): 34 raise NotImplementedError() 35 36 def GetRawDisplayFrameRateMeasurements(self): 37 raise NotImplementedError() 38 39 def IsThermallyThrottled(self): 40 raise NotImplementedError() 41 42 def HasBeenThermallyThrottled(self): 43 raise NotImplementedError() 44 45 def GetSystemCommitCharge(self): 46 performance_info = self._GetPerformanceInfo() 47 return performance_info.CommitTotal * performance_info.PageSize / 1024 48 49 @decorators.Cache 50 def GetSystemTotalPhysicalMemory(self): 51 performance_info = self._GetPerformanceInfo() 52 return performance_info.PhysicalTotal * performance_info.PageSize / 1024 53 54 def GetCpuStats(self, pid): 55 cpu_info = self._GetWin32ProcessInfo(win32process.GetProcessTimes, pid) 56 # Convert 100 nanosecond units to seconds 57 cpu_time = (cpu_info['UserTime'] / 1e7 + 58 cpu_info['KernelTime'] / 1e7) 59 return {'CpuProcessTime': cpu_time} 60 61 def GetCpuTimestamp(self): 62 """Return current timestamp in seconds.""" 63 return {'TotalTime': time.time()} 64 65 def GetMemoryStats(self, pid): 66 memory_info = self._GetWin32ProcessInfo( 67 win32process.GetProcessMemoryInfo, pid) 68 return {'VM': memory_info['PagefileUsage'], 69 'VMPeak': memory_info['PeakPagefileUsage'], 70 'WorkingSetSize': memory_info['WorkingSetSize'], 71 'WorkingSetSizePeak': memory_info['PeakWorkingSetSize']} 72 73 def GetIOStats(self, pid): 74 io_stats = self._GetWin32ProcessInfo(win32process.GetProcessIoCounters, pid) 75 return {'ReadOperationCount': io_stats['ReadOperationCount'], 76 'WriteOperationCount': io_stats['WriteOperationCount'], 77 'ReadTransferCount': io_stats['ReadTransferCount'], 78 'WriteTransferCount': io_stats['WriteTransferCount']} 79 80 def KillProcess(self, pid, kill_process_tree=False): 81 # os.kill for Windows is Python 2.7. 82 cmd = ['taskkill', '/F', '/PID', str(pid)] 83 if kill_process_tree: 84 cmd.append('/T') 85 subprocess.Popen(cmd, stdout=subprocess.PIPE, 86 stderr=subprocess.STDOUT).communicate() 87 88 def GetSystemProcessInfo(self): 89 # [3:] To skip 2 blank lines and header. 90 lines = subprocess.Popen( 91 ['wmic', 'process', 'get', 92 'CommandLine,CreationDate,Name,ParentProcessId,ProcessId', 93 '/format:csv'], 94 stdout=subprocess.PIPE).communicate()[0].splitlines()[3:] 95 process_info = [] 96 for line in lines: 97 if not line: 98 continue 99 parts = line.split(',') 100 pi = {} 101 pi['ProcessId'] = int(parts[-1]) 102 pi['ParentProcessId'] = int(parts[-2]) 103 pi['Name'] = parts[-3] 104 creation_date = None 105 if parts[-4]: 106 creation_date = float(re.split('[+-]', parts[-4])[0]) 107 pi['CreationDate'] = creation_date 108 pi['CommandLine'] = ','.join(parts[1:-4]) 109 process_info.append(pi) 110 return process_info 111 112 def GetChildPids(self, pid): 113 """Retunds a list of child pids of |pid|.""" 114 ppid_map = collections.defaultdict(list) 115 creation_map = {} 116 for pi in self.GetSystemProcessInfo(): 117 ppid_map[pi['ParentProcessId']].append(pi['ProcessId']) 118 if pi['CreationDate']: 119 creation_map[pi['ProcessId']] = pi['CreationDate'] 120 121 def _InnerGetChildPids(pid): 122 if not pid or pid not in ppid_map: 123 return [] 124 ret = [p for p in ppid_map[pid] if creation_map[p] >= creation_map[pid]] 125 for child in ret: 126 if child == pid: 127 continue 128 ret.extend(_InnerGetChildPids(child)) 129 return ret 130 131 return _InnerGetChildPids(pid) 132 133 def GetCommandLine(self, pid): 134 for pi in self.GetSystemProcessInfo(): 135 if pid == pi['ProcessId']: 136 return pi['CommandLine'] 137 raise exceptions.ProcessGoneException() 138 139 def GetOSName(self): 140 return 'win' 141 142 @decorators.Cache 143 def GetOSVersionName(self): 144 os_version = platform.uname()[3] 145 146 if os_version.startswith('5.1.'): 147 return platform_backend.XP 148 if os_version.startswith('6.0.'): 149 return platform_backend.VISTA 150 if os_version.startswith('6.1.'): 151 return platform_backend.WIN7 152 if os_version.startswith('6.2.'): 153 return platform_backend.WIN8 154 155 raise NotImplementedError('Unknown win version %s.' % os_version) 156 157 def CanFlushIndividualFilesFromSystemCache(self): 158 return True 159 160 def _GetWin32ProcessInfo(self, func, pid): 161 mask = (win32con.PROCESS_QUERY_INFORMATION | 162 win32con.PROCESS_VM_READ) 163 handle = None 164 try: 165 handle = win32api.OpenProcess(mask, False, pid) 166 return func(handle) 167 except pywintypes.error, e: 168 errcode = e[0] 169 if errcode == 87: 170 raise exceptions.ProcessGoneException() 171 raise 172 finally: 173 if handle: 174 win32api.CloseHandle(handle) 175 176 def _GetPerformanceInfo(self): 177 class PerformanceInfo(ctypes.Structure): 178 """Struct for GetPerformanceInfo() call 179 http://msdn.microsoft.com/en-us/library/ms683210 180 """ 181 _fields_ = [('size', ctypes.c_ulong), 182 ('CommitTotal', ctypes.c_size_t), 183 ('CommitLimit', ctypes.c_size_t), 184 ('CommitPeak', ctypes.c_size_t), 185 ('PhysicalTotal', ctypes.c_size_t), 186 ('PhysicalAvailable', ctypes.c_size_t), 187 ('SystemCache', ctypes.c_size_t), 188 ('KernelTotal', ctypes.c_size_t), 189 ('KernelPaged', ctypes.c_size_t), 190 ('KernelNonpaged', ctypes.c_size_t), 191 ('PageSize', ctypes.c_size_t), 192 ('HandleCount', ctypes.c_ulong), 193 ('ProcessCount', ctypes.c_ulong), 194 ('ThreadCount', ctypes.c_ulong)] 195 196 def __init__(self): 197 self.size = ctypes.sizeof(self) 198 super(PerformanceInfo, self).__init__() 199 200 performance_info = PerformanceInfo() 201 ctypes.windll.psapi.GetPerformanceInfo( 202 ctypes.byref(performance_info), performance_info.size) 203 return performance_info 204