• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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