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