| # Copyright 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import hashlib, logging, os, re, time, tempfile |
| |
| from autotest_lib.client.bin import utils |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib import file_utils |
| from autotest_lib.client.cros import chrome_binary_test |
| from autotest_lib.client.cros import service_stopper |
| from autotest_lib.client.cros.audio import cmd_utils |
| from autotest_lib.client.cros.power import power_status, power_utils |
| from autotest_lib.client.cros.video import helper_logger |
| |
| # The download base for test assets. |
| DOWNLOAD_BASE = ('http://commondatastorage.googleapis.com' |
| '/chromiumos-test-assets-public/') |
| |
| # The executable name of the vda unittest |
| VDA_BINARY = 'video_decode_accelerator_unittest' |
| |
| # The executable name of the vea unittest |
| VEA_BINARY = 'video_encode_accelerator_unittest' |
| |
| # The input frame rate for the vea_unittest. |
| INPUT_FPS = 30 |
| |
| # The rendering fps in the vda_unittest. |
| RENDERING_FPS = 30 |
| |
| UNIT_PERCENT = '%' |
| UNIT_WATT = 'W' |
| |
| # The regex of the versioning file. |
| # e.g., crowd720-3cfe7b096f765742b4aa79e55fe7c994.yuv |
| RE_VERSIONING_FILE = re.compile(r'(.+)-([0-9a-fA-F]{32})(\..+)?') |
| |
| # Time in seconds to wait for cpu idle until giveup. |
| WAIT_FOR_IDLE_CPU_TIMEOUT = 60 |
| |
| # Maximum percent of cpu usage considered as idle. |
| CPU_IDLE_USAGE = 0.1 |
| |
| # List of thermal throttling services that should be disabled. |
| # - temp_metrics for link. |
| # - thermal for daisy, snow, pit etc. |
| THERMAL_SERVICES = ['temp_metrics', 'thermal'] |
| |
| # Measurement duration in seconds. |
| MEASUREMENT_DURATION = 30 |
| |
| # Time to exclude from calculation after playing a video [seconds]. |
| STABILIZATION_DURATION = 10 |
| |
| # The number of frames used to warm up the rendering. |
| RENDERING_WARM_UP = 15 |
| |
| # A big number, used to keep the [vda|vea]_unittest running during the |
| # measurement. |
| MAX_INT = 2 ** 31 - 1 |
| |
| # Minimum battery charge percentage to run the test |
| BATTERY_INITIAL_CHARGED_MIN = 10 |
| |
| |
| class CpuUsageMeasurer(object): |
| """ Class used to measure the CPU usage.""" |
| |
| def __init__(self): |
| self._service_stopper = None |
| self._original_governors = None |
| |
| def __enter__(self): |
| # Stop the thermal service that may change the cpu frequency. |
| self._service_stopper = service_stopper.ServiceStopper(THERMAL_SERVICES) |
| self._service_stopper.stop_services() |
| |
| if not utils.wait_for_idle_cpu( |
| WAIT_FOR_IDLE_CPU_TIMEOUT, CPU_IDLE_USAGE): |
| raise error.TestError('Could not get idle CPU.') |
| if not utils.wait_for_cool_machine(): |
| raise error.TestError('Could not get cold machine.') |
| |
| # Set the scaling governor to performance mode to set the cpu to the |
| # highest frequency available. |
| self._original_governors = utils.set_high_performance_mode() |
| return self |
| |
| def start(self): |
| self.start_cpu_usage_ = utils.get_cpu_usage() |
| |
| def stop(self): |
| return utils.compute_active_cpu_time( |
| self.start_cpu_usage_, utils.get_cpu_usage()) |
| |
| def __exit__(self, type, value, tb): |
| if self._service_stopper: |
| self._service_stopper.restore_services() |
| self._service_stopper = None |
| if self._original_governors: |
| utils.restore_scaling_governor_states(self._original_governors) |
| self._original_governors = None |
| |
| |
| class PowerMeasurer(object): |
| """ Class used to measure the power consumption.""" |
| |
| def __init__(self): |
| self._backlight = None |
| self._service_stopper = None |
| |
| def __enter__(self): |
| self._backlight = power_utils.Backlight() |
| self._backlight.set_default() |
| |
| self._service_stopper = service_stopper.ServiceStopper( |
| service_stopper.ServiceStopper.POWER_DRAW_SERVICES) |
| self._service_stopper.stop_services() |
| |
| status = power_status.get_status() |
| |
| # Verify that we are running on battery and the battery is sufficiently |
| # charged. |
| status.assert_battery_state(BATTERY_INITIAL_CHARGED_MIN) |
| self._system_power = power_status.SystemPower(status.battery_path) |
| self._power_logger = power_status.PowerLogger([self._system_power]) |
| return self |
| |
| def start(self): |
| self._power_logger.start() |
| |
| def stop(self): |
| self._power_logger.checkpoint('result') |
| keyval = self._power_logger.calc() |
| logging.info(keyval) |
| return keyval['result_' + self._system_power.domain + '_pwr'] |
| |
| def __exit__(self, type, value, tb): |
| if self._backlight: |
| self._backlight.restore() |
| if self._service_stopper: |
| self._service_stopper.restore_services() |
| |
| |
| class DownloadManager(object): |
| """Use this class to download and manage the resources for testing.""" |
| |
| def __init__(self, tmpdir=None): |
| self._download_map = {} |
| self._tmpdir = tmpdir |
| |
| def get_path(self, name): |
| return self._download_map[name] |
| |
| def clear(self): |
| map(os.unlink, self._download_map.values()) |
| self._download_map.clear() |
| |
| def _download_single_file(self, remote_path): |
| url = DOWNLOAD_BASE + remote_path |
| tmp = tempfile.NamedTemporaryFile(delete=False, dir=self._tmpdir) |
| logging.info('download "%s" to "%s"', url, tmp.name) |
| |
| file_utils.download_file(url, tmp.name) |
| md5 = hashlib.md5() |
| with open(tmp.name, 'r') as r: |
| while True: |
| block = r.read(128 * 1024) |
| if not block: |
| break |
| md5.update(block) |
| |
| filename = os.path.basename(remote_path) |
| m = RE_VERSIONING_FILE.match(filename) |
| if m: |
| prefix, md5sum, suffix = m.groups() |
| if md5.hexdigest() != md5sum: |
| raise error.TestError( |
| 'unmatched md5 sum: %s' % md5.hexdigest()) |
| filename = prefix + (suffix or '') |
| self._download_map[filename] = tmp.name |
| |
| def download_all(self, resources): |
| for r in resources: |
| self._download_single_file(r) |
| |
| |
| class video_HangoutHardwarePerf(chrome_binary_test.ChromeBinaryTest): |
| """ |
| The test outputs the cpu usage when doing video encoding and video |
| decoding concurrently. |
| """ |
| |
| version = 1 |
| |
| def get_vda_unittest_cmd_line(self, decode_videos): |
| test_video_data = [] |
| for v in decode_videos: |
| assert len(v) == 6 |
| # Convert to strings, also make a copy of the list. |
| v = map(str, v) |
| v[0] = self._downloads.get_path(v[0]) |
| v[-1:-1] = ['0', '0'] # no fps requirements |
| test_video_data.append(':'.join(v)) |
| cmd_line = [ |
| self.get_chrome_binary_path(VDA_BINARY), |
| '--gtest_filter=DecodeVariations/*/0', |
| '--test_video_data=%s' % ';'.join(test_video_data), |
| '--rendering_warm_up=%d' % RENDERING_WARM_UP, |
| '--rendering_fps=%f' % RENDERING_FPS, |
| '--num_play_throughs=%d' % MAX_INT, |
| helper_logger.chrome_vmodule_flag(), |
| ] |
| cmd_line.append('--ozone-platform=gbm') |
| return cmd_line |
| |
| |
| def get_vea_unittest_cmd_line(self, encode_videos): |
| test_stream_data = [] |
| for v in encode_videos: |
| assert len(v) == 5 |
| # Convert to strings, also make a copy of the list. |
| v = map(str, v) |
| v[0] = self._downloads.get_path(v[0]) |
| # The output destination, ignore the output. |
| v.insert(4, '/dev/null') |
| # Insert the FPS requirement |
| v.append(str(INPUT_FPS)) |
| test_stream_data.append(':'.join(v)) |
| cmd_line = [ |
| self.get_chrome_binary_path(VEA_BINARY), |
| '--gtest_filter=SimpleEncode/*/0', |
| '--test_stream_data=%s' % ';'.join(test_stream_data), |
| '--run_at_fps', |
| '--num_frames_to_encode=%d' % MAX_INT, |
| helper_logger.chrome_vmodule_flag(), |
| ] |
| cmd_line.append('--ozone-platform=gbm') |
| return cmd_line |
| |
| def run_in_parallel(self, *commands): |
| env = os.environ.copy() |
| |
| # To clear the temparory files created by vea_unittest. |
| env['TMPDIR'] = self.tmpdir |
| return map(lambda c: cmd_utils.popen(c, env=env), commands) |
| |
| def simulate_hangout(self, decode_videos, encode_videos, measurer): |
| popens = self.run_in_parallel( |
| self.get_vda_unittest_cmd_line(decode_videos), |
| self.get_vea_unittest_cmd_line(encode_videos)) |
| try: |
| time.sleep(STABILIZATION_DURATION) |
| measurer.start() |
| time.sleep(MEASUREMENT_DURATION) |
| measurement = measurer.stop() |
| |
| # Ensure both encoding and decoding are still alive |
| if any(p.poll() is not None for p in popens): |
| raise error.TestError('vea/vda_unittest failed') |
| |
| return measurement |
| finally: |
| cmd_utils.kill_or_log_returncode(*popens) |
| |
| @helper_logger.video_log_wrapper |
| @chrome_binary_test.nuke_chrome |
| def run_once(self, resources, decode_videos, encode_videos, measurement): |
| self._downloads = DownloadManager(tmpdir = self.tmpdir) |
| try: |
| self._downloads.download_all(resources) |
| if measurement == 'cpu': |
| with CpuUsageMeasurer() as measurer: |
| value = self.simulate_hangout( |
| decode_videos, encode_videos, measurer) |
| self.output_perf_value( |
| description='cpu_usage', value=value * 100, |
| units=UNIT_PERCENT, higher_is_better=False) |
| elif measurement == 'power': |
| with PowerMeasurer() as measurer: |
| value = self.simulate_hangout( |
| decode_videos, encode_videos, measurer) |
| self.output_perf_value( |
| description='power_usage', value=value, |
| units=UNIT_WATT, higher_is_better=False) |
| else: |
| raise error.TestError('Unknown measurement: ' + measurement) |
| finally: |
| self._downloads.clear() |