1# Lint as: python2, python3 2# Copyright 2015 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Facade to access the system-related functionality.""" 7 8import six 9import os 10import threading 11import time 12 13from autotest_lib.client.bin import utils 14 15 16class SystemFacadeNativeError(Exception): 17 """Error in SystemFacadeNative.""" 18 pass 19 20 21class SystemFacadeNative(object): 22 """Facede to access the system-related functionality. 23 24 The methods inside this class only accept Python native types. 25 26 """ 27 SCALING_GOVERNOR_MODES = [ 28 'performance', 29 'powersave', 30 'userspace', 31 'ondemand', 32 'conservative', 33 'schedutil', 34 'interactive', # deprecated since kernel v4.14 35 'sched' # deprecated since kernel v4.14 36 ] 37 38 def __init__(self): 39 self._bg_worker = None 40 41 def set_scaling_governor_mode(self, index, mode): 42 """Set mode of CPU scaling governor on one CPU. 43 44 @param index: CPU index starting from 0. 45 46 @param mode: Mode of scaling governor, accept 'interactive' or 47 'performance'. 48 49 @returns: The original mode. 50 51 """ 52 if mode not in self.SCALING_GOVERNOR_MODES: 53 raise SystemFacadeNativeError('mode %s is invalid' % mode) 54 55 governor_path = os.path.join( 56 '/sys/devices/system/cpu/cpu%d' % index, 57 'cpufreq/scaling_governor') 58 if not os.path.exists(governor_path): 59 raise SystemFacadeNativeError( 60 'scaling governor of CPU %d is not available' % index) 61 62 original_mode = utils.read_one_line(governor_path) 63 utils.open_write_close(governor_path, mode) 64 65 return original_mode 66 67 68 def get_cpu_usage(self): 69 """Returns machine's CPU usage. 70 71 Returns: 72 A dictionary with 'user', 'nice', 'system' and 'idle' values. 73 Sample dictionary: 74 { 75 'user': 254544, 76 'nice': 9, 77 'system': 254768, 78 'idle': 2859878, 79 } 80 """ 81 return utils.get_cpu_usage() 82 83 84 def compute_active_cpu_time(self, cpu_usage_start, cpu_usage_end): 85 """Computes the fraction of CPU time spent non-idling. 86 87 This function should be invoked using before/after values from calls to 88 get_cpu_usage(). 89 """ 90 return utils.compute_active_cpu_time(cpu_usage_start, 91 cpu_usage_end) 92 93 94 def get_mem_total(self): 95 """Returns the total memory available in the system in MBytes.""" 96 return utils.get_mem_total() 97 98 99 def get_mem_free(self): 100 """Returns the currently free memory in the system in MBytes.""" 101 return utils.get_mem_free() 102 103 def get_mem_free_plus_buffers_and_cached(self): 104 """ 105 Returns the free memory in MBytes, counting buffers and cached as free. 106 107 This is most often the most interesting number since buffers and cached 108 memory can be reclaimed on demand. Note however, that there are cases 109 where this as misleading as well, for example used tmpfs space 110 count as Cached but can not be reclaimed on demand. 111 See https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt. 112 """ 113 return utils.get_mem_free_plus_buffers_and_cached() 114 115 def get_ec_temperatures(self): 116 """Uses ectool to return a list of all sensor temperatures in Celsius. 117 """ 118 return utils.get_ec_temperatures() 119 120 def get_current_temperature_max(self): 121 """ 122 Returns the highest reported board temperature (all sensors) in Celsius. 123 """ 124 return utils.get_current_temperature_max() 125 126 def get_current_board(self): 127 """Returns the current device board name.""" 128 return utils.get_current_board() 129 130 131 def get_chromeos_release_version(self): 132 """Returns chromeos version in device under test as string. None on 133 fail. 134 """ 135 return utils.get_chromeos_release_version() 136 137 def get_num_allocated_file_handles(self): 138 """ 139 Returns the number of currently allocated file handles. 140 """ 141 return utils.get_num_allocated_file_handles() 142 143 def get_storage_statistics(self, device=None): 144 """ 145 Fetches statistics for a storage device. 146 """ 147 return utils.get_storage_statistics(device) 148 149 def start_bg_worker(self, command): 150 """ 151 Start executing the command in a background worker. 152 """ 153 self._bg_worker = BackgroundWorker(command, do_process_output=True) 154 self._bg_worker.start() 155 156 def get_and_discard_bg_worker_output(self): 157 """ 158 Returns the output collected so far since the last call to this method. 159 """ 160 if self._bg_worker is None: 161 SystemFacadeNativeError('Background worker has not been started.') 162 163 return self._bg_worker.get_and_discard_output() 164 165 def stop_bg_worker(self): 166 """ 167 Stop the worker. 168 """ 169 if self._bg_worker is None: 170 SystemFacadeNativeError('Background worker has not been started.') 171 172 self._bg_worker.stop() 173 self._bg_worker = None 174 175 176class BackgroundWorker(object): 177 """ 178 Worker intended for executing a command in the background and collecting its 179 output. 180 """ 181 182 def __init__(self, command, do_process_output=False): 183 self._bg_job = None 184 self._command = command 185 self._do_process_output = do_process_output 186 self._output_lock = threading.Lock() 187 self._process_output_thread = None 188 self._stdout = six.StringIO() 189 190 def start(self): 191 """ 192 Start executing the command. 193 """ 194 self._bg_job = utils.BgJob(self._command, stdout_tee=self._stdout) 195 self._bg_job.sp.poll() 196 if self._bg_job.sp.returncode is not None: 197 self._exit_bg_job() 198 199 if self._do_process_output: 200 self._process_output_thread = threading.Thread( 201 target=self._process_output) 202 self._process_output_thread.start() 203 204 def _process_output(self, sleep_interval=0.01): 205 while self._do_process_output: 206 with self._output_lock: 207 self._bg_job.process_output() 208 time.sleep(sleep_interval) 209 210 def get_and_discard_output(self): 211 """ 212 Returns the output collected so far and then clears the output buffer. 213 In other words, subsequent calls to this method will not include output 214 that has already been returned before. 215 """ 216 output = "" 217 with self._output_lock: 218 self._stdout.flush() 219 output = self._stdout.getvalue() 220 self._stdout.truncate(0) 221 self._stdout.seek(0) 222 return output 223 224 def stop(self): 225 """ 226 Stop executing the command. 227 """ 228 if self._do_process_output: 229 self._do_process_output = False 230 self._process_output_thread.join(1) 231 self._exit_bg_job() 232 233 def _exit_bg_job(self): 234 utils.nuke_subprocess(self._bg_job.sp) 235 utils.join_bg_jobs([self._bg_job]) 236 if self._bg_job.result.exit_status > 0: 237 raise SystemFacadeNativeError('Background job failed: %s' % 238 self._bg_job.result.command) 239