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.
217 lines
8.8 KiB
217 lines
8.8 KiB
# Copyright 2020 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.
|
|
"""Provides a management class for using graphics_Power in server tests."""
|
|
|
|
import logging
|
|
import os
|
|
import tempfile
|
|
import threading
|
|
import time
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.server import autotest
|
|
|
|
ROOT_DIR = '/tmp/graphics_Power/'
|
|
DEFAULT_SIGNAL_RUNNING_FILE = os.path.join(ROOT_DIR, 'signal_running')
|
|
DEFAULT_SIGNAL_CHECKPOINT_FILE = os.path.join(ROOT_DIR, 'signal_checkpoint')
|
|
|
|
|
|
class GraphicsPowerThread(threading.Thread):
|
|
"""Thread for running the graphics_Power client test.
|
|
|
|
Provides a threaded management interface for the graphics_Power subtest.
|
|
This class can be used from an autotest server test to log system
|
|
performance metrics in the background on the test host.
|
|
"""
|
|
|
|
class Error(Exception):
|
|
"""Base error that can be inherited to define more specific errors."""
|
|
pass
|
|
|
|
class ThreadNotInitializedError(Error):
|
|
"""An error indicating that the thread was not properly initialized."""
|
|
pass
|
|
|
|
class InitTimeoutError(Error):
|
|
"""An error indicating that a timeout occurred during a blocking call."""
|
|
pass
|
|
|
|
def __init__(self,
|
|
host,
|
|
max_duration_minutes,
|
|
sample_rate_seconds=1,
|
|
test_tag=None,
|
|
pdash_note=None,
|
|
result_dir=None,
|
|
signal_running_file=DEFAULT_SIGNAL_RUNNING_FILE,
|
|
signal_checkpoint_file=DEFAULT_SIGNAL_CHECKPOINT_FILE):
|
|
"""Initializes the thread.
|
|
|
|
Args:
|
|
host: An autotest host instance.
|
|
max_duration_minutes: Float defining the maximum running time of the
|
|
managed sub-test.
|
|
sample_rate_seconds: Optional; Number defining seconds between data
|
|
point acquisition.
|
|
test_tag: Optional; String describing the test that initiated this
|
|
monitoring process; appended to the true test name.
|
|
pdash_note: Optional; A tag that is included as a filter field on
|
|
the ChromeOS power-dashboard.
|
|
result_dir: Optional; String defining the location on the test
|
|
target where post-processed results from this sub-test should be
|
|
saved for retrieval by the managing test process. Set to None if
|
|
results output is not be created.
|
|
signal_running_file: Optional; String defining the location of the
|
|
'running' RPC flag file on the test target. Removal of this file
|
|
triggers the subtest to finish logging and stop gracefully.
|
|
signal_checkpoint_file: Optional; String defining the location of
|
|
the 'checkpoint' RPC flag file on the test target. Modifying
|
|
this file triggers the subtest to create a checkpoint with name
|
|
equal to the utf-8-encoded contents of the first-line and
|
|
optional alternative start time (in seconds since the epoch)
|
|
equal to the second line of the file.
|
|
"""
|
|
super(GraphicsPowerThread, self).__init__(name=__name__)
|
|
self._running = False
|
|
self._autotest_client = autotest.Autotest(host)
|
|
self._host = host
|
|
self._test_thread = None
|
|
|
|
self.max_duration_minutes = max_duration_minutes
|
|
self.sample_rate_seconds = sample_rate_seconds
|
|
self.test_tag = test_tag
|
|
self.pdash_note = pdash_note
|
|
self.result_dir = result_dir
|
|
self.signal_running_file = signal_running_file
|
|
self.signal_checkpoint_file = signal_checkpoint_file
|
|
|
|
def is_running(self):
|
|
"""Return a bool indicating the 'running' state of the subtest.
|
|
|
|
This check can be used to ensure system logging is initialized and
|
|
running before beginning other subtests.
|
|
"""
|
|
try:
|
|
self._host.run('test -f %s' % self.signal_running_file)
|
|
return True
|
|
except (error.AutotestHostRunCmdError, error.AutoservRunError):
|
|
return False
|
|
|
|
def wait_until_running(self, timeout=120):
|
|
"""Block execution until the subtest reports it is logging properly.
|
|
|
|
Args:
|
|
timeout: Optional; Float that defines how long to block before
|
|
timeout occurs. If timeout=None, then block forever
|
|
|
|
Raises:
|
|
RuntimeError: The subtest ended unexpectedly before initialization
|
|
finished.
|
|
GraphicsPowerThread.ThreadNotInitializedError: The thread hasn't
|
|
been started by the managing server test yet.
|
|
GraphicsPowerThread.InitTimeoutError: A timeout occurred while
|
|
waiting for subtest to report itself as running.
|
|
"""
|
|
if timeout:
|
|
time_start = time.time()
|
|
time_end = time_start + timeout
|
|
while True:
|
|
if timeout and time.time() >= time_end:
|
|
self.stop()
|
|
raise self.InitTimeoutError(
|
|
'The graphics_Power subtest initialization timed out')
|
|
if not self.is_alive():
|
|
raise RuntimeError(
|
|
'The graphics_Power subtest failed to initialize')
|
|
if self.is_running():
|
|
break
|
|
time.sleep(1)
|
|
|
|
if not self._test_thread:
|
|
raise self.ThreadNotInitializedError
|
|
|
|
def stop(self, timeout=None):
|
|
"""Gracefully stop the subtest on the test host.
|
|
|
|
If timeout is None, then this is a blocking call that waits forever.
|
|
If timeout is a positive number, then it waits for 'timeout' seconds.
|
|
If timeout is 0, then it returns immediately.
|
|
|
|
Args:
|
|
timeout: Time (seconds) before giving up on joining the thread.
|
|
|
|
Returns:
|
|
A bool indicating if thread was stopped.
|
|
"""
|
|
self._running = False
|
|
self.join(timeout)
|
|
return not self.is_alive()
|
|
|
|
def checkpoint_measurements(self, name, start_time=None):
|
|
"""Save the current log buffers with an associated name.
|
|
|
|
The power-dashboard displays time series data in one or more
|
|
checkpoints that can be used to annotate different phases of a test.
|
|
|
|
By saving a checkpoint, the time series data collected since the end of
|
|
the most recently committed checkpoint (or the test start if no
|
|
checkpoints are saved yet) is annotated on the power-dashboard with the
|
|
specified name. The checkpoint start time can be adjusted with the
|
|
optional 'start_time' argument.
|
|
|
|
Args:
|
|
name: String defining the saved checkpoint's name.
|
|
start_time: Optional; Float indicating the time (in seconds since
|
|
the epoch) at which this checkpoint should actually start. This
|
|
functionally discards data from the beginning of the logged
|
|
duration until start_time.
|
|
"""
|
|
with tempfile.NamedTemporaryFile('w') as tf:
|
|
tf.write(str(name) + '\n')
|
|
if start_time:
|
|
tf.write(str(start_time))
|
|
tf.flush()
|
|
self._host.send_file(tf.name, self.signal_checkpoint_file)
|
|
|
|
def _run_test_async(self):
|
|
self._autotest_client.run_test(
|
|
'graphics_Power',
|
|
tag=self.test_tag,
|
|
max_duration_minutes=self.max_duration_minutes,
|
|
sample_rate_seconds=self.sample_rate_seconds,
|
|
pdash_note=self.pdash_note,
|
|
result_dir=self.result_dir,
|
|
signal_running_file=self.signal_running_file,
|
|
signal_checkpoint_file=self.signal_checkpoint_file)
|
|
|
|
def run(self):
|
|
self._running = True
|
|
self._test_thread = threading.Thread(target=self._run_test_async)
|
|
self._test_thread.start()
|
|
logging.info('Started thread: %s', self.__class__.__name__)
|
|
|
|
def send_stop_signal_and_join():
|
|
"""Emits a stop signal to the test host and joins the thread.
|
|
|
|
Deletes a monitored file on the test host over ssh and waits for
|
|
the graphics_Power sub-test to end gracefully as a consequence.
|
|
"""
|
|
while True:
|
|
self._host.run('rm %s 2>/dev/null || true' %
|
|
self.signal_running_file)
|
|
self._test_thread.join(5)
|
|
if not self._test_thread.is_alive():
|
|
break
|
|
|
|
while True:
|
|
time.sleep(1)
|
|
if not self._test_thread.is_alive():
|
|
logging.debug('The graphics_Power subtest ended')
|
|
break
|
|
elif not self._running:
|
|
logging.debug(
|
|
'Sending stop signal to the graphics_Power subtest')
|
|
send_stop_signal_and_join()
|
|
break
|