1# Copyright (c) 2013 The Chromium OS 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.
4import logging
5import os
6import StringIO
7
8import common
9from autotest_lib.client.common_lib import error
10from autotest_lib.server import test
11from autotest_lib.server import utils
12from autotest_lib.server.cros import telemetry_runner
13
14
15TELEMETRY_TIMEOUT_MINS = 60
16CHROME_SRC_ROOT = '/var/cache/chromeos-cache/distfiles/target/'
17CLIENT_CHROME_ROOT = '/usr/local/telemetry/src'
18RUN_BENCHMARK  = 'tools/perf/run_benchmark'
19
20
21def _find_chrome_root_dir():
22    # Look for chrome source root, either externally mounted, or inside
23    # the chroot.  Prefer chrome-src-internal source tree to chrome-src.
24    sources_list = ('chrome-src-internal', 'chrome-src')
25
26    dir_list = [os.path.join(CHROME_SRC_ROOT, x) for x in sources_list]
27    if 'CHROME_ROOT' in os.environ:
28        dir_list.insert(0, os.environ['CHROME_ROOT'])
29
30    for dir in dir_list:
31        if os.path.exists(dir):
32            chrome_root_dir = dir
33            break
34    else:
35        raise error.TestError('Chrome source directory not found.')
36
37    logging.info('Using Chrome source tree at %s', chrome_root_dir)
38    return os.path.join(chrome_root_dir, 'src')
39
40
41def _ensure_deps(dut, test_name):
42    """
43    Ensure the dependencies are locally available on DUT.
44
45    @param dut: The autotest host object representing DUT.
46    @param test_name: Name of the telemetry test.
47    """
48    # Get DEPs using host's telemetry.
49    chrome_root_dir = _find_chrome_root_dir()
50    format_string = ('python %s/tools/perf/fetch_benchmark_deps.py %s')
51    command = format_string % (chrome_root_dir, test_name)
52    logging.info('Getting DEPs: %s', command)
53    stdout = StringIO.StringIO()
54    stderr = StringIO.StringIO()
55    try:
56        result = utils.run(command, stdout_tee=stdout,
57                           stderr_tee=stderr)
58    except error.CmdError as e:
59        logging.debug('Error occurred getting DEPs: %s\n %s\n',
60                      stdout.getvalue(), stderr.getvalue())
61        raise error.TestFail('Error occurred while getting DEPs.')
62
63    # Download DEPs to DUT.
64    # send_file() relies on rsync over ssh. Couldn't be better.
65    stdout_str = stdout.getvalue()
66    stdout.close()
67    stderr.close()
68    for dep in stdout_str.split():
69        src = os.path.join(chrome_root_dir, dep)
70        dst = os.path.join(CLIENT_CHROME_ROOT, dep)
71        if not os.path.isfile(src):
72            raise error.TestFail('Error occurred while saving DEPs.')
73        logging.info('Copying: %s -> %s', src, dst)
74        try:
75            dut.send_file(src, dst)
76        except:
77            raise error.TestFail('Error occurred while sending DEPs to dut.\n')
78
79
80class telemetry_Crosperf(test.test):
81    """Run one or more telemetry benchmarks under the crosperf script."""
82    version = 1
83
84    def run_once(self, args, client_ip='', dut=None):
85        """
86        Run a single telemetry test.
87
88        @param args: A dictionary of the arguments that were passed
89                to this test.
90        @param client_ip: The ip address of the DUT
91        @param dut: The autotest host object representing DUT.
92
93        @returns A TelemetryResult instance with the results of this execution.
94        """
95        test_name = args['test']
96        test_args = ''
97        if 'test_args' in args:
98            test_args = args['test_args']
99
100        # Decide whether the test will run locally or by a remote server.
101        if 'run_local' in args and args['run_local'].lower() == 'true':
102            # The telemetry scripts will run on DUT.
103            _ensure_deps(dut, test_name)
104            format_string = ('python %s --browser=system %s %s')
105            command = format_string % (os.path.join(CLIENT_CHROME_ROOT,
106                                                    RUN_BENCHMARK),
107                                       test_args, test_name)
108            runner = dut
109        else:
110            # The telemetry scripts will run on server.
111            format_string = ('python %s --browser=cros-chrome --remote=%s '
112                             '%s %s')
113            command = format_string % (os.path.join(_find_chrome_root_dir(),
114                                                    RUN_BENCHMARK),
115                                       client_ip, test_args, test_name)
116            runner = utils
117
118        # Run the test.
119        stdout = StringIO.StringIO()
120        stderr = StringIO.StringIO()
121        try:
122            logging.info('CMD: %s', command)
123            result = runner.run(command, stdout_tee=stdout, stderr_tee=stderr,
124                                timeout=TELEMETRY_TIMEOUT_MINS*60)
125            exit_code = result.exit_status
126        except error.CmdError as e:
127            logging.debug('Error occurred executing telemetry.')
128            exit_code = e.result_obj.exit_status
129            raise error.TestFail('An error occurred while executing '
130                                 'telemetry test.')
131        finally:
132            stdout_str = stdout.getvalue()
133            stderr_str = stderr.getvalue()
134            stdout.close()
135            stderr.close()
136
137
138        # Parse the result.
139        logging.debug('Telemetry completed with exit code: %d.'
140                      '\nstdout:%s\nstderr:%s', exit_code,
141                      stdout_str, stderr_str)
142        logging.info('Telemetry completed with exit code: %d.'
143                     '\nstdout:%s\nstderr:%s', exit_code,
144                     stdout_str, stderr_str)
145
146        result = telemetry_runner.TelemetryResult(exit_code=exit_code,
147                                                  stdout=stdout_str,
148                                                  stderr=stderr_str)
149
150        result.parse_benchmark_results()
151        for data in result.perf_data:
152            self.output_perf_value(description=data['trace'],
153                                   value=data['value'],
154                                   units=data['units'],
155                                   graph=data['graph'])
156
157        return result
158