| 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 957 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 968 [int(o) for o in output.split('\n') if bisect_utils.IsStringInt(o)])) | 968 [int(o) for o in output.split('\n') if bisect_utils.IsStringInt(o)])) |
| 969 revision_work_list = sorted(revision_work_list, reverse=True) | 969 revision_work_list = sorted(revision_work_list, reverse=True) |
| 970 else: | 970 else: |
| 971 cwd = self._GetDepotDirectory(depot) | 971 cwd = self._GetDepotDirectory(depot) |
| 972 revision_work_list = self.source_control.GetRevisionList(bad_revision, | 972 revision_work_list = self.source_control.GetRevisionList(bad_revision, |
| 973 good_revision, cwd=cwd) | 973 good_revision, cwd=cwd) |
| 974 | 974 |
| 975 return revision_work_list | 975 return revision_work_list |
| 976 | 976 |
| 977 def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision): | 977 def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision): |
| 978 svn_revision = self.source_control.SVNFindRev(revision) | 978 commit_position = self.source_control.GetCommitPosition(revision) |
| 979 | 979 |
| 980 if bisect_utils.IsStringInt(svn_revision): | 980 if bisect_utils.IsStringInt(commit_position): |
| 981 # V8 is tricky to bisect, in that there are only a few instances when | 981 # V8 is tricky to bisect, in that there are only a few instances when |
| 982 # we can dive into bleeding_edge and get back a meaningful result. | 982 # we can dive into bleeding_edge and get back a meaningful result. |
| 983 # Try to detect a V8 "business as usual" case, which is when: | 983 # Try to detect a V8 "business as usual" case, which is when: |
| 984 # 1. trunk revision N has description "Version X.Y.Z" | 984 # 1. trunk revision N has description "Version X.Y.Z" |
| 985 # 2. bleeding_edge revision (N-1) has description "Prepare push to | 985 # 2. bleeding_edge revision (N-1) has description "Prepare push to |
| 986 # trunk. Now working on X.Y.(Z+1)." | 986 # trunk. Now working on X.Y.(Z+1)." |
| 987 # | 987 # |
| 988 # As of 01/24/2014, V8 trunk descriptions are formatted: | 988 # As of 01/24/2014, V8 trunk descriptions are formatted: |
| 989 # "Version 3.X.Y (based on bleeding_edge revision rZ)" | 989 # "Version 3.X.Y (based on bleeding_edge revision rZ)" |
| 990 # So we can just try parsing that out first and fall back to the old way. | 990 # So we can just try parsing that out first and fall back to the old way. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1010 git_revision = self.source_control.ResolveToRevision( | 1010 git_revision = self.source_control.ResolveToRevision( |
| 1011 bleeding_edge_revision, 'v8_bleeding_edge', DEPOT_DEPS_NAME, 1, | 1011 bleeding_edge_revision, 'v8_bleeding_edge', DEPOT_DEPS_NAME, 1, |
| 1012 cwd=v8_bleeding_edge_dir) | 1012 cwd=v8_bleeding_edge_dir) |
| 1013 return git_revision | 1013 return git_revision |
| 1014 except (IndexError, ValueError): | 1014 except (IndexError, ValueError): |
| 1015 pass | 1015 pass |
| 1016 | 1016 |
| 1017 if not git_revision: | 1017 if not git_revision: |
| 1018 # Wasn't successful, try the old way of looking for "Prepare push to" | 1018 # Wasn't successful, try the old way of looking for "Prepare push to" |
| 1019 git_revision = self.source_control.ResolveToRevision( | 1019 git_revision = self.source_control.ResolveToRevision( |
| 1020 int(svn_revision) - 1, 'v8_bleeding_edge', DEPOT_DEPS_NAME, -1, | 1020 int(commit_position) - 1, 'v8_bleeding_edge', DEPOT_DEPS_NAME, -1, |
| 1021 cwd=v8_bleeding_edge_dir) | 1021 cwd=v8_bleeding_edge_dir) |
| 1022 | 1022 |
| 1023 if git_revision: | 1023 if git_revision: |
| 1024 revision_info = self.source_control.QueryRevisionInfo(git_revision, | 1024 revision_info = self.source_control.QueryRevisionInfo(git_revision, |
| 1025 cwd=v8_bleeding_edge_dir) | 1025 cwd=v8_bleeding_edge_dir) |
| 1026 | 1026 |
| 1027 if 'Prepare push to trunk' in revision_info['subject']: | 1027 if 'Prepare push to trunk' in revision_info['subject']: |
| 1028 return git_revision | 1028 return git_revision |
| 1029 return None | 1029 return None |
| 1030 | 1030 |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1224 out_dir: Build output directory where downloaded file is stored. | 1224 out_dir: Build output directory where downloaded file is stored. |
| 1225 | 1225 |
| 1226 Returns: | 1226 Returns: |
| 1227 Downloaded archive file path if exists, otherwise None. | 1227 Downloaded archive file path if exists, otherwise None. |
| 1228 """ | 1228 """ |
| 1229 # Source archive file path on cloud storage using Git revision. | 1229 # Source archive file path on cloud storage using Git revision. |
| 1230 source_file = GetRemoteBuildPath( | 1230 source_file = GetRemoteBuildPath( |
| 1231 revision, self.opts.target_platform, target_arch, patch_sha) | 1231 revision, self.opts.target_platform, target_arch, patch_sha) |
| 1232 downloaded_archive = FetchFromCloudStorage(gs_bucket, source_file, out_dir) | 1232 downloaded_archive = FetchFromCloudStorage(gs_bucket, source_file, out_dir) |
| 1233 if not downloaded_archive: | 1233 if not downloaded_archive: |
| 1234 # Get SVN revision for the given SHA. | 1234 # Get commit position for the given SHA. |
| 1235 svn_revision = self.source_control.SVNFindRev(revision) | 1235 commit_position = self.source_control.GetCommitPosition(revision) |
| 1236 if svn_revision: | 1236 if commit_position: |
| 1237 # Source archive file path on cloud storage using SVN revision. | 1237 # Source archive file path on cloud storage using SVN revision. |
| 1238 source_file = GetRemoteBuildPath( | 1238 source_file = GetRemoteBuildPath( |
| 1239 svn_revision, self.opts.target_platform, target_arch, patch_sha) | 1239 commit_position, self.opts.target_platform, target_arch, patch_sha) |
| 1240 return FetchFromCloudStorage(gs_bucket, source_file, out_dir) | 1240 return FetchFromCloudStorage(gs_bucket, source_file, out_dir) |
| 1241 return downloaded_archive | 1241 return downloaded_archive |
| 1242 | 1242 |
| 1243 def DownloadCurrentBuild(self, revision, build_type='Release', patch=None): | 1243 def DownloadCurrentBuild(self, revision, build_type='Release', patch=None): |
| 1244 """Downloads the build archive for the given revision. | 1244 """Downloads the build archive for the given revision. |
| 1245 | 1245 |
| 1246 Args: | 1246 Args: |
| 1247 revision: The Git revision to download or build. | 1247 revision: The Git revision to download or build. |
| 1248 build_type: Target build type ('Release', 'Debug', 'Release_x64' etc.) | 1248 build_type: Target build type ('Release', 'Debug', 'Release_x64' etc.) |
| 1249 patch: A DEPS patch (used while bisecting 3rd party repositories). | 1249 patch: A DEPS patch (used while bisecting 3rd party repositories). |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1400 | 1400 |
| 1401 Returns: | 1401 Returns: |
| 1402 Updated DEPS content as string if deps key is found, otherwise None. | 1402 Updated DEPS content as string if deps key is found, otherwise None. |
| 1403 """ | 1403 """ |
| 1404 # Check whether the depot and revision pattern in DEPS file vars | 1404 # Check whether the depot and revision pattern in DEPS file vars |
| 1405 # e.g. for webkit the format is "webkit_revision": "12345". | 1405 # e.g. for webkit the format is "webkit_revision": "12345". |
| 1406 deps_revision = re.compile(r'(?<="%s": ")([0-9]+)(?=")' % deps_key, | 1406 deps_revision = re.compile(r'(?<="%s": ")([0-9]+)(?=")' % deps_key, |
| 1407 re.MULTILINE) | 1407 re.MULTILINE) |
| 1408 new_data = None | 1408 new_data = None |
| 1409 if re.search(deps_revision, deps_contents): | 1409 if re.search(deps_revision, deps_contents): |
| 1410 svn_revision = self.source_control.SVNFindRev( | 1410 commit_position = self.source_control.GetCommitPosition( |
| 1411 git_revision, self._GetDepotDirectory(depot)) | 1411 git_revision, self._GetDepotDirectory(depot)) |
| 1412 if not svn_revision: | 1412 if not commit_position: |
| 1413 print 'Could not determine SVN revision for %s' % git_revision | 1413 print 'Could not determine commit position for %s' % git_revision |
| 1414 return None | 1414 return None |
| 1415 # Update the revision information for the given depot | 1415 # Update the revision information for the given depot |
| 1416 new_data = re.sub(deps_revision, str(svn_revision), deps_contents) | 1416 new_data = re.sub(deps_revision, str(commit_position), deps_contents) |
| 1417 else: | 1417 else: |
| 1418 # Check whether the depot and revision pattern in DEPS file vars | 1418 # Check whether the depot and revision pattern in DEPS file vars |
| 1419 # e.g. for webkit the format is "webkit_revision": "559a6d4ab7a84c539..". | 1419 # e.g. for webkit the format is "webkit_revision": "559a6d4ab7a84c539..". |
| 1420 deps_revision = re.compile( | 1420 deps_revision = re.compile( |
| 1421 r'(?<=["\']%s["\']: ["\'])([a-fA-F0-9]{40})(?=["\'])' % deps_key, | 1421 r'(?<=["\']%s["\']: ["\'])([a-fA-F0-9]{40})(?=["\'])' % deps_key, |
| 1422 re.MULTILINE) | 1422 re.MULTILINE) |
| 1423 if re.search(deps_revision, deps_contents): | 1423 if re.search(deps_revision, deps_contents): |
| 1424 new_data = re.sub(deps_revision, git_revision, deps_contents) | 1424 new_data = re.sub(deps_revision, git_revision, deps_contents) |
| 1425 if new_data: | 1425 if new_data: |
| 1426 # For v8_bleeding_edge revisions change V8 branch in order | 1426 # For v8_bleeding_edge revisions change V8 branch in order |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1571 # Prior to crrev.com/274857 *only* android-chromium-testshell | 1571 # Prior to crrev.com/274857 *only* android-chromium-testshell |
| 1572 # Then until crrev.com/276628 *both* (android-chromium-testshell and | 1572 # Then until crrev.com/276628 *both* (android-chromium-testshell and |
| 1573 # android-chrome-shell) work. After that rev 276628 *only* | 1573 # android-chrome-shell) work. After that rev 276628 *only* |
| 1574 # android-chrome-shell works. bisect-perf-regression.py script should | 1574 # android-chrome-shell works. bisect-perf-regression.py script should |
| 1575 # handle these cases and set appropriate browser type based on revision. | 1575 # handle these cases and set appropriate browser type based on revision. |
| 1576 if self.opts.target_platform in ['android']: | 1576 if self.opts.target_platform in ['android']: |
| 1577 # When its a third_party depot, get the chromium revision. | 1577 # When its a third_party depot, get the chromium revision. |
| 1578 if depot != 'chromium': | 1578 if depot != 'chromium': |
| 1579 revision = bisect_utils.CheckRunGit( | 1579 revision = bisect_utils.CheckRunGit( |
| 1580 ['rev-parse', 'HEAD'], cwd=self.src_cwd).strip() | 1580 ['rev-parse', 'HEAD'], cwd=self.src_cwd).strip() |
| 1581 svn_revision = self.source_control.SVNFindRev(revision, cwd=self.src_cwd) | 1581 commit_position = self.source_control.GetCommitPosition(revision, |
| 1582 if not svn_revision: | 1582 cwd=self.src_cwd) |
| 1583 if not commit_position: |
| 1583 return command_to_run | 1584 return command_to_run |
| 1584 cmd_re = re.compile('--browser=(?P<browser_type>\S+)') | 1585 cmd_re = re.compile('--browser=(?P<browser_type>\S+)') |
| 1585 matches = cmd_re.search(command_to_run) | 1586 matches = cmd_re.search(command_to_run) |
| 1586 if bisect_utils.IsStringInt(svn_revision) and matches: | 1587 if bisect_utils.IsStringInt(commit_position) and matches: |
| 1587 cmd_browser = matches.group('browser_type') | 1588 cmd_browser = matches.group('browser_type') |
| 1588 if svn_revision <= 274857 and cmd_browser == 'android-chrome-shell': | 1589 if commit_position <= 274857 and cmd_browser == 'android-chrome-shell': |
| 1589 return command_to_run.replace(cmd_browser, | 1590 return command_to_run.replace(cmd_browser, |
| 1590 'android-chromium-testshell') | 1591 'android-chromium-testshell') |
| 1591 elif (svn_revision >= 276628 and | 1592 elif (commit_position >= 276628 and |
| 1592 cmd_browser == 'android-chromium-testshell'): | 1593 cmd_browser == 'android-chromium-testshell'): |
| 1593 return command_to_run.replace(cmd_browser, | 1594 return command_to_run.replace(cmd_browser, |
| 1594 'android-chrome-shell') | 1595 'android-chrome-shell') |
| 1595 return command_to_run | 1596 return command_to_run |
| 1596 | 1597 |
| 1597 def RunPerformanceTestAndParseResults( | 1598 def RunPerformanceTestAndParseResults( |
| 1598 self, command_to_run, metric, reset_on_first_run=False, | 1599 self, command_to_run, metric, reset_on_first_run=False, |
| 1599 upload_on_last_run=False, results_label=None): | 1600 upload_on_last_run=False, results_label=None): |
| 1600 """Runs a performance test on the current revision and parses the results. | 1601 """Runs a performance test on the current revision and parses the results. |
| 1601 | 1602 |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1758 is_base = ((depot == 'chromium') or (depot == 'cros') or | 1759 is_base = ((depot == 'chromium') or (depot == 'cros') or |
| 1759 (depot == 'android-chrome')) | 1760 (depot == 'android-chrome')) |
| 1760 | 1761 |
| 1761 # Some SVN depots were split into multiple git depots, so we need to | 1762 # Some SVN depots were split into multiple git depots, so we need to |
| 1762 # figure out for each mirror which git revision to grab. There's no | 1763 # figure out for each mirror which git revision to grab. There's no |
| 1763 # guarantee that the SVN revision will exist for each of the dependent | 1764 # guarantee that the SVN revision will exist for each of the dependent |
| 1764 # depots, so we have to grep the git logs and grab the next earlier one. | 1765 # depots, so we have to grep the git logs and grab the next earlier one. |
| 1765 if (not is_base | 1766 if (not is_base |
| 1766 and DEPOT_DEPS_NAME[depot]['depends'] | 1767 and DEPOT_DEPS_NAME[depot]['depends'] |
| 1767 and self.source_control.IsGit()): | 1768 and self.source_control.IsGit()): |
| 1768 svn_rev = self.source_control.SVNFindRev(revision) | 1769 commit_position = self.source_control.GetCommitPosition(revision) |
| 1769 | 1770 |
| 1770 for d in DEPOT_DEPS_NAME[depot]['depends']: | 1771 for d in DEPOT_DEPS_NAME[depot]['depends']: |
| 1771 self.ChangeToDepotWorkingDirectory(d) | 1772 self.ChangeToDepotWorkingDirectory(d) |
| 1772 | 1773 |
| 1773 dependant_rev = self.source_control.ResolveToRevision( | 1774 dependant_rev = self.source_control.ResolveToRevision( |
| 1774 svn_rev, d, DEPOT_DEPS_NAME, -1000) | 1775 commit_position, d, DEPOT_DEPS_NAME, -1000) |
| 1775 | 1776 |
| 1776 if dependant_rev: | 1777 if dependant_rev: |
| 1777 revisions_to_sync.append([d, dependant_rev]) | 1778 revisions_to_sync.append([d, dependant_rev]) |
| 1778 | 1779 |
| 1779 num_resolved = len(revisions_to_sync) | 1780 num_resolved = len(revisions_to_sync) |
| 1780 num_needed = len(DEPOT_DEPS_NAME[depot]['depends']) | 1781 num_needed = len(DEPOT_DEPS_NAME[depot]['depends']) |
| 1781 | 1782 |
| 1782 self.ChangeToDepotWorkingDirectory(depot) | 1783 self.ChangeToDepotWorkingDirectory(depot) |
| 1783 | 1784 |
| 1784 if not ((num_resolved - 1) == num_needed): | 1785 if not ((num_resolved - 1) == num_needed): |
| (...skipping 491 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2276 | 2277 |
| 2277 Args: | 2278 Args: |
| 2278 good_revision: Known good revision. | 2279 good_revision: Known good revision. |
| 2279 bad_revision: Known bad revision. | 2280 bad_revision: Known bad revision. |
| 2280 | 2281 |
| 2281 Returns: | 2282 Returns: |
| 2282 A dictionary indicating the result. If revision is not bisectable, | 2283 A dictionary indicating the result. If revision is not bisectable, |
| 2283 this will contain the field "error", otherwise None. | 2284 this will contain the field "error", otherwise None. |
| 2284 """ | 2285 """ |
| 2285 if self.opts.target_platform == 'android': | 2286 if self.opts.target_platform == 'android': |
| 2286 revision_to_check = self.source_control.SVNFindRev(good_revision) | 2287 revision_to_check = self.source_control.GetCommitPosition(good_revision) |
| 2287 if (bisect_utils.IsStringInt(good_revision) | 2288 if (bisect_utils.IsStringInt(good_revision) |
| 2288 and good_revision < 265549): | 2289 and good_revision < 265549): |
| 2289 return {'error': ( | 2290 return {'error': ( |
| 2290 'Bisect cannot continue for the given revision range.\n' | 2291 'Bisect cannot continue for the given revision range.\n' |
| 2291 'It is impossible to bisect Android regressions ' | 2292 'It is impossible to bisect Android regressions ' |
| 2292 'prior to r265549, which allows the bisect bot to ' | 2293 'prior to r265549, which allows the bisect bot to ' |
| 2293 'rely on Telemetry to do apk installation of the most recently ' | 2294 'rely on Telemetry to do apk installation of the most recently ' |
| 2294 'built local ChromeShell(refer to crbug.com/385324).\n' | 2295 'built local ChromeShell(refer to crbug.com/385324).\n' |
| 2295 'Please try bisecting revisions greater than or equal to r265549.')} | 2296 'Please try bisecting revisions greater than or equal to r265549.')} |
| 2296 | 2297 |
| 2297 if bisect_utils.IsWindowsHost(): | 2298 if bisect_utils.IsWindowsHost(): |
| 2298 good_revision = self.source_control.SVNFindRev(good_revision) | 2299 good_revision = self.source_control.GetCommitPosition(good_revision) |
| 2299 bad_revision = self.source_control.SVNFindRev(bad_revision) | 2300 bad_revision = self.source_control.GetCommitPosition(bad_revision) |
| 2300 if (bisect_utils.IsStringInt(good_revision) and | 2301 if (bisect_utils.IsStringInt(good_revision) and |
| 2301 bisect_utils.IsStringInt(bad_revision)): | 2302 bisect_utils.IsStringInt(bad_revision)): |
| 2302 if (289987 <= good_revision < 290716 or | 2303 if (289987 <= good_revision < 290716 or |
| 2303 289987 <= bad_revision < 290716): | 2304 289987 <= bad_revision < 290716): |
| 2304 return {'error': ('Oops! Revision between r289987 and r290716 are ' | 2305 return {'error': ('Oops! Revision between r289987 and r290716 are ' |
| 2305 'marked as dead zone for Windows due to ' | 2306 'marked as dead zone for Windows due to ' |
| 2306 'crbug.com/405274. Please try another range.')} | 2307 'crbug.com/405274. Please try another range.')} |
| 2307 | 2308 |
| 2308 return None | 2309 return None |
| 2309 | 2310 |
| (...skipping 1087 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3397 # bugs. If you change this, please update the perf dashboard as well. | 3398 # bugs. If you change this, please update the perf dashboard as well. |
| 3398 bisect_utils.OutputAnnotationStepStart('Results') | 3399 bisect_utils.OutputAnnotationStepStart('Results') |
| 3399 print 'Error: %s' % e.message | 3400 print 'Error: %s' % e.message |
| 3400 if opts.output_buildbot_annotations: | 3401 if opts.output_buildbot_annotations: |
| 3401 bisect_utils.OutputAnnotationStepClosed() | 3402 bisect_utils.OutputAnnotationStepClosed() |
| 3402 return 1 | 3403 return 1 |
| 3403 | 3404 |
| 3404 | 3405 |
| 3405 if __name__ == '__main__': | 3406 if __name__ == '__main__': |
| 3406 sys.exit(main()) | 3407 sys.exit(main()) |
| OLD | NEW |