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