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 logging
6import os
7import subprocess
8import sys
9
10from telemetry import decorators
11from telemetry.core.platform import linux_based_platform_backend
12from telemetry.core.platform import platform_backend
13from telemetry.core.platform import posix_platform_backend
14from telemetry.core.platform.power_monitor import msr_power_monitor
15from telemetry.util import cloud_storage
16from telemetry.util import support_binaries
17
18
19_POSSIBLE_PERFHOST_APPLICATIONS = [
20  'perfhost_precise',
21  'perfhost_trusty',
22]
23
24
25class LinuxPlatformBackend(
26    posix_platform_backend.PosixPlatformBackend,
27    linux_based_platform_backend.LinuxBasedPlatformBackend):
28  def __init__(self):
29    super(LinuxPlatformBackend, self).__init__()
30    self._power_monitor = msr_power_monitor.MsrPowerMonitor(self)
31
32  def StartRawDisplayFrameRateMeasurement(self):
33    raise NotImplementedError()
34
35  def StopRawDisplayFrameRateMeasurement(self):
36    raise NotImplementedError()
37
38  def GetRawDisplayFrameRateMeasurements(self):
39    raise NotImplementedError()
40
41  def IsThermallyThrottled(self):
42    raise NotImplementedError()
43
44  def HasBeenThermallyThrottled(self):
45    raise NotImplementedError()
46
47  def GetOSName(self):
48    return 'linux'
49
50  @decorators.Cache
51  def GetOSVersionName(self):
52    if not os.path.exists('/etc/lsb-release'):
53      raise NotImplementedError('Unknown Linux OS version')
54
55    codename = None
56    version = None
57    for line in self.GetFileContents('/etc/lsb-release').splitlines():
58      key, _, value = line.partition('=')
59      if key == 'DISTRIB_CODENAME':
60        codename = value.strip()
61      elif key == 'DISTRIB_RELEASE':
62        try:
63          version = float(value)
64        except ValueError:
65          version = 0
66      if codename and version:
67        break
68    return platform_backend.OSVersion(codename, version)
69
70  def CanFlushIndividualFilesFromSystemCache(self):
71    return True
72
73  def FlushEntireSystemCache(self):
74    p = subprocess.Popen(['/sbin/sysctl', '-w', 'vm.drop_caches=3'])
75    p.wait()
76    assert p.returncode == 0, 'Failed to flush system cache'
77
78  def CanLaunchApplication(self, application):
79    if application == 'ipfw' and not self._IsIpfwKernelModuleInstalled():
80      return False
81    return super(LinuxPlatformBackend, self).CanLaunchApplication(application)
82
83  def InstallApplication(self, application):
84    if application == 'ipfw':
85      self._InstallIpfw()
86    elif application == 'avconv':
87      self._InstallBinary(application, fallback_package='libav-tools')
88    elif application in _POSSIBLE_PERFHOST_APPLICATIONS:
89      self._InstallBinary(application)
90    else:
91      raise NotImplementedError(
92          'Please teach Telemetry how to install ' + application)
93
94  def CanMonitorPower(self):
95    return self._power_monitor.CanMonitorPower()
96
97  def CanMeasurePerApplicationPower(self):
98    return self._power_monitor.CanMeasurePerApplicationPower()
99
100  def StartMonitoringPower(self, browser):
101    self._power_monitor.StartMonitoringPower(browser)
102
103  def StopMonitoringPower(self):
104    return self._power_monitor.StopMonitoringPower()
105
106  def ReadMsr(self, msr_number, start=0, length=64):
107    cmd = ['/usr/sbin/rdmsr', '-d', str(msr_number)]
108    (out, err) = subprocess.Popen(cmd,
109                                  stdout=subprocess.PIPE,
110                                  stderr=subprocess.PIPE).communicate()
111    if err:
112      raise OSError(err)
113    try:
114      result = int(out)
115    except ValueError:
116      raise OSError('Cannot interpret rdmsr output: %s' % out)
117    return result >> start & ((1 << length) - 1)
118
119  def _IsIpfwKernelModuleInstalled(self):
120    return 'ipfw_mod' in subprocess.Popen(
121        ['lsmod'], stdout=subprocess.PIPE).communicate()[0]
122
123  def _InstallIpfw(self):
124    ipfw_bin = support_binaries.FindPath('ipfw', self.GetOSName())
125    ipfw_mod = support_binaries.FindPath('ipfw_mod.ko', self.GetOSName())
126
127    try:
128      changed = cloud_storage.GetIfChanged(
129          ipfw_bin, cloud_storage.INTERNAL_BUCKET)
130      changed |= cloud_storage.GetIfChanged(
131          ipfw_mod, cloud_storage.INTERNAL_BUCKET)
132    except cloud_storage.CloudStorageError, e:
133      logging.error(str(e))
134      logging.error('You may proceed by manually building and installing'
135                    'dummynet for your kernel. See: '
136                    'http://info.iet.unipi.it/~luigi/dummynet/')
137      sys.exit(1)
138
139    if changed or not self.CanLaunchApplication('ipfw'):
140      if not self._IsIpfwKernelModuleInstalled():
141        subprocess.check_call(['sudo', 'insmod', ipfw_mod])
142      os.chmod(ipfw_bin, 0755)
143      subprocess.check_call(['sudo', 'cp', ipfw_bin, '/usr/local/sbin'])
144
145    assert self.CanLaunchApplication('ipfw'), 'Failed to install ipfw. ' \
146        'ipfw provided binaries are not supported for linux kernel < 3.13. ' \
147        'You may proceed by manually building and installing dummynet for ' \
148        'your kernel. See: http://info.iet.unipi.it/~luigi/dummynet/'
149
150  def _InstallBinary(self, bin_name, fallback_package=None):
151    bin_path = support_binaries.FindPath(bin_name, self.GetOSName())
152    if not bin_path:
153      raise Exception('Could not find the binary package %s' % bin_name)
154    os.environ['PATH'] += os.pathsep + os.path.dirname(bin_path)
155
156    try:
157      cloud_storage.GetIfChanged(bin_path, cloud_storage.INTERNAL_BUCKET)
158      os.chmod(bin_path, 0755)
159    except cloud_storage.CloudStorageError, e:
160      logging.error(str(e))
161      if fallback_package:
162        raise Exception('You may proceed by manually installing %s via:\n'
163                        'sudo apt-get install %s' %
164                        (bin_name, fallback_package))
165
166    assert self.CanLaunchApplication(bin_name), 'Failed to install ' + bin_name
167