You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
7.4 KiB
239 lines
7.4 KiB
# Lint as: python2, python3
|
|
# Copyright 2015 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""Facade to access the system-related functionality."""
|
|
|
|
import six
|
|
import os
|
|
import threading
|
|
import time
|
|
|
|
from autotest_lib.client.bin import utils
|
|
|
|
|
|
class SystemFacadeNativeError(Exception):
|
|
"""Error in SystemFacadeNative."""
|
|
pass
|
|
|
|
|
|
class SystemFacadeNative(object):
|
|
"""Facede to access the system-related functionality.
|
|
|
|
The methods inside this class only accept Python native types.
|
|
|
|
"""
|
|
SCALING_GOVERNOR_MODES = [
|
|
'performance',
|
|
'powersave',
|
|
'userspace',
|
|
'ondemand',
|
|
'conservative',
|
|
'schedutil',
|
|
'interactive', # deprecated since kernel v4.14
|
|
'sched' # deprecated since kernel v4.14
|
|
]
|
|
|
|
def __init__(self):
|
|
self._bg_worker = None
|
|
|
|
def set_scaling_governor_mode(self, index, mode):
|
|
"""Set mode of CPU scaling governor on one CPU.
|
|
|
|
@param index: CPU index starting from 0.
|
|
|
|
@param mode: Mode of scaling governor, accept 'interactive' or
|
|
'performance'.
|
|
|
|
@returns: The original mode.
|
|
|
|
"""
|
|
if mode not in self.SCALING_GOVERNOR_MODES:
|
|
raise SystemFacadeNativeError('mode %s is invalid' % mode)
|
|
|
|
governor_path = os.path.join(
|
|
'/sys/devices/system/cpu/cpu%d' % index,
|
|
'cpufreq/scaling_governor')
|
|
if not os.path.exists(governor_path):
|
|
raise SystemFacadeNativeError(
|
|
'scaling governor of CPU %d is not available' % index)
|
|
|
|
original_mode = utils.read_one_line(governor_path)
|
|
utils.open_write_close(governor_path, mode)
|
|
|
|
return original_mode
|
|
|
|
|
|
def get_cpu_usage(self):
|
|
"""Returns machine's CPU usage.
|
|
|
|
Returns:
|
|
A dictionary with 'user', 'nice', 'system' and 'idle' values.
|
|
Sample dictionary:
|
|
{
|
|
'user': 254544,
|
|
'nice': 9,
|
|
'system': 254768,
|
|
'idle': 2859878,
|
|
}
|
|
"""
|
|
return utils.get_cpu_usage()
|
|
|
|
|
|
def compute_active_cpu_time(self, cpu_usage_start, cpu_usage_end):
|
|
"""Computes the fraction of CPU time spent non-idling.
|
|
|
|
This function should be invoked using before/after values from calls to
|
|
get_cpu_usage().
|
|
"""
|
|
return utils.compute_active_cpu_time(cpu_usage_start,
|
|
cpu_usage_end)
|
|
|
|
|
|
def get_mem_total(self):
|
|
"""Returns the total memory available in the system in MBytes."""
|
|
return utils.get_mem_total()
|
|
|
|
|
|
def get_mem_free(self):
|
|
"""Returns the currently free memory in the system in MBytes."""
|
|
return utils.get_mem_free()
|
|
|
|
def get_mem_free_plus_buffers_and_cached(self):
|
|
"""
|
|
Returns the free memory in MBytes, counting buffers and cached as free.
|
|
|
|
This is most often the most interesting number since buffers and cached
|
|
memory can be reclaimed on demand. Note however, that there are cases
|
|
where this as misleading as well, for example used tmpfs space
|
|
count as Cached but can not be reclaimed on demand.
|
|
See https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt.
|
|
"""
|
|
return utils.get_mem_free_plus_buffers_and_cached()
|
|
|
|
def get_ec_temperatures(self):
|
|
"""Uses ectool to return a list of all sensor temperatures in Celsius.
|
|
"""
|
|
return utils.get_ec_temperatures()
|
|
|
|
def get_current_temperature_max(self):
|
|
"""
|
|
Returns the highest reported board temperature (all sensors) in Celsius.
|
|
"""
|
|
return utils.get_current_temperature_max()
|
|
|
|
def get_current_board(self):
|
|
"""Returns the current device board name."""
|
|
return utils.get_current_board()
|
|
|
|
|
|
def get_chromeos_release_version(self):
|
|
"""Returns chromeos version in device under test as string. None on
|
|
fail.
|
|
"""
|
|
return utils.get_chromeos_release_version()
|
|
|
|
def get_num_allocated_file_handles(self):
|
|
"""
|
|
Returns the number of currently allocated file handles.
|
|
"""
|
|
return utils.get_num_allocated_file_handles()
|
|
|
|
def get_storage_statistics(self, device=None):
|
|
"""
|
|
Fetches statistics for a storage device.
|
|
"""
|
|
return utils.get_storage_statistics(device)
|
|
|
|
def start_bg_worker(self, command):
|
|
"""
|
|
Start executing the command in a background worker.
|
|
"""
|
|
self._bg_worker = BackgroundWorker(command, do_process_output=True)
|
|
self._bg_worker.start()
|
|
|
|
def get_and_discard_bg_worker_output(self):
|
|
"""
|
|
Returns the output collected so far since the last call to this method.
|
|
"""
|
|
if self._bg_worker is None:
|
|
SystemFacadeNativeError('Background worker has not been started.')
|
|
|
|
return self._bg_worker.get_and_discard_output()
|
|
|
|
def stop_bg_worker(self):
|
|
"""
|
|
Stop the worker.
|
|
"""
|
|
if self._bg_worker is None:
|
|
SystemFacadeNativeError('Background worker has not been started.')
|
|
|
|
self._bg_worker.stop()
|
|
self._bg_worker = None
|
|
|
|
|
|
class BackgroundWorker(object):
|
|
"""
|
|
Worker intended for executing a command in the background and collecting its
|
|
output.
|
|
"""
|
|
|
|
def __init__(self, command, do_process_output=False):
|
|
self._bg_job = None
|
|
self._command = command
|
|
self._do_process_output = do_process_output
|
|
self._output_lock = threading.Lock()
|
|
self._process_output_thread = None
|
|
self._stdout = six.StringIO()
|
|
|
|
def start(self):
|
|
"""
|
|
Start executing the command.
|
|
"""
|
|
self._bg_job = utils.BgJob(self._command, stdout_tee=self._stdout)
|
|
self._bg_job.sp.poll()
|
|
if self._bg_job.sp.returncode is not None:
|
|
self._exit_bg_job()
|
|
|
|
if self._do_process_output:
|
|
self._process_output_thread = threading.Thread(
|
|
target=self._process_output)
|
|
self._process_output_thread.start()
|
|
|
|
def _process_output(self, sleep_interval=0.01):
|
|
while self._do_process_output:
|
|
with self._output_lock:
|
|
self._bg_job.process_output()
|
|
time.sleep(sleep_interval)
|
|
|
|
def get_and_discard_output(self):
|
|
"""
|
|
Returns the output collected so far and then clears the output buffer.
|
|
In other words, subsequent calls to this method will not include output
|
|
that has already been returned before.
|
|
"""
|
|
output = ""
|
|
with self._output_lock:
|
|
self._stdout.flush()
|
|
output = self._stdout.getvalue()
|
|
self._stdout.truncate(0)
|
|
self._stdout.seek(0)
|
|
return output
|
|
|
|
def stop(self):
|
|
"""
|
|
Stop executing the command.
|
|
"""
|
|
if self._do_process_output:
|
|
self._do_process_output = False
|
|
self._process_output_thread.join(1)
|
|
self._exit_bg_job()
|
|
|
|
def _exit_bg_job(self):
|
|
utils.nuke_subprocess(self._bg_job.sp)
|
|
utils.join_bg_jobs([self._bg_job])
|
|
if self._bg_job.result.exit_status > 0:
|
|
raise SystemFacadeNativeError('Background job failed: %s' %
|
|
self._bg_job.result.command)
|