Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(290)

Side by Side Diff: tools/bisect-perf-regression.py

Issue 184293011: Make bisect script to post build request to try server. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/post_perf_builder_job.py » ('j') | tools/post_perf_builder_job.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
45 import re 45 import re
46 import shlex 46 import shlex
47 import shutil 47 import shutil
48 import StringIO 48 import StringIO
49 import subprocess 49 import subprocess
50 import sys 50 import sys
51 import time 51 import time
52 import zipfile 52 import zipfile
53 53
54 import bisect_utils 54 import bisect_utils
55 import post_perf_builder_job
56
55 57
56 try: 58 try:
57 from telemetry.page import cloud_storage 59 from telemetry.page import cloud_storage
58 except ImportError: 60 except ImportError:
59 sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'telemetry')) 61 sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'telemetry'))
60 from telemetry.page import cloud_storage 62 from telemetry.page import cloud_storage
61 63
62 # The additional repositories that might need to be bisected. 64 # The additional repositories that might need to be bisected.
63 # If the repository has any dependant repositories (such as skia/src needs 65 # If the repository has any dependant repositories (such as skia/src needs
64 # skia/include and skia/gyp to be updated), specify them in the 'depends' 66 # skia/include and skia/gyp to be updated), specify them in the 'depends'
(...skipping 1241 matching lines...) Expand 10 before | Expand all | Expand 10 after
1306 source_dir = os.path.join(build_dir, build_type) 1308 source_dir = os.path.join(build_dir, build_type)
1307 destination_dir = os.path.join(build_dir, '%s.bak' % build_type) 1309 destination_dir = os.path.join(build_dir, '%s.bak' % build_type)
1308 if restore: 1310 if restore:
1309 source_dir, destination_dir = destination_dir, source_dir 1311 source_dir, destination_dir = destination_dir, source_dir
1310 if os.path.exists(source_dir): 1312 if os.path.exists(source_dir):
1311 RmTreeAndMkDir(destination_dir, skip_makedir=True) 1313 RmTreeAndMkDir(destination_dir, skip_makedir=True)
1312 shutil.move(source_dir, destination_dir) 1314 shutil.move(source_dir, destination_dir)
1313 return destination_dir 1315 return destination_dir
1314 return None 1316 return None
1315 1317
1316 def DownloadCurrentBuild(self, sha_revision, build_type='Release'): 1318 def DownloadCurrentBuild(self, revision, build_type='Release'):
1317 """Download the build archive for the given revision. 1319 """Download the build archive for the given revision.
1318 1320
1319 Args: 1321 Args:
1320 sha_revision: The git SHA1 for the revision. 1322 revision: The SVN revision to build.
1321 build_type: Target build type ('Release', 'Debug', 'Release_x64' etc.) 1323 build_type: Target build type ('Release', 'Debug', 'Release_x64' etc.)
1322 1324
1323 Returns: 1325 Returns:
1324 True if download succeeds, otherwise False. 1326 True if download succeeds, otherwise False.
1325 """ 1327 """
1326 # Get SVN revision for the given SHA, since builds are archived using SVN
1327 # revision.
1328 revision = self.source_control.SVNFindRev(sha_revision)
1329 if not revision:
1330 raise RuntimeError(
1331 'Failed to determine SVN revision for %s' % sha_revision)
1332
1333 abs_build_dir = os.path.abspath( 1328 abs_build_dir = os.path.abspath(
1334 self.builder.GetBuildOutputDirectory(self.opts, self.src_cwd)) 1329 self.builder.GetBuildOutputDirectory(self.opts, self.src_cwd))
1335 target_build_output_dir = os.path.join(abs_build_dir, build_type) 1330 target_build_output_dir = os.path.join(abs_build_dir, build_type)
1336 # Get build target architecture. 1331 # Get build target architecture.
1337 build_arch = self.opts.target_arch 1332 build_arch = self.opts.target_arch
1338 # File path of the downloaded archive file. 1333 # File path of the downloaded archive file.
1339 archive_file_dest = os.path.join(abs_build_dir, 1334 archive_file_dest = os.path.join(abs_build_dir,
1340 GetZipFileName(revision, build_arch)) 1335 GetZipFileName(revision, build_arch))
1341 if FetchFromCloudStorage(self.opts.gs_bucket, 1336 remote_build = GetRemoteBuildPath(revision, build_arch)
1342 GetRemoteBuildPath(revision, build_arch), 1337 fetch_build_func = lambda: FetchFromCloudStorage(self.opts.gs_bucket,
1343 abs_build_dir): 1338 remote_build,
1344 # Generic name for the archive, created when archive file is extracted. 1339 abs_build_dir)
1345 output_dir = os.path.join(abs_build_dir, 1340 if not fetch_build_func():
1346 GetZipFileName(target_arch=build_arch)) 1341 if not self.PostBuildRequestAndWait(revision, condition=fetch_build_func):
1347 # Unzip build archive directory. 1342 raise RuntimeError('Somewthing went wrong while processing build'
1348 try: 1343 'request for: %s' % revision)
1344
1345 # Generic name for the archive, created when archive file is extracted.
1346 output_dir = os.path.join(abs_build_dir,
1347 GetZipFileName(target_arch=build_arch))
1348 # Unzip build archive directory.
1349 try:
1350 RmTreeAndMkDir(output_dir, skip_makedir=True)
1351 ExtractZip(archive_file_dest, abs_build_dir)
1352 if os.path.exists(output_dir):
1353 self.BackupOrRestoreOutputdirectory(restore=False)
1354 print 'Moving build from %s to %s' % (
1355 output_dir, target_build_output_dir)
1356 shutil.move(output_dir, target_build_output_dir)
1357 return True
1358 raise IOError('Missing extracted folder %s ' % output_dir)
1359 except e:
1360 print 'Somewthing went wrong while extracting archive file: %s' % e
1361 self.BackupOrRestoreOutputdirectory(restore=True)
1362 # Cleanup any leftovers from unzipping.
1363 if os.path.exists(output_dir):
1349 RmTreeAndMkDir(output_dir, skip_makedir=True) 1364 RmTreeAndMkDir(output_dir, skip_makedir=True)
1350 ExtractZip(archive_file_dest, abs_build_dir) 1365 finally:
1351 if os.path.exists(output_dir): 1366 # Delete downloaded archive
1352 self.BackupOrRestoreOutputdirectory(restore=False) 1367 if os.path.exists(archive_file_dest):
1353 print 'Moving build from %s to %s' % ( 1368 os.remove(archive_file_dest)
1354 output_dir, target_build_output_dir) 1369 return False
1355 shutil.move(output_dir, target_build_output_dir) 1370
1356 return True 1371
qyearsley 2014/03/06 23:40:45 Style: Both DownloadCurrentBuild and PostBuildRequ
prasadv 2014/03/07 01:07:44 Done.
1357 raise IOError('Missing extracted folder %s ' % output_dir) 1372 def PostBuildRequestAndWait(self, revision, condition, patch=None):
1358 except e: 1373 """POSTs the build request job to the tryserver instance."""
1359 print 'Somewthing went wrong while extracting archive file: %s' % e 1374
1360 self.BackupOrRestoreOutputdirectory(restore=True) 1375 def GetBuilderNameAndBuildTime(target_arch='ia32'):
1361 # Cleanup any leftovers from unzipping. 1376 """Gets builder name and buildtime in seconds based on platform."""
1362 if os.path.exists(output_dir): 1377 if IsWindows():
1363 RmTreeAndMkDir(output_dir, skip_makedir=True) 1378 if Is64BitWindows() and target_arch == 'x64':
1364 finally: 1379 return ('Win x64 Bisect Builder', 3600) # 60 mins
qyearsley 2014/03/06 23:40:45 Style nit: The style guide suggests spacing inline
prasadv 2014/03/07 01:07:44 Done.
1365 # Delete downloaded archive 1380 return ('Win Bisect Builder', 3600)
1366 if os.path.exists(archive_file_dest): 1381 if IsLinux():
1367 os.remove(archive_file_dest) 1382 return ('Linux Bisect Builder', 1800) # 30 mins
1383 if IsMac():
1384 return ('Mac Bisect Builder', 2700) # 45 mins
1385 raise NotImplementedError('Unsupported Platform "%s".' % sys.platform)
1386 if not condition:
1387 return False
1388
1389 bot_name, build_timeout = GetBuilderNameAndBuildTime(self.opts.target_arch)
1390
1391 # Creates a try job description.
1392 job_args = {'host': self.opts.host,
1393 'port': self.opts.port,
1394 'revision': revision,
1395 'bot': bot_name,
1396 'name': 'Bisect Job-%s' % revision
1397 }
1398 # Update patch information if supplied.
1399 if patch:
1400 job_args['patch'] = patch
1401 #Posts job to build the revision on the server.
qyearsley 2014/03/06 23:40:45 Style: Should add space after #.
prasadv 2014/03/07 01:07:44 Done.
1402 if post_perf_builder_job.PostTryJob(job_args):
1403 poll_interval = 60
qyearsley 2014/03/06 23:40:45 Style: Unnecessary spaces.
prasadv 2014/03/07 01:07:44 Done.
1404 start_time = time.time()
1405 while True:
1406 res = condition()
1407 if res:
1408 return res
1409 elapsed_time = time.time() - start_time
1410 if elapsed_time > build_timeout:
1411 raise RuntimeError('Timed out while waiting %ds for %s build.' %
1412 (build_timeout, revision))
1413 print ('Time elapsed: %ss, still waiting for %s build' %
1414 (elapsed_time, revision))
1415 time.sleep(poll_interval)
1368 return False 1416 return False
1369 1417
1370 def BuildCurrentRevision(self, depot, revision=None): 1418 def BuildCurrentRevision(self, depot, revision=None):
1371 """Builds chrome and performance_ui_tests on the current revision. 1419 """Builds chrome and performance_ui_tests on the current revision.
1372 1420
1373 Returns: 1421 Returns:
1374 True if the build was successful. 1422 True if the build was successful.
1375 """ 1423 """
1376 if self.opts.debug_ignore_build: 1424 if self.opts.debug_ignore_build:
1377 return True 1425 return True
1378 cwd = os.getcwd() 1426 cwd = os.getcwd()
1379 os.chdir(self.src_cwd) 1427 os.chdir(self.src_cwd)
1380 # Fetch build archive for the given revision from the cloud storage when 1428 # Fetch build archive for the given revision from the cloud storage when
1381 # the storage bucket is passed. 1429 # the storage bucket is passed.
1382 if depot == 'chromium' and self.opts.gs_bucket and revision: 1430 if depot == 'chromium' and self.opts.gs_bucket and revision:
1431 # Get SVN revision for the given SHA, since builds are archived using SVN
1432 # revision.
1433 revision = self.source_control.SVNFindRev(revision)
1434 if not revision:
1435 raise RuntimeError(
1436 'Failed to determine SVN revision for %s' % sha_revision)
1383 if self.DownloadCurrentBuild(revision): 1437 if self.DownloadCurrentBuild(revision):
1384 os.chdir(cwd) 1438 os.chdir(cwd)
1385 return True 1439 return True
1386 raise RuntimeError('Failed to download build archive for revision %s.\n' 1440 raise RuntimeError('Failed to download build archive for revision %s.\n'
1387 'Unfortunately, bisection couldn\'t continue any ' 1441 'Unfortunately, bisection couldn\'t continue any '
1388 'further. Please try running script without ' 1442 'further. Please try running script without '
1389 '--gs_bucket flag to produce local builds.' % revision) 1443 '--gs_bucket flag to produce local builds.' % revision)
1390 1444
1445
1391 build_success = self.builder.Build(depot, self.opts) 1446 build_success = self.builder.Build(depot, self.opts)
1392 os.chdir(cwd) 1447 os.chdir(cwd)
1393 return build_success 1448 return build_success
1394 1449
1395 def RunGClientHooks(self): 1450 def RunGClientHooks(self):
1396 """Runs gclient with runhooks command. 1451 """Runs gclient with runhooks command.
1397 1452
1398 Returns: 1453 Returns:
1399 True if gclient reports no errors. 1454 True if gclient reports no errors.
1400 """ 1455 """
(...skipping 1614 matching lines...) Expand 10 before | Expand all | Expand 10 after
3015 self.command = None 3070 self.command = None
3016 self.output_buildbot_annotations = None 3071 self.output_buildbot_annotations = None
3017 self.no_custom_deps = False 3072 self.no_custom_deps = False
3018 self.working_directory = None 3073 self.working_directory = None
3019 self.extra_src = None 3074 self.extra_src = None
3020 self.debug_ignore_build = None 3075 self.debug_ignore_build = None
3021 self.debug_ignore_sync = None 3076 self.debug_ignore_sync = None
3022 self.debug_ignore_perf_test = None 3077 self.debug_ignore_perf_test = None
3023 self.gs_bucket = None 3078 self.gs_bucket = None
3024 self.target_arch = 'ia32' 3079 self.target_arch = 'ia32'
3080 self.host = None
3081 self.port = None
3025 3082
3026 def _CreateCommandLineParser(self): 3083 def _CreateCommandLineParser(self):
3027 """Creates a parser with bisect options. 3084 """Creates a parser with bisect options.
3028 3085
3029 Returns: 3086 Returns:
3030 An instance of optparse.OptionParser. 3087 An instance of optparse.OptionParser.
3031 """ 3088 """
3032 usage = ('%prog [options] [-- chromium-options]\n' 3089 usage = ('%prog [options] [-- chromium-options]\n'
3033 'Perform binary search on revision history to find a minimal ' 3090 'Perform binary search on revision history to find a minimal '
3034 'range of revisions where a peformance metric regressed.\n') 3091 'range of revisions where a peformance metric regressed.\n')
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
3128 type='str', 3185 type='str',
3129 help=('Name of Google Storage bucket to upload or ' 3186 help=('Name of Google Storage bucket to upload or '
3130 'download build. e.g., chrome-perf')) 3187 'download build. e.g., chrome-perf'))
3131 group.add_option('--target_arch', 3188 group.add_option('--target_arch',
3132 type='choice', 3189 type='choice',
3133 choices=['ia32', 'x64', 'arm'], 3190 choices=['ia32', 'x64', 'arm'],
3134 default='ia32', 3191 default='ia32',
3135 dest='target_arch', 3192 dest='target_arch',
3136 help=('The target build architecture. Choices are "ia32" ' 3193 help=('The target build architecture. Choices are "ia32" '
3137 '(default), "x64" or "arm".')) 3194 '(default), "x64" or "arm".'))
3138 3195 group.add_option('--host',
3196 dest='host',
3197 type='str',
3198 help=('Host address of server to produce build by posting'
3199 ' try job request.'))
3200 group.add_option('--port',
3201 dest='port',
3202 type='int',
3203 help=('HTTP port of the server to produce build by posting'
3204 ' try job request.'))
3139 parser.add_option_group(group) 3205 parser.add_option_group(group)
3140 3206
3141 group = optparse.OptionGroup(parser, 'Debug options') 3207 group = optparse.OptionGroup(parser, 'Debug options')
3142 group.add_option('--debug_ignore_build', 3208 group.add_option('--debug_ignore_build',
3143 action="store_true", 3209 action="store_true",
3144 help='DEBUG: Don\'t perform builds.') 3210 help='DEBUG: Don\'t perform builds.')
3145 group.add_option('--debug_ignore_sync', 3211 group.add_option('--debug_ignore_sync',
3146 action="store_true", 3212 action="store_true",
3147 help='DEBUG: Don\'t perform syncs.') 3213 help='DEBUG: Don\'t perform syncs.')
3148 group.add_option('--debug_ignore_perf_test', 3214 group.add_option('--debug_ignore_perf_test',
3149 action="store_true", 3215 action="store_true",
3150 help='DEBUG: Don\'t perform performance tests.') 3216 help='DEBUG: Don\'t perform performance tests.')
3151 parser.add_option_group(group) 3217 parser.add_option_group(group)
3152
3153
3154 return parser 3218 return parser
3155 3219
3156 def ParseCommandLine(self): 3220 def ParseCommandLine(self):
3157 """Parses the command line for bisect options.""" 3221 """Parses the command line for bisect options."""
3158 parser = self._CreateCommandLineParser() 3222 parser = self._CreateCommandLineParser()
3159 (opts, args) = parser.parse_args() 3223 (opts, args) = parser.parse_args()
3160 3224
3161 try: 3225 try:
3162 if not opts.command: 3226 if not opts.command:
3163 raise RuntimeError('missing required parameter: --command') 3227 raise RuntimeError('missing required parameter: --command')
3164 3228
3165 if not opts.good_revision: 3229 if not opts.good_revision:
3166 raise RuntimeError('missing required parameter: --good_revision') 3230 raise RuntimeError('missing required parameter: --good_revision')
3167 3231
3168 if not opts.bad_revision: 3232 if not opts.bad_revision:
3169 raise RuntimeError('missing required parameter: --bad_revision') 3233 raise RuntimeError('missing required parameter: --bad_revision')
3170 3234
3171 if not opts.metric: 3235 if not opts.metric:
3172 raise RuntimeError('missing required parameter: --metric') 3236 raise RuntimeError('missing required parameter: --metric')
3173 3237
3174 if opts.gs_bucket: 3238 if opts.gs_bucket:
3175 if not cloud_storage.List(opts.gs_bucket): 3239 if not cloud_storage.List(opts.gs_bucket):
3176 raise RuntimeError('Invalid Google Storage URL: [%s]', e) 3240 raise RuntimeError('Invalid Google Storage: gs://%s' % opts.gs_bucket)
3177 3241 if not opts.host:
3242 raise RuntimeError('Must specify try server hostname, when '
3243 'gs_bucket is used: --host')
3244 if not opts.port:
3245 raise RuntimeError('Must specify try server port number, when '
3246 'gs_bucket is used: --port')
3178 if opts.target_platform == 'cros': 3247 if opts.target_platform == 'cros':
3179 # Run sudo up front to make sure credentials are cached for later. 3248 # Run sudo up front to make sure credentials are cached for later.
3180 print 'Sudo is required to build cros:' 3249 print 'Sudo is required to build cros:'
3181 print 3250 print
3182 RunProcess(['sudo', 'true']) 3251 RunProcess(['sudo', 'true'])
3183 3252
3184 if not opts.cros_board: 3253 if not opts.cros_board:
3185 raise RuntimeError('missing required parameter: --cros_board') 3254 raise RuntimeError('missing required parameter: --cros_board')
3186 3255
3187 if not opts.cros_remote_ip: 3256 if not opts.cros_remote_ip:
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
3272 3341
3273 if not source_control: 3342 if not source_control:
3274 raise RuntimeError("Sorry, only the git workflow is supported at the " 3343 raise RuntimeError("Sorry, only the git workflow is supported at the "
3275 "moment.") 3344 "moment.")
3276 3345
3277 # gClient sync seems to fail if you're not in master branch. 3346 # gClient sync seems to fail if you're not in master branch.
3278 if (not source_control.IsInProperBranch() and 3347 if (not source_control.IsInProperBranch() and
3279 not opts.debug_ignore_sync and 3348 not opts.debug_ignore_sync and
3280 not opts.working_directory): 3349 not opts.working_directory):
3281 raise RuntimeError("You must switch to master branch to run bisection.") 3350 raise RuntimeError("You must switch to master branch to run bisection.")
3282
3283 bisect_test = BisectPerformanceMetrics(source_control, opts) 3351 bisect_test = BisectPerformanceMetrics(source_control, opts)
3284 try: 3352 try:
3285 bisect_results = bisect_test.Run(opts.command, 3353 bisect_results = bisect_test.Run(opts.command,
3286 opts.bad_revision, 3354 opts.bad_revision,
3287 opts.good_revision, 3355 opts.good_revision,
3288 opts.metric) 3356 opts.metric)
3289 if bisect_results['error']: 3357 if bisect_results['error']:
3290 raise RuntimeError(bisect_results['error']) 3358 raise RuntimeError(bisect_results['error'])
3291 bisect_test.FormatAndPrintResults(bisect_results) 3359 bisect_test.FormatAndPrintResults(bisect_results)
3292 return 0 3360 return 0
3293 finally: 3361 finally:
3294 bisect_test.PerformCleanup() 3362 bisect_test.PerformCleanup()
3295 except RuntimeError, e: 3363 except RuntimeError, e:
3296 if opts.output_buildbot_annotations: 3364 if opts.output_buildbot_annotations:
3297 # The perf dashboard scrapes the "results" step in order to comment on 3365 # The perf dashboard scrapes the "results" step in order to comment on
3298 # bugs. If you change this, please update the perf dashboard as well. 3366 # bugs. If you change this, please update the perf dashboard as well.
3299 bisect_utils.OutputAnnotationStepStart('Results') 3367 bisect_utils.OutputAnnotationStepStart('Results')
3300 print 'Error: %s' % e.message 3368 print 'Error: %s' % e.message
3301 if opts.output_buildbot_annotations: 3369 if opts.output_buildbot_annotations:
3302 bisect_utils.OutputAnnotationStepClosed() 3370 bisect_utils.OutputAnnotationStepClosed()
3303 return 1 3371 return 1
3304 3372
3305 if __name__ == '__main__': 3373 if __name__ == '__main__':
3306 sys.exit(main()) 3374 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | tools/post_perf_builder_job.py » ('j') | tools/post_perf_builder_job.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698