13551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
23551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
33551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)# found in the LICENSE file.
43551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
58bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)"""Runs perf tests.
63551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
73551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)Our buildbot infrastructure requires each slave to run steps serially.
83551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)This is sub-optimal for android, where these steps can run independently on
93551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)multiple connected devices.
103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)The buildbots will run this script multiple times per cycle:
123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)- First: all steps listed in --steps in will be executed in parallel using all
133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)connected devices. Step results will be pickled to disk. Each step has a unique
143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)name. The result code will be ignored if the step name is listed in
153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)--flaky-steps.
163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)The buildbot will treat this step as a regular step, and will not process any
173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)graph data.
183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)- Then, with -print-step STEP_NAME: at this stage, we'll simply print the file
203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)with the step results previously saved. The buildbot will then process the graph
213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)data accordingly.
223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)The JSON steps file contains a dictionary in the format:
2446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles){ "version": int,
2546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  "steps": {
2646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    "foo": {
2746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      "device_affinity": int,
2846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      "cmd": "script_to_execute foo"
2946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    },
3046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    "bar": {
3146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      "device_affinity": int,
3246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      "cmd": "script_to_execute bar"
3346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    }
3446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
3546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
3646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)The JSON flaky steps file contains a list with step names which results should
383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)be ignored:
393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)[
403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  "step_name_foo",
413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  "step_name_bar"
423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)]
433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)Note that script_to_execute necessarily have to take at least the following
458bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)option:
463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  --device: the serial number to be passed to all adb commands.
473551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)"""
483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
4946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)import collections
503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import datetime
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)import json
523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import logging
533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import os
548bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)import pickle
553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import sys
56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import threading
57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import time
583551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)from pylib import cmd_helper
603551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)from pylib import constants
618bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)from pylib import forwarder
623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)from pylib.base import base_test_result
633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)from pylib.base import base_test_runner
643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
66f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)def OutputJsonList(json_input, json_output):
67f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  with file(json_input, 'r') as i:
68f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    all_steps = json.load(i)
696d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  step_names = all_steps['steps'].keys()
70f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  with file(json_output, 'w') as o:
71f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    o.write(json.dumps(step_names))
72f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return 0
73f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
74f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)def PrintTestOutput(test_name):
763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  """Helper method to print the output of previously executed test_name.
773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  Args:
793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    test_name: name of the test that has been previously executed.
803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  Returns:
823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    exit code generated by the test step.
833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  """
8458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  file_name = os.path.join(constants.PERF_OUTPUT_DIR, test_name)
853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if not os.path.exists(file_name):
863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    logging.error('File not found %s', file_name)
873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return 1
883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  with file(file_name, 'r') as f:
903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    persisted_result = pickle.loads(f.read())
91424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  logging.info('*' * 80)
92424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  logging.info('Output from:')
93424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  logging.info(persisted_result['cmd'])
94424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  logging.info('*' * 80)
953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  print persisted_result['output']
963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return persisted_result['exit_code']
983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def PrintSummary(test_names):
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  logging.info('*' * 80)
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  logging.info('Sharding summary')
10346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  device_total_time = collections.defaultdict(int)
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for test_name in test_names:
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    file_name = os.path.join(constants.PERF_OUTPUT_DIR, test_name)
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not os.path.exists(file_name):
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      logging.info('%s : No status file found', test_name)
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    with file(file_name, 'r') as f:
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      result = pickle.loads(f.read())
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    logging.info('%s : exit_code=%d in %d secs at %s',
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 result['name'], result['exit_code'], result['total_time'],
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 result['device'])
11446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    device_total_time[result['device']] += result['total_time']
11546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  for device, device_time in device_total_time.iteritems():
11646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    logging.info('Total for device %s : %d secs', device, device_time)
11746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  logging.info('Total steps time: %d secs', sum(device_total_time.values()))
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class _HeartBeatLogger(object):
121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  # How often to print the heartbeat on flush().
122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  _PRINT_INTERVAL = 30.0
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def __init__(self):
125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    """A file-like class for keeping the buildbot alive."""
126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._len = 0
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._tick = time.time()
128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._stopped = threading.Event()
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._timer = threading.Thread(target=self._runner)
130f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._timer.start()
131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def _runner(self):
133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    while not self._stopped.is_set():
134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self.flush()
135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self._stopped.wait(_HeartBeatLogger._PRINT_INTERVAL)
136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def write(self, data):
138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._len += len(data)
139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def flush(self):
141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    now = time.time()
142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if now - self._tick >= _HeartBeatLogger._PRINT_INTERVAL:
143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self._tick = now
144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      print '--single-step output length %d' % self._len
145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      sys.stdout.flush()
146f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
147f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def stop(self):
148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._stopped.set()
149f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
150f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
1513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)class TestRunner(base_test_runner.BaseTestRunner):
15246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  def __init__(self, test_options, device, shard_index, max_shard, tests,
15346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      flaky_tests):
1543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    """A TestRunner instance runs a perf test on a single device.
1553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    Args:
1573551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      test_options: A PerfOptions object.
1583551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      device: Device to run the tests.
15946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      shard_index: the index of this device.
16046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      max_shards: the maximum shard index.
1613551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      tests: a dict mapping test_name to command.
1623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      flaky_tests: a list of flaky test_name.
1633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    """
1643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    super(TestRunner, self).__init__(device, None, 'Release')
1653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self._options = test_options
16646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    self._shard_index = shard_index
16746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    self._max_shard = max_shard
1683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self._tests = tests
1693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self._flaky_tests = flaky_tests
1703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  @staticmethod
1728bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  def _IsBetter(result):
1738bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    if result['actual_exit_code'] == 0:
1748bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      return True
1758bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    pickled = os.path.join(constants.PERF_OUTPUT_DIR,
1768bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                           result['name'])
1778bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    if not os.path.exists(pickled):
1788bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      return True
1798bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    with file(pickled, 'r') as f:
1808bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      previous = pickle.loads(f.read())
1818bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return result['actual_exit_code'] < previous['actual_exit_code']
1828bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1838bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  @staticmethod
1843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  def _SaveResult(result):
1858bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    if TestRunner._IsBetter(result):
1868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      with file(os.path.join(constants.PERF_OUTPUT_DIR,
1878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                             result['name']), 'w') as f:
1888bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        f.write(pickle.dumps(result))
1893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
19046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  def _CheckDeviceAffinity(self, test_name):
19146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    """Returns True if test_name has affinity for this shard."""
19246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    affinity = (self._tests['steps'][test_name]['device_affinity'] %
19346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                self._max_shard)
19446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    if self._shard_index == affinity:
19546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      return True
19646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    logging.info('Skipping %s on %s (affinity is %s, device is %s)',
19746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                 test_name, self.device_serial, affinity, self._shard_index)
19846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    return False
19946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
2003551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  def _LaunchPerfTest(self, test_name):
2013551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    """Runs a perf test.
2023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
2033551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    Args:
2043551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      test_name: the name of the test to be executed.
2053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
2063551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    Returns:
2073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      A tuple containing (Output, base_test_result.ResultType)
2083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    """
20946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    if not self._CheckDeviceAffinity(test_name):
21046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      return '', base_test_result.ResultType.PASS
21146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
2128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    try:
2138bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      logging.warning('Unmapping device ports')
214a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      forwarder.Forwarder.UnmapAllDevicePorts(self.device)
215a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      self.device.old_interface.RestartAdbdOnDevice()
2168bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    except Exception as e:
2178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      logging.error('Exception when tearing down device %s', e)
2188bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    cmd = ('%s --device %s' %
22046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)           (self._tests['steps'][test_name]['cmd'],
22146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)            self.device_serial))
2223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    logging.info('%s : %s', test_name, cmd)
2233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    start_time = datetime.datetime.now()
22458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
225f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    timeout = 5400
22658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if self._options.no_timeout:
22758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      timeout = None
2284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    full_cmd = cmd
2294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if self._options.dry_run:
2304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      full_cmd = 'echo %s' % cmd
23158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    logfile = sys.stdout
233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if self._options.single_step:
234f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      # Just print a heart-beat so that the outer buildbot scripts won't timeout
235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      # without response.
236f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      logfile = _HeartBeatLogger()
237f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    cwd = os.path.abspath(constants.DIR_SOURCE_ROOT)
238f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if full_cmd.startswith('src/'):
239f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      cwd = os.path.abspath(os.path.join(constants.DIR_SOURCE_ROOT, os.pardir))
240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    try:
241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      exit_code, output = cmd_helper.GetCmdStatusAndOutputWithTimeout(
242cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          full_cmd, timeout, cwd=cwd, shell=True, logfile=logfile)
243cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    finally:
244cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      if self._options.single_step:
245cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        logfile.stop()
2463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    end_time = datetime.datetime.now()
247424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if exit_code is None:
248424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      exit_code = -1
2493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    logging.info('%s : exit_code=%d in %d secs at %s',
2503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                 test_name, exit_code, (end_time - start_time).seconds,
25146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                 self.device_serial)
2523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    result_type = base_test_result.ResultType.FAIL
2533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if exit_code == 0:
2543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      result_type = base_test_result.ResultType.PASS
2558bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    actual_exit_code = exit_code
2563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if test_name in self._flaky_tests:
2574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      # The exit_code is used at the second stage when printing the
2584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      # test output. If the test is flaky, force to "0" to get that step green
2594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      # whilst still gathering data to the perf dashboards.
2604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      # The result_type is used by the test_dispatcher to retry the test.
2613551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      exit_code = 0
2623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
2633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    persisted_result = {
2643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        'name': test_name,
2653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        'output': output,
2663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        'exit_code': exit_code,
2678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        'actual_exit_code': actual_exit_code,
2683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        'result_type': result_type,
2693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        'total_time': (end_time - start_time).seconds,
27046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        'device': self.device_serial,
271424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        'cmd': cmd,
2723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    }
2733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self._SaveResult(persisted_result)
2743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
2753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return (output, result_type)
2763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
2773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  def RunTest(self, test_name):
2783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    """Run a perf test on the device.
2793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
2803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    Args:
2813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      test_name: String to use for logging the test result.
2823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
2833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    Returns:
2843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      A tuple of (TestRunResults, retry).
2853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    """
286a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    _, result_type = self._LaunchPerfTest(test_name)
2873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    results = base_test_result.TestRunResults()
2883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    results.AddResult(base_test_result.BaseTestResult(test_name, result_type))
2893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    retry = None
2903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if not results.DidRunPass():
2913551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      retry = test_name
2923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return results, retry
293