• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import contextlib, hashlib, logging, os, pipes, re, sys, time, tempfile
6
7from autotest_lib.client.bin import utils
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.common_lib import file_utils
10from autotest_lib.client.cros import chrome_binary_test
11from autotest_lib.client.cros import power_status, power_utils
12from autotest_lib.client.cros import service_stopper
13from autotest_lib.client.cros.audio import cmd_utils
14
15# The download base for test assets.
16DOWNLOAD_BASE = ('http://commondatastorage.googleapis.com'
17                 '/chromiumos-test-assets-public/')
18
19# The executable name of the vda unittest
20VDA_BINARY = 'video_decode_accelerator_unittest'
21
22# The executable name of the vea unittest
23VEA_BINARY = 'video_encode_accelerator_unittest'
24
25# The input frame rate for the vea_unittest.
26INPUT_FPS = 30
27
28# The rendering fps in the vda_unittest.
29RENDERING_FPS = 30
30
31# The unit(s) should match chromium/src/tools/perf/unit-info.json.
32UNIT_PERCENT = '%'
33UNIT_WATT = 'W'
34
35# The regex of the versioning file.
36# e.g., crowd720-3cfe7b096f765742b4aa79e55fe7c994.yuv
37RE_VERSIONING_FILE = re.compile(r'(.+)-([0-9a-fA-F]{32})(\..+)?')
38
39# Time in seconds to wait for cpu idle until giveup.
40WAIT_FOR_IDLE_CPU_TIMEOUT = 60
41
42# Maximum percent of cpu usage considered as idle.
43CPU_IDLE_USAGE = 0.1
44
45# List of thermal throttling services that should be disabled.
46# - temp_metrics for link.
47# - thermal for daisy, snow, pit etc.
48THERMAL_SERVICES = ['temp_metrics', 'thermal']
49
50# Measurement duration in seconds.
51MEASUREMENT_DURATION = 30
52
53# Time to exclude from calculation after playing a video [seconds].
54STABILIZATION_DURATION = 10
55
56# The number of frames used to warm up the rendering.
57RENDERING_WARM_UP = 15
58
59# A big number, used to keep the [vda|vea]_unittest running during the
60# measurement.
61MAX_INT = 2 ** 31 - 1
62
63# Minimum battery charge percentage to run the test
64BATTERY_INITIAL_CHARGED_MIN = 10
65
66
67class CpuUsageMeasurer(object):
68    """ Class used to measure the CPU usage."""
69
70    def __init__(self):
71        self._service_stopper = None
72        self._original_governors = None
73
74    def __enter__(self):
75        # Stop the thermal service that may change the cpu frequency.
76        self._service_stopper = service_stopper.ServiceStopper(THERMAL_SERVICES)
77        self._service_stopper.stop_services()
78
79        if not utils.wait_for_idle_cpu(
80                WAIT_FOR_IDLE_CPU_TIMEOUT, CPU_IDLE_USAGE):
81            raise error.TestError('Could not get idle CPU.')
82        if not utils.wait_for_cool_machine():
83            raise error.TestError('Could not get cold machine.')
84
85        # Set the scaling governor to performance mode to set the cpu to the
86        # highest frequency available.
87        self._original_governors = utils.set_high_performance_mode()
88        return self
89
90    def start(self):
91        self.start_cpu_usage_ = utils.get_cpu_usage()
92
93    def stop(self):
94        return utils.compute_active_cpu_time(
95                self.start_cpu_usage_, utils.get_cpu_usage())
96
97    def __exit__(self, type, value, tb):
98        if self._service_stopper:
99            self._service_stopper.restore_services()
100            self._service_stopper = None
101        if self._original_governors:
102            utils.restore_scaling_governor_states(self._original_governors)
103            self._original_governors = None
104
105
106class PowerMeasurer(object):
107    """ Class used to measure the power consumption."""
108
109    def __init__(self):
110        self._backlight = None
111        self._service_stopper = None
112
113    def __enter__(self):
114        self._backlight = power_utils.Backlight()
115        self._backlight.set_default()
116
117        self._service_stopper = service_stopper.ServiceStopper(
118                service_stopper.ServiceStopper.POWER_DRAW_SERVICES)
119        self._service_stopper.stop_services()
120
121        status = power_status.get_status()
122
123        # Verify that we are running on battery and the battery is sufficiently
124        # charged.
125        status.assert_battery_state(BATTERY_INITIAL_CHARGED_MIN)
126        self._system_power = power_status.SystemPower(status.battery_path)
127        self._power_logger = power_status.PowerLogger([self._system_power])
128        return self
129
130    def start(self):
131        self._power_logger.start()
132
133    def stop(self):
134        self._power_logger.checkpoint('result')
135        keyval = self._power_logger.calc()
136        logging.info(keyval)
137        return keyval['result_' + self._system_power.domain + '_pwr']
138
139    def __exit__(self, type, value, tb):
140        if self._backlight:
141            self._backlight.restore()
142        if self._service_stopper:
143            self._service_stopper.restore_services()
144
145
146class DownloadManager(object):
147    """Use this class to download and manage the resources for testing."""
148
149    def __init__(self, tmpdir=None):
150        self._download_map = {}
151        self._tmpdir = tmpdir
152
153    def get_path(self, name):
154        return self._download_map[name]
155
156    def clear(self):
157        map(os.unlink, self._download_map.values())
158        self._download_map.clear()
159
160    def _download_single_file(self, remote_path):
161        url = DOWNLOAD_BASE + remote_path
162        tmp = tempfile.NamedTemporaryFile(delete=False, dir=self._tmpdir)
163        logging.info('download "%s" to "%s"', url, tmp.name)
164
165        file_utils.download_file(url, tmp.name)
166        md5 = hashlib.md5()
167        with open(tmp.name, 'r') as r:
168            md5.update(r.read())
169
170        filename = os.path.basename(remote_path)
171        m = RE_VERSIONING_FILE.match(filename)
172        if m:
173            prefix, md5sum, suffix = m.groups()
174            if md5.hexdigest() != md5sum:
175                raise error.TestError(
176                        'unmatched md5 sum: %s' % md5.hexdigest())
177            filename = prefix + (suffix or '')
178        self._download_map[filename] = tmp.name
179
180    def download_all(self, resources):
181        for r in resources:
182            self._download_single_file(r)
183
184
185class video_HangoutHardwarePerf(chrome_binary_test.ChromeBinaryTest):
186    """
187    The test outputs the cpu usage when doing video encoding and video
188    decoding concurrently.
189    """
190
191    version = 1
192
193    def get_vda_unittest_cmd_line(self, decode_videos):
194        test_video_data = []
195        for v in decode_videos:
196            assert len(v) == 6
197            # Convert to strings, also make a copy of the list.
198            v = map(str, v)
199            v[0] = self._downloads.get_path(v[0])
200            v[-1:-1] = ['0', '0'] # no fps requirements
201            test_video_data.append(':'.join(v))
202        cmd_line = [
203            self.get_chrome_binary_path(VDA_BINARY),
204            '--gtest_filter=DecodeVariations/*/0',
205            '--test_video_data=%s' % ';'.join(test_video_data),
206            '--rendering_warm_up=%d' % RENDERING_WARM_UP,
207            '--rendering_fps=%f' % RENDERING_FPS,
208            '--num_play_throughs=%d' % MAX_INT]
209        if utils.is_freon():
210            cmd_line.append('--ozone-platform=gbm')
211        return cmd_line
212
213
214    def get_vea_unittest_cmd_line(self, encode_videos):
215        test_stream_data = []
216        for v in encode_videos:
217            assert len(v) == 5
218            # Convert to strings, also make a copy of the list.
219            v = map(str, v)
220            v[0] = self._downloads.get_path(v[0])
221            # The output destination, ignore the output.
222            v.insert(4, '/dev/null')
223            # Insert the FPS requirement
224            v.append(str(INPUT_FPS))
225            test_stream_data.append(':'.join(v))
226        cmd_line = [
227            self.get_chrome_binary_path(VEA_BINARY),
228            '--gtest_filter=SimpleEncode/*/0',
229            '--test_stream_data=%s' % ';'.join(test_stream_data),
230            '--run_at_fps',
231            '--num_frames_to_encode=%d' % MAX_INT]
232        if utils.is_freon():
233            cmd_line.append('--ozone-platform=gbm')
234        return cmd_line
235
236    def run_in_parallel(self, *commands):
237        env = os.environ.copy()
238
239        # To clear the temparory files created by vea_unittest.
240        env['TMPDIR'] = self.tmpdir
241        if not utils.is_freon():
242            env['DISPLAY'] = ':0'
243            env['XAUTHORITY'] = '/home/chronos/.Xauthority'
244        return map(lambda c: cmd_utils.popen(c, env=env), commands)
245
246    def simulate_hangout(self, decode_videos, encode_videos, measurer):
247        popens = self.run_in_parallel(
248            self.get_vda_unittest_cmd_line(decode_videos),
249            self.get_vea_unittest_cmd_line(encode_videos))
250        try:
251            time.sleep(STABILIZATION_DURATION)
252            measurer.start()
253            time.sleep(MEASUREMENT_DURATION)
254            measurement = measurer.stop()
255
256            # Ensure both encoding and decoding are still alive
257            if any(p.poll() is not None for p in popens):
258                raise error.TestError('vea/vda_unittest failed')
259
260            return measurement
261        finally:
262            cmd_utils.kill_or_log_returncode(*popens)
263
264    @chrome_binary_test.nuke_chrome
265    def run_once(self, resources, decode_videos, encode_videos, measurement):
266        self._downloads = DownloadManager(tmpdir = self.tmpdir)
267        try:
268            self._downloads.download_all(resources)
269            if measurement == 'cpu':
270                with CpuUsageMeasurer() as measurer:
271                    value = self.simulate_hangout(
272                            decode_videos, encode_videos, measurer)
273                    self.output_perf_value(
274                            description='cpu_usage', value=value * 100,
275                            units=UNIT_PERCENT, higher_is_better=False)
276            elif measurement == 'power':
277                with PowerMeasurer() as measurer:
278                    value = self.simulate_hangout(
279                            decode_videos, encode_videos, measurer)
280                    self.output_perf_value(
281                            description='power_usage', value=value,
282                            units=UNIT_WATT, higher_is_better=False)
283            else:
284                raise error.TestError('Unknown measurement: ' + measurement)
285        finally:
286            self._downloads.clear()
287