blob: 8ce37cb33dc3f92102364609e2b05d07ffa7ca13 [file] [log] [blame]
Stephen Martinis0b18efa102017-09-25 22:55:121#!/usr/bin/env python
2# Copyright 2017 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Runs several telemetry benchmarks.
7
8This script attempts to emulate the contract of gtest-style tests
9invoked via recipes. The main contract is that the caller passes the
10argument:
11
12 --isolated-script-test-output=[FILENAME]
13
14json is written to that file in the format detailed here:
15https://www.chromium.org/developers/the-json-test-results-format
16
Kenneth Russell40274052017-11-14 00:57:4417Optional argument:
18
Kenneth Russella649a46122017-11-21 06:39:5919 --isolated-script-test-filter=[TEST_NAMES]
Kenneth Russell40274052017-11-14 00:57:4420
Kenneth Russella649a46122017-11-21 06:39:5921is a double-colon-separated ("::") list of test names, to run just that subset
22of tests. This list is forwarded to the run_telemetry_benchmark_as_googletest
23script.
Kenneth Russell40274052017-11-14 00:57:4424
Stephen Martinis0b18efa102017-09-25 22:55:1225This script is intended to be the base command invoked by the isolate,
26followed by a subsequent Python script. It could be generalized to
27invoke an arbitrary executable.
28
29It currently runs several benchmarks. The benchmarks it will execute are
Emily Hanley7282b542018-03-02 20:42:4930based on the shard it is running on and the sharding_map_path.
31
32If this is executed with a non-telemetry perf test, the flag --non-telemetry
33has to be passed in to the script so the script knows it is running
34an executable and not the run_benchmark command.
Stephen Martinis0b18efa102017-09-25 22:55:1235
36The results of running the benchmark are put in separate directories per
37benchmark. Two files will be present in each directory; perf_results.json, which
38is the perf specific results (with unenforced format, could be histogram,
39legacy, or chartjson), and test_results.json, which is a JSON test results
40format file
41(https://www.chromium.org/developers/the-json-test-results-format)
42
43This script was derived from run_telemetry_benchmark_as_googletest, and calls
44into that script.
45"""
46
47import argparse
48import json
49import os
50import shutil
51import sys
Ned Nguyen972d234f2018-06-22 01:58:0252import time
Stephen Martinis0b18efa102017-09-25 22:55:1253import tempfile
54import traceback
55
56import common
57
58import run_telemetry_benchmark_as_googletest
Emily Hanley7282b542018-03-02 20:42:4959import run_gtest_perf_test
Stephen Martinis0b18efa102017-09-25 22:55:1260
61
Emily Hanley6156dde2018-05-16 00:35:2462def get_sharding_map_path(args):
63 return os.path.join(
Stephen Martinis6740ad02017-10-10 20:28:4664 os.path.dirname(__file__), '..', '..', 'tools', 'perf', 'core',
Emily Hanley6fff05f2018-06-04 17:03:5065 'shard_maps', args.test_shard_map_filename)
Emily Hanley69ccba22018-02-09 20:15:2566
Emily Hanley7282b542018-03-02 20:42:4967def write_results(
nednguyena8950e42018-05-18 22:42:0668 perf_test_name, perf_results, json_test_results, benchmark_log,
69 isolated_out_dir, encoded):
Emily Hanley7282b542018-03-02 20:42:4970 benchmark_path = os.path.join(isolated_out_dir, perf_test_name)
71
72 os.makedirs(benchmark_path)
73 with open(os.path.join(benchmark_path, 'perf_results.json'), 'w') as f:
74 # non telemetry perf results are already json encoded
75 if encoded:
76 f.write(perf_results)
77 else:
78 json.dump(perf_results, f)
79 with open(os.path.join(benchmark_path, 'test_results.json'), 'w') as f:
80 json.dump(json_test_results, f)
81
nednguyena8950e42018-05-18 22:42:0682 with open(os.path.join(benchmark_path, 'benchmark_log.txt'), 'w') as f:
83 f.write(benchmark_log)
84
Emily Hanley7282b542018-03-02 20:42:4985
Ned Nguyen972d234f2018-06-22 01:58:0286def print_duration(step, start):
87 print 'Duration of %s: %d seconds' % (step, time.time() - start)
88
89
Emily Hanley69ccba22018-02-09 20:15:2590def execute_benchmark(benchmark, isolated_out_dir,
Emily Hanleyfdff4bbd2018-05-25 18:13:5891 args, rest_args, is_reference, stories=None):
Ned Nguyen972d234f2018-06-22 01:58:0292 start = time.time()
Emily Hanley69ccba22018-02-09 20:15:2593 # While we are between chartjson and histogram set we need
Emily Hanley8a0edf52018-04-28 01:12:5894 # to determine which output format to look for or see if it was
95 # already passed in in which case that format applies to all benchmarks
96 # in this run.
Ethan Kuefner4b0d7de2018-05-29 21:53:5097 is_histograms = append_output_format(args, rest_args)
Emily Hanley69ccba22018-02-09 20:15:2598 # Insert benchmark name as first argument to run_benchmark call
Emily Hanley8a0edf52018-04-28 01:12:5899 # which is the first argument in the rest_args. Also need to append
Emily Hanley6156dde2018-05-16 00:35:24100 # output format and smoke test mode.
Emily Hanley8a0edf52018-04-28 01:12:58101 per_benchmark_args = (rest_args[:1] + [benchmark] + rest_args[1:])
Emily Hanley7282b542018-03-02 20:42:49102 benchmark_name = benchmark
Emily Hanley69ccba22018-02-09 20:15:25103 if is_reference:
104 # Need to parse out the browser to replace browser flag with
105 # reference build so we run it reference build as well
106 browser_index = 0
107 for arg in per_benchmark_args:
108 if "browser" in arg:
109 break
110 browser_index = browser_index + 1
111 per_benchmark_args[browser_index] = '--browser=reference'
Emily Hanley84991a952018-02-22 18:12:14112 # Now we need to add in the rest of the reference build args
113 per_benchmark_args.append('--max-failures=5')
114 per_benchmark_args.append('--output-trace-tag=_ref')
Emily Hanley7282b542018-03-02 20:42:49115 benchmark_name = benchmark + '.reference'
Emily Hanley69ccba22018-02-09 20:15:25116
Emily Hanleyfdff4bbd2018-05-25 18:13:58117 # If we are only running a subset of stories, add in the begin and end
118 # index.
119 if stories:
120 if 'begin' in stories.keys():
121 per_benchmark_args.append(
Ned Nguyen2ae55602018-07-25 18:41:08122 ('--story-shard-begin-index=%d' % stories['begin']))
Emily Hanleyfdff4bbd2018-05-25 18:13:58123 if 'end' in stories.keys():
124 per_benchmark_args.append(
Ned Nguyen2ae55602018-07-25 18:41:08125 ('--story-shard-end-index=%d' % stories['end']))
Emily Hanleyfdff4bbd2018-05-25 18:13:58126
Emily Hanley69ccba22018-02-09 20:15:25127 # We don't care exactly what these are. In particular, the perf results
128 # could be any format (chartjson, legacy, histogram). We just pass these
129 # through, and expose these as results for this task.
nednguyena8950e42018-05-18 22:42:06130 rc, perf_results, json_test_results, benchmark_log = (
Emily Hanley69ccba22018-02-09 20:15:25131 run_telemetry_benchmark_as_googletest.run_benchmark(
132 args, per_benchmark_args, is_histograms))
133
Emily Hanley7282b542018-03-02 20:42:49134 write_results(
nednguyena8950e42018-05-18 22:42:06135 benchmark_name, perf_results, json_test_results, benchmark_log,
136 isolated_out_dir, False)
Ned Nguyen972d234f2018-06-22 01:58:02137
138 print_duration('executing benchmark %s' % benchmark_name, start)
Emily Hanley69ccba22018-02-09 20:15:25139 return rc
140
141
Ethan Kuefner4b0d7de2018-05-29 21:53:50142def append_output_format(args, rest_args):
Emily Hanley8a0edf52018-04-28 01:12:58143 # We need to determine if the output format is already passed in
144 # or if we need to define it for this benchmark
145 perf_output_specified = False
146 is_histograms = False
147 if args.output_format:
148 for output_format in args.output_format:
149 if 'histograms' in output_format:
150 perf_output_specified = True
151 is_histograms = True
152 if 'chartjson' in output_format:
153 perf_output_specified = True
154 rest_args.append('--output-format=' + output_format)
155 # When crbug.com/744736 is resolved we no longer have to check
156 # the type of format per benchmark and can rely on it being passed
157 # in as an arg as all benchmarks will output the same format.
158 if not perf_output_specified:
Ethan Kuefner4b0d7de2018-05-29 21:53:50159 rest_args.append('--output-format=histograms')
160 is_histograms = True
Emily Hanley8a0edf52018-04-28 01:12:58161 return is_histograms
162
Stephen Martinis0b18efa102017-09-25 22:55:12163def main():
164 parser = argparse.ArgumentParser()
165 parser.add_argument(
Stephen Martinis65e300c2017-10-10 00:20:34166 '--isolated-script-test-output', required=True)
Emily Hanley69ccba22018-02-09 20:15:25167 # These two flags are passed in from the swarming recipe
168 # but will no longer be needed when we migrate to this new recipe.
169 # For now we need to recognize them so they don't get passed
170 # through to telemetry.
Stephen Martinis0b18efa102017-09-25 22:55:12171 parser.add_argument(
172 '--isolated-script-test-chartjson-output', required=False)
173 parser.add_argument(
174 '--isolated-script-test-perf-output', required=False)
Emily Hanley69ccba22018-02-09 20:15:25175
Kenneth Russell40274052017-11-14 00:57:44176 parser.add_argument(
Kenneth Russella649a46122017-11-21 06:39:59177 '--isolated-script-test-filter', type=str, required=False)
Stephen Martinis0b18efa102017-09-25 22:55:12178 parser.add_argument('--xvfb', help='Start xvfb.', action='store_true')
Emily Hanley7282b542018-03-02 20:42:49179 parser.add_argument('--non-telemetry',
180 help='Type of perf test', type=bool, default=False)
Ned Nguyen3a1d09a12018-08-04 01:10:51181 parser.add_argument('--gtest-benchmark-name',
182 help='Name of the gtest benchmark', type=str,
183 required=False)
184
Emily Hanley8a0edf52018-04-28 01:12:58185 parser.add_argument('--benchmarks',
186 help='Comma separated list of benchmark names'
187 ' to run in lieu of indexing into our benchmark bot maps',
188 required=False)
Emily Hanley6156dde2018-05-16 00:35:24189 # Some executions may have a different sharding scheme and/or set of tests.
Emily Hanley6fff05f2018-06-04 17:03:50190 # These files must live in src/tools/perf/core/shard_maps
Emily Hanley6156dde2018-05-16 00:35:24191 parser.add_argument('--test-shard-map-filename', type=str, required=False)
Emily Hanley8a0edf52018-04-28 01:12:58192 parser.add_argument('--output-format', action='append')
Emily Hanley18ae7762018-05-14 17:07:58193 parser.add_argument('--run-ref-build',
194 help='Run test on reference browser', action='store_true')
Stephen Martinis0b18efa102017-09-25 22:55:12195
196 args, rest_args = parser.parse_known_args()
Stephen Martinis0b18efa102017-09-25 22:55:12197 isolated_out_dir = os.path.dirname(args.isolated_script_test_output)
Emily Hanley8a0edf52018-04-28 01:12:58198 return_code = 0
Stephen Martinis0b18efa102017-09-25 22:55:12199
Emily Hanley7282b542018-03-02 20:42:49200 if args.non_telemetry:
Ned Nguyen3a1d09a12018-08-04 01:10:51201 benchmark_name = args.gtest_benchmark_name
202 # Fallback to use the name of the executable if flag isn't set.
203 # TODO(crbug.com/870899): remove fallback logic and raise parser error if
204 # -non-telemetry is set but --gtest-benchmark-name is not set once pinpoint
205 # is converted to always pass --gtest-benchmark-name flag.
206 if not benchmark_name:
207 benchmark_name = rest_args[0]
Emily Hanley7282b542018-03-02 20:42:49208 return_code, charts, output_json = run_gtest_perf_test.execute_perf_test(
Ned Nguyen0933c722018-07-18 12:20:47209 args, rest_args)
Emily Hanley69ccba22018-02-09 20:15:25210
nednguyena8950e42018-05-18 22:42:06211 write_results(benchmark_name, charts, output_json,
212 benchmark_log='Not available for C++ perf test',
213 isolated_out_dir=isolated_out_dir, encoded=True)
Emily Hanley7282b542018-03-02 20:42:49214 else:
Emily Hanley8a0edf52018-04-28 01:12:58215 # If the user has supplied a list of benchmark names, execute those instead
216 # of the entire suite of benchmarks.
217 if args.benchmarks:
Emily Hanley309422c2018-05-14 15:26:10218 benchmarks = args.benchmarks.split(',')
Emily Hanley8a0edf52018-04-28 01:12:58219 for benchmark in benchmarks:
220 return_code = (execute_benchmark(
221 benchmark, isolated_out_dir, args, rest_args, False) or return_code)
222 else:
223 # First determine what shard we are running on to know how to
224 # index into the bot map to get list of benchmarks to run.
225 total_shards = None
226 shard_index = None
Emily Hanley69ccba22018-02-09 20:15:25227
Emily Hanley8a0edf52018-04-28 01:12:58228 env = os.environ.copy()
229 if 'GTEST_TOTAL_SHARDS' in env:
230 total_shards = env['GTEST_TOTAL_SHARDS']
231 if 'GTEST_SHARD_INDEX' in env:
232 shard_index = env['GTEST_SHARD_INDEX']
Emily Hanley69ccba22018-02-09 20:15:25233
Emily Hanley8a0edf52018-04-28 01:12:58234 if not (total_shards or shard_index):
235 raise Exception('Shard indicators must be present for perf tests')
Stephen Martinis0b18efa102017-09-25 22:55:12236
Emily Hanley6156dde2018-05-16 00:35:24237 sharding_map_path = get_sharding_map_path(args)
Ned Nguyen372312b2018-05-30 20:43:11238
239 # Copy sharding map file to isolated_out_dir so that the collect script
240 # can collect it later.
241 shutil.copyfile(
242 sharding_map_path,
243 os.path.join(isolated_out_dir, 'benchmarks_shard_map.json'))
244
Emily Hanley8a0edf52018-04-28 01:12:58245 with open(sharding_map_path) as f:
246 sharding_map = json.load(f)
Emily Hanley8a0edf52018-04-28 01:12:58247 sharding = sharding_map[shard_index]['benchmarks']
Emily Hanley7282b542018-03-02 20:42:49248
Emily Hanleyfdff4bbd2018-05-25 18:13:58249 for benchmark, stories in sharding.iteritems():
Emily Hanley8a0edf52018-04-28 01:12:58250 # Need to run the benchmark twice on browser and reference build
251 return_code = (execute_benchmark(
Emily Hanleyfdff4bbd2018-05-25 18:13:58252 benchmark, isolated_out_dir, args, rest_args,
253 False, stories=stories) or return_code)
Emily Hanley8a0edf52018-04-28 01:12:58254 # We ignore the return code of the reference build since we do not
255 # monitor it.
Emily Hanley18ae7762018-05-14 17:07:58256 if args.run_ref_build:
Emily Hanleyfdff4bbd2018-05-25 18:13:58257 execute_benchmark(benchmark, isolated_out_dir, args, rest_args, True,
258 stories=stories)
Emily Hanley7282b542018-03-02 20:42:49259
Stephen Martinis0b18efa102017-09-25 22:55:12260 return return_code
261
Emily Hanley9f06503b2018-05-03 18:46:43262
Stephen Martinis0b18efa102017-09-25 22:55:12263# This is not really a "script test" so does not need to manually add
264# any additional compile targets.
265def main_compile_targets(args):
Stephen Martinis782c13f2017-09-29 02:30:14266 json.dump([], args.output)
Stephen Martinis0b18efa102017-09-25 22:55:12267
268
269if __name__ == '__main__':
270 # Conform minimally to the protocol defined by ScriptTest.
271 if 'compile_targets' in sys.argv:
272 funcs = {
273 'run': None,
274 'compile_targets': main_compile_targets,
275 }
276 sys.exit(common.run_script(sys.argv[1:], funcs))
277 sys.exit(main())