1# Copyright (c) 2009 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.
4
5import logging
6import os
7import re
8import shutil
9import traceback
10from autotest_lib.client.common_lib import error
11from autotest_lib.client.bin import utils
12from autotest_lib.server import test, autotest
13
14
15LOWER_IS_BETTER_METRICS = set(['rdbytes', 'seconds'])
16
17
18class platform_BootPerfServer(test.test):
19    """A test that reboots the client and collect boot perf data."""
20    version = 1
21
22
23    def upload_perf_keyvals(self, keyvals):
24        """Upload perf keyvals in dictionary |keyvals| to Chrome perf dashboard.
25
26        This method assumes that the key of a perf keyval is in the format
27        of "units_description". The text before the first underscore represents
28        the units and the rest of the text represents
29        a description of the measured perf value. For instance,
30        'seconds_kernel_to_login', 'rdbytes_kernel_to_startup'.
31
32        @param keyvals: A dictionary that maps a perf metric to its value.
33
34        """
35        for key, val in keyvals.items():
36            match = re.match(r'^(.+?)_.+$', key)
37            if match:
38                units = match.group(1)
39                higher_is_better = units not in LOWER_IS_BETTER_METRICS
40                self.output_perf_value(
41                        description=key, value=val,
42                        units=units, higher_is_better=higher_is_better)
43
44
45    def run_once(self, host=None, upload_perf=False):
46        self.client = host
47        self.client_test = 'platform_BootPerf'
48
49        # Run a login test to complete the OOBE flow, if we haven't already.
50        # This is so that we measure boot times for the stable state.
51        try:
52            self.client.run('ls /home/chronos/.oobe_completed')
53        except error.AutoservRunError:
54            logging.info('Taking client through OOBE.')
55            client_at = autotest.Autotest(self.client)
56            client_at.run_test('login_LoginSuccess', disable_sysinfo=True)
57
58        # Reboot the client
59        logging.info('BootPerfServer: reboot %s', self.client.hostname)
60        try:
61            self.client.reboot(reboot_timeout=90)
62        except error.AutoservRebootError as e:
63            raise error.TestFail('%s.\nTest failed with error %s' % (
64                    traceback.format_exc(), str(e)))
65
66        # Collect the performance metrics by running a client side test
67        logging.info('BootPerfServer: start client test')
68        client_at = autotest.Autotest(self.client)
69        client_at.run_test(
70            self.client_test, last_boot_was_reboot=True, disable_sysinfo=True)
71
72        # In the client results directory are a 'keyval' file, and
73        # various raw bootstat data files.  First promote the client
74        # test 'keyval' as our own.
75        logging.info('BootPerfServer: gather client results')
76        client_results_dir = os.path.join(
77            self.outputdir, self.client_test, "results")
78        src = os.path.join(client_results_dir, "keyval")
79        dst = os.path.join(self.resultsdir, "keyval")
80        if os.path.exists(src):
81            client_results = open(src, "r")
82            server_results = open(dst, "a")
83            shutil.copyfileobj(client_results, server_results)
84            server_results.close()
85            client_results.close()
86        else:
87            logging.warning('Unable to locate %s', src)
88
89        # Upload perf keyvals in the client keyval file to perf dashboard.
90        if upload_perf:
91            logging.info('Output perf data for iteration %03d', self.iteration)
92            perf_keyvals = utils.read_keyval(src, type_tag='perf')
93            self.upload_perf_keyvals(perf_keyvals)
94
95        # Everything that isn't the client 'keyval' file is raw data
96        # from the client test:  move it to a per-iteration
97        # subdirectory.  We move instead of copying so we can be sure
98        # we don't have any stale results in the next iteration
99        if self.iteration is not None:
100            rawdata_dir = "rawdata.%03d" % self.iteration
101        else:
102            rawdata_dir = "rawdata"
103        rawdata_dir = os.path.join(self.resultsdir, rawdata_dir)
104        shutil.move(client_results_dir, rawdata_dir)
105        try:
106            os.remove(os.path.join(rawdata_dir, "keyval"))
107        except Exception:
108            pass
109