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