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

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 def PostBuildRequestAndWait(self, revision, condition, patch=None):
1357 raise IOError('Missing extracted folder %s ' % output_dir) 1372 """POSTs the build request job to the tryserver instance."""
1358 except e: 1373
1359 print 'Somewthing went wrong while extracting archive file: %s' % e 1374 def GetBuilderNameAndBuildTime(target_arch='ia32'):
1360 self.BackupOrRestoreOutputdirectory(restore=True) 1375 """Gets builder name and buildtime in seconds based on platform."""
1361 # Cleanup any leftovers from unzipping. 1376 if IsWindows():
1362 if os.path.exists(output_dir): 1377 if Is64BitWindows() and target_arch == 'x64':
1363 RmTreeAndMkDir(output_dir, skip_makedir=True) 1378 return ('Win x64 Bisect Builder', 3600)
1364 finally: 1379 return ('Win Bisect Builder', 3600)
1365 # Delete downloaded archive 1380 if IsLinux():
1366 if os.path.exists(archive_file_dest): 1381 return ('Linux Bisect Builder', 1800)
1367 os.remove(archive_file_dest) 1382 if IsMac():
1383 return ('Mac Bisect Builder', 2700)
1384 raise NotImplementedError('Unsupported Platform "%s".' % sys.platform)
1385 if not condition:
1386 return False
1387
1388 bot_name, build_timeout = GetBuilderNameAndBuildTime(self.opts.target_arch)
1389
1390 # Creates a try job description.
1391 job_args = {'host': self.opts.host,
1392 'port': self.opts.port,
1393 'revision': revision,
1394 'bot': bot_name,
1395 'name': 'Bisect Job-%s' % revision
1396 }
1397 # Update patch information if supplied.
1398 if patch:
1399 job_args['patch'] = patch
1400 # Posts job to build the revision on the server.
1401 if post_perf_builder_job.PostTryJob(job_args):
1402 poll_interval = 60
1403 start_time = time.time()
1404 while True:
1405 res = condition()
1406 if res:
1407 return res
1408 elapsed_time = time.time() - start_time
1409 if elapsed_time > build_timeout:
1410 raise RuntimeError('Timed out while waiting %ds for %s build.' %
1411 (build_timeout, revision))
1412 print ('Time elapsed: %ss, still waiting for %s build' %
1413 (elapsed_time, revision))
1414 time.sleep(poll_interval)
1368 return False 1415 return False
1369 1416
1370 def BuildCurrentRevision(self, depot, revision=None): 1417 def BuildCurrentRevision(self, depot, revision=None):
1371 """Builds chrome and performance_ui_tests on the current revision. 1418 """Builds chrome and performance_ui_tests on the current revision.
1372 1419
1373 Returns: 1420 Returns:
1374 True if the build was successful. 1421 True if the build was successful.
1375 """ 1422 """
1376 if self.opts.debug_ignore_build: 1423 if self.opts.debug_ignore_build:
1377 return True 1424 return True
1378 cwd = os.getcwd() 1425 cwd = os.getcwd()
1379 os.chdir(self.src_cwd) 1426 os.chdir(self.src_cwd)
1380 # Fetch build archive for the given revision from the cloud storage when 1427 # Fetch build archive for the given revision from the cloud storage when
1381 # the storage bucket is passed. 1428 # the storage bucket is passed.
1382 if depot == 'chromium' and self.opts.gs_bucket and revision: 1429 if depot == 'chromium' and self.opts.gs_bucket and revision:
1430 # Get SVN revision for the given SHA, since builds are archived using SVN
1431 # revision.
1432 revision = self.source_control.SVNFindRev(revision)
1433 if not revision:
1434 raise RuntimeError(
1435 'Failed to determine SVN revision for %s' % sha_revision)
1383 if self.DownloadCurrentBuild(revision): 1436 if self.DownloadCurrentBuild(revision):
1384 os.chdir(cwd) 1437 os.chdir(cwd)
1385 return True 1438 return True
1386 raise RuntimeError('Failed to download build archive for revision %s.\n' 1439 raise RuntimeError('Failed to download build archive for revision %s.\n'
1387 'Unfortunately, bisection couldn\'t continue any ' 1440 'Unfortunately, bisection couldn\'t continue any '
1388 'further. Please try running script without ' 1441 'further. Please try running script without '
1389 '--gs_bucket flag to produce local builds.' % revision) 1442 '--gs_bucket flag to produce local builds.' % revision)
1390 1443
1444
1391 build_success = self.builder.Build(depot, self.opts) 1445 build_success = self.builder.Build(depot, self.opts)
1392 os.chdir(cwd) 1446 os.chdir(cwd)
1393 return build_success 1447 return build_success
1394 1448
1395 def RunGClientHooks(self): 1449 def RunGClientHooks(self):
1396 """Runs gclient with runhooks command. 1450 """Runs gclient with runhooks command.
1397 1451
1398 Returns: 1452 Returns:
1399 True if gclient reports no errors. 1453 True if gclient reports no errors.
1400 """ 1454 """
(...skipping 1614 matching lines...) Expand 10 before | Expand all | Expand 10 after
3015 self.command = None 3069 self.command = None
3016 self.output_buildbot_annotations = None 3070 self.output_buildbot_annotations = None
3017 self.no_custom_deps = False 3071 self.no_custom_deps = False
3018 self.working_directory = None 3072 self.working_directory = None
3019 self.extra_src = None 3073 self.extra_src = None
3020 self.debug_ignore_build = None 3074 self.debug_ignore_build = None
3021 self.debug_ignore_sync = None 3075 self.debug_ignore_sync = None
3022 self.debug_ignore_perf_test = None 3076 self.debug_ignore_perf_test = None
3023 self.gs_bucket = None 3077 self.gs_bucket = None
3024 self.target_arch = 'ia32' 3078 self.target_arch = 'ia32'
3079 self.host = None
3080 self.port = None
3025 3081
3026 def _CreateCommandLineParser(self): 3082 def _CreateCommandLineParser(self):
3027 """Creates a parser with bisect options. 3083 """Creates a parser with bisect options.
3028 3084
3029 Returns: 3085 Returns:
3030 An instance of optparse.OptionParser. 3086 An instance of optparse.OptionParser.
3031 """ 3087 """
3032 usage = ('%prog [options] [-- chromium-options]\n' 3088 usage = ('%prog [options] [-- chromium-options]\n'
3033 'Perform binary search on revision history to find a minimal ' 3089 'Perform binary search on revision history to find a minimal '
3034 'range of revisions where a peformance metric regressed.\n') 3090 'range of revisions where a peformance metric regressed.\n')
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
3128 type='str', 3184 type='str',
3129 help=('Name of Google Storage bucket to upload or ' 3185 help=('Name of Google Storage bucket to upload or '
3130 'download build. e.g., chrome-perf')) 3186 'download build. e.g., chrome-perf'))
3131 group.add_option('--target_arch', 3187 group.add_option('--target_arch',
3132 type='choice', 3188 type='choice',
3133 choices=['ia32', 'x64', 'arm'], 3189 choices=['ia32', 'x64', 'arm'],
3134 default='ia32', 3190 default='ia32',
3135 dest='target_arch', 3191 dest='target_arch',
3136 help=('The target build architecture. Choices are "ia32" ' 3192 help=('The target build architecture. Choices are "ia32" '
3137 '(default), "x64" or "arm".')) 3193 '(default), "x64" or "arm".'))
3138 3194 group.add_option('--host',
tonyg 2014/03/07 02:41:09 Recommend names like --builder-host and --builder-
prasadv 2014/03/07 17:54:53 Done.
3195 dest='host',
3196 type='str',
3197 help=('Host address of server to produce build by posting'
3198 ' try job request.'))
3199 group.add_option('--port',
3200 dest='port',
3201 type='int',
3202 help=('HTTP port of the server to produce build by posting'
3203 ' try job request.'))
3139 parser.add_option_group(group) 3204 parser.add_option_group(group)
3140 3205
3141 group = optparse.OptionGroup(parser, 'Debug options') 3206 group = optparse.OptionGroup(parser, 'Debug options')
3142 group.add_option('--debug_ignore_build', 3207 group.add_option('--debug_ignore_build',
3143 action="store_true", 3208 action="store_true",
3144 help='DEBUG: Don\'t perform builds.') 3209 help='DEBUG: Don\'t perform builds.')
3145 group.add_option('--debug_ignore_sync', 3210 group.add_option('--debug_ignore_sync',
3146 action="store_true", 3211 action="store_true",
3147 help='DEBUG: Don\'t perform syncs.') 3212 help='DEBUG: Don\'t perform syncs.')
3148 group.add_option('--debug_ignore_perf_test', 3213 group.add_option('--debug_ignore_perf_test',
3149 action="store_true", 3214 action="store_true",
3150 help='DEBUG: Don\'t perform performance tests.') 3215 help='DEBUG: Don\'t perform performance tests.')
3151 parser.add_option_group(group) 3216 parser.add_option_group(group)
3152
3153
3154 return parser 3217 return parser
3155 3218
3156 def ParseCommandLine(self): 3219 def ParseCommandLine(self):
3157 """Parses the command line for bisect options.""" 3220 """Parses the command line for bisect options."""
3158 parser = self._CreateCommandLineParser() 3221 parser = self._CreateCommandLineParser()
3159 (opts, args) = parser.parse_args() 3222 (opts, args) = parser.parse_args()
3160 3223
3161 try: 3224 try:
3162 if not opts.command: 3225 if not opts.command:
3163 raise RuntimeError('missing required parameter: --command') 3226 raise RuntimeError('missing required parameter: --command')
3164 3227
3165 if not opts.good_revision: 3228 if not opts.good_revision:
3166 raise RuntimeError('missing required parameter: --good_revision') 3229 raise RuntimeError('missing required parameter: --good_revision')
3167 3230
3168 if not opts.bad_revision: 3231 if not opts.bad_revision:
3169 raise RuntimeError('missing required parameter: --bad_revision') 3232 raise RuntimeError('missing required parameter: --bad_revision')
3170 3233
3171 if not opts.metric: 3234 if not opts.metric:
3172 raise RuntimeError('missing required parameter: --metric') 3235 raise RuntimeError('missing required parameter: --metric')
3173 3236
3174 if opts.gs_bucket: 3237 if opts.gs_bucket:
3175 if not cloud_storage.List(opts.gs_bucket): 3238 if not cloud_storage.List(opts.gs_bucket):
3176 raise RuntimeError('Invalid Google Storage URL: [%s]', e) 3239 raise RuntimeError('Invalid Google Storage: gs://%s' % opts.gs_bucket)
3177 3240 if not opts.host:
3241 raise RuntimeError('Must specify try server hostname, when '
3242 'gs_bucket is used: --host')
3243 if not opts.port:
3244 raise RuntimeError('Must specify try server port number, when '
3245 'gs_bucket is used: --port')
3178 if opts.target_platform == 'cros': 3246 if opts.target_platform == 'cros':
3179 # Run sudo up front to make sure credentials are cached for later. 3247 # Run sudo up front to make sure credentials are cached for later.
3180 print 'Sudo is required to build cros:' 3248 print 'Sudo is required to build cros:'
3181 print 3249 print
3182 RunProcess(['sudo', 'true']) 3250 RunProcess(['sudo', 'true'])
3183 3251
3184 if not opts.cros_board: 3252 if not opts.cros_board:
3185 raise RuntimeError('missing required parameter: --cros_board') 3253 raise RuntimeError('missing required parameter: --cros_board')
3186 3254
3187 if not opts.cros_remote_ip: 3255 if not opts.cros_remote_ip:
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
3272 3340
3273 if not source_control: 3341 if not source_control:
3274 raise RuntimeError("Sorry, only the git workflow is supported at the " 3342 raise RuntimeError("Sorry, only the git workflow is supported at the "
3275 "moment.") 3343 "moment.")
3276 3344
3277 # gClient sync seems to fail if you're not in master branch. 3345 # gClient sync seems to fail if you're not in master branch.
3278 if (not source_control.IsInProperBranch() and 3346 if (not source_control.IsInProperBranch() and
3279 not opts.debug_ignore_sync and 3347 not opts.debug_ignore_sync and
3280 not opts.working_directory): 3348 not opts.working_directory):
3281 raise RuntimeError("You must switch to master branch to run bisection.") 3349 raise RuntimeError("You must switch to master branch to run bisection.")
3282
3283 bisect_test = BisectPerformanceMetrics(source_control, opts) 3350 bisect_test = BisectPerformanceMetrics(source_control, opts)
3284 try: 3351 try:
3285 bisect_results = bisect_test.Run(opts.command, 3352 bisect_results = bisect_test.Run(opts.command,
3286 opts.bad_revision, 3353 opts.bad_revision,
3287 opts.good_revision, 3354 opts.good_revision,
3288 opts.metric) 3355 opts.metric)
3289 if bisect_results['error']: 3356 if bisect_results['error']:
3290 raise RuntimeError(bisect_results['error']) 3357 raise RuntimeError(bisect_results['error'])
3291 bisect_test.FormatAndPrintResults(bisect_results) 3358 bisect_test.FormatAndPrintResults(bisect_results)
3292 return 0 3359 return 0
3293 finally: 3360 finally:
3294 bisect_test.PerformCleanup() 3361 bisect_test.PerformCleanup()
3295 except RuntimeError, e: 3362 except RuntimeError, e:
3296 if opts.output_buildbot_annotations: 3363 if opts.output_buildbot_annotations:
3297 # The perf dashboard scrapes the "results" step in order to comment on 3364 # 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. 3365 # bugs. If you change this, please update the perf dashboard as well.
3299 bisect_utils.OutputAnnotationStepStart('Results') 3366 bisect_utils.OutputAnnotationStepStart('Results')
3300 print 'Error: %s' % e.message 3367 print 'Error: %s' % e.message
3301 if opts.output_buildbot_annotations: 3368 if opts.output_buildbot_annotations:
3302 bisect_utils.OutputAnnotationStepClosed() 3369 bisect_utils.OutputAnnotationStepClosed()
3303 return 1 3370 return 1
3304 3371
3305 if __name__ == '__main__': 3372 if __name__ == '__main__':
3306 sys.exit(main()) 3373 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