| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 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 """Performance Test Bisect Tool | 6 """Performance Test Bisect Tool |
| 7 | 7 |
| 8 This script bisects a series of changelists using binary search. It starts at | 8 This script bisects a series of changelists using binary search. It starts at |
| 9 a bad revision where a performance metric has regressed, and asks for a last | 9 a bad revision where a performance metric has regressed, and asks for a last |
| 10 known-good revision. It will then binary search across this revision range by | 10 known-good revision. It will then binary search across this revision range by |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 command: A list containing the command and args to execute. | 271 command: A list containing the command and args to execute. |
| 272 | 272 |
| 273 Returns: | 273 Returns: |
| 274 The return code of the call. | 274 The return code of the call. |
| 275 """ | 275 """ |
| 276 # On Windows, use shell=True to get PATH interpretation. | 276 # On Windows, use shell=True to get PATH interpretation. |
| 277 shell = IsWindows() | 277 shell = IsWindows() |
| 278 return subprocess.call(command, shell=shell) | 278 return subprocess.call(command, shell=shell) |
| 279 | 279 |
| 280 | 280 |
| 281 def RunProcessAndRetrieveOutput(command): | 281 def RunProcessAndRetrieveOutput(command, cwd=None): |
| 282 """Run an arbitrary command, returning its output and return code. Since | 282 """Run an arbitrary command, returning its output and return code. Since |
| 283 output is collected via communicate(), there will be no output until the | 283 output is collected via communicate(), there will be no output until the |
| 284 call terminates. If you need output while the program runs (ie. so | 284 call terminates. If you need output while the program runs (ie. so |
| 285 that the buildbot doesn't terminate the script), consider RunProcess(). | 285 that the buildbot doesn't terminate the script), consider RunProcess(). |
| 286 | 286 |
| 287 Args: | 287 Args: |
| 288 command: A list containing the command and args to execute. | 288 command: A list containing the command and args to execute. |
| 289 print_output: Optional parameter to write output to stdout as it's | |
| 290 being collected. | |
| 291 | 289 |
| 292 Returns: | 290 Returns: |
| 293 A tuple of the output and return code. | 291 A tuple of the output and return code. |
| 294 """ | 292 """ |
| 295 # On Windows, use shell=True to get PATH interpretation. | 293 # On Windows, use shell=True to get PATH interpretation. |
| 296 shell = IsWindows() | 294 shell = IsWindows() |
| 297 proc = subprocess.Popen(command, | 295 proc = subprocess.Popen(command, shell=shell, stdout=subprocess.PIPE, cwd=cwd) |
| 298 shell=shell, | |
| 299 stdout=subprocess.PIPE) | |
| 300 | 296 |
| 301 (output, _) = proc.communicate() | 297 (output, _) = proc.communicate() |
| 302 | 298 |
| 303 return (output, proc.returncode) | 299 return (output, proc.returncode) |
| 304 | 300 |
| 305 | 301 |
| 306 def RunGit(command): | 302 def RunGit(command, cwd=None): |
| 307 """Run a git subcommand, returning its output and return code. | 303 """Run a git subcommand, returning its output and return code. |
| 308 | 304 |
| 309 Args: | 305 Args: |
| 310 command: A list containing the args to git. | 306 command: A list containing the args to git. |
| 311 | 307 |
| 312 Returns: | 308 Returns: |
| 313 A tuple of the output and return code. | 309 A tuple of the output and return code. |
| 314 """ | 310 """ |
| 315 command = ['git'] + command | 311 command = ['git'] + command |
| 316 | 312 |
| 317 return RunProcessAndRetrieveOutput(command) | 313 return RunProcessAndRetrieveOutput(command, cwd=cwd) |
| 318 | 314 |
| 319 | 315 |
| 320 def CheckRunGit(command): | 316 def CheckRunGit(command, cwd=None): |
| 321 """Run a git subcommand, returning its output and return code. Asserts if | 317 """Run a git subcommand, returning its output and return code. Asserts if |
| 322 the return code of the call is non-zero. | 318 the return code of the call is non-zero. |
| 323 | 319 |
| 324 Args: | 320 Args: |
| 325 command: A list containing the args to git. | 321 command: A list containing the args to git. |
| 326 | 322 |
| 327 Returns: | 323 Returns: |
| 328 A tuple of the output and return code. | 324 A tuple of the output and return code. |
| 329 """ | 325 """ |
| 330 (output, return_code) = RunGit(command) | 326 (output, return_code) = RunGit(command, cwd=cwd) |
| 331 | 327 |
| 332 assert not return_code, 'An error occurred while running'\ | 328 assert not return_code, 'An error occurred while running'\ |
| 333 ' "git %s"' % ' '.join(command) | 329 ' "git %s"' % ' '.join(command) |
| 334 return output | 330 return output |
| 335 | 331 |
| 336 | 332 |
| 337 def SetBuildSystemDefault(build_system): | 333 def SetBuildSystemDefault(build_system): |
| 338 """Sets up any environment variables needed to build with the specified build | 334 """Sets up any environment variables needed to build with the specified build |
| 339 system. | 335 system. |
| 340 | 336 |
| (...skipping 369 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 | 706 |
| 711 if not sync_client: | 707 if not sync_client: |
| 712 results = RunGit(['checkout', revision])[1] | 708 results = RunGit(['checkout', revision])[1] |
| 713 elif sync_client == 'gclient': | 709 elif sync_client == 'gclient': |
| 714 results = self.SyncToRevisionWithGClient(revision) | 710 results = self.SyncToRevisionWithGClient(revision) |
| 715 elif sync_client == 'repo': | 711 elif sync_client == 'repo': |
| 716 results = self.SyncToRevisionWithRepo(revision) | 712 results = self.SyncToRevisionWithRepo(revision) |
| 717 | 713 |
| 718 return not results | 714 return not results |
| 719 | 715 |
| 720 def ResolveToRevision(self, revision_to_check, depot, search): | 716 def ResolveToRevision(self, revision_to_check, depot, search, cwd=None): |
| 721 """If an SVN revision is supplied, try to resolve it to a git SHA1. | 717 """If an SVN revision is supplied, try to resolve it to a git SHA1. |
| 722 | 718 |
| 723 Args: | 719 Args: |
| 724 revision_to_check: The user supplied revision string that may need to be | 720 revision_to_check: The user supplied revision string that may need to be |
| 725 resolved to a git SHA1. | 721 resolved to a git SHA1. |
| 726 depot: The depot the revision_to_check is from. | 722 depot: The depot the revision_to_check is from. |
| 727 search: The number of changelists to try if the first fails to resolve | 723 search: The number of changelists to try if the first fails to resolve |
| 728 to a git hash. If the value is negative, the function will search | 724 to a git hash. If the value is negative, the function will search |
| 729 backwards chronologically, otherwise it will search forward. | 725 backwards chronologically, otherwise it will search forward. |
| 730 | 726 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 746 if search > 0: | 742 if search > 0: |
| 747 search_range = xrange(svn_revision, svn_revision + search, 1) | 743 search_range = xrange(svn_revision, svn_revision + search, 1) |
| 748 else: | 744 else: |
| 749 search_range = xrange(svn_revision, svn_revision + search, -1) | 745 search_range = xrange(svn_revision, svn_revision + search, -1) |
| 750 | 746 |
| 751 for i in search_range: | 747 for i in search_range: |
| 752 svn_pattern = 'git-svn-id: %s@%d' % (depot_svn, i) | 748 svn_pattern = 'git-svn-id: %s@%d' % (depot_svn, i) |
| 753 cmd = ['log', '--format=%H', '-1', '--grep', svn_pattern, | 749 cmd = ['log', '--format=%H', '-1', '--grep', svn_pattern, |
| 754 'origin/master'] | 750 'origin/master'] |
| 755 | 751 |
| 756 (log_output, return_code) = RunGit(cmd) | 752 (log_output, return_code) = RunGit(cmd, cwd=cwd) |
| 757 | 753 |
| 758 assert not return_code, 'An error occurred while running'\ | 754 assert not return_code, 'An error occurred while running'\ |
| 759 ' "git %s"' % ' '.join(cmd) | 755 ' "git %s"' % ' '.join(cmd) |
| 760 | 756 |
| 761 if not return_code: | 757 if not return_code: |
| 762 log_output = log_output.strip() | 758 log_output = log_output.strip() |
| 763 | 759 |
| 764 if log_output: | 760 if log_output: |
| 765 git_revision = log_output | 761 git_revision = log_output |
| 766 | 762 |
| 767 break | 763 break |
| 768 | 764 |
| 769 return git_revision | 765 return git_revision |
| 770 else: | 766 else: |
| 771 if IsStringInt(revision_to_check): | 767 if IsStringInt(revision_to_check): |
| 772 return int(revision_to_check) | 768 return int(revision_to_check) |
| 773 else: | 769 else: |
| 774 cwd = os.getcwd() | 770 cwd = os.getcwd() |
| 775 os.chdir(os.path.join(os.getcwd(), 'src', 'third_party', | 771 os.chdir(os.path.join(os.getcwd(), 'src', 'third_party', |
| 776 'chromiumos-overlay')) | 772 'chromiumos-overlay')) |
| 777 pattern = CROS_VERSION_PATTERN % revision_to_check | 773 pattern = CROS_VERSION_PATTERN % revision_to_check |
| 778 cmd = ['log', '--format=%ct', '-1', '--grep', pattern] | 774 cmd = ['log', '--format=%ct', '-1', '--grep', pattern] |
| 779 | 775 |
| 780 git_revision = None | 776 git_revision = None |
| 781 | 777 |
| 782 log_output = CheckRunGit(cmd) | 778 log_output = CheckRunGit(cmd, cwd=cwd) |
| 783 if log_output: | 779 if log_output: |
| 784 git_revision = log_output | 780 git_revision = log_output |
| 785 git_revision = int(log_output.strip()) | 781 git_revision = int(log_output.strip()) |
| 786 os.chdir(cwd) | 782 os.chdir(cwd) |
| 787 | 783 |
| 788 return git_revision | 784 return git_revision |
| 789 | 785 |
| 790 def IsInProperBranch(self): | 786 def IsInProperBranch(self): |
| 791 """Confirms they're in the master branch for performing the bisection. | 787 """Confirms they're in the master branch for performing the bisection. |
| 792 This is needed or gclient will fail to sync properly. | 788 This is needed or gclient will fail to sync properly. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 813 cmd = ['svn', 'find-rev', revision] | 809 cmd = ['svn', 'find-rev', revision] |
| 814 | 810 |
| 815 output = CheckRunGit(cmd) | 811 output = CheckRunGit(cmd) |
| 816 svn_revision = output.strip() | 812 svn_revision = output.strip() |
| 817 | 813 |
| 818 if IsStringInt(svn_revision): | 814 if IsStringInt(svn_revision): |
| 819 return int(svn_revision) | 815 return int(svn_revision) |
| 820 | 816 |
| 821 return None | 817 return None |
| 822 | 818 |
| 823 def QueryRevisionInfo(self, revision): | 819 def QueryRevisionInfo(self, revision, cwd=None): |
| 824 """Gathers information on a particular revision, such as author's name, | 820 """Gathers information on a particular revision, such as author's name, |
| 825 email, subject, and date. | 821 email, subject, and date. |
| 826 | 822 |
| 827 Args: | 823 Args: |
| 828 revision: Revision you want to gather information on. | 824 revision: Revision you want to gather information on. |
| 829 Returns: | 825 Returns: |
| 830 A dict in the following format: | 826 A dict in the following format: |
| 831 { | 827 { |
| 832 'author': %s, | 828 'author': %s, |
| 833 'email': %s, | 829 'email': %s, |
| 834 'date': %s, | 830 'date': %s, |
| 835 'subject': %s, | 831 'subject': %s, |
| 836 'body': %s, | 832 'body': %s, |
| 837 } | 833 } |
| 838 """ | 834 """ |
| 839 commit_info = {} | 835 commit_info = {} |
| 840 | 836 |
| 841 formats = ['%cN', '%cE', '%s', '%cD', '%b'] | 837 formats = ['%cN', '%cE', '%s', '%cD', '%b'] |
| 842 targets = ['author', 'email', 'subject', 'date', 'body'] | 838 targets = ['author', 'email', 'subject', 'date', 'body'] |
| 843 | 839 |
| 844 for i in xrange(len(formats)): | 840 for i in xrange(len(formats)): |
| 845 cmd = ['log', '--format=%s' % formats[i], '-1', revision] | 841 cmd = ['log', '--format=%s' % formats[i], '-1', revision] |
| 846 output = CheckRunGit(cmd) | 842 output = CheckRunGit(cmd, cwd=cwd) |
| 847 commit_info[targets[i]] = output.rstrip() | 843 commit_info[targets[i]] = output.rstrip() |
| 848 | 844 |
| 849 return commit_info | 845 return commit_info |
| 850 | 846 |
| 851 def CheckoutFileAtRevision(self, file_name, revision): | 847 def CheckoutFileAtRevision(self, file_name, revision): |
| 852 """Performs a checkout on a file at the given revision. | 848 """Performs a checkout on a file at the given revision. |
| 853 | 849 |
| 854 Returns: | 850 Returns: |
| 855 True if successful. | 851 True if successful. |
| 856 """ | 852 """ |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 949 | 945 |
| 950 revision_work_list = list(set( | 946 revision_work_list = list(set( |
| 951 [int(o) for o in output.split('\n') if IsStringInt(o)])) | 947 [int(o) for o in output.split('\n') if IsStringInt(o)])) |
| 952 revision_work_list = sorted(revision_work_list, reverse=True) | 948 revision_work_list = sorted(revision_work_list, reverse=True) |
| 953 else: | 949 else: |
| 954 revision_work_list = self.source_control.GetRevisionList(bad_revision, | 950 revision_work_list = self.source_control.GetRevisionList(bad_revision, |
| 955 good_revision) | 951 good_revision) |
| 956 | 952 |
| 957 return revision_work_list | 953 return revision_work_list |
| 958 | 954 |
| 955 def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision): |
| 956 svn_revision = self.source_control.SVNFindRev(revision) |
| 957 |
| 958 if IsStringInt(svn_revision): |
| 959 # V8 is tricky to bisect, in that there are only a few instances when |
| 960 # we can dive into bleeding_edge and get back a meaningful result. |
| 961 # Try to detect a V8 "business as usual" case, which is when: |
| 962 # 1. trunk revision N has description "Version X.Y.Z" |
| 963 # 2. bleeding_edge revision (N-1) has description "Prepare push to |
| 964 # trunk. Now working on X.Y.(Z+1)." |
| 965 v8_dir = self._GetDepotDirectory('v8') |
| 966 v8_bleeding_edge_dir = self._GetDepotDirectory('v8_bleeding_edge') |
| 967 |
| 968 revision_info = self.source_control.QueryRevisionInfo(revision, |
| 969 cwd=v8_dir) |
| 970 |
| 971 version_re = re.compile("Version (?P<values>[0-9,.]+)") |
| 972 |
| 973 regex_results = version_re.search(revision_info['subject']) |
| 974 |
| 975 if regex_results: |
| 976 version = regex_results.group('values') |
| 977 |
| 978 git_revision = self.source_control.ResolveToRevision( |
| 979 int(svn_revision) - 1, 'v8_bleeding_edge', -1, |
| 980 cwd=v8_bleeding_edge_dir) |
| 981 |
| 982 if git_revision: |
| 983 revision_info = self.source_control.QueryRevisionInfo(git_revision, |
| 984 cwd=v8_bleeding_edge_dir) |
| 985 |
| 986 if 'Prepare push to trunk' in revision_info['subject']: |
| 987 return git_revision |
| 988 return None |
| 989 |
| 990 def _GetNearestV8BleedingEdgeFromTrunk(self, revision, search_forward=True): |
| 991 cwd = self._GetDepotDirectory('v8') |
| 992 cmd = ['log', '--format=%ct', '-1', revision] |
| 993 output = CheckRunGit(cmd, cwd=cwd) |
| 994 commit_time = int(output) |
| 995 commits = [] |
| 996 |
| 997 if search_forward: |
| 998 cmd = ['log', '--format=%H', '-10', '--after=%d' % commit_time, |
| 999 'origin/master'] |
| 1000 output = CheckRunGit(cmd, cwd=cwd) |
| 1001 output = output.split() |
| 1002 commits = output |
| 1003 commits = reversed(commits) |
| 1004 else: |
| 1005 cmd = ['log', '--format=%H', '-10', '--before=%d' % commit_time, |
| 1006 'origin/master'] |
| 1007 output = CheckRunGit(cmd, cwd=cwd) |
| 1008 output = output.split() |
| 1009 commits = output |
| 1010 |
| 1011 bleeding_edge_revision = None |
| 1012 |
| 1013 for c in commits: |
| 1014 bleeding_edge_revision = self._GetV8BleedingEdgeFromV8TrunkIfMappable(c) |
| 1015 if bleeding_edge_revision: |
| 1016 break |
| 1017 |
| 1018 return bleeding_edge_revision |
| 1019 |
| 959 def Get3rdPartyRevisionsFromCurrentRevision(self, depot, revision): | 1020 def Get3rdPartyRevisionsFromCurrentRevision(self, depot, revision): |
| 960 """Parses the DEPS file to determine WebKit/v8/etc... versions. | 1021 """Parses the DEPS file to determine WebKit/v8/etc... versions. |
| 961 | 1022 |
| 962 Returns: | 1023 Returns: |
| 963 A dict in the format {depot:revision} if successful, otherwise None. | 1024 A dict in the format {depot:revision} if successful, otherwise None. |
| 964 """ | 1025 """ |
| 965 | 1026 |
| 966 cwd = os.getcwd() | 1027 cwd = os.getcwd() |
| 967 self.ChangeToDepotWorkingDirectory(depot) | 1028 self.ChangeToDepotWorkingDirectory(depot) |
| 968 | 1029 |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1034 | 1095 |
| 1035 cwd = os.getcwd() | 1096 cwd = os.getcwd() |
| 1036 self.ChangeToDepotWorkingDirectory('chromium') | 1097 self.ChangeToDepotWorkingDirectory('chromium') |
| 1037 return_code = CheckRunGit(['log', '-1', '--format=%H', | 1098 return_code = CheckRunGit(['log', '-1', '--format=%H', |
| 1038 '--author=chrome-release@google.com', '--grep=to %s' % version, | 1099 '--author=chrome-release@google.com', '--grep=to %s' % version, |
| 1039 'origin/master']) | 1100 'origin/master']) |
| 1040 os.chdir(cwd) | 1101 os.chdir(cwd) |
| 1041 | 1102 |
| 1042 results['chromium'] = output.strip() | 1103 results['chromium'] = output.strip() |
| 1043 elif depot == 'v8': | 1104 elif depot == 'v8': |
| 1105 # We can't try to map the trunk revision to bleeding edge yet, because |
| 1106 # we don't know which direction to try to search in. Have to wait until |
| 1107 # the bisect has narrowed the results down to 2 v8 rolls. |
| 1044 results['v8_bleeding_edge'] = None | 1108 results['v8_bleeding_edge'] = None |
| 1045 | 1109 |
| 1046 svn_revision = self.source_control.SVNFindRev(revision) | |
| 1047 | |
| 1048 if IsStringInt(svn_revision): | |
| 1049 # V8 is tricky to bisect, in that there are only a few instances when | |
| 1050 # we can dive into bleeding_edge and get back a meaningful result. | |
| 1051 # Try to detect a V8 "business as usual" case, which is when: | |
| 1052 # 1. trunk revision N has description "Version X.Y.Z" | |
| 1053 # 2. bleeding_edge revision (N-1) has description "Prepare push to | |
| 1054 # trunk. Now working on X.Y.(Z+1)." | |
| 1055 self.ChangeToDepotWorkingDirectory(depot) | |
| 1056 | |
| 1057 revision_info = self.source_control.QueryRevisionInfo(revision) | |
| 1058 | |
| 1059 version_re = re.compile("Version (?P<values>[0-9,.]+)") | |
| 1060 | |
| 1061 regex_results = version_re.search(revision_info['subject']) | |
| 1062 | |
| 1063 if regex_results: | |
| 1064 version = regex_results.group('values') | |
| 1065 | |
| 1066 self.ChangeToDepotWorkingDirectory('v8_bleeding_edge') | |
| 1067 | |
| 1068 git_revision = self.source_control.ResolveToRevision( | |
| 1069 int(svn_revision) - 1, 'v8_bleeding_edge', -1) | |
| 1070 | |
| 1071 if git_revision: | |
| 1072 revision_info = self.source_control.QueryRevisionInfo(git_revision) | |
| 1073 | |
| 1074 if 'Prepare push to trunk' in revision_info['subject']: | |
| 1075 results['v8_bleeding_edge'] = git_revision | |
| 1076 | |
| 1077 return results | 1110 return results |
| 1078 | 1111 |
| 1079 def BuildCurrentRevision(self, depot): | 1112 def BuildCurrentRevision(self, depot): |
| 1080 """Builds chrome and performance_ui_tests on the current revision. | 1113 """Builds chrome and performance_ui_tests on the current revision. |
| 1081 | 1114 |
| 1082 Returns: | 1115 Returns: |
| 1083 True if the build was successful. | 1116 True if the build was successful. |
| 1084 """ | 1117 """ |
| 1085 if self.opts.debug_ignore_build: | 1118 if self.opts.debug_ignore_build: |
| 1086 return True | 1119 return True |
| (...skipping 513 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1600 | 1633 |
| 1601 Returns: | 1634 Returns: |
| 1602 True if the current_value is closer to the known_good_value than the | 1635 True if the current_value is closer to the known_good_value than the |
| 1603 known_bad_value. | 1636 known_bad_value. |
| 1604 """ | 1637 """ |
| 1605 dist_to_good_value = abs(current_value['mean'] - known_good_value['mean']) | 1638 dist_to_good_value = abs(current_value['mean'] - known_good_value['mean']) |
| 1606 dist_to_bad_value = abs(current_value['mean'] - known_bad_value['mean']) | 1639 dist_to_bad_value = abs(current_value['mean'] - known_bad_value['mean']) |
| 1607 | 1640 |
| 1608 return dist_to_good_value < dist_to_bad_value | 1641 return dist_to_good_value < dist_to_bad_value |
| 1609 | 1642 |
| 1643 def _GetDepotDirectory(self, depot_name): |
| 1644 if depot_name == 'chromium': |
| 1645 return self.src_cwd |
| 1646 elif depot_name == 'cros': |
| 1647 return self.cros_cwd |
| 1648 elif depot_name in DEPOT_NAMES: |
| 1649 return self.depot_cwd[depot_name] |
| 1650 else: |
| 1651 assert False, 'Unknown depot [ %s ] encountered. Possibly a new one'\ |
| 1652 ' was added without proper support?' %\ |
| 1653 (depot_name,) |
| 1654 |
| 1610 def ChangeToDepotWorkingDirectory(self, depot_name): | 1655 def ChangeToDepotWorkingDirectory(self, depot_name): |
| 1611 """Given a depot, changes to the appropriate working directory. | 1656 """Given a depot, changes to the appropriate working directory. |
| 1612 | 1657 |
| 1613 Args: | 1658 Args: |
| 1614 depot_name: The name of the depot (see DEPOT_NAMES). | 1659 depot_name: The name of the depot (see DEPOT_NAMES). |
| 1615 """ | 1660 """ |
| 1616 if depot_name == 'chromium': | 1661 os.chdir(self._GetDepotDirectory(depot_name)) |
| 1617 os.chdir(self.src_cwd) | |
| 1618 elif depot_name == 'cros': | |
| 1619 os.chdir(self.cros_cwd) | |
| 1620 elif depot_name in DEPOT_NAMES: | |
| 1621 os.chdir(self.depot_cwd[depot_name]) | |
| 1622 else: | |
| 1623 assert False, 'Unknown depot [ %s ] encountered. Possibly a new one'\ | |
| 1624 ' was added without proper support?' %\ | |
| 1625 (depot_name,) | |
| 1626 | 1662 |
| 1627 def FindNextDepotToBisect(self, current_revision, min_revision_data, | 1663 def _FillInV8BleedingEdgeInfo(self, min_revision_data, max_revision_data): |
| 1628 max_revision_data): | 1664 r1 = self._GetNearestV8BleedingEdgeFromTrunk(min_revision_data['revision'], |
| 1665 search_forward=True) |
| 1666 r2 = self._GetNearestV8BleedingEdgeFromTrunk(max_revision_data['revision'], |
| 1667 search_forward=False) |
| 1668 min_revision_data['external']['v8_bleeding_edge'] = r1 |
| 1669 max_revision_data['external']['v8_bleeding_edge'] = r2 |
| 1670 |
| 1671 if (not self._GetV8BleedingEdgeFromV8TrunkIfMappable( |
| 1672 min_revision_data['revision']) or |
| 1673 not self._GetV8BleedingEdgeFromV8TrunkIfMappable( |
| 1674 max_revision_data['revision'])): |
| 1675 self.warnings.append('Trunk revisions in V8 did not map directly to ' |
| 1676 'bleeding_edge. Attempted to expand the range to find V8 rolls which ' |
| 1677 'did map directly to bleeding_edge revisions, but results might not ' |
| 1678 'be valid.') |
| 1679 |
| 1680 def _FindNextDepotToBisect(self, current_depot, current_revision, |
| 1681 min_revision_data, max_revision_data): |
| 1629 """Given the state of the bisect, decides which depot the script should | 1682 """Given the state of the bisect, decides which depot the script should |
| 1630 dive into next (if any). | 1683 dive into next (if any). |
| 1631 | 1684 |
| 1632 Args: | 1685 Args: |
| 1686 current_depot: Current depot being bisected. |
| 1633 current_revision: Current revision synced to. | 1687 current_revision: Current revision synced to. |
| 1634 min_revision_data: Data about the earliest revision in the bisect range. | 1688 min_revision_data: Data about the earliest revision in the bisect range. |
| 1635 max_revision_data: Data about the latest revision in the bisect range. | 1689 max_revision_data: Data about the latest revision in the bisect range. |
| 1636 | 1690 |
| 1637 Returns: | 1691 Returns: |
| 1638 The depot to bisect next, or None. | 1692 The depot to bisect next, or None. |
| 1639 """ | 1693 """ |
| 1640 external_depot = None | 1694 external_depot = None |
| 1641 for current_depot in DEPOT_NAMES: | 1695 for next_depot in DEPOT_NAMES: |
| 1642 if DEPOT_DEPS_NAME[current_depot].has_key('platform'): | 1696 if DEPOT_DEPS_NAME[next_depot].has_key('platform'): |
| 1643 if DEPOT_DEPS_NAME[current_depot]['platform'] != os.name: | 1697 if DEPOT_DEPS_NAME[next_depot]['platform'] != os.name: |
| 1644 continue | 1698 continue |
| 1645 | 1699 |
| 1646 if not (DEPOT_DEPS_NAME[current_depot]["recurse"] and | 1700 if not (DEPOT_DEPS_NAME[next_depot]["recurse"] and |
| 1647 DEPOT_DEPS_NAME[current_depot]['from'] == | 1701 DEPOT_DEPS_NAME[next_depot]['from'] == |
| 1648 min_revision_data['depot']): | 1702 min_revision_data['depot']): |
| 1649 continue | 1703 continue |
| 1650 | 1704 |
| 1651 if (min_revision_data['external'][current_depot] == | 1705 if current_depot == 'v8': |
| 1652 max_revision_data['external'][current_depot]): | 1706 # We grab the bleeding_edge info here rather than earlier because we |
| 1707 # finally have the revision range. From that we can search forwards and |
| 1708 # backwards to try to match trunk revisions to bleeding_edge. |
| 1709 self._FillInV8BleedingEdgeInfo(min_revision_data, max_revision_data) |
| 1710 |
| 1711 if (min_revision_data['external'][next_depot] == |
| 1712 max_revision_data['external'][next_depot]): |
| 1653 continue | 1713 continue |
| 1654 | 1714 |
| 1655 if (min_revision_data['external'][current_depot] and | 1715 if (min_revision_data['external'][next_depot] and |
| 1656 max_revision_data['external'][current_depot]): | 1716 max_revision_data['external'][next_depot]): |
| 1657 external_depot = current_depot | 1717 external_depot = next_depot |
| 1658 break | 1718 break |
| 1659 | 1719 |
| 1660 return external_depot | 1720 return external_depot |
| 1661 | 1721 |
| 1662 def PrepareToBisectOnDepot(self, | 1722 def PrepareToBisectOnDepot(self, |
| 1663 current_depot, | 1723 current_depot, |
| 1664 end_revision, | 1724 end_revision, |
| 1665 start_revision, | 1725 start_revision, |
| 1666 previous_depot, | 1726 previous_depot, |
| 1667 previous_revision): | 1727 previous_revision): |
| (...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2047 if max_revision - min_revision <= 1: | 2107 if max_revision - min_revision <= 1: |
| 2048 current_depot = min_revision_data['depot'] | 2108 current_depot = min_revision_data['depot'] |
| 2049 if min_revision_data['passed'] == '?': | 2109 if min_revision_data['passed'] == '?': |
| 2050 next_revision_index = min_revision | 2110 next_revision_index = min_revision |
| 2051 elif max_revision_data['passed'] == '?': | 2111 elif max_revision_data['passed'] == '?': |
| 2052 next_revision_index = max_revision | 2112 next_revision_index = max_revision |
| 2053 elif current_depot in ['cros', 'chromium', 'v8']: | 2113 elif current_depot in ['cros', 'chromium', 'v8']: |
| 2054 previous_revision = revision_list[min_revision] | 2114 previous_revision = revision_list[min_revision] |
| 2055 # If there were changes to any of the external libraries we track, | 2115 # If there were changes to any of the external libraries we track, |
| 2056 # should bisect the changes there as well. | 2116 # should bisect the changes there as well. |
| 2057 external_depot = self.FindNextDepotToBisect( | 2117 external_depot = self._FindNextDepotToBisect(current_depot, |
| 2058 previous_revision, min_revision_data, max_revision_data) | 2118 previous_revision, min_revision_data, max_revision_data) |
| 2059 | 2119 |
| 2060 # If there was no change in any of the external depots, the search | 2120 # If there was no change in any of the external depots, the search |
| 2061 # is over. | 2121 # is over. |
| 2062 if not external_depot: | 2122 if not external_depot: |
| 2063 if current_depot == 'v8': | 2123 if current_depot == 'v8': |
| 2064 self.warnings.append('Unfortunately, V8 bisection couldn\'t ' | 2124 self.warnings.append('Unfortunately, V8 bisection couldn\'t ' |
| 2065 'continue any further. The script can only bisect into ' | 2125 'continue any further. The script can only bisect into ' |
| 2066 'V8\'s bleeding_edge repository if both the current and ' | 2126 'V8\'s bleeding_edge repository if both the current and ' |
| 2067 'previous revisions in trunk map directly to revisions in ' | 2127 'previous revisions in trunk map directly to revisions in ' |
| (...skipping 778 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2846 # The perf dashboard scrapes the "results" step in order to comment on | 2906 # The perf dashboard scrapes the "results" step in order to comment on |
| 2847 # bugs. If you change this, please update the perf dashboard as well. | 2907 # bugs. If you change this, please update the perf dashboard as well. |
| 2848 bisect_utils.OutputAnnotationStepStart('Results') | 2908 bisect_utils.OutputAnnotationStepStart('Results') |
| 2849 print 'Error: %s' % e.message | 2909 print 'Error: %s' % e.message |
| 2850 if opts.output_buildbot_annotations: | 2910 if opts.output_buildbot_annotations: |
| 2851 bisect_utils.OutputAnnotationStepClosed() | 2911 bisect_utils.OutputAnnotationStepClosed() |
| 2852 return 1 | 2912 return 1 |
| 2853 | 2913 |
| 2854 if __name__ == '__main__': | 2914 if __name__ == '__main__': |
| 2855 sys.exit(main()) | 2915 sys.exit(main()) |
| OLD | NEW |