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