1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# found in the LICENSE file.
4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)try:
6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  import resource  # pylint: disable=F0401
7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)except ImportError:
8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  resource = None  # Not available on all platforms
9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from telemetry import decorators
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)from telemetry.core import exceptions
12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)from telemetry.core.platform import platform_backend
13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciclass LinuxBasedPlatformBackend(platform_backend.PlatformBackend):
16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  """Abstract platform containing functionality shared by all linux based OSes.
18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  Subclasses must implement RunCommand, GetFileContents, GetPsOutput, and
201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ParseCStateSample."""
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def GetSystemCommitCharge(self):
231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    meminfo_contents = self.GetFileContents('/proc/meminfo')
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    meminfo = self._GetProcFileDict(meminfo_contents)
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not meminfo:
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return None
27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return (self._ConvertKbToByte(meminfo['MemTotal'])
28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            - self._ConvertKbToByte(meminfo['MemFree'])
29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            - self._ConvertKbToByte(meminfo['Buffers'])
30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            - self._ConvertKbToByte(meminfo['Cached']))
31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  @decorators.Cache
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def GetSystemTotalPhysicalMemory(self):
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    meminfo_contents = self.GetFileContents('/proc/meminfo')
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    meminfo = self._GetProcFileDict(meminfo_contents)
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not meminfo:
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return None
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return self._ConvertKbToByte(meminfo['MemTotal'])
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def GetCpuStats(self, pid):
41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    stats = self._GetProcFileForPid(pid, 'stat')
42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if not stats:
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return {}
44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    stats = stats.split()
45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    utime = float(stats[13])
46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    stime = float(stats[14])
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    cpu_process_jiffies = utime + stime
48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return {'CpuProcessTime': cpu_process_jiffies}
49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def GetCpuTimestamp(self):
511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    timer_list = self.GetFileContents('/proc/timer_list')
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    total_jiffies = float(self._GetProcJiffies(timer_list))
53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return {'TotalTime': total_jiffies}
54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def GetMemoryStats(self, pid):
56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    status_contents = self._GetProcFileForPid(pid, 'status')
57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    stats = self._GetProcFileForPid(pid, 'stat').split()
58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    status = self._GetProcFileDict(status_contents)
59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if not status or not stats or 'Z' in status['State']:
60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return {}
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    vm = int(stats[22])
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    vm_peak = (self._ConvertKbToByte(status['VmPeak'])
63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)               if 'VmPeak' in status else vm)
64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    wss = int(stats[23]) * resource.getpagesize()
65a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    wss_peak = (self._ConvertKbToByte(status['VmHWM'])
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                if 'VmHWM' in status else wss)
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private_dirty_bytes = 0
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for line in self._GetProcFileForPid(pid, 'smaps').splitlines():
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if line.startswith('Private_Dirty:'):
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        private_dirty_bytes += self._ConvertKbToByte(line.split(':')[1].strip())
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return {'VM': vm,
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            'VMPeak': vm_peak,
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            'PrivateDirty': private_dirty_bytes,
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            'WorkingSetSize': wss,
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            'WorkingSetSizePeak': wss_peak}
78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def GetIOStats(self, pid):
80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    io_contents = self._GetProcFileForPid(pid, 'io')
81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    io = self._GetProcFileDict(io_contents)
82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return {'ReadOperationCount': int(io['syscr']),
83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            'WriteOperationCount': int(io['syscw']),
84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            'ReadTransferCount': int(io['rchar']),
85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            'WriteTransferCount': int(io['wchar'])}
86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def GetFileContents(self, filename):
88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    raise NotImplementedError()
89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def GetPsOutput(self, columns, pid=None):
911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    raise NotImplementedError()
921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def RunCommand(self, cmd):
941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    raise NotImplementedError()
951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  @staticmethod
971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  def ParseCStateSample(sample):
981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    """Parse a single c-state residency sample.
991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    Args:
1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        sample: A sample of c-state residency times to be parsed. Organized as
1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            a dictionary mapping CPU name to a string containing all c-state
1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            names, the times in each state, the latency of each state, and the
1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            time at which the sample was taken all separated by newlines.
1051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            Ex: {'cpu0': 'C0\nC1\n5000\n2000\n20\n30\n1406673171'}
1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    Returns:
1081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        Dictionary associating a c-state with a time.
1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    """
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    raise NotImplementedError()
111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def _IsPidAlive(self, pid):
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    assert pid, 'pid is required'
1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return bool(self.GetPsOutput(['pid'], pid) == str(pid))
115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def _GetProcFileForPid(self, pid, filename):
117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    try:
1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return self.GetFileContents('/proc/%s/%s' % (pid, filename))
119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    except IOError:
120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if not self._IsPidAlive(pid):
121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        raise exceptions.ProcessGoneException()
122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      raise
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def _ConvertKbToByte(self, value):
125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return int(value.replace('kB','')) * 1024
126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def _GetProcFileDict(self, contents):
128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    retval = {}
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    for line in contents.splitlines():
130f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      key, value = line.split(':')
131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      retval[key.strip()] = value.strip()
132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return retval
133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def _GetProcJiffies(self, timer_list):
135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    """Parse '/proc/timer_list' output and returns the first jiffies attribute.
136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    Multi-CPU machines will have multiple 'jiffies:' lines, all of which will be
138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    essentially the same.  Return the first one."""
139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if isinstance(timer_list, str):
140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      timer_list = timer_list.splitlines()
141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    for line in timer_list:
142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if line.startswith('jiffies:'):
143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        _, value = line.split(':')
144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return value
145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    raise Exception('Unable to find jiffies from /proc/timer_list')
146