OLD | NEW |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """API for the perf try job recipe module. | 5 """API for the perf try job recipe module. |
6 | 6 |
7 This API is meant to enable the perf try job recipe on any chromium-supported | 7 This API is meant to enable the perf try job recipe on any chromium-supported |
8 platform for any test that can be run via buildbot, perf or otherwise. | 8 platform for any test that can be run via buildbot, perf or otherwise. |
9 """ | 9 """ |
10 | 10 |
11 import re | 11 import re |
12 import urllib | 12 import urllib |
| 13 import uuid |
13 | 14 |
14 from recipe_engine import recipe_api | 15 from recipe_engine import recipe_api |
| 16 from . import build_state |
15 | 17 |
16 PERF_CONFIG_FILE = 'tools/run-perf-test.cfg' | 18 PERF_CONFIG_FILE = 'tools/run-perf-test.cfg' |
17 WEBKIT_PERF_CONFIG_FILE = 'third_party/WebKit/Tools/run-perf-test.cfg' | 19 WEBKIT_PERF_CONFIG_FILE = 'third_party/WebKit/Tools/run-perf-test.cfg' |
18 PERF_BENCHMARKS_PATH = 'tools/perf/benchmarks' | 20 PERF_BENCHMARKS_PATH = 'tools/perf/benchmarks' |
19 PERF_MEASUREMENTS_PATH = 'tools/perf/measurements' | 21 PERF_MEASUREMENTS_PATH = 'tools/perf/measurements' |
20 BUILDBOT_BUILDERNAME = 'BUILDBOT_BUILDERNAME' | 22 BUILDBOT_BUILDERNAME = 'BUILDBOT_BUILDERNAME' |
21 BENCHMARKS_JSON_FILE = 'benchmarks.json' | 23 BENCHMARKS_JSON_FILE = 'benchmarks.json' |
22 | 24 |
23 CLOUD_RESULTS_LINK = (r'\s(?P<VALUES>http://storage.googleapis.com/' | 25 CLOUD_RESULTS_LINK = (r'\s(?P<VALUES>http://storage.googleapis.com/' |
24 'chromium-telemetry/html-results/results-[a-z0-9-_]+)\s') | 26 'chromium-telemetry/html-results/results-[a-z0-9-_]+)\s') |
25 PROFILER_RESULTS_LINK = (r'\s(?P<VALUES>https://console.developers.google.com/' | 27 PROFILER_RESULTS_LINK = (r'\s(?P<VALUES>https://console.developers.google.com/' |
26 'm/cloudstorage/b/[a-z-]+/o/profiler-[a-z0-9-_.]+)\s') | 28 'm/cloudstorage/b/[a-z-]+/o/profiler-[a-z0-9-_.]+)\s') |
27 RESULTS_BANNER = """ | 29 RESULTS_BANNER = """ |
28 ===== PERF TRY JOB RESULTS ===== | 30 ===== PERF TRY JOB RESULTS ===== |
29 | 31 |
30 Test Command: %(command)s | 32 Test Command: %(command)s |
31 Test Metric: %(metric)s | 33 Test Metric: %(metric)s |
32 Relative Change: %(relative_change).05f%% | 34 Relative Change: %(relative_change).05f%% |
33 Standard Error: +- %(std_err).05f delta | 35 Standard Error: +- %(std_err).05f delta |
34 | 36 |
35 %(results)s | 37 %(results)s |
36 """ | 38 """ |
37 | 39 SERVICE_ACCOUNT = 'chromium-bisect' |
38 | 40 |
39 class PerfTryJobApi(recipe_api.RecipeApi): | 41 class PerfTryJobApi(recipe_api.RecipeApi): |
40 | 42 |
41 def __init__(self, *args, **kwargs): | 43 def __init__(self, *args, **kwargs): |
42 super(PerfTryJobApi, self).__init__(*args, **kwargs) | 44 super(PerfTryJobApi, self).__init__(*args, **kwargs) |
43 | 45 |
44 def start_perf_try_job(self, affected_files, bot_update_step, bot_db): | 46 def start_perf_try_job(self, affected_files, bot_update_step, bot_db): |
45 """Entry point pert tryjob or CQ tryjob.""" | 47 """Entry point pert tryjob or CQ tryjob.""" |
46 perf_config = self._get_perf_config(affected_files) | 48 perf_config = self._get_perf_config(affected_files) |
47 if perf_config: | 49 if perf_config: |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
187 # try job leading to unwanted cache and temp data. The best way to | 189 # try job leading to unwanted cache and temp data. The best way to |
188 # ensure the old build directory is removed before doing any | 190 # ensure the old build directory is removed before doing any |
189 # compilation. | 191 # compilation. |
190 self.m.file.rmtree( | 192 self.m.file.rmtree( |
191 'build directory', | 193 'build directory', |
192 self.m.chromium.c.build_dir.join(self.m.chromium.c.build_config_fs)) | 194 self.m.chromium.c.build_dir.join(self.m.chromium.c.build_config_fs)) |
193 self.m.chromium_tests.transient_check( | 195 self.m.chromium_tests.transient_check( |
194 update_step, | 196 update_step, |
195 lambda transform_name: self.m.chromium_tests.run_mb_and_compile( | 197 lambda transform_name: self.m.chromium_tests.run_mb_and_compile( |
196 compile_targets, None, name_suffix=transform_name(''))) | 198 compile_targets, None, name_suffix=transform_name(''))) |
197 else: | 199 else: # pragma: no cover |
198 self.m.chromium_tests.run_mb_and_compile( | 200 self.m.chromium_tests.run_mb_and_compile( |
199 compile_targets, None, name_suffix=' %s' % name) | 201 compile_targets, None, name_suffix=' %s' % name) |
200 | 202 |
201 def _run_test(self, cfg, **kwargs): | 203 def _run_test(self, cfg, **kwargs): |
202 """Runs test from config and return results.""" | 204 """Runs test from config and return results.""" |
203 values, overall_output, retcodes = self.m.bisect_tester.run_test( | 205 values, overall_output, retcodes = self.m.bisect_tester.run_test( |
204 cfg, **kwargs) | 206 cfg, **kwargs) |
205 all_values = self.m.bisect_tester.digest_run_results(values, retcodes, cfg) | 207 all_values = self.m.bisect_tester.digest_run_results(values, retcodes, cfg) |
206 overall_success = True | 208 overall_success = True |
207 if (not kwargs.get('allow_flakes', True) and | 209 if (not kwargs.get('allow_flakes', True) and |
208 cfg.get('test_type', 'perf') != 'return_code'): | 210 cfg.get('test_type', 'perf') != 'return_code'): |
209 overall_success = all(v == 0 for v in retcodes) | 211 overall_success = all(v == 0 for v in retcodes) |
210 return { | 212 return { |
211 'results': all_values, | 213 'results': all_values, |
212 'ret_code': overall_success, | 214 'ret_code': overall_success, |
213 'output': ''.join(overall_output) | 215 'output': ''.join(overall_output) |
214 } | 216 } |
215 | 217 |
216 def _build_and_run_tests(self, cfg, update_step, bot_db, revision, | 218 def _build_and_run_tests(self, cfg, update_step, bot_db, revision_hash, |
217 **kwargs): | 219 **kwargs): |
218 """Compiles binaries and runs tests for a given a revision.""" | 220 """Compiles binaries and runs tests for a given a revision.""" |
219 update_step = self._checkout_revision(update_step, bot_db, revision) | 221 with_patch = kwargs.get('name') == 'With Patch' |
220 self._compile(kwargs['name'], self.m.properties['mastername'], | 222 update_step = self._checkout_revision(update_step, bot_db, revision_hash) |
221 self.m.properties['buildername'], update_step, bot_db) | 223 if update_step.presentation.properties: |
222 | 224 revision_hash = update_step.presentation.properties['got_revision'] |
| 225 revision = build_state.BuildState(self, revision_hash, with_patch) |
| 226 # request build and wait for it only when the build is nonexistent |
| 227 if with_patch or not self._gsutil_file_exists(revision.build_file_path): |
| 228 try: |
| 229 self._request_build(revision, with_patch) |
| 230 self._wait_for(revision) |
| 231 except Exception, e: |
| 232 raise self.m.step.StepFailure('Error occurs when building %s: %s' % ( |
| 233 revision.build_id, str(e))) |
| 234 self._download_build(update_step, bot_db, revision) |
223 if self.m.chromium.c.TARGET_PLATFORM == 'android': | 235 if self.m.chromium.c.TARGET_PLATFORM == 'android': |
224 self.m.chromium_android.adb_install_apk('ChromePublic.apk') | 236 self.m.chromium_android.adb_install_apk('ChromePublic.apk') |
225 | 237 |
226 return self._run_test(cfg, **kwargs) | 238 return self._run_test(cfg, **kwargs) |
227 | 239 |
| 240 def _gsutil_file_exists(self, path): |
| 241 """Returns True if a file exists at the given GS path.""" |
| 242 try: |
| 243 self.m.gsutil(['ls', path], name='exists') |
| 244 except self.m.step.StepFailure: # pragma: no cover |
| 245 return False |
| 246 return True |
| 247 |
| 248 # Duplicate code from auto_bisect.revision_state._request_build |
| 249 def _request_build(self, revision, with_patch): |
| 250 if self.m.chromium.c.TARGET_PLATFORM == 'android': |
| 251 self.m.chromium_android.clean_local_files() |
| 252 else: |
| 253 # Removes any chrome temporary files or build.dead directories. |
| 254 self.m.chromium.cleanup_temp() |
| 255 if with_patch: |
| 256 properties = { |
| 257 'parent_got_revision': revision.commit_hash, |
| 258 'clobber': True, |
| 259 'build_archive_url': revision.build_file_path, |
| 260 'issue': self.m.properties['issue'], |
| 261 'patch_storage': self.m.properties['patch_storage'], |
| 262 'patchset': self.m.properties['patchset'], |
| 263 'rietveld': self.m.properties['rietveld'] |
| 264 } |
| 265 else: |
| 266 properties = { |
| 267 'parent_got_revision': revision.commit_hash, |
| 268 'clobber': True, |
| 269 'build_archive_url': revision.build_file_path, |
| 270 } |
| 271 bot_name = self.get_builder_bot_for_this_platform() |
| 272 if self.m.properties.get('is_test'): |
| 273 client_operation_id = '123456' |
| 274 else: |
| 275 client_operation_id = uuid.uuid4().hex # pragma: no cover |
| 276 build_details = { |
| 277 'bucket': 'master.' + self.m.properties['mastername'], |
| 278 'parameters': { |
| 279 'buildername': bot_name, |
| 280 'properties': properties |
| 281 }, |
| 282 'client_operation_id': client_operation_id, |
| 283 'tags':{} |
| 284 } |
| 285 result = self.m.buildbucket.put( |
| 286 [build_details], |
| 287 self.m.service_account.get_json_path(SERVICE_ACCOUNT)) |
| 288 revision.build_id = result.stdout['results'][0]['build']['id'] |
| 289 |
| 290 |
| 291 def _wait_for(self, revision): # pragma: no cover |
| 292 while True: |
| 293 if revision.is_completed(): |
| 294 if revision.is_build_archived: |
| 295 break |
| 296 raise self.m.step.StepFailure('Build %s fails' % revision.build_id) |
| 297 else: |
| 298 self.m.python.inline( |
| 299 'sleeping', |
| 300 """ |
| 301 import sys |
| 302 import time |
| 303 time.sleep(20*60) |
| 304 sys.exit(0) |
| 305 """) |
| 306 |
| 307 # Duplicate code from auto_bisect.api.start_test_run_for_bisect |
| 308 def _download_build(self, update_step, bot_db, |
| 309 revision, run_locally=False, |
| 310 skip_download=False): |
| 311 mastername = self.m.properties.get('mastername') |
| 312 buildername = self.m.properties.get('buildername') |
| 313 bot_config = bot_db.get_bot_config(mastername, buildername) |
| 314 build_archive_url = revision.build_file_path |
| 315 if not skip_download: |
| 316 if self.m.chromium.c.TARGET_PLATFORM == 'android': |
| 317 # The best way to ensure the old build directory is not used is to |
| 318 # remove it. |
| 319 build_dir = self.m.chromium.c.build_dir.join( |
| 320 self.m.chromium.c.build_config_fs) |
| 321 self.m.file.rmtree('build directory', build_dir) |
| 322 |
| 323 # The way android builders on tryserver.chromium.perf are archived is |
| 324 # different from builders on chromium.perf. In order to support both |
| 325 # forms of archives, we added this temporary hack until builders are |
| 326 # fixed. See http://crbug.com/535218. |
| 327 zip_dir = self.m.path.join(self.m.path['checkout'], 'full-build-linux') |
| 328 if self.m.path.exists(zip_dir): # pragma: no cover |
| 329 self.m.file.rmtree('full-build-linux directory', zip_dir) |
| 330 gs_bucket = 'gs://%s/' % revision.bucket |
| 331 archive_path = build_archive_url[len(gs_bucket):] |
| 332 self.m.chromium_android.download_build( |
| 333 bucket=bot_config['bucket'], |
| 334 path=archive_path) |
| 335 |
| 336 # The way android builders on tryserver.chromium.perf are archived is |
| 337 # different from builders on chromium.perf. In order to support both |
| 338 # forms of archives, we added this temporary hack until builders are |
| 339 # fixed. See http://crbug.com/535218. |
| 340 if self.m.path.exists(zip_dir): # pragma: no cover |
| 341 self.m.python.inline( |
| 342 'moving full-build-linux to out/Release', |
| 343 """ |
| 344 import shutil |
| 345 import sys |
| 346 shutil.move(sys.argv[1], sys.argv[2]) |
| 347 """, |
| 348 args=[zip_dir, build_dir]) |
| 349 else: |
| 350 self.m.chromium_tests.download_and_unzip_build( |
| 351 mastername, buildername, update_step, bot_db, |
| 352 build_archive_url=build_archive_url, |
| 353 build_revision=revision.commit_hash, |
| 354 override_bot_type='tester') |
| 355 |
| 356 # Duplicate code from auto_bisect.bisector.get_builder_bot_for_this_platform |
| 357 def get_builder_bot_for_this_platform(self): # pragma: no cover |
| 358 bot_name = self.m.properties.get('buildername', '') |
| 359 if 'win' in bot_name: |
| 360 if any(b in bot_name for b in ['x64', 'gpu']): |
| 361 return 'winx64_bisect_builder' |
| 362 return 'win_perf_bisect_builder' |
| 363 |
| 364 if 'android' in bot_name: |
| 365 if 'nexus9' in bot_name: |
| 366 return 'android_arm64_perf_bisect_builder' |
| 367 return 'android_perf_bisect_builder' |
| 368 |
| 369 if 'mac' in bot_name: |
| 370 return 'mac_perf_bisect_builder' |
| 371 |
| 372 return 'linux_perf_bisect_builder' |
| 373 |
228 def _load_config_file(self, name, src_path, **kwargs): | 374 def _load_config_file(self, name, src_path, **kwargs): |
229 """Attempts to load the specified config file and grab config dict.""" | 375 """Attempts to load the specified config file and grab config dict.""" |
230 step_result = self.m.python( | 376 step_result = self.m.python( |
231 name, | 377 name, |
232 self.resource('load_config_to_json.py'), | 378 self.resource('load_config_to_json.py'), |
233 ['--source', src_path, '--output_json', self.m.json.output()], | 379 ['--source', src_path, '--output_json', self.m.json.output()], |
234 **kwargs) | 380 **kwargs) |
235 if not step_result.json.output: # pragma: no cover | 381 if not step_result.json.output: # pragma: no cover |
236 raise self.m.step.StepFailure('Loading config file failed. [%s]' % | 382 raise self.m.step.StepFailure('Loading config file failed. [%s]' % |
237 src_path) | 383 src_path) |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
511 | 657 |
512 def _prepend_src_to_path_in_command(test_cfg): | 658 def _prepend_src_to_path_in_command(test_cfg): |
513 command_to_run = [] | 659 command_to_run = [] |
514 for v in test_cfg.get('command').split(): | 660 for v in test_cfg.get('command').split(): |
515 if v in ['./tools/perf/run_benchmark', | 661 if v in ['./tools/perf/run_benchmark', |
516 'tools/perf/run_benchmark', | 662 'tools/perf/run_benchmark', |
517 'tools\\perf\\run_benchmark']: | 663 'tools\\perf\\run_benchmark']: |
518 v = 'src/tools/perf/run_benchmark' | 664 v = 'src/tools/perf/run_benchmark' |
519 command_to_run.append(v) | 665 command_to_run.append(v) |
520 test_cfg.update({'command': ' '.join(command_to_run)}) | 666 test_cfg.update({'command': ' '.join(command_to_run)}) |
OLD | NEW |