| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Chromium auto-bisect tool | 6 """Chromium auto-bisect tool |
| 7 | 7 |
| 8 This script bisects a range of commits using binary search. It starts by getting | 8 This script bisects a range of commits using binary search. It starts by getting |
| 9 reference values for the specified "good" and "bad" commits. Then, for revisions | 9 reference values for the specified "good" and "bad" commits. Then, for revisions |
| 10 in between, it will get builds, run tests and classify intermediate revisions as | 10 in between, it will get builds, run tests and classify intermediate revisions as |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 121 # Git master branch name. | 121 # Git master branch name. |
| 122 BISECT_MASTER_BRANCH = 'master' | 122 BISECT_MASTER_BRANCH = 'master' |
| 123 # File to store 'git diff' content. | 123 # File to store 'git diff' content. |
| 124 BISECT_PATCH_FILE = 'deps_patch.txt' | 124 BISECT_PATCH_FILE = 'deps_patch.txt' |
| 125 # SVN repo where the bisect try jobs are submitted. | 125 # SVN repo where the bisect try jobs are submitted. |
| 126 PERF_SVN_REPO_URL = 'svn://svn.chromium.org/chrome-try/try-perf' | 126 PERF_SVN_REPO_URL = 'svn://svn.chromium.org/chrome-try/try-perf' |
| 127 FULL_SVN_REPO_URL = 'svn://svn.chromium.org/chrome-try/try' | 127 FULL_SVN_REPO_URL = 'svn://svn.chromium.org/chrome-try/try' |
| 128 ANDROID_CHROME_SVN_REPO_URL = ('svn://svn.chromium.org/chrome-try-internal/' | 128 ANDROID_CHROME_SVN_REPO_URL = ('svn://svn.chromium.org/chrome-try-internal/' |
| 129 'try-perf') | 129 'try-perf') |
| 130 | 130 |
| 131 |
| 131 class RunGitError(Exception): | 132 class RunGitError(Exception): |
| 132 | 133 |
| 133 def __str__(self): | 134 def __str__(self): |
| 134 return '%s\nError executing git command.' % self.args[0] | 135 return '%s\nError executing git command.' % self.args[0] |
| 135 | 136 |
| 136 | 137 |
| 137 def GetSHA1HexDigest(contents): | 138 def GetSHA1HexDigest(contents): |
| 138 """Returns SHA1 hex digest of the given string.""" | 139 """Returns SHA1 hex digest of the given string.""" |
| 139 return hashlib.sha1(contents).hexdigest() | 140 return hashlib.sha1(contents).hexdigest() |
| 140 | 141 |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 454 current_arg_split = current_arg.split('=') | 455 current_arg_split = current_arg.split('=') |
| 455 | 456 |
| 456 # Check 2 cases, --arg=<val> and --arg <val> | 457 # Check 2 cases, --arg=<val> and --arg <val> |
| 457 if len(current_arg_split) == 2: | 458 if len(current_arg_split) == 2: |
| 458 arg_dict[arg_to_parse] = current_arg_split[1] | 459 arg_dict[arg_to_parse] = current_arg_split[1] |
| 459 elif i + 1 < len(command_args): | 460 elif i + 1 < len(command_args): |
| 460 arg_dict[arg_to_parse] = command_args[i+1] | 461 arg_dict[arg_to_parse] = command_args[i+1] |
| 461 | 462 |
| 462 path_to_generate = os.path.join('tools', 'perf', 'generate_profile') | 463 path_to_generate = os.path.join('tools', 'perf', 'generate_profile') |
| 463 | 464 |
| 464 if arg_dict.has_key('--profile-dir') and arg_dict.has_key('--browser'): | 465 if '--profile-dir' in arg_dict and '--browser' in arg_dict: |
| 465 profile_path, profile_type = os.path.split(arg_dict['--profile-dir']) | 466 profile_path, profile_type = os.path.split(arg_dict['--profile-dir']) |
| 466 return not bisect_utils.RunProcess(['python', path_to_generate, | 467 return not bisect_utils.RunProcess( |
| 467 '--profile-type-to-generate', profile_type, | 468 [ |
| 468 '--browser', arg_dict['--browser'], '--output-dir', profile_path]) | 469 'python', path_to_generate, |
| 470 '--profile-type-to-generate', profile_type, |
| 471 '--browser', arg_dict['--browser'], |
| 472 '--output-dir', profile_path |
| 473 ]) |
| 469 return False | 474 return False |
| 470 return True | 475 return True |
| 471 | 476 |
| 472 | 477 |
| 473 def _CheckRegressionConfidenceError( | 478 def _CheckRegressionConfidenceError( |
| 474 good_revision, | 479 good_revision, |
| 475 bad_revision, | 480 bad_revision, |
| 476 known_good_value, | 481 known_good_value, |
| 477 known_bad_value): | 482 known_bad_value): |
| 478 """Checks whether we can be confident beyond a certain degree that the given | 483 """Checks whether we can be confident beyond a certain degree that the given |
| 479 metrics represent a regression. | 484 metrics represent a regression. |
| 480 | 485 |
| 481 Args: | 486 Args: |
| 482 good_revision: string representing the commit considered 'good' | 487 good_revision: string representing the commit considered 'good' |
| 483 bad_revision: Same as above for 'bad'. | 488 bad_revision: Same as above for 'bad'. |
| 484 known_good_value: A dict with at least: 'values', 'mean' and 'std_err' | 489 known_good_value: A dict with at least: 'values', 'mean' and 'std_err' |
| 485 known_bad_value: Same as above. | 490 known_bad_value: Same as above. |
| 486 | 491 |
| 487 Returns: | 492 Returns: |
| 488 False if there is no error (i.e. we can be confident there's a regressioni), | 493 False if there is no error (i.e. we can be confident there's a regression), |
| 489 a string containing the details of the lack of confidence otherwise. | 494 a string containing the details of the lack of confidence otherwise. |
| 490 """ | 495 """ |
| 491 error = False | 496 error = False |
| 492 # Adding good and bad values to a parameter list. | 497 # Adding good and bad values to a parameter list. |
| 493 confidence_params = [] | 498 confidence_params = [] |
| 494 for l in [known_bad_value['values'], known_good_value['values']]: | 499 for l in [known_bad_value['values'], known_good_value['values']]: |
| 495 # Flatten if needed, by averaging the values in each nested list | 500 # Flatten if needed, by averaging the values in each nested list |
| 496 if isinstance(l, list) and all([isinstance(x, list) for x in l]): | 501 if isinstance(l, list) and all([isinstance(x, list) for x in l]): |
| 497 averages = map(math_utils.Mean, l) | 502 averages = map(math_utils.Mean, l) |
| 498 confidence_params.append(averages) | 503 confidence_params.append(averages) |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 569 if returncode: | 574 if returncode: |
| 570 raise RunGitError('Deleting branch failed, %s', output) | 575 raise RunGitError('Deleting branch failed, %s', output) |
| 571 | 576 |
| 572 # Check if the tree is dirty: make sure the index is up to date and then | 577 # Check if the tree is dirty: make sure the index is up to date and then |
| 573 # run diff-index. | 578 # run diff-index. |
| 574 bisect_utils.RunGit(['update-index', '--refresh', '-q']) | 579 bisect_utils.RunGit(['update-index', '--refresh', '-q']) |
| 575 output, returncode = bisect_utils.RunGit(['diff-index', 'HEAD']) | 580 output, returncode = bisect_utils.RunGit(['diff-index', 'HEAD']) |
| 576 if output: | 581 if output: |
| 577 raise RunGitError('Cannot send a try job with a dirty tree.') | 582 raise RunGitError('Cannot send a try job with a dirty tree.') |
| 578 | 583 |
| 579 # Create/check out the telemetry-tryjob branch, and edit the configs | 584 # Create and check out the telemetry-tryjob branch, and edit the configs |
| 580 # for the tryjob there. | 585 # for the try job there. |
| 581 output, returncode = bisect_utils.RunGit(['checkout', '-b', new_branch]) | 586 output, returncode = bisect_utils.RunGit(['checkout', '-b', new_branch]) |
| 582 if returncode: | 587 if returncode: |
| 583 raise RunGitError('Failed to checkout branch: %s.' % output) | 588 raise RunGitError('Failed to checkout branch: %s.' % output) |
| 584 | 589 |
| 585 output, returncode = bisect_utils.RunGit( | 590 output, returncode = bisect_utils.RunGit( |
| 586 ['branch', '--set-upstream-to', parent_branch]) | 591 ['branch', '--set-upstream-to', parent_branch]) |
| 587 if returncode: | 592 if returncode: |
| 588 raise RunGitError('Error in git branch --set-upstream-to') | 593 raise RunGitError('Error in git branch --set-upstream-to') |
| 589 | 594 |
| 590 | 595 |
| 591 def _StartBuilderTryJob( | 596 def _StartBuilderTryJob( |
| 592 builder_type, git_revision, builder_name, job_name, patch=None): | 597 builder_type, git_revision, builder_name, job_name, patch=None): |
| 593 """Attempts to run a try job from the current directory. | 598 """Attempts to run a try job from the current directory. |
| 594 | 599 |
| 595 Args: | 600 Args: |
| 596 builder_type: One of the builder types in fetch_build, e.g. "perf". | 601 builder_type: One of the builder types in fetch_build, e.g. "perf". |
| 597 git_revision: A git commit hash. | 602 git_revision: A git commit hash. |
| 598 builder_name: Name of the bisect bot to be used for try job. | 603 builder_name: Name of the bisect bot to be used for try job. |
| 599 bisect_job_name: Try job name, used to identify which bisect | 604 bisect_job_name: Try job name, used to identify which bisect |
| 600 job was responsible for requesting a build. | 605 job was responsible for requesting a build. |
| 601 patch: A DEPS patch (used while bisecting dependency repositories), | 606 patch: A DEPS patch (used while bisecting dependency repositories), |
| 602 or None if we're bisecting the top-level repository. | 607 or None if we're bisecting the top-level repository. |
| 603 """ | 608 """ |
| 604 # TODO(prasadv, qyearsley): Make this a method of BuildArchive | 609 # TODO(prasadv, qyearsley): Make this a method of BuildArchive |
| 605 # (which may be renamed to BuilderTryBot or Builder). | 610 # (which may be renamed to BuilderTryBot or Builder). |
| 606 try: | 611 try: |
| 607 # Temporary branch for running tryjob. | 612 # Temporary branch for running a try job. |
| 608 _PrepareBisectBranch(BISECT_MASTER_BRANCH, BISECT_TRYJOB_BRANCH) | 613 _PrepareBisectBranch(BISECT_MASTER_BRANCH, BISECT_TRYJOB_BRANCH) |
| 609 patch_content = '/dev/null' | 614 patch_content = '/dev/null' |
| 610 # Create a temporary patch file. | 615 # Create a temporary patch file. |
| 611 if patch: | 616 if patch: |
| 612 WriteStringToFile(patch, BISECT_PATCH_FILE) | 617 WriteStringToFile(patch, BISECT_PATCH_FILE) |
| 613 patch_content = BISECT_PATCH_FILE | 618 patch_content = BISECT_PATCH_FILE |
| 614 | 619 |
| 615 try_command = [ | 620 try_command = [ |
| 616 'try', | 621 'try', |
| 617 '--bot=%s' % builder_name, | 622 '--bot=%s' % builder_name, |
| 618 '--revision=%s' % git_revision, | 623 '--revision=%s' % git_revision, |
| 619 '--name=%s' % job_name, | 624 '--name=%s' % job_name, |
| 620 '--svn_repo=%s' % _TryJobSvnRepo(builder_type), | 625 '--svn_repo=%s' % _TryJobSvnRepo(builder_type), |
| 621 '--diff=%s' % patch_content, | 626 '--diff=%s' % patch_content, |
| 622 ] | 627 ] |
| 623 # Execute try job to build revision. | 628 # Execute try job to build revision. |
| 624 print try_command | 629 print try_command |
| 625 output, return_code = bisect_utils.RunGit(try_command) | 630 output, return_code = bisect_utils.RunGit(try_command) |
| 626 | 631 |
| 627 command_string = ' '.join(['git'] + try_command) | 632 command_string = ' '.join(['git'] + try_command) |
| 628 if return_code: | 633 if return_code: |
| 629 raise RunGitError('Could not execute tryjob: %s.\n' | 634 raise RunGitError('Could not execute try job: %s.\n' |
| 630 'Error: %s' % (command_string, output)) | 635 'Error: %s' % (command_string, output)) |
| 631 logging.info('Try job successfully submitted.\n TryJob Details: %s\n%s', | 636 logging.info('Try job successfully submitted.\n TryJob Details: %s\n%s', |
| 632 command_string, output) | 637 command_string, output) |
| 633 finally: | 638 finally: |
| 634 # Delete patch file if exists. | 639 # Delete patch file if exists. |
| 635 try: | 640 try: |
| 636 os.remove(BISECT_PATCH_FILE) | 641 os.remove(BISECT_PATCH_FILE) |
| 637 except OSError as e: | 642 except OSError as e: |
| 638 if e.errno != errno.ENOENT: | 643 if e.errno != errno.ENOENT: |
| 639 raise | 644 raise |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 740 results[depot_name] = None | 745 results[depot_name] = None |
| 741 return results | 746 return results |
| 742 except ImportError: | 747 except ImportError: |
| 743 deps_file_contents = ReadStringFromFile(deps_file) | 748 deps_file_contents = ReadStringFromFile(deps_file) |
| 744 parse_results = _ParseRevisionsFromDEPSFileManually(deps_file_contents) | 749 parse_results = _ParseRevisionsFromDEPSFileManually(deps_file_contents) |
| 745 results = {} | 750 results = {} |
| 746 for depot_name, depot_revision in parse_results.iteritems(): | 751 for depot_name, depot_revision in parse_results.iteritems(): |
| 747 depot_revision = depot_revision.strip('@') | 752 depot_revision = depot_revision.strip('@') |
| 748 logging.warn(depot_name, depot_revision) | 753 logging.warn(depot_name, depot_revision) |
| 749 for cur_name, cur_data in bisect_utils.DEPOT_DEPS_NAME.iteritems(): | 754 for cur_name, cur_data in bisect_utils.DEPOT_DEPS_NAME.iteritems(): |
| 750 if (cur_data.has_key('deps_var') and | 755 if cur_data.get('deps_var') == depot_name: |
| 751 cur_data['deps_var'] == depot_name): | |
| 752 src_name = cur_name | 756 src_name = cur_name |
| 753 results[src_name] = depot_revision | 757 results[src_name] = depot_revision |
| 754 break | 758 break |
| 755 return results | 759 return results |
| 756 | 760 |
| 757 def _Get3rdPartyRevisions(self, depot): | 761 def _Get3rdPartyRevisions(self, depot): |
| 758 """Parses the DEPS file to determine WebKit/v8/etc... versions. | 762 """Parses the DEPS file to determine WebKit/v8/etc... versions. |
| 759 | 763 |
| 760 Args: | 764 Args: |
| 761 depot: A depot name. Should be in the DEPOT_NAMES list. | 765 depot: A depot name. Should be in the DEPOT_NAMES list. |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 916 extra_src=self.opts.extra_src) | 920 extra_src=self.opts.extra_src) |
| 917 | 921 |
| 918 try: | 922 try: |
| 919 _StartBuilderTryJob(self.opts.builder_type, git_revision, builder_name, | 923 _StartBuilderTryJob(self.opts.builder_type, git_revision, builder_name, |
| 920 job_name=build_request_id, patch=deps_patch) | 924 job_name=build_request_id, patch=deps_patch) |
| 921 except RunGitError as e: | 925 except RunGitError as e: |
| 922 logging.warn('Failed to post builder try job for revision: [%s].\n' | 926 logging.warn('Failed to post builder try job for revision: [%s].\n' |
| 923 'Error: %s', git_revision, e) | 927 'Error: %s', git_revision, e) |
| 924 return None | 928 return None |
| 925 | 929 |
| 926 # Get the buildbot master url to monitor build status. | 930 # Get the buildbot master URL to monitor build status. |
| 927 buildbot_server_url = fetch_build.GetBuildBotUrl( | 931 buildbot_server_url = fetch_build.GetBuildBotUrl( |
| 928 builder_type=self.opts.builder_type, | 932 builder_type=self.opts.builder_type, |
| 929 target_arch=self.opts.target_arch, | 933 target_arch=self.opts.target_arch, |
| 930 target_platform=self.opts.target_platform, | 934 target_platform=self.opts.target_platform, |
| 931 extra_src=self.opts.extra_src) | 935 extra_src=self.opts.extra_src) |
| 932 | 936 |
| 933 archive_filename, error_msg = _WaitUntilBuildIsReady( | 937 archive_filename, error_msg = _WaitUntilBuildIsReady( |
| 934 fetch_build_func, builder_name, build_request_id, build_timeout, | 938 fetch_build_func, builder_name, build_request_id, build_timeout, |
| 935 buildbot_server_url) | 939 buildbot_server_url) |
| 936 if not archive_filename: | 940 if not archive_filename: |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1020 """Checks if build can be downloaded based on target platform and depot.""" | 1024 """Checks if build can be downloaded based on target platform and depot.""" |
| 1021 if (self.opts.target_platform in ['chromium', 'android', 'android-chrome'] | 1025 if (self.opts.target_platform in ['chromium', 'android', 'android-chrome'] |
| 1022 and self.opts.builder_type): | 1026 and self.opts.builder_type): |
| 1023 # In case of android-chrome platform, download archives only for | 1027 # In case of android-chrome platform, download archives only for |
| 1024 # android-chrome depot; for other depots such as chromium, v8, skia | 1028 # android-chrome depot; for other depots such as chromium, v8, skia |
| 1025 # etc., build the binary locally. | 1029 # etc., build the binary locally. |
| 1026 if self.opts.target_platform == 'android-chrome': | 1030 if self.opts.target_platform == 'android-chrome': |
| 1027 return depot == 'android-chrome' | 1031 return depot == 'android-chrome' |
| 1028 else: | 1032 else: |
| 1029 return (depot == 'chromium' or | 1033 return (depot == 'chromium' or |
| 1030 'chromium' in bisect_utils.DEPOT_DEPS_NAME[depot]['from'] or | 1034 'chromium' in bisect_utils.DEPOT_DEPS_NAME[depot]['from'] or |
| 1031 'v8' in bisect_utils.DEPOT_DEPS_NAME[depot]['from']) | 1035 'v8' in bisect_utils.DEPOT_DEPS_NAME[depot]['from']) |
| 1032 return False | 1036 return False |
| 1033 | 1037 |
| 1034 def UpdateDepsContents(self, deps_contents, depot, git_revision, deps_key): | 1038 def UpdateDepsContents(self, deps_contents, depot, git_revision, deps_key): |
| 1035 """Returns modified version of DEPS file contents. | 1039 """Returns modified version of DEPS file contents. |
| 1036 | 1040 |
| 1037 Args: | 1041 Args: |
| 1038 deps_contents: DEPS file content. | 1042 deps_contents: DEPS file content. |
| 1039 depot: Current depot being bisected. | 1043 depot: Current depot being bisected. |
| 1040 git_revision: A git hash to be updated in DEPS. | 1044 git_revision: A git hash to be updated in DEPS. |
| 1041 deps_key: Key in vars section of DEPS file to be searched. | 1045 deps_key: Key in vars section of DEPS file to be searched. |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1355 if parsed_metric: | 1359 if parsed_metric: |
| 1356 metric_values.append(math_utils.Mean(parsed_metric)) | 1360 metric_values.append(math_utils.Mean(parsed_metric)) |
| 1357 # If we're bisecting on a metric (ie, changes in the mean or | 1361 # If we're bisecting on a metric (ie, changes in the mean or |
| 1358 # standard deviation) and no metric values are produced, bail out. | 1362 # standard deviation) and no metric values are produced, bail out. |
| 1359 if not metric_values: | 1363 if not metric_values: |
| 1360 break | 1364 break |
| 1361 elif self._IsBisectModeReturnCode(): | 1365 elif self._IsBisectModeReturnCode(): |
| 1362 metric_values.append(return_code) | 1366 metric_values.append(return_code) |
| 1363 | 1367 |
| 1364 elapsed_minutes = (time.time() - start_time) / 60.0 | 1368 elapsed_minutes = (time.time() - start_time) / 60.0 |
| 1365 time_limit = self.opts.max_time_minutes * test_run_multiplier | 1369 time_limit = self.opts.max_time_minutes * test_run_multiplier |
| 1366 if elapsed_minutes >= time_limit: | 1370 if elapsed_minutes >= time_limit: |
| 1367 break | 1371 break |
| 1368 | 1372 |
| 1369 if metric and len(metric_values) == 0: | 1373 if metric and len(metric_values) == 0: |
| 1370 err_text = 'Metric %s was not found in the test output.' % metric | 1374 err_text = 'Metric %s was not found in the test output.' % metric |
| 1371 # TODO(qyearsley): Consider also getting and displaying a list of metrics | 1375 # TODO(qyearsley): Consider also getting and displaying a list of metrics |
| 1372 # that were found in the output here. | 1376 # that were found in the output here. |
| 1373 return (err_text, failure_code, output_of_all_runs) | 1377 return (err_text, failure_code, output_of_all_runs) |
| 1374 | 1378 |
| 1375 # If we're bisecting on return codes, we're really just looking for zero vs | 1379 # If we're bisecting on return codes, we're really just looking for zero vs |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1435 def _RunPostSync(self, _depot): | 1439 def _RunPostSync(self, _depot): |
| 1436 """Performs any work after syncing. | 1440 """Performs any work after syncing. |
| 1437 | 1441 |
| 1438 Args: | 1442 Args: |
| 1439 depot: Depot name. | 1443 depot: Depot name. |
| 1440 | 1444 |
| 1441 Returns: | 1445 Returns: |
| 1442 True if successful. | 1446 True if successful. |
| 1443 """ | 1447 """ |
| 1444 if 'android' in self.opts.target_platform: | 1448 if 'android' in self.opts.target_platform: |
| 1445 if not builder.SetupAndroidBuildEnvironment(self.opts, | 1449 if not builder.SetupAndroidBuildEnvironment( |
| 1446 path_to_src=self.src_cwd): | 1450 self.opts, path_to_src=self.src_cwd): |
| 1447 return False | 1451 return False |
| 1448 | 1452 |
| 1449 return self.RunGClientHooks() | 1453 return self.RunGClientHooks() |
| 1450 | 1454 |
| 1451 @staticmethod | 1455 @staticmethod |
| 1452 def ShouldSkipRevision(depot, revision): | 1456 def ShouldSkipRevision(depot, revision): |
| 1453 """Checks whether a particular revision can be safely skipped. | 1457 """Checks whether a particular revision can be safely skipped. |
| 1454 | 1458 |
| 1455 Some commits can be safely skipped (such as a DEPS roll for the repos | 1459 Some commits can be safely skipped (such as a DEPS roll for the repos |
| 1456 still using .DEPS.git), since the tool is git based those changes | 1460 still using .DEPS.git), since the tool is git based those changes |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1541 | 1545 |
| 1542 # A value other than 0 indicates that the test couldn't be run, and results | 1546 # A value other than 0 indicates that the test couldn't be run, and results |
| 1543 # should also include an error message. | 1547 # should also include an error message. |
| 1544 if results[1] != 0: | 1548 if results[1] != 0: |
| 1545 return results | 1549 return results |
| 1546 | 1550 |
| 1547 external_revisions = self._Get3rdPartyRevisions(depot) | 1551 external_revisions = self._Get3rdPartyRevisions(depot) |
| 1548 | 1552 |
| 1549 if not external_revisions is None: | 1553 if not external_revisions is None: |
| 1550 return (results[0], results[1], external_revisions, | 1554 return (results[0], results[1], external_revisions, |
| 1551 time.time() - after_build_time, after_build_time - | 1555 time.time() - after_build_time, after_build_time - |
| 1552 start_build_time) | 1556 start_build_time) |
| 1553 else: | 1557 else: |
| 1554 return ('Failed to parse DEPS file for external revisions.', | 1558 return ('Failed to parse DEPS file for external revisions.', |
| 1555 BUILD_RESULT_FAIL) | 1559 BUILD_RESULT_FAIL) |
| 1556 | 1560 |
| 1557 def _SyncRevision(self, depot, revision, sync_client): | 1561 def _SyncRevision(self, depot, revision, sync_client): |
| 1558 """Syncs depot to particular revision. | 1562 """Syncs depot to particular revision. |
| 1559 | 1563 |
| 1560 Args: | 1564 Args: |
| 1561 depot: The depot that's being used at the moment (src, webkit, etc.) | 1565 depot: The depot that's being used at the moment (src, webkit, etc.) |
| 1562 revision: The revision to sync to. | 1566 revision: The revision to sync to. |
| 1563 sync_client: Program used to sync, e.g. "gclient". Can be None. | 1567 sync_client: Program used to sync, e.g. "gclient". Can be None. |
| 1564 | 1568 |
| 1565 Returns: | 1569 Returns: |
| 1566 True if successful, False otherwise. | 1570 True if successful, False otherwise. |
| 1567 """ | 1571 """ |
| 1568 self.depot_registry.ChangeToDepotDir(depot) | 1572 self.depot_registry.ChangeToDepotDir(depot) |
| 1569 | 1573 |
| 1570 if sync_client: | 1574 if sync_client: |
| 1571 self.PerformPreBuildCleanup() | 1575 self.PerformPreBuildCleanup() |
| 1572 | 1576 |
| 1573 # When using gclient to sync, you need to specify the depot you | 1577 # When using gclient to sync, you need to specify the depot you |
| 1574 # want so that all the dependencies sync properly as well. | 1578 # want so that all the dependencies sync properly as well. |
| 1575 # i.e. gclient sync src@<SHA1> | 1579 # i.e. gclient sync src@<SHA1> |
| 1576 if sync_client == 'gclient' and revision: | 1580 if sync_client == 'gclient' and revision: |
| 1577 revision = '%s@%s' % (bisect_utils.DEPOT_DEPS_NAME[depot]['src'], | 1581 revision = '%s@%s' % (bisect_utils.DEPOT_DEPS_NAME[depot]['src'], |
| 1578 revision) | 1582 revision) |
| 1579 if depot == 'chromium' and self.opts.target_platform == 'android-chrome': | 1583 if depot == 'chromium' and self.opts.target_platform == 'android-chrome': |
| 1580 return self._SyncRevisionsForAndroidChrome(revision) | 1584 return self._SyncRevisionsForAndroidChrome(revision) |
| 1581 | 1585 |
| 1582 return source_control.SyncToRevision(revision, sync_client) | 1586 return source_control.SyncToRevision(revision, sync_client) |
| 1583 | 1587 |
| 1584 def _SyncRevisionsForAndroidChrome(self, revision): | 1588 def _SyncRevisionsForAndroidChrome(self, revision): |
| 1585 """Syncs android-chrome and chromium repos to particular revision. | 1589 """Syncs android-chrome and chromium repos to particular revision. |
| 1586 | 1590 |
| 1587 This is a special case for android-chrome as the gclient sync for chromium | 1591 This is a special case for android-chrome as the gclient sync for chromium |
| 1588 overwrites the android-chrome revision to TOT. Therefore both the repos | 1592 overwrites the android-chrome revision to TOT. Therefore both the repos |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1610 current_value: The value of the metric being checked. | 1614 current_value: The value of the metric being checked. |
| 1611 known_bad_value: The reference value for a "failed" run. | 1615 known_bad_value: The reference value for a "failed" run. |
| 1612 known_good_value: The reference value for a "passed" run. | 1616 known_good_value: The reference value for a "passed" run. |
| 1613 | 1617 |
| 1614 Returns: | 1618 Returns: |
| 1615 True if the current_value is closer to the known_good_value than the | 1619 True if the current_value is closer to the known_good_value than the |
| 1616 known_bad_value. | 1620 known_bad_value. |
| 1617 """ | 1621 """ |
| 1618 if self.opts.bisect_mode == bisect_utils.BISECT_MODE_STD_DEV: | 1622 if self.opts.bisect_mode == bisect_utils.BISECT_MODE_STD_DEV: |
| 1619 dist_to_good_value = abs(current_value['std_dev'] - | 1623 dist_to_good_value = abs(current_value['std_dev'] - |
| 1620 known_good_value['std_dev']) | 1624 known_good_value['std_dev']) |
| 1621 dist_to_bad_value = abs(current_value['std_dev'] - | 1625 dist_to_bad_value = abs(current_value['std_dev'] - |
| 1622 known_bad_value['std_dev']) | 1626 known_bad_value['std_dev']) |
| 1623 else: | 1627 else: |
| 1624 dist_to_good_value = abs(current_value['mean'] - known_good_value['mean']) | 1628 dist_to_good_value = abs(current_value['mean'] - known_good_value['mean']) |
| 1625 dist_to_bad_value = abs(current_value['mean'] - known_bad_value['mean']) | 1629 dist_to_bad_value = abs(current_value['mean'] - known_bad_value['mean']) |
| 1626 | 1630 |
| 1627 return dist_to_good_value < dist_to_bad_value | 1631 return dist_to_good_value < dist_to_bad_value |
| 1628 | 1632 |
| 1629 def _GetV8BleedingEdgeFromV8TrunkIfMappable( | 1633 def _GetV8BleedingEdgeFromV8TrunkIfMappable( |
| 1630 self, revision, bleeding_edge_branch): | 1634 self, revision, bleeding_edge_branch): |
| 1631 """Gets v8 bleeding edge revision mapped to v8 revision in trunk. | 1635 """Gets v8 bleeding edge revision mapped to v8 revision in trunk. |
| 1632 | 1636 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1687 return re_results.group('git_revision') | 1691 return re_results.group('git_revision') |
| 1688 except (IndexError, ValueError): | 1692 except (IndexError, ValueError): |
| 1689 pass | 1693 pass |
| 1690 if not git_revision: | 1694 if not git_revision: |
| 1691 # Wasn't successful, try the old way of looking for "Prepare push to" | 1695 # Wasn't successful, try the old way of looking for "Prepare push to" |
| 1692 git_revision = source_control.ResolveToRevision( | 1696 git_revision = source_control.ResolveToRevision( |
| 1693 int(commit_position) - 1, 'v8_bleeding_edge', | 1697 int(commit_position) - 1, 'v8_bleeding_edge', |
| 1694 bisect_utils.DEPOT_DEPS_NAME, -1, cwd=v8_bleeding_edge_dir) | 1698 bisect_utils.DEPOT_DEPS_NAME, -1, cwd=v8_bleeding_edge_dir) |
| 1695 | 1699 |
| 1696 if git_revision: | 1700 if git_revision: |
| 1697 revision_info = source_control.QueryRevisionInfo(git_revision, | 1701 revision_info = source_control.QueryRevisionInfo( |
| 1698 cwd=v8_bleeding_edge_dir) | 1702 git_revision, cwd=v8_bleeding_edge_dir) |
| 1699 | 1703 |
| 1700 if 'Prepare push to trunk' in revision_info['subject']: | 1704 if 'Prepare push to trunk' in revision_info['subject']: |
| 1701 return git_revision | 1705 return git_revision |
| 1702 return None | 1706 return None |
| 1703 | 1707 |
| 1704 def _GetNearestV8BleedingEdgeFromTrunk( | 1708 def _GetNearestV8BleedingEdgeFromTrunk( |
| 1705 self, revision, v8_branch, bleeding_edge_branch, search_forward=True): | 1709 self, revision, v8_branch, bleeding_edge_branch, search_forward=True): |
| 1706 """Gets the nearest V8 roll and maps to bleeding edge revision. | 1710 """Gets the nearest V8 roll and maps to bleeding edge revision. |
| 1707 | 1711 |
| 1708 V8 is a bit tricky to bisect since it isn't just rolled out like blink. | 1712 V8 is a bit tricky to bisect since it isn't just rolled out like blink. |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1765 | 1769 |
| 1766 # Support for the chromium revisions with external V8 repo. | 1770 # Support for the chromium revisions with external V8 repo. |
| 1767 # ie https://chromium.googlesource.com/external/v8.git | 1771 # ie https://chromium.googlesource.com/external/v8.git |
| 1768 cmd = ['config', '--get', 'remote.origin.url'] | 1772 cmd = ['config', '--get', 'remote.origin.url'] |
| 1769 v8_repo_url = bisect_utils.CheckRunGit(cmd, cwd=cwd) | 1773 v8_repo_url = bisect_utils.CheckRunGit(cmd, cwd=cwd) |
| 1770 | 1774 |
| 1771 if 'external/v8.git' in v8_repo_url: | 1775 if 'external/v8.git' in v8_repo_url: |
| 1772 v8_branch = 'origin/master' | 1776 v8_branch = 'origin/master' |
| 1773 bleeding_edge_branch = 'origin/bleeding_edge' | 1777 bleeding_edge_branch = 'origin/bleeding_edge' |
| 1774 | 1778 |
| 1775 r1 = self._GetNearestV8BleedingEdgeFromTrunk(min_revision_state.revision, | 1779 r1 = self._GetNearestV8BleedingEdgeFromTrunk( |
| 1776 v8_branch, bleeding_edge_branch, search_forward=True) | 1780 min_revision_state.revision, |
| 1777 r2 = self._GetNearestV8BleedingEdgeFromTrunk(max_revision_state.revision, | 1781 v8_branch, |
| 1778 v8_branch, bleeding_edge_branch, search_forward=False) | 1782 bleeding_edge_branch, |
| 1783 search_forward=True) |
| 1784 r2 = self._GetNearestV8BleedingEdgeFromTrunk( |
| 1785 max_revision_state.revision, |
| 1786 v8_branch, |
| 1787 bleeding_edge_branch, |
| 1788 search_forward=False) |
| 1779 min_revision_state.external['v8_bleeding_edge'] = r1 | 1789 min_revision_state.external['v8_bleeding_edge'] = r1 |
| 1780 max_revision_state.external['v8_bleeding_edge'] = r2 | 1790 max_revision_state.external['v8_bleeding_edge'] = r2 |
| 1781 | 1791 |
| 1782 if (not self._GetV8BleedingEdgeFromV8TrunkIfMappable( | 1792 if (not self._GetV8BleedingEdgeFromV8TrunkIfMappable( |
| 1783 min_revision_state.revision, bleeding_edge_branch) | 1793 min_revision_state.revision, bleeding_edge_branch) |
| 1784 or not self._GetV8BleedingEdgeFromV8TrunkIfMappable( | 1794 or not self._GetV8BleedingEdgeFromV8TrunkIfMappable( |
| 1785 max_revision_state.revision, bleeding_edge_branch)): | 1795 max_revision_state.revision, bleeding_edge_branch)): |
| 1786 self.warnings.append( | 1796 self.warnings.append( |
| 1787 'Trunk revisions in V8 did not map directly to bleeding_edge. ' | 1797 'Trunk revisions in V8 did not map directly to bleeding_edge. ' |
| 1788 'Attempted to expand the range to find V8 rolls which did map ' | 1798 'Attempted to expand the range to find V8 rolls which did map ' |
| 1789 'directly to bleeding_edge revisions, but results might not be ' | 1799 'directly to bleeding_edge revisions, but results might not be ' |
| 1790 'valid.') | 1800 'valid.') |
| 1791 | 1801 |
| 1792 def _FindNextDepotToBisect( | 1802 def _FindNextDepotToBisect( |
| 1793 self, current_depot, min_revision_state, max_revision_state): | 1803 self, current_depot, min_revision_state, max_revision_state): |
| 1794 """Decides which depot the script should dive into next (if any). | 1804 """Decides which depot the script should dive into next (if any). |
| 1795 | 1805 |
| 1796 Args: | 1806 Args: |
| 1797 current_depot: Current depot being bisected. | 1807 current_depot: Current depot being bisected. |
| 1798 min_revision_state: State of the earliest revision in the bisect range. | 1808 min_revision_state: State of the earliest revision in the bisect range. |
| 1799 max_revision_state: State of the latest revision in the bisect range. | 1809 max_revision_state: State of the latest revision in the bisect range. |
| 1800 | 1810 |
| 1801 Returns: | 1811 Returns: |
| 1802 Name of the depot to bisect next, or None. | 1812 Name of the depot to bisect next, or None. |
| 1803 """ | 1813 """ |
| 1804 external_depot = None | 1814 external_depot = None |
| 1805 for next_depot in bisect_utils.DEPOT_NAMES: | 1815 for next_depot in bisect_utils.DEPOT_NAMES: |
| 1806 if bisect_utils.DEPOT_DEPS_NAME[next_depot].has_key('platform'): | 1816 if ('platform' in bisect_utils.DEPOT_DEPS_NAME[next_depot] and |
| 1807 if bisect_utils.DEPOT_DEPS_NAME[next_depot]['platform'] != os.name: | 1817 bisect_utils.DEPOT_DEPS_NAME[next_depot]['platform'] != os.name): |
| 1808 continue | 1818 continue |
| 1809 | 1819 |
| 1810 if not (bisect_utils.DEPOT_DEPS_NAME[next_depot]['recurse'] | 1820 if not (bisect_utils.DEPOT_DEPS_NAME[next_depot]['recurse'] |
| 1811 and min_revision_state.depot | 1821 and min_revision_state.depot |
| 1812 in bisect_utils.DEPOT_DEPS_NAME[next_depot]['from']): | 1822 in bisect_utils.DEPOT_DEPS_NAME[next_depot]['from']): |
| 1813 continue | 1823 continue |
| 1814 | 1824 |
| 1815 if current_depot == 'v8': | 1825 if current_depot == 'v8': |
| 1816 # We grab the bleeding_edge info here rather than earlier because we | 1826 # We grab the bleeding_edge info here rather than earlier because we |
| 1817 # finally have the revision range. From that we can search forwards and | 1827 # finally have the revision range. From that we can search forwards and |
| 1818 # backwards to try to match trunk revisions to bleeding_edge. | 1828 # backwards to try to match trunk revisions to bleeding_edge. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1843 Returns: | 1853 Returns: |
| 1844 A list containing the revisions between |start_revision| and | 1854 A list containing the revisions between |start_revision| and |
| 1845 |end_revision| inclusive. | 1855 |end_revision| inclusive. |
| 1846 """ | 1856 """ |
| 1847 # Change into working directory of external library to run | 1857 # Change into working directory of external library to run |
| 1848 # subsequent commands. | 1858 # subsequent commands. |
| 1849 self.depot_registry.ChangeToDepotDir(current_depot) | 1859 self.depot_registry.ChangeToDepotDir(current_depot) |
| 1850 | 1860 |
| 1851 # V8 (and possibly others) is merged in periodically. Bisecting | 1861 # V8 (and possibly others) is merged in periodically. Bisecting |
| 1852 # this directory directly won't give much good info. | 1862 # this directory directly won't give much good info. |
| 1853 if bisect_utils.DEPOT_DEPS_NAME[current_depot].has_key('custom_deps'): | 1863 if 'custom_deps' in bisect_utils.DEPOT_DEPS_NAME[current_depot]: |
| 1854 config_path = os.path.join(self.src_cwd, '..') | 1864 config_path = os.path.join(self.src_cwd, '..') |
| 1855 if bisect_utils.RunGClientAndCreateConfig( | 1865 if bisect_utils.RunGClientAndCreateConfig( |
| 1856 self.opts, bisect_utils.DEPOT_DEPS_NAME[current_depot]['custom_deps'], | 1866 self.opts, bisect_utils.DEPOT_DEPS_NAME[current_depot]['custom_deps'], |
| 1857 cwd=config_path): | 1867 cwd=config_path): |
| 1858 return [] | 1868 return [] |
| 1859 if bisect_utils.RunGClient( | 1869 if bisect_utils.RunGClient( |
| 1860 ['sync', '--revision', previous_revision], cwd=self.src_cwd): | 1870 ['sync', '--revision', previous_revision], cwd=self.src_cwd): |
| 1861 return [] | 1871 return [] |
| 1862 | 1872 |
| 1863 if current_depot == 'v8_bleeding_edge': | 1873 if current_depot == 'v8_bleeding_edge': |
| 1864 self.depot_registry.ChangeToDepotDir('chromium') | 1874 self.depot_registry.ChangeToDepotDir('chromium') |
| 1865 | 1875 |
| 1866 shutil.move('v8', 'v8.bak') | 1876 shutil.move('v8', 'v8.bak') |
| 1867 shutil.move('v8_bleeding_edge', 'v8') | 1877 shutil.move('v8_bleeding_edge', 'v8') |
| 1868 | 1878 |
| 1869 self.cleanup_commands.append(['mv', 'v8', 'v8_bleeding_edge']) | 1879 self.cleanup_commands.append(['mv', 'v8', 'v8_bleeding_edge']) |
| 1870 self.cleanup_commands.append(['mv', 'v8.bak', 'v8']) | 1880 self.cleanup_commands.append(['mv', 'v8.bak', 'v8']) |
| 1871 | 1881 |
| 1872 self.depot_registry.SetDepotDir('v8_bleeding_edge', | 1882 self.depot_registry.SetDepotDir( |
| 1873 os.path.join(self.src_cwd, 'v8')) | 1883 'v8_bleeding_edge', os.path.join(self.src_cwd, 'v8')) |
| 1874 self.depot_registry.SetDepotDir('v8', os.path.join(self.src_cwd, | 1884 self.depot_registry.SetDepotDir( |
| 1875 'v8.bak')) | 1885 'v8', os.path.join(self.src_cwd, 'v8.bak')) |
| 1876 | 1886 |
| 1877 self.depot_registry.ChangeToDepotDir(current_depot) | 1887 self.depot_registry.ChangeToDepotDir(current_depot) |
| 1878 | 1888 |
| 1879 depot_revision_list = self.GetRevisionList(current_depot, | 1889 depot_revision_list = self.GetRevisionList(current_depot, |
| 1880 end_revision, | 1890 end_revision, |
| 1881 start_revision) | 1891 start_revision) |
| 1882 | 1892 |
| 1883 self.depot_registry.ChangeToDepotDir('chromium') | 1893 self.depot_registry.ChangeToDepotDir('chromium') |
| 1884 | 1894 |
| 1885 return depot_revision_list | 1895 return depot_revision_list |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1953 bisect_utils.FILE_DEPS_GIT, oldest_deps_change, bad_revision) | 1963 bisect_utils.FILE_DEPS_GIT, oldest_deps_change, bad_revision) |
| 1954 | 1964 |
| 1955 if len(changes_to_deps) != len(changes_to_gitdeps): | 1965 if len(changes_to_deps) != len(changes_to_gitdeps): |
| 1956 # Grab the timestamp of the last DEPS change | 1966 # Grab the timestamp of the last DEPS change |
| 1957 cmd = ['log', '--format=%ct', '-1', changes_to_deps[0]] | 1967 cmd = ['log', '--format=%ct', '-1', changes_to_deps[0]] |
| 1958 output = bisect_utils.CheckRunGit(cmd) | 1968 output = bisect_utils.CheckRunGit(cmd) |
| 1959 commit_time = int(output) | 1969 commit_time = int(output) |
| 1960 | 1970 |
| 1961 # Try looking for a commit that touches the .DEPS.git file in the | 1971 # Try looking for a commit that touches the .DEPS.git file in the |
| 1962 # next 15 minutes after the DEPS file change. | 1972 # next 15 minutes after the DEPS file change. |
| 1963 cmd = ['log', '--format=%H', '-1', | 1973 cmd = [ |
| 1964 '--before=%d' % (commit_time + 900), '--after=%d' % commit_time, | 1974 'log', '--format=%H', '-1', |
| 1965 'origin/master', '--', bisect_utils.FILE_DEPS_GIT] | 1975 '--before=%d' % (commit_time + 900), |
| 1976 '--after=%d' % commit_time, |
| 1977 'origin/master', '--', bisect_utils.FILE_DEPS_GIT |
| 1978 ] |
| 1966 output = bisect_utils.CheckRunGit(cmd) | 1979 output = bisect_utils.CheckRunGit(cmd) |
| 1967 output = output.strip() | 1980 output = output.strip() |
| 1968 if output: | 1981 if output: |
| 1969 self.warnings.append('Detected change to DEPS and modified ' | 1982 self.warnings.append( |
| 1983 'Detected change to DEPS and modified ' |
| 1970 'revision range to include change to .DEPS.git') | 1984 'revision range to include change to .DEPS.git') |
| 1971 return (output, good_revision) | 1985 return (output, good_revision) |
| 1972 else: | 1986 else: |
| 1973 self.warnings.append('Detected change to DEPS but couldn\'t find ' | 1987 self.warnings.append( |
| 1988 'Detected change to DEPS but couldn\'t find ' |
| 1974 'matching change to .DEPS.git') | 1989 'matching change to .DEPS.git') |
| 1975 return (bad_revision, good_revision) | 1990 return (bad_revision, good_revision) |
| 1976 | 1991 |
| 1977 def CheckIfRevisionsInProperOrder( | 1992 def CheckIfRevisionsInProperOrder( |
| 1978 self, target_depot, good_revision, bad_revision): | 1993 self, target_depot, good_revision, bad_revision): |
| 1979 """Checks that |good_revision| is an earlier revision than |bad_revision|. | 1994 """Checks that |good_revision| is an earlier revision than |bad_revision|. |
| 1980 | 1995 |
| 1981 Args: | 1996 Args: |
| 1982 good_revision: Number/tag of the known good revision. | 1997 good_revision: Number/tag of the known good revision. |
| 1983 bad_revision: Number/tag of the known bad revision. | 1998 bad_revision: Number/tag of the known bad revision. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1994 'depot %s', good_position, bad_position, target_depot) | 2009 'depot %s', good_position, bad_position, target_depot) |
| 1995 good_position = source_control.GetCommitTime(good_revision, cwd=cwd) | 2010 good_position = source_control.GetCommitTime(good_revision, cwd=cwd) |
| 1996 bad_position = source_control.GetCommitTime(bad_revision, cwd=cwd) | 2011 bad_position = source_control.GetCommitTime(bad_revision, cwd=cwd) |
| 1997 | 2012 |
| 1998 return good_position <= bad_position | 2013 return good_position <= bad_position |
| 1999 | 2014 |
| 2000 def CanPerformBisect(self, good_revision, bad_revision): | 2015 def CanPerformBisect(self, good_revision, bad_revision): |
| 2001 """Checks whether a given revision is bisectable. | 2016 """Checks whether a given revision is bisectable. |
| 2002 | 2017 |
| 2003 Checks for following: | 2018 Checks for following: |
| 2004 1. Non-bisectable revsions for android bots (refer to crbug.com/385324). | 2019 1. Non-bisectable revisions for android bots (refer to crbug.com/385324). |
| 2005 2. Non-bisectable revsions for Windows bots (refer to crbug.com/405274). | 2020 2. Non-bisectable revisions for Windows bots (refer to crbug.com/405274). |
| 2006 | 2021 |
| 2007 Args: | 2022 Args: |
| 2008 good_revision: Known good revision. | 2023 good_revision: Known good revision. |
| 2009 bad_revision: Known bad revision. | 2024 bad_revision: Known bad revision. |
| 2010 | 2025 |
| 2011 Returns: | 2026 Returns: |
| 2012 A dictionary indicating the result. If revision is not bisectable, | 2027 A dictionary indicating the result. If revision is not bisectable, |
| 2013 this will contain the field "error", otherwise None. | 2028 this will contain the field "error", otherwise None. |
| 2014 """ | 2029 """ |
| 2015 if self.opts.target_platform == 'android': | 2030 if self.opts.target_platform == 'android': |
| (...skipping 29 matching lines...) Expand all Loading... |
| 2045 performance tests again with and without the CL, adding the results to | 2060 performance tests again with and without the CL, adding the results to |
| 2046 the over bisect results. | 2061 the over bisect results. |
| 2047 | 2062 |
| 2048 Args: | 2063 Args: |
| 2049 results: BisectResults from the bisect. | 2064 results: BisectResults from the bisect. |
| 2050 target_depot: The target depot we're bisecting. | 2065 target_depot: The target depot we're bisecting. |
| 2051 command_to_run: Specify the command to execute the performance test. | 2066 command_to_run: Specify the command to execute the performance test. |
| 2052 metric: The performance metric to monitor. | 2067 metric: The performance metric to monitor. |
| 2053 """ | 2068 """ |
| 2054 run_results_tot, run_results_reverted = self._RevertCulpritCLAndRetest( | 2069 run_results_tot, run_results_reverted = self._RevertCulpritCLAndRetest( |
| 2055 results, target_depot, command_to_run, metric) | 2070 results, target_depot, command_to_run, metric) |
| 2056 | 2071 |
| 2057 results.AddRetestResults(run_results_tot, run_results_reverted) | 2072 results.AddRetestResults(run_results_tot, run_results_reverted) |
| 2058 | 2073 |
| 2059 if len(results.culprit_revisions) != 1: | 2074 if len(results.culprit_revisions) != 1: |
| 2060 return | 2075 return |
| 2061 | 2076 |
| 2062 # Cleanup reverted files if anything is left. | 2077 # Cleanup reverted files if anything is left. |
| 2063 _, _, culprit_depot = results.culprit_revisions[0] | 2078 _, _, culprit_depot = results.culprit_revisions[0] |
| 2064 bisect_utils.CheckRunGit(['reset', '--hard', 'HEAD'], | 2079 bisect_utils.CheckRunGit( |
| 2080 ['reset', '--hard', 'HEAD'], |
| 2065 cwd=self.depot_registry.GetDepotDir(culprit_depot)) | 2081 cwd=self.depot_registry.GetDepotDir(culprit_depot)) |
| 2066 | 2082 |
| 2067 def _RevertCL(self, culprit_revision, culprit_depot): | 2083 def _RevertCL(self, culprit_revision, culprit_depot): |
| 2068 """Reverts the specified revision in the specified depot.""" | 2084 """Reverts the specified revision in the specified depot.""" |
| 2069 if self.opts.output_buildbot_annotations: | 2085 if self.opts.output_buildbot_annotations: |
| 2070 bisect_utils.OutputAnnotationStepStart( | 2086 bisect_utils.OutputAnnotationStepStart( |
| 2071 'Reverting culprit CL: %s' % culprit_revision) | 2087 'Reverting culprit CL: %s' % culprit_revision) |
| 2072 _, return_code = bisect_utils.RunGit( | 2088 _, return_code = bisect_utils.RunGit( |
| 2073 ['revert', '--no-commit', culprit_revision], | 2089 ['revert', '--no-commit', culprit_revision], |
| 2074 cwd=self.depot_registry.GetDepotDir(culprit_depot)) | 2090 cwd=self.depot_registry.GetDepotDir(culprit_depot)) |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2125 'Culprit CL is in another depot, attempting to revert and build' | 2141 'Culprit CL is in another depot, attempting to revert and build' |
| 2126 ' locally to retest. This may not match the performance of official' | 2142 ' locally to retest. This may not match the performance of official' |
| 2127 ' builds.') | 2143 ' builds.') |
| 2128 | 2144 |
| 2129 run_results_reverted = self._RunTestWithAnnotations( | 2145 run_results_reverted = self._RunTestWithAnnotations( |
| 2130 'Re-Testing ToT with reverted culprit', | 2146 'Re-Testing ToT with reverted culprit', |
| 2131 'Failed to run reverted CL.', | 2147 'Failed to run reverted CL.', |
| 2132 head_revision, target_depot, command_to_run, metric, force_build) | 2148 head_revision, target_depot, command_to_run, metric, force_build) |
| 2133 | 2149 |
| 2134 # Clear the reverted file(s). | 2150 # Clear the reverted file(s). |
| 2135 bisect_utils.RunGit(['reset', '--hard', 'HEAD'], | 2151 bisect_utils.RunGit( |
| 2152 ['reset', '--hard', 'HEAD'], |
| 2136 cwd=self.depot_registry.GetDepotDir(culprit_depot)) | 2153 cwd=self.depot_registry.GetDepotDir(culprit_depot)) |
| 2137 | 2154 |
| 2138 # Retesting with the reverted CL failed, so bail out of retesting against | 2155 # Retesting with the reverted CL failed, so bail out of retesting against |
| 2139 # ToT. | 2156 # ToT. |
| 2140 if run_results_reverted[1]: | 2157 if run_results_reverted[1]: |
| 2141 return (None, None) | 2158 return (None, None) |
| 2142 | 2159 |
| 2143 run_results_tot = self._RunTestWithAnnotations( | 2160 run_results_tot = self._RunTestWithAnnotations( |
| 2144 'Re-Testing ToT', | 2161 'Re-Testing ToT', |
| 2145 'Failed to run ToT.', | 2162 'Failed to run ToT.', |
| 2146 head_revision, target_depot, command_to_run, metric, force_build) | 2163 head_revision, target_depot, command_to_run, metric, force_build) |
| 2147 | 2164 |
| 2148 return (run_results_tot, run_results_reverted) | 2165 return (run_results_tot, run_results_reverted) |
| 2149 | 2166 |
| 2150 def _RunTestWithAnnotations(self, step_text, error_text, head_revision, | 2167 def _RunTestWithAnnotations( |
| 2168 self, step_text, error_text, head_revision, |
| 2151 target_depot, command_to_run, metric, force_build): | 2169 target_depot, command_to_run, metric, force_build): |
| 2152 """Runs the performance test and outputs start/stop annotations. | 2170 """Runs the performance test and outputs start/stop annotations. |
| 2153 | 2171 |
| 2154 Args: | 2172 Args: |
| 2155 results: BisectResults from the bisect. | 2173 results: BisectResults from the bisect. |
| 2156 target_depot: The target depot we're bisecting. | 2174 target_depot: The target depot we're bisecting. |
| 2157 command_to_run: Specify the command to execute the performance test. | 2175 command_to_run: Specify the command to execute the performance test. |
| 2158 metric: The performance metric to monitor. | 2176 metric: The performance metric to monitor. |
| 2159 force_build: Whether to force a build locally. | 2177 force_build: Whether to force a build locally. |
| 2160 | 2178 |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2323 | 2341 |
| 2324 # Check how likely it is that the good and bad results are different | 2342 # Check how likely it is that the good and bad results are different |
| 2325 # beyond chance-induced variation. | 2343 # beyond chance-induced variation. |
| 2326 confidence_error = False | 2344 confidence_error = False |
| 2327 if not self.opts.debug_ignore_regression_confidence: | 2345 if not self.opts.debug_ignore_regression_confidence: |
| 2328 confidence_error = _CheckRegressionConfidenceError(good_revision, | 2346 confidence_error = _CheckRegressionConfidenceError(good_revision, |
| 2329 bad_revision, | 2347 bad_revision, |
| 2330 known_good_value, | 2348 known_good_value, |
| 2331 known_bad_value) | 2349 known_bad_value) |
| 2332 if confidence_error: | 2350 if confidence_error: |
| 2351 # If there is no significant difference between "good" and "bad" |
| 2352 # revision results, then the "bad revision" is considered "good". |
| 2353 # TODO(qyearsley): Remove this if it is not necessary. |
| 2354 bad_revision_state.passed = True |
| 2333 self.warnings.append(confidence_error) | 2355 self.warnings.append(confidence_error) |
| 2334 bad_revision_state.passed = True # Marking the 'bad' revision as good. | |
| 2335 return BisectResults(bisect_state, self.depot_registry, self.opts, | 2356 return BisectResults(bisect_state, self.depot_registry, self.opts, |
| 2336 self.warnings) | 2357 self.warnings) |
| 2337 | 2358 |
| 2338 while True: | 2359 while True: |
| 2339 if not revision_states: | 2360 if not revision_states: |
| 2340 break | 2361 break |
| 2341 | 2362 |
| 2342 if max_revision - min_revision <= 1: | 2363 if max_revision - min_revision <= 1: |
| 2343 min_revision_state = revision_states[min_revision] | 2364 min_revision_state = revision_states[min_revision] |
| 2344 max_revision_state = revision_states[max_revision] | 2365 max_revision_state = revision_states[max_revision] |
| 2345 current_depot = min_revision_state.depot | 2366 current_depot = min_revision_state.depot |
| 2346 # TODO(sergiyb): Under which conditions can first two branches be hit? | 2367 # TODO(sergiyb): Under which conditions can first two branches be hit? |
| 2347 if min_revision_state.passed == '?': | 2368 if min_revision_state.passed == '?': |
| 2348 next_revision_index = min_revision | 2369 next_revision_index = min_revision |
| 2349 elif max_revision_state.passed == '?': | 2370 elif max_revision_state.passed == '?': |
| 2350 next_revision_index = max_revision | 2371 next_revision_index = max_revision |
| 2351 elif current_depot in ['android-chrome', 'chromium', 'v8']: | 2372 elif current_depot in ['android-chrome', 'chromium', 'v8']: |
| 2352 previous_revision = revision_states[min_revision].revision | 2373 previous_revision = revision_states[min_revision].revision |
| 2353 # If there were changes to any of the external libraries we track, | 2374 # If there were changes to any of the external libraries we track, |
| 2354 # should bisect the changes there as well. | 2375 # should bisect the changes there as well. |
| 2355 external_depot = self._FindNextDepotToBisect( | 2376 external_depot = self._FindNextDepotToBisect( |
| 2356 current_depot, min_revision_state, max_revision_state) | 2377 current_depot, min_revision_state, max_revision_state) |
| 2357 # If there was no change in any of the external depots, the search | 2378 # If there was no change in any of the external depots, the search |
| 2358 # is over. | 2379 # is over. |
| 2359 if not external_depot: | 2380 if not external_depot: |
| 2360 if current_depot == 'v8': | 2381 if current_depot == 'v8': |
| 2361 self.warnings.append('Unfortunately, V8 bisection couldn\'t ' | 2382 self.warnings.append( |
| 2383 'Unfortunately, V8 bisection couldn\'t ' |
| 2362 'continue any further. The script can only bisect into ' | 2384 'continue any further. The script can only bisect into ' |
| 2363 'V8\'s bleeding_edge repository if both the current and ' | 2385 'V8\'s bleeding_edge repository if both the current and ' |
| 2364 'previous revisions in trunk map directly to revisions in ' | 2386 'previous revisions in trunk map directly to revisions in ' |
| 2365 'bleeding_edge.') | 2387 'bleeding_edge.') |
| 2366 break | 2388 break |
| 2367 | 2389 |
| 2368 earliest_revision = max_revision_state.external[external_depot] | 2390 earliest_revision = max_revision_state.external[external_depot] |
| 2369 latest_revision = min_revision_state.external[external_depot] | 2391 latest_revision = min_revision_state.external[external_depot] |
| 2370 | 2392 |
| 2371 new_revision_list = self.PrepareToBisectOnDepot( | 2393 new_revision_list = self.PrepareToBisectOnDepot( |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2441 | 2463 |
| 2442 # If the build is broken, remove it and redo search. | 2464 # If the build is broken, remove it and redo search. |
| 2443 revision_states.pop(next_revision_index) | 2465 revision_states.pop(next_revision_index) |
| 2444 | 2466 |
| 2445 max_revision -= 1 | 2467 max_revision -= 1 |
| 2446 | 2468 |
| 2447 if self.opts.output_buildbot_annotations: | 2469 if self.opts.output_buildbot_annotations: |
| 2448 self.printer.PrintPartialResults(bisect_state) | 2470 self.printer.PrintPartialResults(bisect_state) |
| 2449 bisect_utils.OutputAnnotationStepClosed() | 2471 bisect_utils.OutputAnnotationStepClosed() |
| 2450 | 2472 |
| 2451 | |
| 2452 self._ConfidenceExtraTestRuns(min_revision_state, max_revision_state, | 2473 self._ConfidenceExtraTestRuns(min_revision_state, max_revision_state, |
| 2453 command_to_run, metric) | 2474 command_to_run, metric) |
| 2454 results = BisectResults(bisect_state, self.depot_registry, self.opts, | 2475 results = BisectResults(bisect_state, self.depot_registry, self.opts, |
| 2455 self.warnings) | 2476 self.warnings) |
| 2456 | 2477 |
| 2457 self._GatherResultsFromRevertedCulpritCL( | 2478 self._GatherResultsFromRevertedCulpritCL( |
| 2458 results, target_depot, command_to_run, metric) | 2479 results, target_depot, command_to_run, metric) |
| 2459 | 2480 |
| 2460 return results | 2481 return results |
| 2461 else: | 2482 else: |
| (...skipping 343 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2805 else: | 2826 else: |
| 2806 print 'Could not confirm bug is closed, proceeding.' | 2827 print 'Could not confirm bug is closed, proceeding.' |
| 2807 if opts.output_buildbot_annotations: | 2828 if opts.output_buildbot_annotations: |
| 2808 bisect_utils.OutputAnnotationStepClosed() | 2829 bisect_utils.OutputAnnotationStepClosed() |
| 2809 if issue_closed: | 2830 if issue_closed: |
| 2810 results = BisectResults(abort_reason='the bug is closed.') | 2831 results = BisectResults(abort_reason='the bug is closed.') |
| 2811 bisect_test = BisectPerformanceMetrics(opts, os.getcwd()) | 2832 bisect_test = BisectPerformanceMetrics(opts, os.getcwd()) |
| 2812 bisect_test.printer.FormatAndPrintResults(results) | 2833 bisect_test.printer.FormatAndPrintResults(results) |
| 2813 return 0 | 2834 return 0 |
| 2814 | 2835 |
| 2815 | |
| 2816 if opts.extra_src: | 2836 if opts.extra_src: |
| 2817 extra_src = bisect_utils.LoadExtraSrc(opts.extra_src) | 2837 extra_src = bisect_utils.LoadExtraSrc(opts.extra_src) |
| 2818 if not extra_src: | 2838 if not extra_src: |
| 2819 raise RuntimeError('Invalid or missing --extra_src.') | 2839 raise RuntimeError('Invalid or missing --extra_src.') |
| 2820 bisect_utils.AddAdditionalDepotInfo(extra_src.GetAdditionalDepotInfo()) | 2840 bisect_utils.AddAdditionalDepotInfo(extra_src.GetAdditionalDepotInfo()) |
| 2821 | 2841 |
| 2822 if opts.working_directory: | 2842 if opts.working_directory: |
| 2823 custom_deps = bisect_utils.DEFAULT_GCLIENT_CUSTOM_DEPS | 2843 custom_deps = bisect_utils.DEFAULT_GCLIENT_CUSTOM_DEPS |
| 2824 if opts.no_custom_deps: | 2844 if opts.no_custom_deps: |
| 2825 custom_deps = None | 2845 custom_deps = None |
| (...skipping 30 matching lines...) Expand all Loading... |
| 2856 # bugs. If you change this, please update the perf dashboard as well. | 2876 # bugs. If you change this, please update the perf dashboard as well. |
| 2857 bisect_utils.OutputAnnotationStepStart('Results') | 2877 bisect_utils.OutputAnnotationStepStart('Results') |
| 2858 print 'Runtime Error: %s' % e | 2878 print 'Runtime Error: %s' % e |
| 2859 if opts.output_buildbot_annotations: | 2879 if opts.output_buildbot_annotations: |
| 2860 bisect_utils.OutputAnnotationStepClosed() | 2880 bisect_utils.OutputAnnotationStepClosed() |
| 2861 return 1 | 2881 return 1 |
| 2862 | 2882 |
| 2863 | 2883 |
| 2864 if __name__ == '__main__': | 2884 if __name__ == '__main__': |
| 2865 sys.exit(main()) | 2885 sys.exit(main()) |
| OLD | NEW |