[Autotest] Measure autoserv run time end to end with graphite

Add a whitelist in global config to store a white list of test with run time
measurement enabled. Autoserv uses the white list to filter tests need to
have run time measured.

BUG=chromium:237811
TEST=tested with autoserv in local machine
DEPLOY=none

Change-Id: I6340f5984d47143f648aa8907e4941856a89dbbd
Reviewed-on: https://gerrit.chromium.org/gerrit/57807
Tested-by: Dan Shi <dshi@chromium.org>
Commit-Queue: Dan Shi <dshi@chromium.org>
Reviewed-by: Dan Shi <dshi@chromium.org>
diff --git a/client/cros/constants.py b/client/cros/constants.py
index 6258d0c..8ad2b8f 100644
--- a/client/cros/constants.py
+++ b/client/cros/constants.py
@@ -136,4 +136,4 @@
 SHILL_XMLRPC_SERVER_READY_METHOD = 'ready'
 
 # Bug filing
-CHROME_VERSION = 'CHROME_VERSION'
+CHROME_VERSION = 'CHROME_VERSION'
\ No newline at end of file
diff --git a/global_config.ini b/global_config.ini
index e91f7a8..e23592c 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -33,6 +33,8 @@
 [AUTOSERV]
 # Autotest potential install paths
 client_autodir_paths: /usr/local/autotest
+# White list of tests with run time measurement enabled.
+measure_run_time_tests: desktopui_ScreenLocker,login_LoginSuccess,security_ProfilePermissions
 
 [CLIENT]
 drop_caches: False
diff --git a/server/autoserv b/server/autoserv
index 10c2875..8d2623e 100755
--- a/server/autoserv
+++ b/server/autoserv
@@ -10,10 +10,12 @@
 
 import common
 
-from autotest_lib.client.common_lib.global_config import global_config
-require_atfork = global_config.get_config_value(
+from autotest_lib.client.common_lib import control_data
+from autotest_lib.client.common_lib import global_config
+require_atfork = global_config.global_config.get_config_value(
         'AUTOSERV', 'require_atfork_module', type=bool, default=True)
 
+
 try:
     import atfork
     atfork.monkeypatch_os_fork_functions()
@@ -31,9 +33,14 @@
         print e
         sys.exit(1)
 
+from autotest_lib.server import frontend
+from autotest_lib.server.hosts import site_host
 from autotest_lib.server import server_logging_config
 from autotest_lib.server import server_job, utils, autoserv_parser, autotest
+from autotest_lib.server import utils as server_utils
+
 from autotest_lib.client.common_lib import pidfile, logging_manager
+from autotest_lib.site_utils.graphite import stats
 
 def log_alarm(signum, frame):
     logging.error("Received SIGALARM. Ignoring and continuing on.")
@@ -185,6 +192,14 @@
 
 
 def main():
+    # White list of tests with run time measurement enabled.
+    measure_run_time_tests_names = global_config.global_config.get_config_value(
+                        'AUTOSERV', 'measure_run_time_tests', type=str)
+    if measure_run_time_tests_names:
+        measure_run_time_tests = [t.strip() for t in
+                                  measure_run_time_tests_names.split(',')]
+    else:
+        measure_run_time_tests = []
     # grab the parser
     parser = autoserv_parser.autoserv_parser
     parser.parse_args()
@@ -239,6 +254,24 @@
     autotest.BaseAutotest.set_install_in_tmpdir(
         parser.options.install_in_tmpdir)
 
+    timer = None
+    try:
+        # Take the first argument as control file name, get the test name from
+        # the control file. If the test name exists in the list of tests with
+        # run time measurement enabled, start a timer to begin measurement.
+        if (len(parser.args) > 0 and parser.args[0] != '' and
+            parser.options.machines):
+            test_name = control_data.parse_control(parser.args[0]).name
+            if test_name in measure_run_time_tests:
+                machines = parser.options.machines.replace(',', ' '
+                                                           ).strip().split()
+                afe = frontend.AFE()
+                board = server_utils.get_board_from_afe(machines[0], afe)
+                timer = stats.Timer('autoserv_run_time.%s.%s' %
+                                    (board, test_name))
+                timer.start()
+    except control_data.ControlVariableException as e:
+        logging.error(str(e))
     exit_code = 0
     try:
         try:
@@ -253,6 +286,8 @@
     finally:
         if pid_file_manager:
             pid_file_manager.close_file(exit_code)
+        if timer:
+            timer.stop()
     sys.exit(exit_code)
 
 
diff --git a/server/hosts/site_host.py b/server/hosts/site_host.py
index e35b6b1..6a9d7be 100644
--- a/server/hosts/site_host.py
+++ b/server/hosts/site_host.py
@@ -21,6 +21,7 @@
 from autotest_lib.client.cros import constants
 from autotest_lib.server import autoserv_parser
 from autotest_lib.server import autotest
+from autotest_lib.server import utils as server_utils
 from autotest_lib.server.cros.dynamic_suite import constants as ds_constants
 from autotest_lib.server.cros.dynamic_suite import tools, frontend_wrappers
 from autotest_lib.server.cros.servo import servo
@@ -657,21 +658,6 @@
             self.run('rm -rf ' + path)
 
 
-    def _get_label_from_afe(self, label_prefix):
-        """Retrieve a host's specific label from the AFE.
-
-        Looks for a host label that has the form <label_prefix>:<value>
-        and returns the "<value>" part of the label. None is returned
-        if there is not a label matching the pattern
-
-        @returns the label that matches the prefix or 'None'
-        """
-        labels = self._AFE.get_labels(name__startswith=label_prefix,
-                                      host__hostname__in=[self.hostname])
-        if labels and len(labels) == 1:
-            return labels[0].name.split(label_prefix, 1)[1]
-
-
     def _get_board_from_afe(self):
         """Retrieve this host's board from its labels in the AFE.
 
@@ -681,7 +667,7 @@
 
         @returns board from label, or `None`.
         """
-        return self._get_label_from_afe(ds_constants.BOARD_PREFIX)
+        return server_utils.get_board_from_afe(self.hostname, self._AFE)
 
 
     def get_build(self):
@@ -692,7 +678,7 @@
         @returns The current build or None if it could not find it or if there
                  were multiple build labels assigned to this host.
         """
-        return self._get_label_from_afe(ds_constants.VERSION_PREFIX)
+        return server_utils.get_build_from_afe(self.hostname, self._AFE)
 
 
     def _install_repair(self):
diff --git a/server/site_utils.py b/server/site_utils.py
new file mode 100644
index 0000000..d4ae343
--- /dev/null
+++ b/server/site_utils.py
@@ -0,0 +1,57 @@
+# Copyright (c) 2013 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 logging
+
+from autotest_lib.server.cros.dynamic_suite import constants
+
+
+def get_label_from_afe(hostname, label_prefix, afe):
+    """Retrieve a host's specific label from the AFE.
+
+    Looks for a host label that has the form <label_prefix>:<value>
+    and returns the "<value>" part of the label. None is returned
+    if there is not a label matching the pattern
+
+    @param hostname: hostname of given DUT.
+    @param label_prefix: prefix of label to be matched, e.g., |board:|
+    @param afe: afe instance.
+    @returns the label that matches the prefix or 'None'
+
+    """
+    labels = afe.get_labels(name__startswith=label_prefix,
+                            host__hostname__in=[hostname])
+    if labels and len(labels) == 1:
+        return labels[0].name.split(label_prefix, 1)[1]
+
+
+def get_board_from_afe(hostname, afe):
+    """Retrieve given host's board from its labels in the AFE.
+
+    Looks for a host label of the form "board:<board>", and
+    returns the "<board>" part of the label.  `None` is returned
+    if there is not a single, unique label matching the pattern.
+
+    @param hostname: hostname of given DUT.
+    @param afe: afe instance.
+    @returns board from label, or `None`.
+
+    """
+    return get_label_from_afe(hostname, constants.BOARD_PREFIX, afe)
+
+
+def get_build_from_afe(hostname, afe):
+    """Retrieve the current build for given host from the AFE.
+
+    Looks through the host's labels in the AFE to determine its build.
+
+    @param hostname: hostname of given DUT.
+    @param afe: afe instance.
+    @returns The current build or None if it could not find it or if there
+             were multiple build labels assigned to this host.
+
+    """
+    return get_label_from_afe(hostname, constants.VERSION_PREFIX, afe)
+
+