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