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 5try: 6 import resource # pylint: disable=F0401 7except ImportError: 8 resource = None # Not available on all platforms 9 10from telemetry.core import exceptions 11from telemetry.core.platform import platform_backend 12 13 14class ProcSupportingPlatformBackend(platform_backend.PlatformBackend): 15 16 """Represents a platform that supports /proc. 17 18 Subclasses must implement _GetFileContents and _GetPsOutput.""" 19 20 def GetSystemCommitCharge(self): 21 meminfo_contents = self._GetFileContents('/proc/meminfo') 22 meminfo = self._GetProcFileDict(meminfo_contents) 23 return (self._ConvertKbToByte(meminfo['MemTotal']) 24 - self._ConvertKbToByte(meminfo['MemFree']) 25 - self._ConvertKbToByte(meminfo['Buffers']) 26 - self._ConvertKbToByte(meminfo['Cached'])) 27 28 def GetCpuStats(self, pid): 29 stats = self._GetProcFileForPid(pid, 'stat') 30 if not stats: 31 return {} 32 stats = stats.split() 33 utime = float(stats[13]) 34 stime = float(stats[14]) 35 cpu_process_jiffies = utime + stime 36 return {'CpuProcessTime': cpu_process_jiffies} 37 38 def GetCpuTimestamp(self): 39 timer_list = self._GetFileContents('/proc/timer_list') 40 total_jiffies = float(self._GetProcJiffies(timer_list)) 41 return {'TotalTime': total_jiffies} 42 43 def GetMemoryStats(self, pid): 44 status_contents = self._GetProcFileForPid(pid, 'status') 45 stats = self._GetProcFileForPid(pid, 'stat').split() 46 status = self._GetProcFileDict(status_contents) 47 if not status or not stats or 'Z' in status['State']: 48 return {} 49 vm = int(stats[22]) 50 vm_peak = (self._ConvertKbToByte(status['VmPeak']) 51 if 'VmPeak' in status else vm) 52 wss = int(stats[23]) * resource.getpagesize() 53 wss_peak = (self._ConvertKbToByte(status['VmHWM']) 54 if 'VmHWM' in status else wss) 55 return {'VM': vm, 56 'VMPeak': vm_peak, 57 'WorkingSetSize': wss, 58 'WorkingSetSizePeak': wss_peak} 59 60 def GetIOStats(self, pid): 61 io_contents = self._GetProcFileForPid(pid, 'io') 62 io = self._GetProcFileDict(io_contents) 63 return {'ReadOperationCount': int(io['syscr']), 64 'WriteOperationCount': int(io['syscw']), 65 'ReadTransferCount': int(io['rchar']), 66 'WriteTransferCount': int(io['wchar'])} 67 68 def _GetFileContents(self, filename): 69 raise NotImplementedError() 70 71 def _GetPsOutput(self, columns, pid=None): 72 raise NotImplementedError() 73 74 def _IsPidAlive(self, pid): 75 assert pid, 'pid is required' 76 return bool(self._GetPsOutput(['pid'], pid) == str(pid)) 77 78 def _GetProcFileForPid(self, pid, filename): 79 try: 80 return self._GetFileContents('/proc/%s/%s' % (pid, filename)) 81 except IOError: 82 if not self._IsPidAlive(pid): 83 raise exceptions.ProcessGoneException() 84 raise 85 86 def _ConvertKbToByte(self, value): 87 return int(value.replace('kB','')) * 1024 88 89 def _GetProcFileDict(self, contents): 90 retval = {} 91 for line in contents.splitlines(): 92 key, value = line.split(':') 93 retval[key.strip()] = value.strip() 94 return retval 95 96 def _GetProcJiffies(self, timer_list): 97 """Parse '/proc/timer_list' output and returns the first jiffies attribute. 98 99 Multi-CPU machines will have multiple 'jiffies:' lines, all of which will be 100 essentially the same. Return the first one.""" 101 if isinstance(timer_list, str): 102 timer_list = timer_list.splitlines() 103 for line in timer_list: 104 if line.startswith('jiffies:'): 105 _, value = line.split(':') 106 return value 107 raise Exception('Unable to find jiffies from /proc/timer_list') 108