239 lines
9.6 KiB
Python
239 lines
9.6 KiB
Python
# 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.
|
|
"""Implementation of the graphics_TraceReplayExtended server test."""
|
|
|
|
import logging
|
|
import os
|
|
import threading
|
|
import time
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.server import test
|
|
from autotest_lib.server.cros.graphics import graphics_power
|
|
from autotest_lib.server.site_tests.tast import tast
|
|
|
|
|
|
class TastManagerThread(threading.Thread):
|
|
"""Thread for running a local tast test from an autotest server test."""
|
|
|
|
def __init__(self,
|
|
host,
|
|
tast_instance,
|
|
client_test,
|
|
max_duration_minutes,
|
|
build_bundle,
|
|
varslist=None,
|
|
command_args=None):
|
|
"""Initializes the thread.
|
|
|
|
Args:
|
|
host: An autotest host instance.
|
|
tast_instance: An instance of the tast.tast() class.
|
|
client_test: String identifying which tast test to run.
|
|
max_duration_minutes: Float defining the maximum running time of the
|
|
managed sub-test.
|
|
build_bundle: String defining which tast test bundle to build and
|
|
query for the client_test.
|
|
varslist: list of strings that define dynamic variables made
|
|
available to tast tests at runtime via `tast run -var=name=value
|
|
...`. Each string should be formatted as 'name=value'.
|
|
command_args: list of strings that are passed as args to the `tast
|
|
run` command.
|
|
"""
|
|
super(TastManagerThread, self).__init__(name=__name__)
|
|
self.tast = tast_instance
|
|
self.tast.initialize(
|
|
host=host,
|
|
test_exprs=[client_test],
|
|
ignore_test_failures=True,
|
|
max_run_sec=max_duration_minutes * 60,
|
|
command_args=command_args if command_args else [],
|
|
build_bundle=build_bundle,
|
|
varslist=varslist)
|
|
|
|
def run(self):
|
|
logging.info('Started thread: %s', self.__class__.__name__)
|
|
self.tast.run_once()
|
|
|
|
|
|
class graphics_TraceReplayExtended(test.test):
|
|
"""Autotest server test for running repeated trace replays.
|
|
|
|
This test simultaneously initiates system performance logging and extended
|
|
trace replay processes on a target host, and parses their test results for
|
|
combined analysis and reporting.
|
|
"""
|
|
version = 1
|
|
|
|
@staticmethod
|
|
def _initialize_dir_on_host(host, directory):
|
|
"""Initialize a directory to a consistent (empty) state on the host.
|
|
|
|
Args:
|
|
host: An autotest host instance.
|
|
directory: String defining the location of the directory to
|
|
initialize.
|
|
|
|
Raises:
|
|
TestFail: If the directory cannot be initialized.
|
|
"""
|
|
try:
|
|
host.run('rm -r %(0)s 2>/dev/null || true; ! test -d %(0)s' %
|
|
{'0': directory})
|
|
host.run('mkdir -p %s' % directory)
|
|
except (error.AutotestHostRunCmdError, error.AutoservRunError) as err:
|
|
logging.exception(err)
|
|
raise error.TestFail(
|
|
'Failed to initialize directory "%s" on the test host' %
|
|
directory)
|
|
|
|
@staticmethod
|
|
def _cleanup_dir_on_host(host, directory):
|
|
"""Ensure that a directory and its contents are deleted on the host.
|
|
|
|
Args:
|
|
host: An autotest host instance.
|
|
directory: String defining the location of the directory to delete.
|
|
|
|
Raises:
|
|
TestFail: If the directory remains on the host.
|
|
"""
|
|
try:
|
|
host.run('rm -r %(0)s || true; ! test -d %(0)s' % {'0': directory})
|
|
except (error.AutotestHostRunCmdError, error.AutoservRunError) as err:
|
|
logging.exception(err)
|
|
raise error.TestFail(
|
|
'Failed to cleanup directory "%s" on the test host' % directory)
|
|
|
|
def run_once(self,
|
|
host,
|
|
client_tast_test,
|
|
max_duration_minutes,
|
|
tast_build_bundle='cros',
|
|
tast_varslist=None,
|
|
tast_command_args=None):
|
|
"""Runs the test.
|
|
|
|
Args:
|
|
host: An autotest host instance.
|
|
client_tast_test: String defining which tast test to run.
|
|
max_duration_minutes: Float defining the maximum running time of the
|
|
managed sub-test.
|
|
tast_build_bundle: String defining which tast test bundle to build
|
|
and query for the client_test.
|
|
tast_varslist: list of strings that define dynamic variables made
|
|
available to tast tests at runtime via `tast run -var=name=value
|
|
...`. Each string should be formatted as 'name=value'.
|
|
tast_command_args: list of strings that are passed as args to the
|
|
`tast run` command.
|
|
"""
|
|
# Construct a suffix tag indicating which managing test is using logged
|
|
# data from the graphics_Power subtest.
|
|
trace_name = client_tast_test.split('.')[-1]
|
|
|
|
# workaround for running test locally since crrev/c/2374267 and
|
|
# crrev/i/2374267
|
|
if not tast_command_args:
|
|
tast_command_args = []
|
|
tast_command_args.extend([
|
|
'extraallowedbuckets=termina-component-testing,cros-containers-staging'
|
|
])
|
|
|
|
# Define paths of signal files for basic RPC/IPC between sub-tests.
|
|
temp_io_root = '/tmp/%s/' % self.__class__.__name__
|
|
result_dir = os.path.join(temp_io_root, 'results')
|
|
signal_running_file = os.path.join(temp_io_root, 'signal_running')
|
|
signal_checkpoint_file = os.path.join(temp_io_root, 'signal_checkpoint')
|
|
|
|
# This test is responsible for creating/deleting root and resultdir.
|
|
logging.debug('Creating temporary IPC/RPC dir: %s', temp_io_root)
|
|
self._initialize_dir_on_host(host, temp_io_root)
|
|
self._initialize_dir_on_host(host, result_dir)
|
|
|
|
# Start background system performance monitoring process on the test
|
|
# target (via an autotest client 'power_Test').
|
|
logging.debug('Connecting to autotest client on host')
|
|
graphics_power_thread = graphics_power.GraphicsPowerThread(
|
|
host=host,
|
|
max_duration_minutes=max_duration_minutes,
|
|
test_tag='Trace' + '.' + trace_name,
|
|
pdash_note='',
|
|
result_dir=result_dir,
|
|
signal_running_file=signal_running_file,
|
|
signal_checkpoint_file=signal_checkpoint_file)
|
|
graphics_power_thread.start()
|
|
|
|
logging.info('Waiting for graphics_Power subtest to initialize...')
|
|
try:
|
|
graphics_power_thread.wait_until_running(timeout=120)
|
|
except Exception as err:
|
|
logging.exception(err)
|
|
raise error.TestFail(
|
|
'An error occured during graphics_Power subtest initialization')
|
|
logging.info('The graphics_Power subtest was properly initialized')
|
|
|
|
# Start repeated trace replay process on the test target (via a tast
|
|
# local test).
|
|
logging.info('Running Tast test: %s', client_tast_test)
|
|
tast_outputdir = os.path.join(self.outputdir, 'tast')
|
|
if not os.path.exists(tast_outputdir):
|
|
logging.debug('Creating tast outputdir: %s', tast_outputdir)
|
|
os.makedirs(tast_outputdir)
|
|
|
|
if not tast_varslist:
|
|
tast_varslist = []
|
|
tast_varslist.extend([
|
|
'graphics.TraceReplayExtended.resultDir=' + result_dir,
|
|
'graphics.TraceReplayExtended.signalRunningFile=' +
|
|
signal_running_file,
|
|
'graphics.TraceReplayExtended.signalCheckpointFile=' +
|
|
signal_checkpoint_file,
|
|
])
|
|
|
|
tast_instance = tast.tast(
|
|
job=self.job, bindir=self.bindir, outputdir=tast_outputdir)
|
|
tast_manager_thread = TastManagerThread(
|
|
host,
|
|
tast_instance,
|
|
client_tast_test,
|
|
max_duration_minutes,
|
|
tast_build_bundle,
|
|
varslist=tast_varslist,
|
|
command_args=tast_command_args)
|
|
tast_manager_thread.start()
|
|
|
|
# Block until both subtests finish.
|
|
threads = [graphics_power_thread, tast_manager_thread]
|
|
stop_attempts = 0
|
|
while threads:
|
|
# TODO(ryanneph): Move stop signal emission to tast test instance.
|
|
if (not tast_manager_thread.is_alive() and
|
|
graphics_power_thread.is_alive() and stop_attempts < 1):
|
|
logging.info('Attempting to stop graphics_Power thread')
|
|
graphics_power_thread.stop(timeout=0)
|
|
stop_attempts += 1
|
|
|
|
# Raise test failure if graphics_Power thread ends before tast test.
|
|
if (not graphics_power_thread.is_alive() and
|
|
tast_manager_thread.is_alive()):
|
|
raise error.TestFail(
|
|
'The graphics_Power subtest ended too soon.')
|
|
|
|
for thread in list(threads):
|
|
if not thread.is_alive():
|
|
logging.info('Thread "%s" has ended',
|
|
thread.__class__.__name__)
|
|
threads.remove(thread)
|
|
time.sleep(1)
|
|
|
|
client_result_dir = os.path.join(self.outputdir, 'client_results')
|
|
logging.info('Saving client results to %s', client_result_dir)
|
|
host.get_file(result_dir, client_result_dir)
|
|
|
|
# Ensure the host filesystem is clean for the next test.
|
|
self._cleanup_dir_on_host(host, result_dir)
|
|
self._cleanup_dir_on_host(host, temp_io_root)
|
|
|
|
# TODO(ryanneph): Implement results parsing/analysis/reporting
|