| Index: build/android/pylib/perf/perf_test_instance.py
|
| diff --git a/build/android/pylib/perf/perf_test_instance.py b/build/android/pylib/perf/perf_test_instance.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..17f3fd4543eec3965cece76e0b66e6f4cb6e09a1
|
| --- /dev/null
|
| +++ b/build/android/pylib/perf/perf_test_instance.py
|
| @@ -0,0 +1,237 @@
|
| +# Copyright 2016 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 json
|
| +import logging
|
| +import os
|
| +import pickle
|
| +import re
|
| +
|
| +from devil import base_error
|
| +from devil.utils import cmd_helper
|
| +from pylib import constants
|
| +from pylib.base import base_test_result
|
| +from pylib.base import test_instance
|
| +from pylib.constants import host_paths
|
| +
|
| +
|
| +_GIT_CR_POS_RE = re.compile(r'^Cr-Commit-Position: refs/heads/master@{#(\d+)}$')
|
| +
|
| +
|
| +def _GetPersistedResult(test_name):
|
| + file_name = os.path.join(constants.PERF_OUTPUT_DIR, test_name)
|
| + if not os.path.exists(file_name):
|
| + logging.error('File not found %s', file_name)
|
| + return None
|
| +
|
| + with file(file_name, 'r') as f:
|
| + return pickle.loads(f.read())
|
| +
|
| +
|
| +def _GetChromiumRevision():
|
| + # pylint: disable=line-too-long
|
| + """Get the git hash and commit position of the chromium master branch.
|
| +
|
| + See:
|
| + https://chromium.googlesource.com/chromium/tools/build/+/387e3cf3/scripts/slave/runtest.py#211
|
| +
|
| + Returns:
|
| + A dictionary with 'revision' and 'commit_pos' keys.
|
| + """
|
| + # pylint: enable=line-too-long
|
| + status, output = cmd_helper.GetCmdStatusAndOutput(
|
| + ['git', 'log', '-n', '1', '--pretty=format:%H%n%B', 'HEAD'],
|
| + cwd=host_paths.DIR_SOURCE_ROOT)
|
| + revision = None
|
| + commit_pos = None
|
| + if not status:
|
| + lines = output.splitlines()
|
| + revision = lines[0]
|
| + for line in reversed(lines):
|
| + m = _GIT_CR_POS_RE.match(line.strip())
|
| + if m:
|
| + commit_pos = int(m.group(1))
|
| + break
|
| + return {'revision': revision, 'commit_pos': commit_pos}
|
| +
|
| +
|
| +class PerfTestInstance(test_instance.TestInstance):
|
| + def __init__(self, args, _):
|
| + super(PerfTestInstance, self).__init__()
|
| +
|
| + self._collect_chartjson_data = args.collect_chartjson_data
|
| + self._dry_run = args.dry_run
|
| + self._flaky_steps = args.flaky_steps
|
| + self._output_dir_archive_path = args.output_dir_archive_path
|
| + self._known_devices_file = args.known_devices_file
|
| + self._max_battery_temp = args.max_battery_temp
|
| + self._min_battery_level = args.min_battery_level
|
| + self._no_timeout = args.no_timeout
|
| + self._output_chartjson_data = args.output_chartjson_data
|
| + self._output_json_list = args.output_json_list
|
| + self._print_step = args.print_step
|
| + self._single_step = (
|
| + ' '.join(args.single_step_command) if args.single_step else None)
|
| + self._steps = args.steps
|
| + self._test_filter = args.test_filter
|
| + self._write_buildbot_json = args.write_buildbot_json
|
| +
|
| + def SetUp(self):
|
| + pass
|
| +
|
| + def TearDown(self):
|
| + pass
|
| +
|
| + def OutputJsonList(self):
|
| + try:
|
| + with file(self._steps, 'r') as i:
|
| + all_steps = json.load(i)
|
| +
|
| + step_values = []
|
| + for k, v in all_steps['steps'].iteritems():
|
| + data = {'test': k, 'device_affinity': v['device_affinity']}
|
| +
|
| + persisted_result = _GetPersistedResult(k)
|
| + if persisted_result:
|
| + data['start_time'] = persisted_result['start_time']
|
| + data['end_time'] = persisted_result['end_time']
|
| + data['total_time'] = persisted_result['total_time']
|
| + data['has_archive'] = persisted_result['archive_bytes'] is not None
|
| + step_values.append(data)
|
| +
|
| + with file(self._output_json_list, 'w') as o:
|
| + o.write(json.dumps(step_values))
|
| + return base_test_result.ResultType.PASS
|
| + except KeyError:
|
| + logging.exception('Persistent results file missing key.')
|
| + return base_test_result.ResultType.FAIL
|
| +
|
| + def PrintTestOutput(self):
|
| + """Helper method to print the output of previously executed test_name.
|
| +
|
| + Test_name is passed from the command line as print_step
|
| +
|
| + Returns:
|
| + exit code generated by the test step.
|
| + """
|
| + persisted_result = _GetPersistedResult(self._print_step)
|
| + if not persisted_result:
|
| + raise PersistentDataError('No data for test %s found.' % self._print_step)
|
| + logging.info('*' * 80)
|
| + logging.info('Output from:')
|
| + logging.info(persisted_result['cmd'])
|
| + logging.info('*' * 80)
|
| +
|
| + output_formatted = ''
|
| + persisted_outputs = persisted_result['output']
|
| + for i in xrange(len(persisted_outputs)):
|
| + output_formatted += '\n\nOutput from run #%d:\n\n%s' % (
|
| + i, persisted_outputs[i])
|
| + print output_formatted
|
| +
|
| + if self._output_chartjson_data:
|
| + with file(self._output_chartjson_data, 'w') as f:
|
| + f.write(persisted_result['chartjson'])
|
| +
|
| + if self._output_dir_archive_path:
|
| + if persisted_result['archive_bytes'] is not None:
|
| + with file(self._output_dir_archive_path, 'wb') as f:
|
| + f.write(persisted_result['archive_bytes'])
|
| + else:
|
| + logging.error('The output dir was not archived.')
|
| + if persisted_result['exit_code'] == 0:
|
| + return base_test_result.ResultType.PASS
|
| + return base_test_result.ResultType.FAIL
|
| +
|
| + #override
|
| + def TestType(self):
|
| + return 'perf'
|
| +
|
| + @staticmethod
|
| + def ReadChartjsonOutput(output_dir):
|
| + if not output_dir:
|
| + return ''
|
| + json_output_path = os.path.join(output_dir, 'results-chart.json')
|
| + try:
|
| + with open(json_output_path) as f:
|
| + return f.read()
|
| + except IOError:
|
| + logging.exception('Exception when reading chartjson.')
|
| + logging.error('This usually means that telemetry did not run, so it could'
|
| + ' not generate the file. Please check the device running'
|
| + ' the test.')
|
| + return ''
|
| +
|
| + def WriteBuildBotJson(self, output_dir):
|
| + """Write metadata about the buildbot environment to the output dir."""
|
| + if not output_dir or not self._write_buildbot_json:
|
| + return
|
| + data = {
|
| + 'chromium': _GetChromiumRevision(),
|
| + 'environment': dict(os.environ)
|
| + }
|
| + with open(os.path.join(output_dir, 'buildbot.json'), 'w') as f:
|
| + json.dump(data, f, sort_keys=True, separators=(',', ': '))
|
| +
|
| + @property
|
| + def collect_chartjson_data(self):
|
| + return self._collect_chartjson_data
|
| +
|
| + @property
|
| + def dry_run(self):
|
| + return self._dry_run
|
| +
|
| + @property
|
| + def flaky_steps(self):
|
| + return self._flaky_steps
|
| +
|
| + @property
|
| + def known_devices_file(self):
|
| + return self._known_devices_file
|
| +
|
| + @property
|
| + def max_battery_temp(self):
|
| + return self._max_battery_temp
|
| +
|
| + @property
|
| + def min_battery_level(self):
|
| + return self._min_battery_level
|
| +
|
| + @property
|
| + def no_timeout(self):
|
| + return self._no_timeout
|
| +
|
| + @property
|
| + def output_chartjson_data(self):
|
| + return self._output_chartjson_data
|
| +
|
| + @property
|
| + def output_dir_archive_path(self):
|
| + return self._output_dir_archive_path
|
| +
|
| + @property
|
| + def output_json_list(self):
|
| + return self._output_json_list
|
| +
|
| + @property
|
| + def print_step(self):
|
| + return self._print_step
|
| +
|
| + @property
|
| + def single_step(self):
|
| + return self._single_step
|
| +
|
| + @property
|
| + def steps(self):
|
| + return self._steps
|
| +
|
| + @property
|
| + def test_filter(self):
|
| + return self._test_filter
|
| +
|
| +
|
| +class PersistentDataError(base_error.BaseError):
|
| + def __init__(self, message):
|
| + super(PersistentDataError, self).__init__(message)
|
| + self._is_infra_error = True
|
|
|