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 1049 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1060 depot: Name of depot being bisected. | 1060 depot: Name of depot being bisected. |
1061 | 1061 |
1062 Returns: | 1062 Returns: |
1063 A dict in the format {depot:revision} if successful, otherwise None. | 1063 A dict in the format {depot:revision} if successful, otherwise None. |
1064 """ | 1064 """ |
1065 try: | 1065 try: |
1066 deps_data = { | 1066 deps_data = { |
1067 'Var': lambda _: deps_data["vars"][_], | 1067 'Var': lambda _: deps_data["vars"][_], |
1068 'From': lambda *args: None, | 1068 'From': lambda *args: None, |
1069 } | 1069 } |
1070 execfile(bisect_utils.FILE_DEPS_GIT, {}, deps_data) | 1070 |
| 1071 deps_file = bisect_utils.FILE_DEPS_GIT |
| 1072 if not os.path.exists(deps_file): |
| 1073 deps_file = bisect_utils.FILE_DEPS |
| 1074 execfile(deps_file, {}, deps_data) |
1071 deps_data = deps_data['deps'] | 1075 deps_data = deps_data['deps'] |
1072 | 1076 |
1073 rxp = re.compile(".git@(?P<revision>[a-fA-F0-9]+)") | 1077 rxp = re.compile(".git@(?P<revision>[a-fA-F0-9]+)") |
1074 results = {} | 1078 results = {} |
1075 for depot_name, depot_data in DEPOT_DEPS_NAME.iteritems(): | 1079 for depot_name, depot_data in DEPOT_DEPS_NAME.iteritems(): |
1076 if (depot_data.get('platform') and | 1080 if (depot_data.get('platform') and |
1077 depot_data.get('platform') != os.name): | 1081 depot_data.get('platform') != os.name): |
1078 continue | 1082 continue |
1079 | 1083 |
1080 if (depot_data.get('recurse') and depot in depot_data.get('from')): | 1084 if (depot_data.get('recurse') and depot in depot_data.get('from')): |
1081 depot_data_src = depot_data.get('src') or depot_data.get('src_old') | 1085 depot_data_src = depot_data.get('src') or depot_data.get('src_old') |
1082 src_dir = deps_data.get(depot_data_src) | 1086 src_dir = deps_data.get(depot_data_src) |
1083 if src_dir: | 1087 if src_dir: |
1084 self.depot_cwd[depot_name] = os.path.join(self.src_cwd, | 1088 self.depot_cwd[depot_name] = os.path.join(self.src_cwd, |
1085 depot_data_src[4:]) | 1089 depot_data_src[4:]) |
1086 re_results = rxp.search(src_dir) | 1090 re_results = rxp.search(src_dir) |
1087 if re_results: | 1091 if re_results: |
1088 results[depot_name] = re_results.group('revision') | 1092 results[depot_name] = re_results.group('revision') |
1089 else: | 1093 else: |
1090 warning_text = ('Could not parse revision for %s while bisecting ' | 1094 warning_text = ('Could not parse revision for %s while bisecting ' |
1091 '%s' % (depot_name, depot)) | 1095 '%s' % (depot_name, depot)) |
1092 if not warning_text in self.warnings: | 1096 if not warning_text in self.warnings: |
1093 self.warnings.append(warning_text) | 1097 self.warnings.append(warning_text) |
1094 else: | 1098 else: |
1095 results[depot_name] = None | 1099 results[depot_name] = None |
1096 return results | 1100 return results |
1097 except ImportError: | 1101 except ImportError: |
1098 deps_file_contents = ReadStringFromFile(bisect_utils.FILE_DEPS_GIT) | 1102 deps_file_contents = ReadStringFromFile(deps_file) |
1099 parse_results = _ParseRevisionsFromDEPSFileManually(deps_file_contents) | 1103 parse_results = _ParseRevisionsFromDEPSFileManually(deps_file_contents) |
1100 results = {} | 1104 results = {} |
1101 for depot_name, depot_revision in parse_results.iteritems(): | 1105 for depot_name, depot_revision in parse_results.iteritems(): |
1102 depot_revision = depot_revision.strip('@') | 1106 depot_revision = depot_revision.strip('@') |
1103 print depot_name, depot_revision | 1107 print depot_name, depot_revision |
1104 for current_name, current_data in DEPOT_DEPS_NAME.iteritems(): | 1108 for current_name, current_data in DEPOT_DEPS_NAME.iteritems(): |
1105 if (current_data.has_key('deps_var') and | 1109 if (current_data.has_key('deps_var') and |
1106 current_data['deps_var'] == depot_name): | 1110 current_data['deps_var'] == depot_name): |
1107 src_name = current_name | 1111 src_name = current_name |
1108 results[src_name] = depot_revision | 1112 results[src_name] = depot_revision |
(...skipping 661 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1770 def PerformWebkitDirectoryCleanup(self, revision): | 1774 def PerformWebkitDirectoryCleanup(self, revision): |
1771 """Cleans up the Webkit directory before syncing another revision. | 1775 """Cleans up the Webkit directory before syncing another revision. |
1772 | 1776 |
1773 If the script is switching between Blink and WebKit during bisect, | 1777 If the script is switching between Blink and WebKit during bisect, |
1774 its faster to just delete the directory rather than leave it up to git | 1778 its faster to just delete the directory rather than leave it up to git |
1775 to sync. | 1779 to sync. |
1776 | 1780 |
1777 Returns: | 1781 Returns: |
1778 True if successful. | 1782 True if successful. |
1779 """ | 1783 """ |
1780 if not self.source_control.CheckoutFileAtRevision( | |
1781 bisect_utils.FILE_DEPS_GIT, revision, cwd=self.src_cwd): | |
1782 return False | |
1783 | |
1784 cwd = os.getcwd() | 1784 cwd = os.getcwd() |
1785 os.chdir(self.src_cwd) | 1785 os.chdir(self.src_cwd) |
1786 | 1786 |
1787 is_blink = bisect_utils.IsDepsFileBlink() | 1787 is_blink = bisect_utils.IsDepsFileBlink(revision) |
1788 | 1788 |
1789 os.chdir(cwd) | 1789 os.chdir(cwd) |
1790 | 1790 |
1791 if not self.source_control.RevertFileToHead( | |
1792 bisect_utils.FILE_DEPS_GIT): | |
1793 return False | |
1794 | |
1795 if self.was_blink != is_blink: | 1791 if self.was_blink != is_blink: |
1796 self.was_blink = is_blink | 1792 self.was_blink = is_blink |
1797 # Removes third_party/Webkit directory. | 1793 # Removes third_party/Webkit directory. |
1798 return bisect_utils.RemoveThirdPartyDirectory('Webkit') | 1794 return bisect_utils.RemoveThirdPartyDirectory('Webkit') |
1799 return True | 1795 return True |
1800 | 1796 |
1801 def PerformCrosChrootCleanup(self): | 1797 def PerformCrosChrootCleanup(self): |
1802 """Deletes the chroot. | 1798 """Deletes the chroot. |
1803 | 1799 |
1804 Returns: | 1800 Returns: |
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2168 | 2164 |
2169 print | 2165 print |
2170 print 'Revisions to bisect on [%s]:' % depot | 2166 print 'Revisions to bisect on [%s]:' % depot |
2171 for revision_id in revision_list: | 2167 for revision_id in revision_list: |
2172 print ' -> %s' % (revision_id, ) | 2168 print ' -> %s' % (revision_id, ) |
2173 print | 2169 print |
2174 | 2170 |
2175 if self.opts.output_buildbot_annotations: | 2171 if self.opts.output_buildbot_annotations: |
2176 bisect_utils.OutputAnnotationStepClosed() | 2172 bisect_utils.OutputAnnotationStepClosed() |
2177 | 2173 |
2178 def NudgeRevisionsIfDEPSChange(self, bad_revision, good_revision): | 2174 def NudgeRevisionsIfDEPSChange(self, bad_revision, good_revision, |
| 2175 good_svn_revision=None): |
2179 """Checks to see if changes to DEPS file occurred, and that the revision | 2176 """Checks to see if changes to DEPS file occurred, and that the revision |
2180 range also includes the change to .DEPS.git. If it doesn't, attempts to | 2177 range also includes the change to .DEPS.git. If it doesn't, attempts to |
2181 expand the revision range to include it. | 2178 expand the revision range to include it. |
2182 | 2179 |
2183 Args: | 2180 Args: |
2184 bad_rev: First known bad revision. | 2181 bad_revision: First known bad git revision. |
2185 good_revision: Last known good revision. | 2182 good_revision: Last known good git revision. |
| 2183 good_svn_revision: Last known good svn revision. |
2186 | 2184 |
2187 Returns: | 2185 Returns: |
2188 A tuple with the new bad and good revisions. | 2186 A tuple with the new bad and good revisions. |
2189 """ | 2187 """ |
| 2188 # DONOT perform nudge because at revision 291563 .DEPS.git was removed |
| 2189 # and source contain only DEPS file for dependency changes. |
| 2190 if good_svn_revision >= 291563: |
| 2191 return (bad_revision, good_revision) |
| 2192 |
2190 if self.source_control.IsGit() and self.opts.target_platform == 'chromium': | 2193 if self.source_control.IsGit() and self.opts.target_platform == 'chromium': |
2191 changes_to_deps = self.source_control.QueryFileRevisionHistory( | 2194 changes_to_deps = self.source_control.QueryFileRevisionHistory( |
2192 'DEPS', good_revision, bad_revision) | 2195 'DEPS', good_revision, bad_revision) |
2193 | 2196 |
2194 if changes_to_deps: | 2197 if changes_to_deps: |
2195 # DEPS file was changed, search from the oldest change to DEPS file to | 2198 # DEPS file was changed, search from the oldest change to DEPS file to |
2196 # bad_revision to see if there are matching .DEPS.git changes. | 2199 # bad_revision to see if there are matching .DEPS.git changes. |
2197 oldest_deps_change = changes_to_deps[-1] | 2200 oldest_deps_change = changes_to_deps[-1] |
2198 changes_to_gitdeps = self.source_control.QueryFileRevisionHistory( | 2201 changes_to_gitdeps = self.source_control.QueryFileRevisionHistory( |
2199 bisect_utils.FILE_DEPS_GIT, oldest_deps_change, bad_revision) | 2202 bisect_utils.FILE_DEPS_GIT, oldest_deps_change, bad_revision) |
2200 | 2203 |
2201 if len(changes_to_deps) != len(changes_to_gitdeps): | 2204 if len(changes_to_deps) != len(changes_to_gitdeps): |
2202 # Grab the timestamp of the last DEPS change | 2205 # Grab the timestamp of the last DEPS change |
2203 cmd = ['log', '--format=%ct', '-1', changes_to_deps[0]] | 2206 cmd = ['log', '--format=%ct', '-1', changes_to_deps[0]] |
2204 output = bisect_utils.CheckRunGit(cmd) | 2207 output = bisect_utils.CheckRunGit(cmd) |
2205 commit_time = int(output) | 2208 commit_time = int(output) |
2206 | 2209 |
2207 # Try looking for a commit that touches the .DEPS.git file in the | 2210 # Try looking for a commit that touches the .DEPS.git file in the |
2208 # next 15 minutes after the DEPS file change. | 2211 # next 15 minutes after the DEPS file change. |
2209 cmd = ['log', '--format=%H', '-1', | 2212 cmd = ['log', '--format=%H', '-1', |
2210 '--before=%d' % (commit_time + 900), '--after=%d' % commit_time, | 2213 '--before=%d' % (commit_time + 900), '--after=%d' % commit_time, |
2211 'origin/master', bisect_utils.FILE_DEPS_GIT] | 2214 'origin/master', '--', bisect_utils.FILE_DEPS_GIT] |
2212 output = bisect_utils.CheckRunGit(cmd) | 2215 output = bisect_utils.CheckRunGit(cmd) |
2213 output = output.strip() | 2216 output = output.strip() |
2214 if output: | 2217 if output: |
2215 self.warnings.append('Detected change to DEPS and modified ' | 2218 self.warnings.append('Detected change to DEPS and modified ' |
2216 'revision range to include change to .DEPS.git') | 2219 'revision range to include change to .DEPS.git') |
2217 return (output, good_revision) | 2220 return (output, good_revision) |
2218 else: | 2221 else: |
2219 self.warnings.append('Detected change to DEPS but couldn\'t find ' | 2222 self.warnings.append('Detected change to DEPS but couldn\'t find ' |
2220 'matching change to .DEPS.git') | 2223 'matching change to .DEPS.git') |
2221 return (bad_revision, good_revision) | 2224 return (bad_revision, good_revision) |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2331 cwd = os.getcwd() | 2334 cwd = os.getcwd() |
2332 self.ChangeToDepotWorkingDirectory(target_depot) | 2335 self.ChangeToDepotWorkingDirectory(target_depot) |
2333 | 2336 |
2334 # If they passed SVN revisions, we can try match them to git SHA1 hashes. | 2337 # If they passed SVN revisions, we can try match them to git SHA1 hashes. |
2335 bad_revision = self.source_control.ResolveToRevision( | 2338 bad_revision = self.source_control.ResolveToRevision( |
2336 bad_revision_in, target_depot, DEPOT_DEPS_NAME, 100) | 2339 bad_revision_in, target_depot, DEPOT_DEPS_NAME, 100) |
2337 good_revision = self.source_control.ResolveToRevision( | 2340 good_revision = self.source_control.ResolveToRevision( |
2338 good_revision_in, target_depot, DEPOT_DEPS_NAME, -100) | 2341 good_revision_in, target_depot, DEPOT_DEPS_NAME, -100) |
2339 | 2342 |
2340 os.chdir(cwd) | 2343 os.chdir(cwd) |
2341 | |
2342 if bad_revision is None: | 2344 if bad_revision is None: |
2343 results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % bad_revision_in | 2345 results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % bad_revision_in |
2344 return results | 2346 return results |
2345 | 2347 |
2346 if good_revision is None: | 2348 if good_revision is None: |
2347 results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % good_revision_in | 2349 results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % good_revision_in |
2348 return results | 2350 return results |
2349 | 2351 |
2350 # Check that they didn't accidentally swap good and bad revisions. | 2352 # Check that they didn't accidentally swap good and bad revisions. |
2351 if not self.CheckIfRevisionsInProperOrder( | 2353 if not self.CheckIfRevisionsInProperOrder( |
2352 target_depot, good_revision, bad_revision): | 2354 target_depot, good_revision, bad_revision): |
2353 results['error'] = ('bad_revision < good_revision, did you swap these ' | 2355 results['error'] = ('bad_revision < good_revision, did you swap these ' |
2354 'by mistake?') | 2356 'by mistake?') |
2355 return results | 2357 return results |
2356 | |
2357 bad_revision, good_revision = self.NudgeRevisionsIfDEPSChange( | 2358 bad_revision, good_revision = self.NudgeRevisionsIfDEPSChange( |
2358 bad_revision, good_revision) | 2359 bad_revision, good_revision, good_revision_in) |
2359 | |
2360 if self.opts.output_buildbot_annotations: | 2360 if self.opts.output_buildbot_annotations: |
2361 bisect_utils.OutputAnnotationStepStart('Gathering Revisions') | 2361 bisect_utils.OutputAnnotationStepStart('Gathering Revisions') |
2362 | 2362 |
2363 cannot_bisect = self.CanPerformBisect(good_revision) | 2363 cannot_bisect = self.CanPerformBisect(good_revision) |
2364 if cannot_bisect: | 2364 if cannot_bisect: |
2365 results['error'] = cannot_bisect.get('error') | 2365 results['error'] = cannot_bisect.get('error') |
2366 return results | 2366 return results |
2367 | 2367 |
2368 print 'Gathering revision range for bisection.' | 2368 print 'Gathering revision range for bisection.' |
2369 # Retrieve a list of revisions to do bisection on. | 2369 # Retrieve a list of revisions to do bisection on. |
(...skipping 992 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3362 # bugs. If you change this, please update the perf dashboard as well. | 3362 # bugs. If you change this, please update the perf dashboard as well. |
3363 bisect_utils.OutputAnnotationStepStart('Results') | 3363 bisect_utils.OutputAnnotationStepStart('Results') |
3364 print 'Error: %s' % e.message | 3364 print 'Error: %s' % e.message |
3365 if opts.output_buildbot_annotations: | 3365 if opts.output_buildbot_annotations: |
3366 bisect_utils.OutputAnnotationStepClosed() | 3366 bisect_utils.OutputAnnotationStepClosed() |
3367 return 1 | 3367 return 1 |
3368 | 3368 |
3369 | 3369 |
3370 if __name__ == '__main__': | 3370 if __name__ == '__main__': |
3371 sys.exit(main()) | 3371 sys.exit(main()) |
OLD | NEW |