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 import decorators 11from telemetry.core import exceptions 12from telemetry.core.platform import platform_backend 13 14 15class LinuxBasedPlatformBackend(platform_backend.PlatformBackend): 16 17 """Abstract platform containing functionality shared by all linux based OSes. 18 19 Subclasses must implement RunCommand, GetFileContents, GetPsOutput, and 20 ParseCStateSample.""" 21 22 def GetSystemCommitCharge(self): 23 meminfo_contents = self.GetFileContents('/proc/meminfo') 24 meminfo = self._GetProcFileDict(meminfo_contents) 25 if not meminfo: 26 return None 27 return (self._ConvertKbToByte(meminfo['MemTotal']) 28 - self._ConvertKbToByte(meminfo['MemFree']) 29 - self._ConvertKbToByte(meminfo['Buffers']) 30 - self._ConvertKbToByte(meminfo['Cached'])) 31 32 @decorators.Cache 33 def GetSystemTotalPhysicalMemory(self): 34 meminfo_contents = self.GetFileContents('/proc/meminfo') 35 meminfo = self._GetProcFileDict(meminfo_contents) 36 if not meminfo: 37 return None 38 return self._ConvertKbToByte(meminfo['MemTotal']) 39 40 def GetCpuStats(self, pid): 41 stats = self._GetProcFileForPid(pid, 'stat') 42 if not stats: 43 return {} 44 stats = stats.split() 45 utime = float(stats[13]) 46 stime = float(stats[14]) 47 cpu_process_jiffies = utime + stime 48 return {'CpuProcessTime': cpu_process_jiffies} 49 50 def GetCpuTimestamp(self): 51 timer_list = self.GetFileContents('/proc/timer_list') 52 total_jiffies = float(self._GetProcJiffies(timer_list)) 53 return {'TotalTime': total_jiffies} 54 55 def GetMemoryStats(self, pid): 56 status_contents = self._GetProcFileForPid(pid, 'status') 57 stats = self._GetProcFileForPid(pid, 'stat').split() 58 status = self._GetProcFileDict(status_contents) 59 if not status or not stats or 'Z' in status['State']: 60 return {} 61 vm = int(stats[22]) 62 vm_peak = (self._ConvertKbToByte(status['VmPeak']) 63 if 'VmPeak' in status else vm) 64 wss = int(stats[23]) * resource.getpagesize() 65 wss_peak = (self._ConvertKbToByte(status['VmHWM']) 66 if 'VmHWM' in status else wss) 67 68 private_dirty_bytes = 0 69 for line in self._GetProcFileForPid(pid, 'smaps').splitlines(): 70 if line.startswith('Private_Dirty:'): 71 private_dirty_bytes += self._ConvertKbToByte(line.split(':')[1].strip()) 72 73 return {'VM': vm, 74 'VMPeak': vm_peak, 75 'PrivateDirty': private_dirty_bytes, 76 'WorkingSetSize': wss, 77 'WorkingSetSizePeak': wss_peak} 78 79 def GetIOStats(self, pid): 80 io_contents = self._GetProcFileForPid(pid, 'io') 81 io = self._GetProcFileDict(io_contents) 82 return {'ReadOperationCount': int(io['syscr']), 83 'WriteOperationCount': int(io['syscw']), 84 'ReadTransferCount': int(io['rchar']), 85 'WriteTransferCount': int(io['wchar'])} 86 87 def GetFileContents(self, filename): 88 raise NotImplementedError() 89 90 def GetPsOutput(self, columns, pid=None): 91 raise NotImplementedError() 92 93 def RunCommand(self, cmd): 94 raise NotImplementedError() 95 96 @staticmethod 97 def ParseCStateSample(sample): 98 """Parse a single c-state residency sample. 99 100 Args: 101 sample: A sample of c-state residency times to be parsed. Organized as 102 a dictionary mapping CPU name to a string containing all c-state 103 names, the times in each state, the latency of each state, and the 104 time at which the sample was taken all separated by newlines. 105 Ex: {'cpu0': 'C0\nC1\n5000\n2000\n20\n30\n1406673171'} 106 107 Returns: 108 Dictionary associating a c-state with a time. 109 """ 110 raise NotImplementedError() 111 112 def _IsPidAlive(self, pid): 113 assert pid, 'pid is required' 114 return bool(self.GetPsOutput(['pid'], pid) == str(pid)) 115 116 def _GetProcFileForPid(self, pid, filename): 117 try: 118 return self.GetFileContents('/proc/%s/%s' % (pid, filename)) 119 except IOError: 120 if not self._IsPidAlive(pid): 121 raise exceptions.ProcessGoneException() 122 raise 123 124 def _ConvertKbToByte(self, value): 125 return int(value.replace('kB','')) * 1024 126 127 def _GetProcFileDict(self, contents): 128 retval = {} 129 for line in contents.splitlines(): 130 key, value = line.split(':') 131 retval[key.strip()] = value.strip() 132 return retval 133 134 def _GetProcJiffies(self, timer_list): 135 """Parse '/proc/timer_list' output and returns the first jiffies attribute. 136 137 Multi-CPU machines will have multiple 'jiffies:' lines, all of which will be 138 essentially the same. Return the first one.""" 139 if isinstance(timer_list, str): 140 timer_list = timer_list.splitlines() 141 for line in timer_list: 142 if line.startswith('jiffies:'): 143 _, value = line.split(':') 144 return value 145 raise Exception('Unable to find jiffies from /proc/timer_list') 146