telemetry_Crosperf: Remove device_setup_utils in telemetry_Croseprf
This patch uses device_setup_utils imported to autotest earlier and
reformatted telemetry_Crosperf.
TEST=tested with test_that.
BUG=chromium:984790
Change-Id: Ic25f89b492cec265c70ce2c1849fe280bebd792b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2016089
Tested-by: Zhizhou Yang <zhizhouy@google.com>
Auto-Submit: Zhizhou Yang <zhizhouy@google.com>
Commit-Queue: Zhizhou Yang <zhizhouy@google.com>
Reviewed-by: Katherine Threlkeld <kathrelkeld@chromium.org>
diff --git a/server/site_tests/telemetry_Crosperf/device_setup_utils.py b/server/site_tests/telemetry_Crosperf/device_setup_utils.py
deleted file mode 100755
index 6ca9da1..0000000
--- a/server/site_tests/telemetry_Crosperf/device_setup_utils.py
+++ /dev/null
@@ -1,525 +0,0 @@
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
-#
-# Copyright 2019 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.
-
-"""Utils for setting devices
-
-This script provides utils to set device specs.
-"""
-
-from __future__ import division
-from __future__ import print_function
-
-__author__ = 'zhizhouy@google.com (Zhizhou Yang)'
-
-import logging
-import re
-import time
-
-from contextlib import contextmanager
-
-class DutWrapper(object):
- """Wrap DUT parameters inside."""
-
- def __init__(self,
- dut,
- dut_config=None):
- self.dut = dut
- self.dut_config = dut_config
-
- def RunCommandOnDut(self, command, ignore_status=False):
- """Helper function to run command on DUT."""
- result = self.dut.run(command, ignore_status=ignore_status)
-
- ret, msg, err_msg = result.exit_status, result.stdout, result.stderr
-
- if ret:
- err_msg = ('Command execution on DUT %s failed.\n'
- 'Failing command: %s\n'
- 'returned %d\n'
- 'Error message: %s' % (self.dut.hostname, command,
- ret, err_msg))
- if ignore_status:
- logging.warning(err_msg +
- '\n(Failure is considered non-fatal. Continue.)')
- else:
- logging.error(err_msg)
-
- return ret, msg, err_msg
-
- def DisableASLR(self):
- """Disable ASLR on DUT."""
- disable_aslr = ('set -e; '
- 'if [[ -e /proc/sys/kernel/randomize_va_space ]]; then '
- ' echo 0 > /proc/sys/kernel/randomize_va_space; '
- 'fi')
- logging.info('Disable ASLR.')
- self.RunCommandOnDut(disable_aslr)
-
- def SetCpuGovernor(self, governor, ignore_status=False):
- """Setup CPU Governor on DUT."""
- set_gov_cmd = (
- 'for f in `ls -d /sys/devices/system/cpu/cpu*/cpufreq 2>/dev/null`; do '
- # Skip writing scaling_governor if cpu is offline.
- ' [[ -e ${f/cpufreq/online} ]] && grep -q 0 ${f/cpufreq/online} '
- ' && continue; '
- ' cd $f; '
- ' if [[ -e scaling_governor ]]; then '
- ' echo %s > scaling_governor; fi; '
- 'done; ')
- logging.info('Setup CPU Governor: %s.', governor)
- ret, _, _ = self.RunCommandOnDut(
- set_gov_cmd % governor, ignore_status=ignore_status)
- return ret
-
- def DisableTurbo(self):
- """Disable Turbo on DUT"""
- dis_turbo_cmd = (
- 'if [[ -e /sys/devices/system/cpu/intel_pstate/no_turbo ]]; then '
- ' if grep -q 0 /sys/devices/system/cpu/intel_pstate/no_turbo; then '
- ' echo -n 1 > /sys/devices/system/cpu/intel_pstate/no_turbo; '
- ' fi; '
- 'fi; ')
- logging.info('Disable Turbo.')
- self.RunCommandOnDut(dis_turbo_cmd)
-
- def SetupCpuUsage(self):
- """Setup CPU usage.
-
- Based on self.dut_config['cpu_usage'] configure CPU cores
- utilization.
- """
-
- if self.dut_config.get('cpu_usage') in ('big_only', 'little_only'):
- _, arch, _ = self.RunCommandOnDut('uname -m')
-
- if arch.lower().startswith('arm') or arch.lower().startswith('aarch64'):
- self.SetupArmCores()
-
- def SetupArmCores(self):
- """Setup ARM big/little cores."""
-
- # CPU implemeters/part numbers of big/LITTLE CPU.
- # Format: dict(CPU implementer: set(CPU part numbers))
- LITTLE_CORES = {
- '0x41': {
- '0xd01', # Cortex A32
- '0xd03', # Cortex A53
- '0xd04', # Cortex A35
- '0xd05', # Cortex A55
- },
- }
- BIG_CORES = {
- '0x41': {
- '0xd07', # Cortex A57
- '0xd08', # Cortex A72
- '0xd09', # Cortex A73
- '0xd0a', # Cortex A75
- '0xd0b', # Cortex A76
- },
- }
-
- # Values of CPU Implementer and CPU part number are exposed by cpuinfo.
- # Format:
- # =================
- # processor : 0
- # model name : ARMv8 Processor rev 4 (v8l)
- # BogoMIPS : 48.00
- # Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
- # CPU implementer : 0x41
- # CPU architecture: 8
- # CPU variant : 0x0
- # CPU part : 0xd03
- # CPU revision : 4
-
- _, cpuinfo, _ = self.RunCommandOnDut('cat /proc/cpuinfo')
-
- # List of all CPU cores: 0, 1, ..
- proc_matches = re.findall(r'^processor\s*: (\d+)$', cpuinfo, re.MULTILINE)
- # List of all corresponding CPU implementers
- impl_matches = re.findall(r'^CPU implementer\s*: (0x[\da-f]+)$', cpuinfo,
- re.MULTILINE)
- # List of all corresponding CPU part numbers
- part_matches = re.findall(r'^CPU part\s*: (0x[\da-f]+)$', cpuinfo,
- re.MULTILINE)
- assert len(proc_matches) == len(impl_matches)
- assert len(part_matches) == len(impl_matches)
-
- all_cores = set(proc_matches)
- dut_big_cores = {
- core
- for core, impl, part in zip(proc_matches, impl_matches, part_matches)
- if impl in BIG_CORES and part in BIG_CORES[impl]
- }
- dut_lit_cores = {
- core
- for core, impl, part in zip(proc_matches, impl_matches, part_matches)
- if impl in LITTLE_CORES and part in LITTLE_CORES[impl]
- }
-
- if self.dut_config['cpu_usage'] == 'big_only':
- cores_to_enable = dut_big_cores
- cores_to_disable = all_cores - dut_big_cores
- elif self.dut_config['cpu_usage'] == 'little_only':
- cores_to_enable = dut_lit_cores
- cores_to_disable = all_cores - dut_lit_cores
- else:
- logging.warning(
- 'cpu_usage=%s is not supported on ARM.\n'
- 'Ignore ARM CPU setup and continue.', self.dut_config['cpu_usage'])
- return
-
- if cores_to_enable:
- cmd_enable_cores = ('echo 1 | tee /sys/devices/system/cpu/cpu{%s}/online'
- % ','.join(sorted(cores_to_enable)))
-
- cmd_disable_cores = ''
- if cores_to_disable:
- cmd_disable_cores = (
- 'echo 0 | tee /sys/devices/system/cpu/cpu{%s}/online' % ','.join(
- sorted(cores_to_disable)))
-
- self.RunCommandOnDut('; '.join([cmd_enable_cores, cmd_disable_cores]))
- else:
- # If there are no cores enabled by dut_config then configuration
- # is invalid for current platform and should be ignored.
- logging.warning(
- '"cpu_usage" is invalid for targeted platform.\n'
- 'dut_config[cpu_usage]=%s\n'
- 'dut big cores: %s\n'
- 'dut little cores: %s\n'
- 'Ignore ARM CPU setup and continue.', (self.dut_config['cpu_usage'],
- dut_big_cores, dut_lit_cores))
-
- def GetCpuOnline(self):
- """Get online status of CPU cores.
-
- Return dict of {int(cpu_num): <0|1>}.
- """
- get_cpu_online_cmd = ('paste -d" "'
- ' <(ls /sys/devices/system/cpu/cpu*/online)'
- ' <(cat /sys/devices/system/cpu/cpu*/online)')
- _, online_output_str, _ = self.RunCommandOnDut(get_cpu_online_cmd)
-
- # Here is the output we expect to see:
- # -----------------
- # /sys/devices/system/cpu/cpu0/online 0
- # /sys/devices/system/cpu/cpu1/online 1
-
- cpu_online = {}
- cpu_online_match = re.compile(r'^[/\S]+/cpu(\d+)/[/\S]+\s+(\d+)$')
- for line in online_output_str.splitlines():
- match = cpu_online_match.match(line)
- if match:
- cpu = int(match.group(1))
- status = int(match.group(2))
- cpu_online[cpu] = status
- # At least one CPU has to be online.
- assert cpu_online
-
- return cpu_online
-
- def SetupCpuFreq(self, online_cores):
- """Setup CPU frequency.
-
- Based on self.dut_config['cpu_freq_pct'] setup frequency of online CPU cores
- to a supported value which is less or equal to (freq_pct * max_freq / 100)
- limited by min_freq.
-
- NOTE: scaling_available_frequencies support is required.
- Otherwise the function has no effect.
- """
- freq_percent = self.dut_config['cpu_freq_pct']
- list_all_avail_freq_cmd = ('ls /sys/devices/system/cpu/cpu{%s}/cpufreq/'
- 'scaling_available_frequencies')
- # Ignore error to support general usage of frequency setup.
- # Not all platforms support scaling_available_frequencies.
- ret, all_avail_freq_str, _ = self.RunCommandOnDut(
- list_all_avail_freq_cmd % ','.join(str(core) for core in online_cores),
- ignore_status=True)
- if ret or not all_avail_freq_str:
- # No scalable frequencies available for the core.
- return ret
- for avail_freq_path in all_avail_freq_str.split():
- # Get available freq from every scaling_available_frequency path.
- # Error is considered fatal in self.RunCommandOnDut().
- _, avail_freq_str, _ = self.RunCommandOnDut('cat ' + avail_freq_path)
- assert avail_freq_str
-
- all_avail_freq = sorted(
- int(freq_str) for freq_str in avail_freq_str.split())
- min_freq = all_avail_freq[0]
- max_freq = all_avail_freq[-1]
- # Calculate the frequency we are targeting.
- target_freq = round(max_freq * freq_percent / 100)
- # More likely it's not in the list of supported frequencies
- # and our goal is to find the one which is less or equal.
- # Default is min and we will try to maximize it.
- avail_ngt_target = min_freq
- # Find the largest not greater than the target.
- for next_largest in reversed(all_avail_freq):
- if next_largest <= target_freq:
- avail_ngt_target = next_largest
- break
-
- max_freq_path = avail_freq_path.replace('scaling_available_frequencies',
- 'scaling_max_freq')
- min_freq_path = avail_freq_path.replace('scaling_available_frequencies',
- 'scaling_min_freq')
- # With default ignore_status=False we expect 0 status or Fatal error.
- self.RunCommandOnDut('echo %s | tee %s %s' %
- (avail_ngt_target, max_freq_path, min_freq_path))
-
- def WaitCooldown(self):
- """Wait for DUT to cool down to certain temperature."""
- waittime = 0
- timeout_in_sec = int(self.dut_config['cooldown_time']) * 60
- # Temperature from sensors come in uCelsius units.
- temp_in_ucels = int(self.dut_config['cooldown_temp']) * 1000
- sleep_interval = 30
-
- # Wait until any of two events occurs:
- # 1. CPU cools down to a specified temperature.
- # 2. Timeout cooldown_time expires.
- # For the case when targeted temperature is not reached within specified
- # timeout the benchmark is going to start with higher initial CPU temp.
- # In the worst case it may affect test results but at the same time we
- # guarantee the upper bound of waiting time.
- # TODO(denik): Report (or highlight) "high" CPU temperature in test results.
- # "high" should be calculated based on empirical data per platform.
- # Based on such reports we can adjust CPU configuration or
- # cooldown limits accordingly.
- while waittime < timeout_in_sec:
- _, temp_output, _ = self.RunCommandOnDut(
- 'cat /sys/class/thermal/thermal_zone*/temp', ignore_status=True)
- if any(int(temp) > temp_in_ucels for temp in temp_output.split()):
- time.sleep(sleep_interval)
- waittime += sleep_interval
- else:
- # Exit the loop when:
- # 1. Reported temp numbers from all thermal sensors do not exceed
- # 'cooldown_temp' or
- # 2. No data from the sensors.
- break
-
- logging.info('Cooldown wait time: %.1f min', (waittime / 60))
- return waittime
-
- def DecreaseWaitTime(self):
- """Change the ten seconds wait time for pagecycler to two seconds."""
- FILE = '/usr/local/telemetry/src/tools/perf/page_sets/page_cycler_story.py'
- ret = self.RunCommandOnDut('ls ' + FILE)
-
- if not ret:
- sed_command = 'sed -i "s/_TTI_WAIT_TIME = 10/_TTI_WAIT_TIME = 2/g" '
- self.RunCommandOnDut(sed_command + FILE)
-
- def StopUI(self):
- """Stop UI on DUT."""
- self.RunCommandOnDut('stop ui')
-
- def StartUI(self):
- """Start UI on DUT."""
- self.RunCommandOnDut('start ui')
-
- def KerncmdUpdateNeeded(self, intel_pstate):
- """Check whether kernel cmdline update is needed.
-
- Args:
- intel_pstate: kernel command line argument (active, passive, no_hwp)
-
- Returns:
- True if update is needed.
- """
-
- good = 0
-
- # Check that dut platform supports hwp
- cmd = "grep -q '^flags.*hwp' /proc/cpuinfo"
- ret_code, _, _ = self.RunCommandOnDut(cmd, ignore_status=True)
- if ret_code != good:
- # Intel hwp is not supported, update is not needed.
- return False
-
- kern_cmdline_cmd = 'grep -q "intel_pstate=%s" /proc/cmdline' % intel_pstate
- ret_code, _, _ = self.RunCommandOnDut(kern_cmdline_cmd, ignore_status=True)
- logging.info('grep /proc/cmdline returned %d', ret_code)
- if (intel_pstate and ret_code == good or
- not intel_pstate and ret_code != good):
- # No need to updated cmdline if:
- # 1. We are setting intel_pstate and we found it is already set.
- # 2. Not using intel_pstate and it is not in cmdline.
- return False
-
- # Otherwise we need to update intel_pstate.
- return True
-
- def UpdateKerncmdIntelPstate(self, intel_pstate):
- """Update kernel command line.
-
- Args:
- intel_pstate: kernel command line argument (active, passive, no_hwp)
- """
-
- good = 0
-
- # First phase is to remove rootfs verification to allow cmdline change.
- remove_verif_cmd = ' '.join([
- '/usr/share/vboot/bin/make_dev_ssd.sh',
- '--remove_rootfs_verification',
- '--partition %d',
- ])
- # Command for partition 2.
- verif_part2_failed, _, _ = self.RunCommandOnDut(
- remove_verif_cmd % 2, ignore_status=True)
- # Command for partition 4
- # Some machines in the lab use partition 4 to boot from,
- # so cmdline should be update for both partitions.
- verif_part4_failed, _, _ = self.RunCommandOnDut(
- remove_verif_cmd % 4, ignore_status=True)
- if verif_part2_failed or verif_part4_failed:
- logging.error(
- 'ERROR. Failed to update kernel cmdline on partition %d.\n'
- 'Remove verification failed with status %d',
- 2 if verif_part2_failed else 4, verif_part2_failed or
- verif_part4_failed)
-
- self.RunCommandOnDut('reboot && exit')
- # Give enough time for dut to complete reboot
- # TODO(denik): Replace with the function checking machine availability.
- time.sleep(30)
-
- # Second phase to update intel_pstate in kernel cmdline.
- kern_cmdline = '\n'.join([
- 'tmpfile=$(mktemp)',
- 'partnumb=%d',
- 'pstate=%s',
- # Store kernel cmdline in a temp file.
- '/usr/share/vboot/bin/make_dev_ssd.sh --partition ${partnumb}'
- ' --save_config ${tmpfile}',
- # Remove intel_pstate argument if present.
- "sed -i -r 's/ intel_pstate=[A-Za-z_]+//g' ${tmpfile}.${partnumb}",
- # Insert intel_pstate with a new value if it is set.
- '[[ -n ${pstate} ]] &&'
- ' sed -i -e \"s/ *$/ intel_pstate=${pstate}/\" ${tmpfile}.${partnumb}',
- # Save the change in kernel cmdline.
- # After completion we have to reboot.
- '/usr/share/vboot/bin/make_dev_ssd.sh --partition ${partnumb}'
- ' --set_config ${tmpfile}'
- ])
- kern_part2_cmdline_cmd = kern_cmdline % (2, intel_pstate)
- logging.info(
- 'Command to change kernel command line: %s', kern_part2_cmdline_cmd)
- upd_part2_failed, _, _ = self.RunCommandOnDut(
- kern_part2_cmdline_cmd, ignore_status=True)
- # Again here we are updating cmdline for partition 4
- # in addition to partition 2. Without this some machines
- # in the lab might fail.
- kern_part4_cmdline_cmd = kern_cmdline % (4, intel_pstate)
- logging.info(
- 'Command to change kernel command line: %s', kern_part4_cmdline_cmd)
- upd_part4_failed, _, _ = self.RunCommandOnDut(
- kern_part4_cmdline_cmd, ignore_status=True)
- if upd_part2_failed or upd_part4_failed:
- logging.error(
- 'ERROR. Failed to update kernel cmdline on partition %d.\n'
- 'intel_pstate update failed with status %d',
- 2 if upd_part2_failed else 4, upd_part2_failed or upd_part4_failed)
-
- self.RunCommandOnDut('reboot && exit')
- # Wait 30s after reboot.
- time.sleep(30)
-
- # Verification phase.
- # Check that cmdline was updated.
- # Throw an exception if not.
- kern_cmdline_cmd = 'grep -q "intel_pstate=%s" /proc/cmdline' % intel_pstate
- ret_code, _, _ = self.RunCommandOnDut(kern_cmdline_cmd, ignore_status=True)
- if (intel_pstate and ret_code != good or
- not intel_pstate and ret_code == good):
- # Kernel cmdline doesn't match input intel_pstate.
- logging.error(
- 'ERROR. Failed to update kernel cmdline. '
- 'Final verification failed with status %d', ret_code)
-
- logging.info('Kernel cmdline updated successfully.')
-
- @contextmanager
- def PauseUI(self):
- """Stop UI before and Start UI after the context block.
-
- Context manager will make sure UI is always resumed at the end.
- """
- self.StopUI()
- try:
- yield
-
- finally:
- self.StartUI()
-
- def SetupDevice(self):
- """Setup device to get it ready for testing.
-
- @Returns Wait time of cool down for this benchmark run.
- """
- logging.info('Update kernel cmdline if necessary and reboot')
- intel_pstate = self.dut_config.get('intel_pstate')
- if intel_pstate and self.KerncmdUpdateNeeded(intel_pstate):
- self.UpdateKerncmdIntelPstate(intel_pstate)
-
- wait_time = 0
- # Pause UI while configuring the DUT.
- # This will accelerate setup (waiting for cooldown has x10 drop)
- # and help to reset a Chrome state left after the previous test.
- with self.PauseUI():
- # Unless the user turns on ASLR in the flag, we first disable ASLR
- # before running the benchmarks
- if not self.dut_config.get('enable_aslr'):
- self.DisableASLR()
-
- # CPU usage setup comes first where we enable/disable cores.
- self.SetupCpuUsage()
- cpu_online_status = self.GetCpuOnline()
- # List of online cores of type int (core number).
- online_cores = [
- core for core, status in cpu_online_status.items() if status
- ]
- if self.dut_config.get('cooldown_time'):
- # Setup power conservative mode for effective cool down.
- # Set ignore status since powersave may no be available
- # on all platforms and we are going to handle it.
- ret = self.SetCpuGovernor('powersave', ignore_status=True)
- if ret:
- # "powersave" is not available, use "ondemand".
- # Still not a fatal error if it fails.
- ret = self.SetCpuGovernor('ondemand', ignore_status=True)
- # TODO(denik): Run comparison test for 'powersave' and 'ondemand'
- # on scarlet and kevin64.
- # We might have to consider reducing freq manually to the min
- # if it helps to reduce waiting time.
- wait_time = self.WaitCooldown()
-
- # Setup CPU governor for the benchmark run.
- # It overwrites the previous governor settings.
- governor = self.dut_config.get('governor')
- # FIXME(denik): Pass online cores to governor setup.
- if governor:
- self.SetCpuGovernor(governor)
-
- # Disable Turbo and Setup CPU freq should ALWAYS proceed governor setup
- # since governor may change:
- # - frequency;
- # - turbo/boost.
- self.DisableTurbo()
- if self.dut_config.get('cpu_freq_pct'):
- self.SetupCpuFreq(online_cores)
-
- self.DecreaseWaitTime()
- # FIXME(denik): Currently we are not recovering the previous cpufreq
- # settings since we do reboot/setup every time anyway.
- # But it may change in the future and then we have to recover the
- # settings.
- return wait_time
diff --git a/server/site_tests/telemetry_Crosperf/telemetry_Crosperf.py b/server/site_tests/telemetry_Crosperf/telemetry_Crosperf.py
index cf270bf..ecbf950 100644
--- a/server/site_tests/telemetry_Crosperf/telemetry_Crosperf.py
+++ b/server/site_tests/telemetry_Crosperf/telemetry_Crosperf.py
@@ -17,19 +17,16 @@
from autotest_lib.server import test
from autotest_lib.server import utils
from autotest_lib.server.cros import telemetry_runner
+from autotest_lib.server.cros.crosperf import device_setup_utils
from autotest_lib.site_utils import test_runner_utils
-from device_setup_utils import DutWrapper
-
WAIT_FOR_CMD_TIMEOUT_SECS = 60
-DUT_COMMON_SSH_OPTIONS = ['-o StrictHostKeyChecking=no',
- '-o UserKnownHostsFile=/dev/null',
- '-o BatchMode=yes',
- '-o ConnectTimeout=30',
- '-o ServerAliveInterval=900',
- '-o ServerAliveCountMax=3',
- '-o ConnectionAttempts=4',
- '-o Protocol=2']
+DUT_COMMON_SSH_OPTIONS = [
+ '-o StrictHostKeyChecking=no', '-o UserKnownHostsFile=/dev/null',
+ '-o BatchMode=yes', '-o ConnectTimeout=30',
+ '-o ServerAliveInterval=900', '-o ServerAliveCountMax=3',
+ '-o ConnectionAttempts=4', '-o Protocol=2'
+]
DUT_SCP_OPTIONS = ' '.join(DUT_COMMON_SSH_OPTIONS)
RSA_KEY = '-i %s' % test_runner_utils.TEST_KEY_PATH
@@ -59,315 +56,328 @@
class telemetry_Crosperf(test.test):
- """Run one or more telemetry benchmarks under the crosperf script."""
- version = 1
-
- def scp_telemetry_results(self,
- client_ip,
- dut,
- file,
- host_dir,
- ignore_status=False):
- """Copy telemetry results from dut.
-
- @param client_ip: The ip address of the DUT
- @param dut: The autotest host object representing DUT.
- @param file: The file to copy from DUT.
- @param host_dir: The directory on host to put the file .
-
- @returns status code for scp command.
"""
- cmd = []
- src = ('root@%s:%s' %
- (dut.hostname if dut else client_ip,
- file))
- cmd.extend(['scp', DUT_SCP_OPTIONS, RSA_KEY, '-v',
- src, host_dir])
- command = ' '.join(cmd)
+ Run one or more telemetry benchmarks under the crosperf script.
- logging.debug('Retrieving Results: %s', command)
- try:
- result = utils.run(
- command,
- timeout=WAIT_FOR_CMD_TIMEOUT_SECS,
- ignore_status=ignore_status)
- exit_code = result.exit_status
- except Exception as e:
- logging.error('Failed to retrieve results: %s', e)
- raise
-
- logging.debug('command return value: %d', exit_code)
- return exit_code
-
- @contextmanager
- def no_background(self, *_args):
- """Background stub."""
- yield
-
- @contextmanager
- def run_in_background_with_log(self, cmd, dut, log_path):
- """Get cpustats periodically in background.
-
- NOTE:
- No error handling, exception or error report
- if command fails to run in background.
- Command failure is silenced.
"""
- logging.info('Running in background:\n%s', cmd)
- pid = dut.run_background(cmd)
- try:
- # TODO(denik): replace with more reliable way
- # to check run success/failure in background.
- # Wait some time before checking the process.
- check = dut.run('sleep 5; kill -0 %s' % pid, ignore_status=True)
- if check.exit_status != 0:
- # command wasn't started correctly
- logging.error(
- "Background command wasn't started correctly.\n"
- 'Command:\n%s', cmd)
- pid = ''
+ version = 1
+
+ def scp_telemetry_results(self, client_ip, dut, file, host_dir,
+ ignore_status=False):
+ """
+ Copy telemetry results from dut.
+
+ @param client_ip: The ip address of the DUT
+ @param dut: The autotest host object representing DUT.
+ @param file: The file to copy from DUT.
+ @param host_dir: The directory on host to put the file .
+
+ @returns status code for scp command.
+
+ """
+ cmd = []
+ src = ('root@%s:%s' % (dut.hostname if dut else client_ip, file))
+ cmd.extend(['scp', DUT_SCP_OPTIONS, RSA_KEY, '-v', src, host_dir])
+ command = ' '.join(cmd)
+
+ logging.debug('Retrieving Results: %s', command)
+ try:
+ result = utils.run(
+ command,
+ timeout=WAIT_FOR_CMD_TIMEOUT_SECS,
+ ignore_status=ignore_status)
+ exit_code = result.exit_status
+ except Exception as e:
+ logging.error('Failed to retrieve results: %s', e)
+ raise
+
+ logging.debug('command return value: %d', exit_code)
+ return exit_code
+
+ @contextmanager
+ def no_background(self, *_args):
+ """
+ Background stub.
+
+ """
yield
- return
- logging.info('Background process started successfully, pid %s', pid)
- yield
+ @contextmanager
+ def run_in_background_with_log(self, cmd, dut, log_path):
+ """
+ Get cpustats periodically in background.
- finally:
- if pid:
- # Stop background processes.
- logging.info('Killing background process, pid %s', pid)
- # Kill the process blindly. OK if it's already gone.
- # There is an issue when underlying child processes stay alive while
- # the parent master process is killed.
- # The solution is to kill the chain of processes via process group
- # id.
- dut.run('pgid=$(cat /proc/%s/stat | cut -d")" -f2 | cut -d" " -f4)'
- ' && kill -- -$pgid 2>/dev/null' % pid, ignore_status=True)
+ NOTE: No error handling, exception or error report if command fails
+ to run in background. Command failure is silenced.
- # Copy the results to results directory with silenced failure.
- scp_res = self.scp_telemetry_results(
- '', dut, log_path, self.resultsdir, ignore_status=True)
- if scp_res:
- logging.error(
- 'scp of cpuinfo logs failed '
- 'with error %d.', scp_res)
+ """
+ logging.info('Running in background:\n%s', cmd)
+ pid = dut.run_background(cmd)
+ try:
+ # TODO(denik http://crbug.com/966514): replace with more reliable
+ # way to check run success/failure in background.
+ # Wait some time before checking the process.
+ check = dut.run('sleep 5; kill -0 %s' % pid, ignore_status=True)
+ if check.exit_status != 0:
+ # command wasn't started correctly
+ logging.error(
+ "Background command wasn't started correctly.\n"
+ 'Command:\n%s', cmd)
+ pid = ''
+ yield
+ return
- def run_cpustats_in_background(self, dut, log_name):
- """Run command to collect CPU stats in background."""
+ logging.info('Background process started successfully, pid %s',
+ pid)
+ yield
- log_path = '/tmp/%s' % log_name
- cpu_stats_cmd = (
- 'cpulog=%s; '
- 'rm -f ${cpulog}; '
- # Stop after 720*0.5min=6hours if not killed at clean-up phase.
- 'for i in {1..720} ; do '
- # Collect current CPU frequency on all cores and thermal data.
- ' paste -d" " '
- ' <(ls /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq) '
- ' <(cat /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq) '
- ' >> ${cpulog} || break; ' # exit loop if fails.
- ' paste -d" "'
- ' <(cat /sys/class/thermal/thermal_zone*/type)'
- ' <(cat /sys/class/thermal/thermal_zone*/temp)'
- # Filter in thermal data from only CPU-related sources.
- ' | grep -iE "soc|cpu|pkg|big|little" >> ${cpulog} || break; '
- # Repeat every 30 sec.
- ' sleep 30; '
- 'done;'
- ) % log_path
+ finally:
+ if pid:
+ # Stop background processes.
+ logging.info('Killing background process, pid %s', pid)
+ # Kill the process blindly. OK if it's already gone.
+ # There is an issue when underlying child processes stay alive
+ # while the parent master process is killed.
+ # The solution is to kill the chain of processes via process
+ # group id.
+ dut.run('pgid=$(cat /proc/%s/stat | cut -d")" -f2 | '
+ 'cut -d" " -f4) && kill -- -$pgid 2>/dev/null' % pid,
+ ignore_status=True)
- return self.run_in_background_with_log(cpu_stats_cmd, dut, log_path)
+ # Copy the results to results directory with silenced failure.
+ scp_res = self.scp_telemetry_results(
+ '', dut, log_path, self.resultsdir, ignore_status=True)
+ if scp_res:
+ logging.error(
+ 'scp of cpuinfo logs failed '
+ 'with error %d.', scp_res)
- def run_top_in_background(self, dut, log_name, interval_in_sec):
- """Run top in background."""
+ def run_cpustats_in_background(self, dut, log_name):
+ """
+ Run command to collect CPU stats in background.
- log_path = '/tmp/%s' % log_name
- top_cmd = (
- # Run top in batch mode with specified interval and filter out top
- # system summary and processes not consuming %CPU.
- # Output of each iteration is separated by a blank line.
- 'HOME=/usr/local COLUMNS=128 top -bi -d%.1f'
- ' | grep -E "^[ 0-9]|^$" > %s;'
- ) % (interval_in_sec, log_path)
+ """
+ log_path = '/tmp/%s' % log_name
+ cpu_stats_cmd = (
+ 'cpulog=%s; '
+ 'rm -f ${cpulog}; '
+ # Stop after 720*0.5min=6hours if not killed at clean-up phase.
+ 'for i in {1..720} ; do '
+ # Collect current CPU frequency on all cores and thermal data.
+ ' paste -d" " '
+ ' <(ls /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq)'
+ ' <(cat /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq)'
+ ' >> ${cpulog} || break; ' # exit loop if fails.
+ ' paste -d" "'
+ ' <(cat /sys/class/thermal/thermal_zone*/type)'
+ ' <(cat /sys/class/thermal/thermal_zone*/temp)'
+ # Filter in thermal data from only CPU-related sources.
+ ' | grep -iE "soc|cpu|pkg|big|little" >> ${cpulog} || break; '
+ # Repeat every 30 sec.
+ ' sleep 30; '
+ 'done;') % log_path
- return self.run_in_background_with_log(top_cmd, dut, log_path)
+ return self.run_in_background_with_log(cpu_stats_cmd, dut, log_path)
- def run_turbostat_in_background(self, dut, log_name):
- """Run turbostat in background."""
+ def run_top_in_background(self, dut, log_name, interval_in_sec):
+ """
+ Run top in background.
- log_path = '/tmp/%s' % log_name
- turbostat_cmd = (
- 'nohup turbostat --quiet --interval 10 '
- '--show=CPU,Bzy_MHz,Avg_MHz,TSC_MHz,Busy%%,IRQ,CoreTmp '
- '1> %s'
- ) % log_path
+ """
+ log_path = '/tmp/%s' % log_name
+ top_cmd = (
+ # Run top in batch mode with specified interval and filter out
+ # top system summary and processes not consuming %CPU.
+ # Output of each iteration is separated by a blank line.
+ 'HOME=/usr/local COLUMNS=128 top -bi -d%.1f'
+ ' | grep -E "^[ 0-9]|^$" > %s;') % (interval_in_sec, log_path)
- return self.run_in_background_with_log(turbostat_cmd, dut, log_path)
+ return self.run_in_background_with_log(top_cmd, dut, log_path)
- def run_cpuinfo(self, dut, log_name):
- """Collect CPU info of "dut" into "log_name" file."""
+ def run_turbostat_in_background(self, dut, log_name):
+ """
+ Run turbostat in background.
- cpuinfo_cmd = (
- 'for cpunum in '
- " $(awk '/^processor/ { print $NF ; }' /proc/cpuinfo ) ; do "
- ' for i in `ls -d /sys/devices/system/cpu/cpu"${cpunum}"/cpufreq/'
- '{cpuinfo_cur_freq,scaling_*_freq,scaling_governor} '
- ' 2>/dev/null` ; do '
- ' paste -d" " <(echo "${i}") <(cat "${i}"); '
- ' done;'
- 'done;'
- 'for cpudata in'
- ' /sys/devices/system/cpu/intel_pstate/no_turbo'
- ' /sys/devices/system/cpu/online; do '
- ' if [[ -e "${cpudata}" ]] ; then '
- ' paste <(echo "${cpudata}") <(cat "${cpudata}"); '
- ' fi; '
- 'done; '
- # Adding thermal data to the log.
- 'paste -d" "'
- ' <(cat /sys/class/thermal/thermal_zone*/type)'
- ' <(cat /sys/class/thermal/thermal_zone*/temp)')
+ """
+ log_path = '/tmp/%s' % log_name
+ turbostat_cmd = (
+ 'nohup turbostat --quiet --interval 10 '
+ '--show=CPU,Bzy_MHz,Avg_MHz,TSC_MHz,Busy%%,IRQ,CoreTmp '
+ '1> %s') % log_path
- # Get CPUInfo at the end of the test.
- logging.info('Get cpuinfo: %s', cpuinfo_cmd)
- with open(os.path.join(self.resultsdir, log_name), 'w') as cpu_log_file:
- # Handle command failure gracefully.
- res = dut.run(
- cpuinfo_cmd, stdout_tee=cpu_log_file, ignore_status=True)
- if res.exit_status:
- logging.error('Get cpuinfo command failed with %d.',
- res.exit_status)
+ return self.run_in_background_with_log(turbostat_cmd, dut, log_path)
- def run_once(self, args, client_ip='', dut=None):
- """Run a single telemetry test.
+ def run_cpuinfo(self, dut, log_name):
+ """
+ Collect CPU info of "dut" into "log_name" file.
- @param args: A dictionary of the arguments that were passed
- to this test.
- @param client_ip: The ip address of the DUT
- @param dut: The autotest host object representing DUT.
+ """
+ cpuinfo_cmd = (
+ 'for cpunum in '
+ " $(awk '/^processor/ { print $NF ; }' /proc/cpuinfo ) ; do "
+ ' for i in'
+ ' `ls -d /sys/devices/system/cpu/cpu"${cpunum}"/cpufreq/'
+ '{cpuinfo_cur_freq,scaling_*_freq,scaling_governor} '
+ ' 2>/dev/null` ; do '
+ ' paste -d" " <(echo "${i}") <(cat "${i}"); '
+ ' done;'
+ 'done;'
+ 'for cpudata in'
+ ' /sys/devices/system/cpu/intel_pstate/no_turbo'
+ ' /sys/devices/system/cpu/online; do '
+ ' if [[ -e "${cpudata}" ]] ; then '
+ ' paste <(echo "${cpudata}") <(cat "${cpudata}"); '
+ ' fi; '
+ 'done; '
+ # Adding thermal data to the log.
+ 'paste -d" "'
+ ' <(cat /sys/class/thermal/thermal_zone*/type)'
+ ' <(cat /sys/class/thermal/thermal_zone*/temp)')
- @returns A TelemetryResult instance with the results of this execution.
- """
- test_name = args.get('test', '')
- test_args = args.get('test_args', '')
- profiler_args = args.get('profiler_args', '')
+ # Get CPUInfo at the end of the test.
+ logging.info('Get cpuinfo: %s', cpuinfo_cmd)
+ with open(os.path.join(self.resultsdir, log_name),
+ 'w') as cpu_log_file:
+ # Handle command failure gracefully.
+ res = dut.run(
+ cpuinfo_cmd, stdout_tee=cpu_log_file, ignore_status=True)
+ if res.exit_status:
+ logging.error('Get cpuinfo command failed with %d.',
+ res.exit_status)
- dut_config_str = args.get('dut_config', '{}')
- dut_config = json.loads(dut_config_str)
- # Setup device with dut_config arguments before running test
- if dut_config:
- run_on_dut = DutWrapper(dut, dut_config)
- wait_time = run_on_dut.SetupDevice()
- # Wait time can be used to accumulate cooldown time in Crosperf.
- with open(os.path.join(self.resultsdir, WAIT_TIME_LOG), 'w') as f:
- f.write(str(wait_time))
+ def run_once(self, args, client_ip='', dut=None):
+ """
+ Run a single telemetry test.
- output_format = 'histograms'
+ @param args: A dictionary of the arguments that were passed
+ to this test.
+ @param client_ip: The ip address of the DUT
+ @param dut: The autotest host object representing DUT.
- # For local runs, we set local=True and use local chrome source to run
- # tests; for lab runs, we use devserver instead.
- # By default to be True.
- local = args.get('local', 'true').lower() == 'true'
+ @returns A TelemetryResult instance with the results of this execution.
+ """
+ test_name = args.get('test', '')
+ test_args = args.get('test_args', '')
+ profiler_args = args.get('profiler_args', '')
- # If run_local=true, telemetry benchmark will run on DUT, otherwise
- # run remotely from host.
- # By default to be False.
- # TODO(zhizhouy): It is better to change the field name from "run_local"
- # to "telemetry_on_dut" in crosperf experiment files for consistency.
- telemetry_on_dut = args.get('run_local', '').lower() == 'true'
+ dut_config_str = args.get('dut_config', '{}')
+ dut_config = json.loads(dut_config_str)
+ # Setup device with dut_config arguments before running test
+ if dut_config:
+ wait_time = device_setup_utils.setup_device(dut, dut_config)
+ # Wait time can be used to accumulate cooldown time in Crosperf.
+ with open(os.path.join(self.resultsdir, WAIT_TIME_LOG), 'w') as f:
+ f.write(str(wait_time))
- # Init TelemetryRunner.
- tr = telemetry_runner.TelemetryRunner(
- dut, local=local, telemetry_on_dut=telemetry_on_dut)
+ output_format = 'histograms'
- # Run the test. And collect profile if needed.
- try:
- # If profiler_args specified, we want to add several more options
- # to the command so that run_benchmark will collect system wide
- # profiles.
- if profiler_args:
- profiler_opts = ['--interval-profiling-period=story_run',
- '--interval-profiling-target=system_wide',
- '--interval-profiler-options="%s"' % profiler_args]
+ # For local runs, we set local=True and use local chrome source to run
+ # tests; for lab runs, we use devserver instead.
+ # By default to be True.
+ local = args.get('local', 'true').lower() == 'true'
- top_interval = dut_config.get('top_interval', 0)
- turbostat = dut_config.get('turbostat')
+ # If run_local=true, telemetry benchmark will run on DUT, otherwise
+ # run remotely from host.
+ # By default to be False.
+ # TODO(zhizhouy): It is better to change the field name from "run_local"
+ # to "telemetry_on_dut" in crosperf experiment files for consistency.
+ telemetry_on_dut = args.get('run_local', '').lower() == 'true'
- run_cpuinfo = self.run_cpustats_in_background if dut \
- else self.no_background
- run_turbostat = self.run_turbostat_in_background if (
- dut and turbostat) else self.no_background
- run_top = self.run_top_in_background if (
- dut and top_interval > 0) else self.no_background
+ # Init TelemetryRunner.
+ tr = telemetry_runner.TelemetryRunner(
+ dut, local=local, telemetry_on_dut=telemetry_on_dut)
- # FIXME(denik): replace with ExitStack.
- with run_cpuinfo(dut, CPUSTATS_LOG) as _cpu_cm, \
- run_turbostat(dut, TURBOSTAT_LOG) as _turbo_cm, \
- run_top(dut, TOP_LOG, top_interval) as _top_cm:
+ # Run the test. And collect profile if needed.
+ try:
+ # If profiler_args specified, we want to add several more options
+ # to the command so that run_benchmark will collect system wide
+ # profiles.
+ if profiler_args:
+ profiler_opts = [
+ '--interval-profiling-period=story_run',
+ '--interval-profiling-target=system_wide',
+ '--interval-profiler-options="%s"' % profiler_args
+ ]
- arguments = []
- if test_args:
- arguments.extend(shlex.split(test_args))
+ top_interval = dut_config.get('top_interval', 0)
+ turbostat = dut_config.get('turbostat')
+
+ run_cpuinfo = self.run_cpustats_in_background if dut \
+ else self.no_background
+ run_turbostat = self.run_turbostat_in_background if (
+ dut and turbostat) else self.no_background
+ run_top = self.run_top_in_background if (
+ dut and top_interval > 0) else self.no_background
+
+ # FIXME(denik): replace with ExitStack.
+ with run_cpuinfo(dut, CPUSTATS_LOG) as _cpu_cm, \
+ run_turbostat(dut, TURBOSTAT_LOG) as _turbo_cm, \
+ run_top(dut, TOP_LOG, top_interval) as _top_cm:
+
+ arguments = []
+ if test_args:
+ arguments.extend(shlex.split(test_args))
+ if profiler_args:
+ arguments.extend(profiler_opts)
+ logging.debug('Telemetry Arguments: %s', arguments)
+ result = tr.run_telemetry_benchmark(
+ test_name,
+ None,
+ *arguments,
+ ex_output_format=output_format,
+ results_dir=self.resultsdir,
+ no_verbose=True)
+ logging.info('Telemetry completed with exit status: %s.',
+ result.status)
+ logging.info('output: %s\n', result.output)
+
+ except (error.TestFail, error.TestWarn):
+ logging.debug(
+ 'Test did not succeed while executing telemetry test.')
+ raise
+ except:
+ logging.debug('Unexpected failure on telemetry_Crosperf.')
+ raise
+ finally:
+ if dut:
+ self.run_cpuinfo(dut, CPUINFO_LOG)
+
+ # Copy the histograms.json file into the test_that results directory,
+ # if necessary.
+ if telemetry_on_dut:
+ result = self.scp_telemetry_results(
+ client_ip, dut,
+ os.path.join(DUT_CHROME_RESULTS_DIR, 'histograms.json'),
+ self.resultsdir)
+ else:
+ filepath = os.path.join(self.resultsdir, 'histograms.json')
+ if not os.path.exists(filepath):
+ raise RuntimeError('Missing results file: %s' % filepath)
+
+ # Copy the perf data file into the test_that profiling directory,
+ # if necessary. It always comes from DUT.
if profiler_args:
- arguments.extend(profiler_opts)
- logging.debug('Telemetry Arguments: %s', arguments)
- result = tr.run_telemetry_benchmark(test_name, None,
- *arguments,
- ex_output_format=output_format,
- results_dir=self.resultsdir,
- no_verbose=True)
- logging.info('Telemetry completed with exit status: %s.',
- result.status)
- logging.info('output: %s\n', result.output)
+ filepath = os.path.join(self.resultsdir, 'artifacts')
+ if not os.path.isabs(filepath):
+ raise RuntimeError('Expected absolute path of '
+ 'arfifacts: %s' % filepath)
+ perf_exist = False
+ for root, _dirs, files in os.walk(filepath):
+ for f in files:
+ if f.endswith('.perf.data'):
+ perf_exist = True
+ src_file = os.path.join(root, f)
+ # results-cache.py in crosperf supports multiple
+ # perf.data files, but only if they are named exactly
+ # so. Therefore, create a subdir for each perf.data
+ # file.
+ dst_dir = os.path.join(self.profdir, ''.join(
+ f.split('.')[:-2]))
+ os.makedirs(dst_dir)
+ dst_file = os.path.join(dst_dir, 'perf.data')
+ shutil.copyfile(src_file, dst_file)
+ if not perf_exist:
+ raise error.TestFail('Error: No profiles collected, test may '
+ 'not run correctly.')
- except (error.TestFail, error.TestWarn):
- logging.debug('Test did not succeed while executing telemetry test.')
- raise
- except:
- logging.debug('Unexpected failure on telemetry_Crosperf.')
- raise
- finally:
- if dut:
- self.run_cpuinfo(dut, CPUINFO_LOG)
-
- # Copy the histograms.json file into the test_that results directory,
- # if necessary.
- if telemetry_on_dut:
- result = self.scp_telemetry_results(
- client_ip, dut,
- os.path.join(DUT_CHROME_RESULTS_DIR, 'histograms.json'),
- self.resultsdir)
- else:
- filepath = os.path.join(self.resultsdir, 'histograms.json')
- if not os.path.exists(filepath):
- exit_code = -1
- raise RuntimeError('Missing results file: %s' % filepath)
-
- # Copy the perf data file into the test_that profiling directory,
- # if necessary. It always comes from DUT.
- if profiler_args:
- filepath = os.path.join(self.resultsdir, 'artifacts')
- if not os.path.isabs(filepath):
- raise RuntimeError('Expected absolute path of '
- 'arfifacts: %s' % filepath)
- perf_exist = False
- for root, _dirs, files in os.walk(filepath):
- for f in files:
- if f.endswith('.perf.data'):
- perf_exist = True
- src_file = os.path.join(root, f)
- # results-cache.py in crosperf supports multiple
- # perf.data files, but only if they are named exactly
- # so. Therefore, create a subdir for each perf.data
- # file.
- dst_dir = os.path.join(self.profdir,
- ''.join(f.split('.')[:-2]))
- os.makedirs(dst_dir)
- dst_file = os.path.join(dst_dir, 'perf.data')
- shutil.copyfile(src_file, dst_file)
- if not perf_exist:
- exit_code = -1
- raise error.TestFail('Error: No profiles collected, test may '
- 'not run correctly.')
-
- return result
+ return result