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 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
118 | 118 |
119 # Git branch name used to run bisect try jobs. | 119 # Git branch name used to run bisect try jobs. |
120 BISECT_TRYJOB_BRANCH = 'bisect-tryjob' | 120 BISECT_TRYJOB_BRANCH = 'bisect-tryjob' |
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 | 128 ANDROID_CHROME_SVN_REPO_URL = ('svn://svn.chromium.org/chrome-try-internal/' |
129 'try-perf') | |
129 | 130 |
130 class RunGitError(Exception): | 131 class RunGitError(Exception): |
131 | 132 |
132 def __str__(self): | 133 def __str__(self): |
133 return '%s\nError executing git command.' % self.args[0] | 134 return '%s\nError executing git command.' % self.args[0] |
134 | 135 |
135 | 136 |
136 def GetSHA1HexDigest(contents): | 137 def GetSHA1HexDigest(contents): |
137 """Returns SHA1 hex digest of the given string.""" | 138 """Returns SHA1 hex digest of the given string.""" |
138 return hashlib.sha1(contents).hexdigest() | 139 return hashlib.sha1(contents).hexdigest() |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
188 # the DEPS file with the following format: | 189 # the DEPS file with the following format: |
189 # 'depot_name': 'revision', | 190 # 'depot_name': 'revision', |
190 vars_body = re_results.group('vars_body') | 191 vars_body = re_results.group('vars_body') |
191 rxp = re.compile(r"'(?P<depot_body>[\w_-]+)':[\s]+'(?P<rev_body>[\w@]+)'", | 192 rxp = re.compile(r"'(?P<depot_body>[\w_-]+)':[\s]+'(?P<rev_body>[\w@]+)'", |
192 re.MULTILINE) | 193 re.MULTILINE) |
193 re_results = rxp.findall(vars_body) | 194 re_results = rxp.findall(vars_body) |
194 | 195 |
195 return dict(re_results) | 196 return dict(re_results) |
196 | 197 |
197 | 198 |
198 def _WaitUntilBuildIsReady(fetch_build_func, builder_name, builder_type, | 199 def _WaitUntilBuildIsReady(fetch_build_func, builder_name, build_request_id, |
199 build_request_id, max_timeout): | 200 max_timeout, buildbot_server_url): |
200 """Waits until build is produced by bisect builder on try server. | 201 """Waits until build is produced by bisect builder on try server. |
201 | 202 |
202 Args: | 203 Args: |
203 fetch_build_func: Function to check and download build from cloud storage. | 204 fetch_build_func: Function to check and download build from cloud storage. |
204 builder_name: Builder bot name on try server. | 205 builder_name: Builder bot name on try server. |
205 builder_type: Builder type, e.g. "perf" or "full". Refer to the constants | |
206 |fetch_build| which determine the valid values that can be passed. | |
207 build_request_id: A unique ID of the build request posted to try server. | 206 build_request_id: A unique ID of the build request posted to try server. |
208 max_timeout: Maximum time to wait for the build. | 207 max_timeout: Maximum time to wait for the build. |
208 buildbot_server_url: Buildbot url to check build status. | |
209 | 209 |
210 Returns: | 210 Returns: |
211 Downloaded archive file path if exists, otherwise None. | 211 Downloaded archive file path if exists, otherwise None. |
212 """ | 212 """ |
213 # Build number on the try server. | 213 # Build number on the try server. |
214 build_num = None | 214 build_num = None |
215 # Interval to check build on cloud storage. | 215 # Interval to check build on cloud storage. |
216 poll_interval = 60 | 216 poll_interval = 60 |
217 # Interval to check build status on try server in seconds. | 217 # Interval to check build status on try server in seconds. |
218 status_check_interval = 600 | 218 status_check_interval = 600 |
219 last_status_check = time.time() | 219 last_status_check = time.time() |
220 start_time = time.time() | 220 start_time = time.time() |
221 | |
221 while True: | 222 while True: |
222 # Checks for build on gs://chrome-perf and download if exists. | 223 # Checks for build on gs://chrome-perf and download if exists. |
223 res = fetch_build_func() | 224 res = fetch_build_func() |
224 if res: | 225 if res: |
225 return (res, 'Build successfully found') | 226 return (res, 'Build successfully found') |
226 elapsed_status_check = time.time() - last_status_check | 227 elapsed_status_check = time.time() - last_status_check |
227 # To avoid overloading try server with status check requests, we check | 228 # To avoid overloading try server with status check requests, we check |
228 # build status for every 10 minutes. | 229 # build status for every 10 minutes. |
229 if elapsed_status_check > status_check_interval: | 230 if elapsed_status_check > status_check_interval: |
230 last_status_check = time.time() | 231 last_status_check = time.time() |
231 if not build_num: | 232 if not build_num: |
232 # Get the build number on try server for the current build. | 233 # Get the build number on try server for the current build. |
233 build_num = request_build.GetBuildNumFromBuilder( | 234 build_num = request_build.GetBuildNumFromBuilder( |
234 build_request_id, builder_name, builder_type) | 235 build_request_id, builder_name, buildbot_server_url) |
235 # Check the status of build using the build number. | 236 # Check the status of build using the build number. |
236 # Note: Build is treated as PENDING if build number is not found | 237 # Note: Build is treated as PENDING if build number is not found |
237 # on the the try server. | 238 # on the the try server. |
238 build_status, status_link = request_build.GetBuildStatus( | 239 build_status, status_link = request_build.GetBuildStatus( |
239 build_num, builder_name, builder_type) | 240 build_num, builder_name, buildbot_server_url) |
240 if build_status == request_build.FAILED: | 241 if build_status == request_build.FAILED: |
241 return (None, 'Failed to produce build, log: %s' % status_link) | 242 return (None, 'Failed to produce build, log: %s' % status_link) |
242 elapsed_time = time.time() - start_time | 243 elapsed_time = time.time() - start_time |
243 if elapsed_time > max_timeout: | 244 if elapsed_time > max_timeout: |
244 return (None, 'Timed out: %ss without build' % max_timeout) | 245 return (None, 'Timed out: %ss without build' % max_timeout) |
245 | 246 |
246 logging.info('Time elapsed: %ss without build.', elapsed_time) | 247 logging.info('Time elapsed: %ss without build.', elapsed_time) |
247 time.sleep(poll_interval) | 248 time.sleep(poll_interval) |
248 # For some reason, mac bisect bots were not flushing stdout periodically. | 249 # For some reason, mac bisect bots were not flushing stdout periodically. |
249 # As a result buildbot command is timed-out. Flush stdout on all platforms | 250 # As a result buildbot command is timed-out. Flush stdout on all platforms |
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
640 bisect_utils.RunGit(['checkout', '-f', BISECT_MASTER_BRANCH]) | 641 bisect_utils.RunGit(['checkout', '-f', BISECT_MASTER_BRANCH]) |
641 bisect_utils.RunGit(['branch', '-D', BISECT_TRYJOB_BRANCH]) | 642 bisect_utils.RunGit(['branch', '-D', BISECT_TRYJOB_BRANCH]) |
642 | 643 |
643 | 644 |
644 def _TryJobSvnRepo(builder_type): | 645 def _TryJobSvnRepo(builder_type): |
645 """Returns an SVN repo to use for try jobs based on the builder type.""" | 646 """Returns an SVN repo to use for try jobs based on the builder type.""" |
646 if builder_type == fetch_build.PERF_BUILDER: | 647 if builder_type == fetch_build.PERF_BUILDER: |
647 return PERF_SVN_REPO_URL | 648 return PERF_SVN_REPO_URL |
648 if builder_type == fetch_build.FULL_BUILDER: | 649 if builder_type == fetch_build.FULL_BUILDER: |
649 return FULL_SVN_REPO_URL | 650 return FULL_SVN_REPO_URL |
651 if builder_type == fetch_build.ANDROID_CHROME_PERF_BUILDER: | |
652 return ANDROID_CHROME_SVN_REPO_URL | |
650 raise NotImplementedError('Unknown builder type "%s".' % builder_type) | 653 raise NotImplementedError('Unknown builder type "%s".' % builder_type) |
651 | 654 |
652 | 655 |
653 class BisectPerformanceMetrics(object): | 656 class BisectPerformanceMetrics(object): |
654 """This class contains functionality to perform a bisection of a range of | 657 """This class contains functionality to perform a bisection of a range of |
655 revisions to narrow down where performance regressions may have occurred. | 658 revisions to narrow down where performance regressions may have occurred. |
656 | 659 |
657 The main entry-point is the Run method. | 660 The main entry-point is the Run method. |
658 """ | 661 """ |
659 | 662 |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
807 revision: The git revision to download. | 810 revision: The git revision to download. |
808 depot: The name of a dependency repository. Should be in DEPOT_NAMES. | 811 depot: The name of a dependency repository. Should be in DEPOT_NAMES. |
809 build_type: Target build type, e.g. Release', 'Debug', 'Release_x64' etc. | 812 build_type: Target build type, e.g. Release', 'Debug', 'Release_x64' etc. |
810 create_patch: Create a patch with any locally modified files. | 813 create_patch: Create a patch with any locally modified files. |
811 | 814 |
812 Returns: | 815 Returns: |
813 True if download succeeds, otherwise False. | 816 True if download succeeds, otherwise False. |
814 """ | 817 """ |
815 patch = None | 818 patch = None |
816 patch_sha = None | 819 patch_sha = None |
817 if depot != 'chromium': | 820 if depot not in ['chromium', 'android-chrome']: |
qyearsley
2015/03/10 23:52:32
Note: a tuple is used at https://code.google.com/p
prasadv
2015/03/11 18:50:38
Done.
| |
818 # Create a DEPS patch with new revision for dependency repository. | 821 # Create a DEPS patch with new revision for dependency repository. |
819 self._CreateDEPSPatch(depot, revision) | 822 self._CreateDEPSPatch(depot, revision) |
820 create_patch = True | 823 create_patch = True |
821 | 824 |
822 if create_patch: | 825 if create_patch: |
823 revision, patch = self._CreatePatch(revision) | 826 revision, patch = self._CreatePatch(revision) |
824 | 827 |
825 if patch: | 828 if patch: |
826 # Get the SHA of the DEPS changes patch. | 829 # Get the SHA of the DEPS changes patch. |
827 patch_sha = GetSHA1HexDigest(patch) | 830 patch_sha = GetSHA1HexDigest(patch) |
(...skipping 24 matching lines...) Expand all Loading... | |
852 the DEPS, if applicable. | 855 the DEPS, if applicable. |
853 deps_patch_sha: The SHA1 hex digest of the above patch. | 856 deps_patch_sha: The SHA1 hex digest of the above patch. |
854 | 857 |
855 Returns: | 858 Returns: |
856 File path of the downloaded file if successful, otherwise None. | 859 File path of the downloaded file if successful, otherwise None. |
857 """ | 860 """ |
858 bucket_name, remote_path = fetch_build.GetBucketAndRemotePath( | 861 bucket_name, remote_path = fetch_build.GetBucketAndRemotePath( |
859 revision, builder_type=self.opts.builder_type, | 862 revision, builder_type=self.opts.builder_type, |
860 target_arch=self.opts.target_arch, | 863 target_arch=self.opts.target_arch, |
861 target_platform=self.opts.target_platform, | 864 target_platform=self.opts.target_platform, |
862 deps_patch_sha=deps_patch_sha) | 865 deps_patch_sha=deps_patch_sha, |
866 extra_src=self.opts.extra_src) | |
863 output_dir = os.path.abspath(build_dir) | 867 output_dir = os.path.abspath(build_dir) |
864 fetch_build_func = lambda: fetch_build.FetchFromCloudStorage( | 868 fetch_build_func = lambda: fetch_build.FetchFromCloudStorage( |
865 bucket_name, remote_path, output_dir) | 869 bucket_name, remote_path, output_dir) |
866 | 870 |
867 is_available = fetch_build.BuildIsAvailable(bucket_name, remote_path) | 871 is_available = fetch_build.BuildIsAvailable(bucket_name, remote_path) |
868 if is_available: | 872 if is_available: |
869 return fetch_build_func() | 873 return fetch_build_func() |
870 | 874 |
871 # When build archive doesn't exist, make a request and wait. | 875 # When build archive doesn't exist, make a request and wait. |
872 return self._RequestBuildAndWait( | 876 return self._RequestBuildAndWait( |
(...skipping 28 matching lines...) Expand all Loading... | |
901 # This ID is added to "Reason" property of the build. | 905 # This ID is added to "Reason" property of the build. |
902 build_request_id = GetSHA1HexDigest( | 906 build_request_id = GetSHA1HexDigest( |
903 '%s-%s-%s' % (git_revision, deps_patch, time.time())) | 907 '%s-%s-%s' % (git_revision, deps_patch, time.time())) |
904 | 908 |
905 # Revert any changes to DEPS file. | 909 # Revert any changes to DEPS file. |
906 bisect_utils.CheckRunGit(['reset', '--hard', 'HEAD'], cwd=self.src_cwd) | 910 bisect_utils.CheckRunGit(['reset', '--hard', 'HEAD'], cwd=self.src_cwd) |
907 | 911 |
908 builder_name, build_timeout = fetch_build.GetBuilderNameAndBuildTime( | 912 builder_name, build_timeout = fetch_build.GetBuilderNameAndBuildTime( |
909 builder_type=self.opts.builder_type, | 913 builder_type=self.opts.builder_type, |
910 target_arch=self.opts.target_arch, | 914 target_arch=self.opts.target_arch, |
911 target_platform=self.opts.target_platform) | 915 target_platform=self.opts.target_platform, |
916 extra_src=self.opts.extra_src) | |
912 | 917 |
913 try: | 918 try: |
914 _StartBuilderTryJob(self.opts.builder_type, git_revision, builder_name, | 919 _StartBuilderTryJob(self.opts.builder_type, git_revision, builder_name, |
915 job_name=build_request_id, patch=deps_patch) | 920 job_name=build_request_id, patch=deps_patch) |
916 except RunGitError as e: | 921 except RunGitError as e: |
917 logging.warn('Failed to post builder try job for revision: [%s].\n' | 922 logging.warn('Failed to post builder try job for revision: [%s].\n' |
918 'Error: %s', git_revision, e) | 923 'Error: %s', git_revision, e) |
919 return None | 924 return None |
920 | 925 |
926 # Get the buildbot master url to monitor build status. | |
927 buildbot_server_url = fetch_build.GetBuildBotUrl( | |
928 builder_type=self.opts.builder_type, | |
929 target_arch=self.opts.target_arch, | |
930 target_platform=self.opts.target_platform, | |
931 extra_src=self.opts.extra_src) | |
932 | |
921 archive_filename, error_msg = _WaitUntilBuildIsReady( | 933 archive_filename, error_msg = _WaitUntilBuildIsReady( |
922 fetch_build_func, builder_name, self.opts.builder_type, | 934 fetch_build_func, builder_name, build_request_id, build_timeout, |
923 build_request_id, build_timeout) | 935 buildbot_server_url) |
924 if not archive_filename: | 936 if not archive_filename: |
925 logging.warn('%s [revision: %s]', error_msg, git_revision) | 937 logging.warn('%s [revision: %s]', error_msg, git_revision) |
926 return archive_filename | 938 return archive_filename |
927 | 939 |
928 def _UnzipAndMoveBuildProducts(self, downloaded_file, build_dir, | 940 def _UnzipAndMoveBuildProducts(self, downloaded_file, build_dir, |
929 build_type='Release'): | 941 build_type='Release'): |
930 """Unzips the build archive and moves it to the build output directory. | 942 """Unzips the build archive and moves it to the build output directory. |
931 | 943 |
932 The build output directory is wherever the binaries are expected to | 944 The build output directory is wherever the binaries are expected to |
933 be in order to start Chrome and run tests. | 945 be in order to start Chrome and run tests. |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
999 if bisect_utils.IsWindowsHost(): | 1011 if bisect_utils.IsWindowsHost(): |
1000 return 'full-build-win32' | 1012 return 'full-build-win32' |
1001 if bisect_utils.IsLinuxHost(): | 1013 if bisect_utils.IsLinuxHost(): |
1002 return 'full-build-linux' | 1014 return 'full-build-linux' |
1003 if bisect_utils.IsMacHost(): | 1015 if bisect_utils.IsMacHost(): |
1004 return 'full-build-mac' | 1016 return 'full-build-mac' |
1005 raise NotImplementedError('Unknown platform "%s".' % sys.platform) | 1017 raise NotImplementedError('Unknown platform "%s".' % sys.platform) |
1006 | 1018 |
1007 def IsDownloadable(self, depot): | 1019 def IsDownloadable(self, depot): |
1008 """Checks if build can be downloaded based on target platform and depot.""" | 1020 """Checks if build can be downloaded based on target platform and depot.""" |
1009 if (self.opts.target_platform in ['chromium', 'android'] | 1021 if (self.opts.target_platform in ['chromium', 'android', 'android-chrome'] |
1010 and self.opts.builder_type): | 1022 and self.opts.builder_type): |
1011 return (depot == 'chromium' or | 1023 # Incase of android-chrome platform, download archives only for |
qyearsley
2015/03/10 23:52:32
Incase -> In case
prasadv
2015/03/11 18:50:38
Done.
| |
1024 # for android-chrome depot, for other depots such as chromium, v8, skia | |
qyearsley
2015/03/10 23:52:32
The first "for" can be removed (it's already on th
prasadv
2015/03/11 18:50:38
Done.
| |
1025 # etc build the binary locally. | |
qyearsley
2015/03/10 23:52:32
"etc" -> "etc.,"
prasadv
2015/03/11 18:50:38
Done.
| |
1026 if self.opts.target_platform == 'android-chrome': | |
1027 return depot == 'android-chrome' | |
qyearsley
2015/03/10 23:52:32
Indentation should be two spaces.
prasadv
2015/03/11 18:50:38
Done.
| |
1028 else: | |
1029 return (depot == 'chromium' or | |
1012 'chromium' in bisect_utils.DEPOT_DEPS_NAME[depot]['from'] or | 1030 'chromium' in bisect_utils.DEPOT_DEPS_NAME[depot]['from'] or |
1013 'v8' in bisect_utils.DEPOT_DEPS_NAME[depot]['from']) | 1031 'v8' in bisect_utils.DEPOT_DEPS_NAME[depot]['from']) |
1014 return False | 1032 return False |
1015 | 1033 |
1016 def UpdateDepsContents(self, deps_contents, depot, git_revision, deps_key): | 1034 def UpdateDepsContents(self, deps_contents, depot, git_revision, deps_key): |
1017 """Returns modified version of DEPS file contents. | 1035 """Returns modified version of DEPS file contents. |
1018 | 1036 |
1019 Args: | 1037 Args: |
1020 deps_contents: DEPS file content. | 1038 deps_contents: DEPS file content. |
1021 depot: Current depot being bisected. | 1039 depot: Current depot being bisected. |
(...skipping 1802 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2824 # bugs. If you change this, please update the perf dashboard as well. | 2842 # bugs. If you change this, please update the perf dashboard as well. |
2825 bisect_utils.OutputAnnotationStepStart('Results') | 2843 bisect_utils.OutputAnnotationStepStart('Results') |
2826 print 'Runtime Error: %s' % e | 2844 print 'Runtime Error: %s' % e |
2827 if opts.output_buildbot_annotations: | 2845 if opts.output_buildbot_annotations: |
2828 bisect_utils.OutputAnnotationStepClosed() | 2846 bisect_utils.OutputAnnotationStepClosed() |
2829 return 1 | 2847 return 1 |
2830 | 2848 |
2831 | 2849 |
2832 if __name__ == '__main__': | 2850 if __name__ == '__main__': |
2833 sys.exit(main()) | 2851 sys.exit(main()) |
OLD | NEW |