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 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 BUILD_RESULT_FAIL = 1 | 160 BUILD_RESULT_FAIL = 1 |
161 BUILD_RESULT_SKIPPED = 2 | 161 BUILD_RESULT_SKIPPED = 2 |
162 | 162 |
163 # Maximum time in seconds to wait after posting build request to the try server. | 163 # Maximum time in seconds to wait after posting build request to the try server. |
164 # TODO: Change these values based on the actual time taken by buildbots on | 164 # TODO: Change these values based on the actual time taken by buildbots on |
165 # the try server. | 165 # the try server. |
166 MAX_MAC_BUILD_TIME = 14400 | 166 MAX_MAC_BUILD_TIME = 14400 |
167 MAX_WIN_BUILD_TIME = 14400 | 167 MAX_WIN_BUILD_TIME = 14400 |
168 MAX_LINUX_BUILD_TIME = 14400 | 168 MAX_LINUX_BUILD_TIME = 14400 |
169 | 169 |
170 # The confidence percentage at which confidence can be consider "high". | 170 # The percentage at which confidence is considered high. |
171 HIGH_CONFIDENCE = 95 | 171 HIGH_CONFIDENCE = 95 |
172 | 172 |
173 # Patch template to add a new file, DEPS.sha under src folder. | 173 # Patch template to add a new file, DEPS.sha under src folder. |
174 # This file contains SHA1 value of the DEPS changes made while bisecting | 174 # This file contains SHA1 value of the DEPS changes made while bisecting |
175 # dependency repositories. This patch send along with DEPS patch to try server. | 175 # dependency repositories. This patch send along with DEPS patch to try server. |
176 # When a build requested is posted with a patch, bisect builders on try server, | 176 # When a build requested is posted with a patch, bisect builders on try server, |
177 # once build is produced, it reads SHA value from this file and appends it | 177 # once build is produced, it reads SHA value from this file and appends it |
178 # to build archive filename. | 178 # to build archive filename. |
179 DEPS_SHA_PATCH = """diff --git DEPS.sha DEPS.sha | 179 DEPS_SHA_PATCH = """diff --git DEPS.sha DEPS.sha |
180 new file mode 100644 | 180 new file mode 100644 |
(...skipping 1061 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1242 Returns: | 1242 Returns: |
1243 Path to backup or restored location as string. otherwise None if it fails. | 1243 Path to backup or restored location as string. otherwise None if it fails. |
1244 """ | 1244 """ |
1245 build_dir = os.path.abspath( | 1245 build_dir = os.path.abspath( |
1246 builder.GetBuildOutputDirectory(self.opts, self.src_cwd)) | 1246 builder.GetBuildOutputDirectory(self.opts, self.src_cwd)) |
1247 source_dir = os.path.join(build_dir, build_type) | 1247 source_dir = os.path.join(build_dir, build_type) |
1248 destination_dir = os.path.join(build_dir, '%s.bak' % build_type) | 1248 destination_dir = os.path.join(build_dir, '%s.bak' % build_type) |
1249 if restore: | 1249 if restore: |
1250 source_dir, destination_dir = destination_dir, source_dir | 1250 source_dir, destination_dir = destination_dir, source_dir |
1251 if os.path.exists(source_dir): | 1251 if os.path.exists(source_dir): |
1252 RmTreeAndMkDir(destination_dir, skip_makedir=True) | 1252 RemoveDirectoryTree(destination_dir) |
1253 shutil.move(source_dir, destination_dir) | 1253 shutil.move(source_dir, destination_dir) |
1254 return destination_dir | 1254 return destination_dir |
1255 return None | 1255 return None |
1256 | 1256 |
1257 def GetBuildArchiveForRevision(self, revision, gs_bucket, target_arch, | 1257 def GetBuildArchiveForRevision(self, revision, gs_bucket, target_arch, |
1258 patch_sha, out_dir): | 1258 patch_sha, out_dir): |
1259 """Checks and downloads build archive for a given revision. | 1259 """Checks and downloads build archive for a given revision. |
1260 | 1260 |
1261 Checks for build archive with Git hash or SVN revision. If either of the | 1261 Checks for build archive with Git hash or SVN revision. If either of the |
1262 file exists, then downloads the archive file. | 1262 file exists, then downloads the archive file. |
(...skipping 19 matching lines...) Expand all Loading... |
1282 # Source archive file path on cloud storage using SVN revision. | 1282 # Source archive file path on cloud storage using SVN revision. |
1283 source_file = GetRemoteBuildPath( | 1283 source_file = GetRemoteBuildPath( |
1284 commit_position, self.opts.target_platform, target_arch, patch_sha) | 1284 commit_position, self.opts.target_platform, target_arch, patch_sha) |
1285 return FetchFromCloudStorage(gs_bucket, source_file, out_dir) | 1285 return FetchFromCloudStorage(gs_bucket, source_file, out_dir) |
1286 return downloaded_archive | 1286 return downloaded_archive |
1287 | 1287 |
1288 def DownloadCurrentBuild(self, revision, depot, build_type='Release'): | 1288 def DownloadCurrentBuild(self, revision, depot, build_type='Release'): |
1289 """Downloads the build archive for the given revision. | 1289 """Downloads the build archive for the given revision. |
1290 | 1290 |
1291 Args: | 1291 Args: |
1292 revision: The Git revision to download or build. | 1292 revision: The git revision to download or build. |
1293 build_type: Target build type ('Release', 'Debug', 'Release_x64' etc.) | 1293 depot: The name of a dependency repository. Should be in DEPOT_NAMES. |
1294 patch: A DEPS patch (used while bisecting 3rd party repositories). | 1294 build_type: Target build type, e.g. Release', 'Debug', 'Release_x64' etc. |
1295 | 1295 |
1296 Returns: | 1296 Returns: |
1297 True if download succeeds, otherwise False. | 1297 True if download succeeds, otherwise False. |
1298 """ | 1298 """ |
1299 patch = None | 1299 patch = None |
1300 patch_sha = None | 1300 patch_sha = None |
1301 if depot != 'chromium': | 1301 if depot != 'chromium': |
1302 # Create a DEPS patch with new revision for dependency repository. | 1302 # Create a DEPS patch with new revision for dependency repository. |
1303 revision, patch = self.CreateDEPSPatch(depot, revision) | 1303 revision, patch = self.CreateDEPSPatch(depot, revision) |
1304 | 1304 |
1305 if patch: | 1305 if patch: |
1306 # Get the SHA of the DEPS changes patch. | 1306 # Get the SHA of the DEPS changes patch. |
1307 patch_sha = GetSHA1HexDigest(patch) | 1307 patch_sha = GetSHA1HexDigest(patch) |
1308 | 1308 |
1309 # Update the DEPS changes patch with a patch to create a new file named | 1309 # Update the DEPS changes patch with a patch to create a new file named |
1310 # 'DEPS.sha' and add patch_sha evaluated above to it. | 1310 # 'DEPS.sha' and add patch_sha evaluated above to it. |
1311 patch = '%s\n%s' % (patch, DEPS_SHA_PATCH % {'deps_sha': patch_sha}) | 1311 patch = '%s\n%s' % (patch, DEPS_SHA_PATCH % {'deps_sha': patch_sha}) |
1312 | 1312 |
1313 # Get Build output directory | 1313 # Get build output directory. |
1314 abs_build_dir = os.path.abspath( | 1314 build_dir = builder.GetBuildOutputDirectory(self.opts, self.src_cwd) |
1315 builder.GetBuildOutputDirectory(self.opts, self.src_cwd)) | 1315 abs_build_dir = os.path.abspath(build_dir) |
1316 | 1316 |
1317 fetch_build_func = lambda: self.GetBuildArchiveForRevision( | 1317 fetch_build_func = lambda: self.GetBuildArchiveForRevision( |
1318 revision, self.opts.gs_bucket, self.opts.target_arch, | 1318 revision, self.opts.gs_bucket, self.opts.target_arch, |
1319 patch_sha, abs_build_dir) | 1319 patch_sha, abs_build_dir) |
1320 | 1320 |
1321 # Downloaded archive file path, downloads build archive for given revision. | 1321 # Downloaded archive file path, downloads build archive for given revision. |
1322 downloaded_file = fetch_build_func() | 1322 downloaded_file = fetch_build_func() |
1323 | 1323 |
1324 # When build archive doesn't exists, post a build request to tryserver | 1324 # When build archive doesn't exists, post a build request to tryserver |
1325 # and wait for the build to be produced. | 1325 # and wait for the build to be produced. |
1326 if not downloaded_file: | 1326 if not downloaded_file: |
1327 downloaded_file = self.PostBuildRequestAndWait( | 1327 downloaded_file = self.PostBuildRequestAndWait( |
1328 revision, fetch_build=fetch_build_func, patch=patch) | 1328 revision, fetch_build=fetch_build_func, patch=patch) |
1329 if not downloaded_file: | 1329 if not downloaded_file: |
1330 return False | 1330 return False |
1331 | 1331 |
1332 # Generic name for the archive, created when archive file is extracted. | 1332 # Generic name for the archive, created when archive file is extracted. |
1333 output_dir = os.path.join( | 1333 output_dir = os.path.join( |
1334 abs_build_dir, GetZipFileName(target_arch=self.opts.target_arch)) | 1334 abs_build_dir, GetZipFileName(target_arch=self.opts.target_arch)) |
| 1335 |
1335 # Unzip build archive directory. | 1336 # Unzip build archive directory. |
1336 try: | 1337 try: |
1337 RmTreeAndMkDir(output_dir, skip_makedir=True) | 1338 RemoveDirectoryTree(output_dir) |
1338 self.BackupOrRestoreOutputDirectory(restore=False) | 1339 self.BackupOrRestoreOutputDirectory(restore=False) |
1339 # Build output directory based on target(e.g. out/Release, out/Debug). | 1340 # Build output directory based on target(e.g. out/Release, out/Debug). |
1340 target_build_output_dir = os.path.join(abs_build_dir, build_type) | 1341 target_build_output_dir = os.path.join(abs_build_dir, build_type) |
1341 ExtractZip(downloaded_file, abs_build_dir) | 1342 ExtractZip(downloaded_file, abs_build_dir) |
1342 if not os.path.exists(output_dir): | 1343 if not os.path.exists(output_dir): |
1343 # Due to recipe changes, the builds extract folder contains | 1344 # Due to recipe changes, the builds extract folder contains |
1344 # out/Release instead of full-build-<platform>/Release. | 1345 # out/Release instead of full-build-<platform>/Release. |
1345 if os.path.exists(os.path.join(abs_build_dir, 'out', build_type)): | 1346 if os.path.exists(os.path.join(abs_build_dir, 'out', build_type)): |
1346 output_dir = os.path.join(abs_build_dir, 'out', build_type) | 1347 output_dir = os.path.join(abs_build_dir, 'out', build_type) |
1347 else: | 1348 else: |
1348 raise IOError('Missing extracted folder %s ' % output_dir) | 1349 raise IOError('Missing extracted folder %s ' % output_dir) |
1349 | 1350 |
1350 print 'Moving build from %s to %s' % ( | 1351 print 'Moving build from %s to %s' % ( |
1351 output_dir, target_build_output_dir) | 1352 output_dir, target_build_output_dir) |
1352 shutil.move(output_dir, target_build_output_dir) | 1353 shutil.move(output_dir, target_build_output_dir) |
1353 return True | 1354 return True |
1354 except Exception as e: | 1355 except Exception as e: |
1355 print 'Something went wrong while extracting archive file: %s' % e | 1356 print 'Something went wrong while extracting archive file: %s' % e |
1356 self.BackupOrRestoreOutputDirectory(restore=True) | 1357 self.BackupOrRestoreOutputDirectory(restore=True) |
1357 # Cleanup any leftovers from unzipping. | 1358 # Cleanup any leftovers from unzipping. |
1358 if os.path.exists(output_dir): | 1359 if os.path.exists(output_dir): |
1359 RmTreeAndMkDir(output_dir, skip_makedir=True) | 1360 RemoveDirectoryTree(output_dir) |
1360 finally: | 1361 finally: |
1361 # Delete downloaded archive | 1362 # Delete downloaded archive |
1362 if os.path.exists(downloaded_file): | 1363 if os.path.exists(downloaded_file): |
1363 os.remove(downloaded_file) | 1364 os.remove(downloaded_file) |
1364 return False | 1365 return False |
1365 | 1366 |
1366 def PostBuildRequestAndWait(self, git_revision, fetch_build, patch=None): | 1367 def PostBuildRequestAndWait(self, git_revision, fetch_build, patch=None): |
1367 """POSTs the build request job to the try server instance. | 1368 """POSTs the build request job to the try server instance. |
1368 | 1369 |
1369 A try job build request is posted to tryserver.chromium.perf master, | 1370 A try job build request is posted to tryserver.chromium.perf master, |
(...skipping 1512 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2882 opts: The options parsed from the command line. | 2883 opts: The options parsed from the command line. |
2883 | 2884 |
2884 Returns: | 2885 Returns: |
2885 True if the platform and build system are supported. | 2886 True if the platform and build system are supported. |
2886 """ | 2887 """ |
2887 # Haven't tested the script out on any other platforms yet. | 2888 # Haven't tested the script out on any other platforms yet. |
2888 supported = ['posix', 'nt'] | 2889 supported = ['posix', 'nt'] |
2889 return os.name in supported | 2890 return os.name in supported |
2890 | 2891 |
2891 | 2892 |
2892 def RmTreeAndMkDir(path_to_dir, skip_makedir=False): | 2893 def RemakeDirectoryTree(path_to_dir): |
2893 """Removes the directory tree specified, and then creates an empty | 2894 """Removes a directory tree and replaces it with an empty one. |
2894 directory in the same location (if not specified to skip). | |
2895 | 2895 |
2896 Args: | 2896 Returns True if successful, False otherwise. |
2897 path_to_dir: Path to the directory tree. | 2897 """ |
2898 skip_makedir: Whether to skip creating empty directory, default is False. | 2898 if not RemoveDirectoryTree(path_to_dir): |
| 2899 return False |
| 2900 return MaybeMakeDirectory(path_to_dir) |
2899 | 2901 |
2900 Returns: | 2902 |
2901 True if successful, False if an error occurred. | 2903 def RemoveDirectoryTree(path_to_dir): |
2902 """ | 2904 """Removes a directory tree. Returns True if successful or False otherwise.""" |
2903 try: | 2905 try: |
2904 if os.path.exists(path_to_dir): | 2906 if os.path.exists(path_to_dir): |
2905 shutil.rmtree(path_to_dir) | 2907 shutil.rmtree(path_to_dir) |
2906 except OSError, e: | 2908 except OSError, e: |
2907 if e.errno != errno.ENOENT: | 2909 if e.errno != errno.ENOENT: |
2908 return False | 2910 return False |
2909 | |
2910 if not skip_makedir: | |
2911 return MaybeMakeDirectory(path_to_dir) | |
2912 | |
2913 return True | 2911 return True |
2914 | 2912 |
2915 | 2913 |
2916 def RemoveBuildFiles(build_type): | 2914 def RemoveBuildFiles(build_type): |
2917 """Removes build files from previous runs.""" | 2915 """Removes build files from previous runs.""" |
2918 if RmTreeAndMkDir(os.path.join('out', build_type)): | 2916 out_dir = os.path.join('out', build_type) |
2919 if RmTreeAndMkDir(os.path.join('build', build_type)): | 2917 build_dir = os.path.join('build', build_type) |
2920 return True | 2918 return RemakeDirectoryTree(out_dir) and RemakeDirectoryTree(build_dir) |
2921 return False | |
2922 | 2919 |
2923 | 2920 |
2924 class BisectOptions(object): | 2921 class BisectOptions(object): |
2925 """Options to be used when running bisection.""" | 2922 """Options to be used when running bisection.""" |
2926 def __init__(self): | 2923 def __init__(self): |
2927 super(BisectOptions, self).__init__() | 2924 super(BisectOptions, self).__init__() |
2928 | 2925 |
2929 self.target_platform = 'chromium' | 2926 self.target_platform = 'chromium' |
2930 self.build_preference = None | 2927 self.build_preference = None |
2931 self.good_revision = None | 2928 self.good_revision = None |
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3257 # bugs. If you change this, please update the perf dashboard as well. | 3254 # bugs. If you change this, please update the perf dashboard as well. |
3258 bisect_utils.OutputAnnotationStepStart('Results') | 3255 bisect_utils.OutputAnnotationStepStart('Results') |
3259 print 'Error: %s' % e.message | 3256 print 'Error: %s' % e.message |
3260 if opts.output_buildbot_annotations: | 3257 if opts.output_buildbot_annotations: |
3261 bisect_utils.OutputAnnotationStepClosed() | 3258 bisect_utils.OutputAnnotationStepClosed() |
3262 return 1 | 3259 return 1 |
3263 | 3260 |
3264 | 3261 |
3265 if __name__ == '__main__': | 3262 if __name__ == '__main__': |
3266 sys.exit(main()) | 3263 sys.exit(main()) |
OLD | NEW |