Commit bd0f0208 authored by qyearsley's avatar qyearsley Committed by Commit bot

Use builder_type when requesting/fetching builds, and add support for full linux builds.

Changes in this CL:
 - In request_build.py, add a URL for linux try bot.
 - Add sample config for functional bisect.
 - Make perf-related config parameters optional.
 - Use builder type when starting build try jobs, and when fetching builds.

A few other changes:
 - Update bisect_perf_regression.py file docstring.
 - Rename _BuilderTryjob to _StartBuilderTryJob and refactor.
 - Remove unused code in request_build.py.

Proposed follow-up:
 - Move functions related to builder to be methods of one class, so that the builder-specific info/logic (e.g. builder bot and archive location) is in one place.

BUG=

Review URL: https://codereview.chromium.org/806943007

Cr-Commit-Position: refs/heads/master@{#313415}
parent db0e8ddf
#!/usr/bin/env python #!/usr/bin/env python
# Copyright (c) 2013 The Chromium Authors. All rights reserved. # Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
"""Performance Test Bisect Tool """Chromium auto-bisect tool
This script bisects a series of changelists using binary search. It starts at This script bisects a range of commits using binary search. It starts by getting
a bad revision where a performance metric has regressed, and asks for a last reference values for the specified "good" and "bad" commits. Then, for revisions
known-good revision. It will then binary search across this revision range by in between, it will get builds, run tests and classify intermediate revisions as
syncing, building, and running a performance test. If the change is "good" or "bad" until an adjacent "good" and "bad" revision is found; this is
suspected to occur as a result of WebKit/V8 changes, the script will the culprit.
further bisect changes to those depots and attempt to narrow down the revision
range. If the culprit is a roll if a depedency repository (e.g. v8), it will then
expand the revision range and continue the bisect until a culprit revision in
Example usage using SVN revisions: the dependency repository is found.
./tools/bisect_perf_regression.py -c\ Example usage using git commit hashes, bisecting a performance test based on
"out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\ the mean value of a particular metric:
-g 168222 -b 168232 -m shutdown/simple-user-quit
./tools/auto_bisect/bisect_perf_regression.py
Be aware that if you're using the git workflow and specify an SVN revision, --command "out/Release/performance_ui_tests \
the script will attempt to find the git SHA1 where SVN changes up to that --gtest_filter=ShutdownTest.SimpleUserQuit"\
revision were merged in. --metric shutdown/simple-user-quit
--good_revision 1f6e67861535121c5c819c16a666f2436c207e7b\
Example usage using git hashes: --bad-revision b732f23b4f81c382db0b23b9035f3dadc7d925bb\
./tools/bisect_perf_regression.py -c\ Example usage using git commit positions, bisecting a functional test based on
"out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\ whether it passes or fails.
-g 1f6e67861535121c5c819c16a666f2436c207e7b\
-b b732f23b4f81c382db0b23b9035f3dadc7d925bb\ ./tools/auto_bisect/bisect_perf_regression.py\
-m shutdown/simple-user-quit --command "out/Release/content_unittests -single-process-tests \
--gtest_filter=GpuMemoryBufferImplTests"\
--good_revision 408222\
--bad_revision 408232\
--bisect_mode return_code\
--builder_type full
In practice, the auto-bisect tool is usually run on tryserver.chromium.perf
try bots, and is started by tools/run-bisect-perf-regression.py using
config parameters from tools/auto_bisect/bisect.cfg.
""" """
import copy import copy
...@@ -117,7 +126,8 @@ BISECT_MASTER_BRANCH = 'master' ...@@ -117,7 +126,8 @@ BISECT_MASTER_BRANCH = 'master'
# File to store 'git diff' content. # File to store 'git diff' content.
BISECT_PATCH_FILE = 'deps_patch.txt' BISECT_PATCH_FILE = 'deps_patch.txt'
# SVN repo where the bisect try jobs are submitted. # SVN repo where the bisect try jobs are submitted.
SVN_REPO_URL = 'svn://svn.chromium.org/chrome-try/try-perf' PERF_SVN_REPO_URL = 'svn://svn.chromium.org/chrome-try/try-perf'
FULL_SVN_REPO_URL = 'svn://svn.chromium.org/chrome-try/try'
class RunGitError(Exception): class RunGitError(Exception):
...@@ -188,13 +198,13 @@ def _ParseRevisionsFromDEPSFileManually(deps_file_contents): ...@@ -188,13 +198,13 @@ def _ParseRevisionsFromDEPSFileManually(deps_file_contents):
return dict(re_results) return dict(re_results)
def _WaitUntilBuildIsReady(fetch_build_func, bot_name, builder_type, def _WaitUntilBuildIsReady(fetch_build_func, builder_name, builder_type,
build_request_id, max_timeout): build_request_id, max_timeout):
"""Waits until build is produced by bisect builder on try server. """Waits until build is produced by bisect builder on try server.
Args: Args:
fetch_build_func: Function to check and download build from cloud storage. fetch_build_func: Function to check and download build from cloud storage.
bot_name: Builder bot name on try server. builder_name: Builder bot name on try server.
builder_type: Builder type, e.g. "perf" or "full". Refer to the constants builder_type: Builder type, e.g. "perf" or "full". Refer to the constants
|fetch_build| which determine the valid values that can be passed. |fetch_build| which determine the valid values that can be passed.
build_request_id: A unique ID of the build request posted to try server. build_request_id: A unique ID of the build request posted to try server.
...@@ -224,12 +234,12 @@ def _WaitUntilBuildIsReady(fetch_build_func, bot_name, builder_type, ...@@ -224,12 +234,12 @@ def _WaitUntilBuildIsReady(fetch_build_func, bot_name, builder_type,
if not build_num: if not build_num:
# Get the build number on try server for the current build. # Get the build number on try server for the current build.
build_num = request_build.GetBuildNumFromBuilder( build_num = request_build.GetBuildNumFromBuilder(
build_request_id, bot_name, builder_type) build_request_id, builder_name, builder_type)
# Check the status of build using the build number. # Check the status of build using the build number.
# Note: Build is treated as PENDING if build number is not found # Note: Build is treated as PENDING if build number is not found
# on the the try server. # on the the try server.
build_status, status_link = request_build.GetBuildStatus( build_status, status_link = request_build.GetBuildStatus(
build_num, bot_name, builder_type) build_num, builder_name, builder_type)
if build_status == request_build.FAILED: if build_status == request_build.FAILED:
return (None, 'Failed to produce build, log: %s' % status_link) return (None, 'Failed to produce build, log: %s' % status_link)
elapsed_time = time.time() - start_time elapsed_time = time.time() - start_time
...@@ -580,41 +590,84 @@ def _PrepareBisectBranch(parent_branch, new_branch): ...@@ -580,41 +590,84 @@ def _PrepareBisectBranch(parent_branch, new_branch):
raise RunGitError('Error in git branch --set-upstream-to') raise RunGitError('Error in git branch --set-upstream-to')
def _BuilderTryjob(git_revision, bot_name, bisect_job_name, patch=None): def _GetBuilderName(builder_type, target_platform):
"""Attempts to run a tryjob from the current directory. """Gets builder bot name and build time in seconds based on platform."""
# TODO(prasadv, qyearsley): Make this a method of BuildArchive
# (which may be renamed to BuilderTryBot or Builder).
if builder_type == fetch_build.FULL_BUILDER:
# The following builder is on tryserver.chromium.linux.
# TODO(qyearsley): Change this name when more platforms are supported.
return 'bisect_builder'
if builder_type == fetch_build.PERF_BUILDER:
if bisect_utils.IsWindowsHost():
return 'win_perf_bisect_builder'
if bisect_utils.IsLinuxHost():
if target_platform == 'android':
return 'android_perf_bisect_builder'
return 'linux_perf_bisect_builder'
if bisect_utils.IsMacHost():
return 'mac_perf_bisect_builder'
raise NotImplementedError('Unsupported platform "%s".' % sys.platform)
raise NotImplementedError('Unsupported builder type "%s".' % builder_type)
def _GetBuilderBuildTime():
"""Returns the time to wait for a build after requesting one."""
# TODO(prasadv, qyearsley): Make this a method of BuildArchive
# (which may be renamed to BuilderTryBot or Builder).
if bisect_utils.IsWindowsHost():
return MAX_WIN_BUILD_TIME
if bisect_utils.IsLinuxHost():
return MAX_LINUX_BUILD_TIME
if bisect_utils.IsMacHost():
return MAX_MAC_BUILD_TIME
raise NotImplementedError('Unsupported Platform "%s".' % sys.platform)
def _StartBuilderTryJob(
builder_type, git_revision, builder_name, job_name, patch=None):
"""Attempts to run a try job from the current directory.
Args: Args:
git_revision: A Git hash revision. builder_type: One of the builder types in fetch_build, e.g. "perf".
bot_name: Name of the bisect bot to be used for try job. git_revision: A git commit hash.
bisect_job_name: Bisect try job name. builder_name: Name of the bisect bot to be used for try job.
patch: A DEPS patch (used while bisecting 3rd party repositories). bisect_job_name: Try job name, used to identify which bisect
job was responsible for requesting a build.
patch: A DEPS patch (used while bisecting dependency repositories),
or None if we're bisecting the top-level repository.
""" """
# TODO(prasadv, qyearsley): Make this a method of BuildArchive
# (which may be renamed to BuilderTryBot or Builder).
try: try:
# Temporary branch for running tryjob. # Temporary branch for running tryjob.
_PrepareBisectBranch(BISECT_MASTER_BRANCH, BISECT_TRYJOB_BRANCH) _PrepareBisectBranch(BISECT_MASTER_BRANCH, BISECT_TRYJOB_BRANCH)
patch_content = '/dev/null' patch_content = '/dev/null'
# Create a temporary patch file, if it fails raise an exception. # Create a temporary patch file.
if patch: if patch:
WriteStringToFile(patch, BISECT_PATCH_FILE) WriteStringToFile(patch, BISECT_PATCH_FILE)
patch_content = BISECT_PATCH_FILE patch_content = BISECT_PATCH_FILE
try_cmd = ['try', try_command = [
'-b', bot_name, 'try',
'-r', git_revision, '--bot=%s' % builder_name,
'-n', bisect_job_name, '--revision=%s' % git_revision,
'--svn_repo=%s' % SVN_REPO_URL, '--name=%s' % job_name,
'--diff=%s' % patch_content '--svn_repo=%s' % _TryJobSvnRepo(builder_type),
] '--diff=%s' % patch_content,
]
# Execute try job to build revision. # Execute try job to build revision.
output, returncode = bisect_utils.RunGit(try_cmd) print try_command
output, return_code = bisect_utils.RunGit(try_command)
if returncode: command_string = ' '.join(['git'] + try_command)
raise RunGitError('Could not execute tryjob: %s.\n Error: %s' % ( if return_code:
'git %s' % ' '.join(try_cmd), output)) raise RunGitError('Could not execute tryjob: %s.\n'
'Error: %s' % (command_string, output))
logging.info('Try job successfully submitted.\n TryJob Details: %s\n%s', logging.info('Try job successfully submitted.\n TryJob Details: %s\n%s',
'git %s' % ' '.join(try_cmd), output) command_string, output)
finally: finally:
# Delete patch file if exists # Delete patch file if exists.
try: try:
os.remove(BISECT_PATCH_FILE) os.remove(BISECT_PATCH_FILE)
except OSError as e: except OSError as e:
...@@ -625,6 +678,15 @@ def _BuilderTryjob(git_revision, bot_name, bisect_job_name, patch=None): ...@@ -625,6 +678,15 @@ def _BuilderTryjob(git_revision, bot_name, bisect_job_name, patch=None):
bisect_utils.RunGit(['branch', '-D', BISECT_TRYJOB_BRANCH]) bisect_utils.RunGit(['branch', '-D', BISECT_TRYJOB_BRANCH])
def _TryJobSvnRepo(builder_type):
"""Returns an SVN repo to use for try jobs based on the builder type."""
if builder_type == fetch_build.PERF_BUILDER:
return PERF_SVN_REPO_URL
if builder_type == fetch_build.FULL_BUILDER:
return FULL_SVN_REPO_URL
raise NotImplementedError('Unknown builder type "%s".' % builder_type)
class BisectPerformanceMetrics(object): class BisectPerformanceMetrics(object):
"""This class contains functionality to perform a bisection of a range of """This class contains functionality to perform a bisection of a range of
revisions to narrow down where performance regressions may have occurred. revisions to narrow down where performance regressions may have occurred.
...@@ -831,7 +893,8 @@ class BisectPerformanceMetrics(object): ...@@ -831,7 +893,8 @@ class BisectPerformanceMetrics(object):
File path of the downloaded file if successful, otherwise None. File path of the downloaded file if successful, otherwise None.
""" """
bucket_name, remote_path = fetch_build.GetBucketAndRemotePath( bucket_name, remote_path = fetch_build.GetBucketAndRemotePath(
revision, target_arch=self.opts.target_arch, revision, builder_type=self.opts.builder_type,
target_arch=self.opts.target_arch,
target_platform=self.opts.target_platform, target_platform=self.opts.target_platform,
deps_patch_sha=deps_patch_sha) deps_patch_sha=deps_patch_sha)
output_dir = os.path.abspath(build_dir) output_dir = os.path.abspath(build_dir)
...@@ -879,49 +942,25 @@ class BisectPerformanceMetrics(object): ...@@ -879,49 +942,25 @@ class BisectPerformanceMetrics(object):
# Revert any changes to DEPS file. # Revert any changes to DEPS file.
bisect_utils.CheckRunGit(['reset', '--hard', 'HEAD'], cwd=self.src_cwd) bisect_utils.CheckRunGit(['reset', '--hard', 'HEAD'], cwd=self.src_cwd)
bot_name = self._GetBuilderName(self.opts.target_platform) builder_name = _GetBuilderName(
build_timeout = self._GetBuilderBuildTime() self.opts.builder_type, self.opts.target_platform)
build_timeout = _GetBuilderBuildTime()
try: try:
_BuilderTryjob(git_revision, bot_name, build_request_id, deps_patch) _StartBuilderTryJob(self.opts.builder_type, git_revision, builder_name,
job_name=build_request_id, patch=deps_patch)
except RunGitError as e: except RunGitError as e:
logging.warn('Failed to post builder try job for revision: [%s].\n' logging.warn('Failed to post builder try job for revision: [%s].\n'
'Error: %s', git_revision, e) 'Error: %s', git_revision, e)
return None return None
archive_filename, error_msg = _WaitUntilBuildIsReady( archive_filename, error_msg = _WaitUntilBuildIsReady(
fetch_build_func, bot_name, self.opts.builder_type, build_request_id, fetch_build_func, builder_name, self.opts.builder_type,
build_timeout) build_request_id, build_timeout)
if not archive_filename: if not archive_filename:
logging.warn('%s [revision: %s]', error_msg, git_revision) logging.warn('%s [revision: %s]', error_msg, git_revision)
return archive_filename return archive_filename
@staticmethod
def _GetBuilderName(target_platform, builder_type=fetch_build.PERF_BUILDER):
"""Gets builder bot name and build time in seconds based on platform."""
if builder_type != fetch_build.PERF_BUILDER:
raise NotImplementedError('No builder names for non-perf builds yet.')
if bisect_utils.IsWindowsHost():
return 'win_perf_bisect_builder'
if bisect_utils.IsLinuxHost():
if target_platform == 'android':
return 'android_perf_bisect_builder'
return 'linux_perf_bisect_builder'
if bisect_utils.IsMacHost():
return 'mac_perf_bisect_builder'
raise NotImplementedError('Unsupported Platform "%s".' % sys.platform)
@staticmethod
def _GetBuilderBuildTime():
"""Returns the time to wait for a build after requesting one."""
if bisect_utils.IsWindowsHost():
return MAX_WIN_BUILD_TIME
if bisect_utils.IsLinuxHost():
return MAX_LINUX_BUILD_TIME
if bisect_utils.IsMacHost():
return MAX_MAC_BUILD_TIME
raise NotImplementedError('Unsupported Platform "%s".' % sys.platform)
def _UnzipAndMoveBuildProducts(self, downloaded_file, build_dir, def _UnzipAndMoveBuildProducts(self, downloaded_file, build_dir,
build_type='Release'): build_type='Release'):
"""Unzips the build archive and moves it to the build output directory. """Unzips the build archive and moves it to the build output directory.
...@@ -1004,7 +1043,7 @@ class BisectPerformanceMetrics(object): ...@@ -1004,7 +1043,7 @@ class BisectPerformanceMetrics(object):
def IsDownloadable(self, depot): def IsDownloadable(self, depot):
"""Checks if build can be downloaded based on target platform and depot.""" """Checks if build can be downloaded based on target platform and depot."""
if (self.opts.target_platform in ['chromium', 'android'] if (self.opts.target_platform in ['chromium', 'android']
and self.opts.builder_type == fetch_build.PERF_BUILDER): and self.opts.builder_type):
return (depot == 'chromium' or return (depot == 'chromium' or
'chromium' in bisect_utils.DEPOT_DEPS_NAME[depot]['from'] or 'chromium' in bisect_utils.DEPOT_DEPS_NAME[depot]['from'] or
'v8' in bisect_utils.DEPOT_DEPS_NAME[depot]['from']) 'v8' in bisect_utils.DEPOT_DEPS_NAME[depot]['from'])
......
...@@ -13,6 +13,7 @@ sys.path.append(os.path.join(SRC, 'third_party', 'pymock')) ...@@ -13,6 +13,7 @@ sys.path.append(os.path.join(SRC, 'third_party', 'pymock'))
import bisect_perf_regression import bisect_perf_regression
import bisect_utils import bisect_utils
import fetch_build
import mock import mock
import source_control import source_control
...@@ -584,7 +585,7 @@ class GitTryJobTestCases(unittest.TestCase): ...@@ -584,7 +585,7 @@ class GitTryJobTestCases(unittest.TestCase):
bisect_perf_regression._PrepareBisectBranch, bisect_perf_regression._PrepareBisectBranch,
parent_branch, new_branch) parent_branch, new_branch)
def testBuilderTryJobForException(self): def testStartBuilderTryJobForException(self):
git_revision = 'ac4a9f31fe2610bd146857bbd55d7a260003a888' git_revision = 'ac4a9f31fe2610bd146857bbd55d7a260003a888'
bot_name = 'linux_perf_bisect_builder' bot_name = 'linux_perf_bisect_builder'
bisect_job_name = 'testBisectJobname' bisect_job_name = 'testBisectJobname'
...@@ -602,16 +603,17 @@ class GitTryJobTestCases(unittest.TestCase): ...@@ -602,16 +603,17 @@ class GitTryJobTestCases(unittest.TestCase):
(['branch', '--set-upstream-to', parent_branch], (['branch', '--set-upstream-to', parent_branch],
('Setuptream fails', 0)), ('Setuptream fails', 0)),
(['try', (['try',
'-b', bot_name, '--bot=%s' % bot_name,
'-r', git_revision, '--revision=%s' % git_revision,
'-n', bisect_job_name, '--name=%s' % bisect_job_name,
'--svn_repo=%s' % bisect_perf_regression.SVN_REPO_URL, '--svn_repo=%s' % bisect_perf_regression.PERF_SVN_REPO_URL,
'--diff=%s' % patch_content '--diff=%s' % patch_content
], (None, 1)), ], (None, 1)),
] ]
self._AssertRunGitExceptions(try_cmd, self._AssertRunGitExceptions(
bisect_perf_regression._BuilderTryjob, try_cmd, bisect_perf_regression._StartBuilderTryJob,
git_revision, bot_name, bisect_job_name, patch) fetch_build.PERF_BUILDER, git_revision, bot_name, bisect_job_name,
patch)
def testBuilderTryJob(self): def testBuilderTryJob(self):
git_revision = 'ac4a9f31fe2610bd146857bbd55d7a260003a888' git_revision = 'ac4a9f31fe2610bd146857bbd55d7a260003a888'
...@@ -631,16 +633,17 @@ class GitTryJobTestCases(unittest.TestCase): ...@@ -631,16 +633,17 @@ class GitTryJobTestCases(unittest.TestCase):
(['branch', '--set-upstream-to', parent_branch], (['branch', '--set-upstream-to', parent_branch],
('Setuptream fails', 0)), ('Setuptream fails', 0)),
(['try', (['try',
'-b', bot_name, '--bot=%s' % bot_name,
'-r', git_revision, '--revision=%s' % git_revision,
'-n', bisect_job_name, '--name=%s' % bisect_job_name,
'--svn_repo=%s' % bisect_perf_regression.SVN_REPO_URL, '--svn_repo=%s' % bisect_perf_regression.PERF_SVN_REPO_URL,
'--diff=%s' % patch_content '--diff=%s' % patch_content
], (None, 0)), ], (None, 0)),
] ]
self._SetupRunGitMock(try_cmd) self._SetupRunGitMock(try_cmd)
bisect_perf_regression._BuilderTryjob( bisect_perf_regression._StartBuilderTryJob(
git_revision, bot_name, bisect_job_name, patch) fetch_build.PERF_BUILDER, git_revision, bot_name, bisect_job_name,
patch)
if __name__ == '__main__': if __name__ == '__main__':
......
# This should reproduce the regression in http://crbug.com/425582.
# It was based on:
# http://build.chromium.org/p/tryserver.chromium.perf/builders/linux_perf_bisect/builds/704
config = {
'command': 'out/Release/content_unittests --single-process-tests --gtest_filter=DOMStorageAreaTest',
'good_revision': '311607',
'bad_revision': '311608',
'bisect_mode': 'return_code',
'builder_type': 'full',
}
# Workaround git try issue, see crbug.com/257689
...@@ -51,6 +51,8 @@ def GetBucketAndRemotePath(revision, builder_type=PERF_BUILDER, ...@@ -51,6 +51,8 @@ def GetBucketAndRemotePath(revision, builder_type=PERF_BUILDER,
Returns: Returns:
A pair of strings (bucket, path), where the archive is expected to be. A pair of strings (bucket, path), where the archive is expected to be.
""" """
logging.info('Creating BuildArchive, type "%s", arch "%s", platform "%s".',
builder_type, target_arch, target_platform)
build_archive = BuildArchive.Create( build_archive = BuildArchive.Create(
builder_type, target_arch=target_arch, target_platform=target_platform, builder_type, target_arch=target_arch, target_platform=target_platform,
extra_src=extra_src) extra_src=extra_src)
...@@ -70,7 +72,9 @@ class BuildArchive(object): ...@@ -70,7 +72,9 @@ class BuildArchive(object):
@staticmethod @staticmethod
def Create(builder_type, target_arch='ia32', target_platform='chromium', def Create(builder_type, target_arch='ia32', target_platform='chromium',
extra_src=None): extra_src=None):
logging.info('Creating BuildArchive, type "%s", arch "%s", platform "%s".',
builder_type, target_arch, target_platform)
if builder_type == PERF_BUILDER: if builder_type == PERF_BUILDER:
return PerfBuildArchive(target_arch, target_platform) return PerfBuildArchive(target_arch, target_platform)
if builder_type == FULL_BUILDER: if builder_type == FULL_BUILDER:
...@@ -414,4 +418,3 @@ def Main(argv): ...@@ -414,4 +418,3 @@ def Main(argv):
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(Main(sys.argv)) sys.exit(Main(sys.argv))
...@@ -26,23 +26,16 @@ BUILDER_JSON_URL = ('%(server_url)s/json/builders/%(bot_name)s/builds/' ...@@ -26,23 +26,16 @@ BUILDER_JSON_URL = ('%(server_url)s/json/builders/%(bot_name)s/builds/'
'%(build_num)s?as_text=1&filter=0') '%(build_num)s?as_text=1&filter=0')
# URL template for displaying build steps. # URL template for displaying build steps.
BUILDER_HTML_URL = ('%(server_url)s/builders/%(bot_name)s/builds/%(build_num)s') BUILDER_HTML_URL = '%(server_url)s/builders/%(bot_name)s/builds/%(build_num)s'
# Try server status page for the perf try server. # Try server status page URLs, used to get build status.
PERF_TRY_SERVER_URL = 'http://build.chromium.org/p/tryserver.chromium.perf' PERF_TRY_SERVER_URL = 'http://build.chromium.org/p/tryserver.chromium.perf'
LINUX_TRY_SERVER_URL = 'http://build.chromium.org/p/tryserver.chromium.linux'
# Hostname of the tryserver where perf bisect builders are hosted. # Status codes that can be returned by the GetBuildStatus method
# This is used for posting build requests to the tryserver.
PERF_BISECT_BUILDER_HOST = 'master4.golo.chromium.org'
# The default 'try_job_port' on tryserver to post build request.
PERF_BISECT_BUILDER_PORT = 8341
# Status codes that can be returned by the GetBuildStatus method.
# From buildbot.status.builder. # From buildbot.status.builder.
# See: http://docs.buildbot.net/current/developer/results.html # See: http://docs.buildbot.net/current/developer/results.html
SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION, RETRY, TRYPENDING = range(7) SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION, RETRY, TRYPENDING = range(7)
OK = (SUCCESS, WARNINGS) # These indicate build is complete. OK = (SUCCESS, WARNINGS) # These indicate build is complete.
FAILED = (FAILURE, EXCEPTION, SKIPPED) # These indicate build failure. FAILED = (FAILURE, EXCEPTION, SKIPPED) # These indicate build failure.
PENDING = (RETRY, TRYPENDING) # These indicate in progress or in pending queue. PENDING = (RETRY, TRYPENDING) # These indicate in progress or in pending queue.
...@@ -54,51 +47,6 @@ class ServerAccessError(Exception): ...@@ -54,51 +47,6 @@ class ServerAccessError(Exception):
return '%s\nSorry, cannot connect to server.' % self.args[0] return '%s\nSorry, cannot connect to server.' % self.args[0]
def PostTryJob(host, port, params):
"""Sends a build request to the server using the HTTP protocol.
The required parameters are:
'revision': "src@rev", where rev is a git hash or SVN revision.
'bot': Name of builder bot to use, e.g. "win_perf_bisect_builder".
Args:
host: Hostname of the try server.
port: Port of the try server.
params: A dictionary of parameters to be sent in the POST request.
Returns:
True if the request is posted successfully.
Raises:
ServerAccessError: Failed to make a request to the try server.
ValueError: Not all of the expected inputs were given.
"""
if not params.get('revision'):
raise ValueError('Missing revision number.')
if not params.get('bot'):
raise ValueError('Missing bot name.')
base_url = 'http://%s:%s/send_try_patch' % (host, port)
print 'Posting build request by HTTP.'
print 'URL: %s, params: %s' % (base_url, params)
connection = None
try:
print 'Opening connection...'
connection = urllib2.urlopen(base_url, urllib.urlencode(params))
print 'Done, request sent to server to produce build.'
except IOError as e:
raise ServerAccessError('%s is unaccessible. Reason: %s' % (base_url, e))
if not connection:
raise ServerAccessError('%s is unaccessible.' % base_url)
response = connection.read()
print 'Received response from server: %s' % response
if response != 'OK':
raise ServerAccessError('Response was not "OK".')
return True
def _IsBuildRunning(build_data): def _IsBuildRunning(build_data):
"""Checks whether the build is in progress on buildbot. """Checks whether the build is in progress on buildbot.
...@@ -218,8 +166,9 @@ def _GetBuildBotUrl(builder_type): ...@@ -218,8 +166,9 @@ def _GetBuildBotUrl(builder_type):
""" """
if builder_type == fetch_build.PERF_BUILDER: if builder_type == fetch_build.PERF_BUILDER:
return PERF_TRY_SERVER_URL return PERF_TRY_SERVER_URL
else: if builder_type == fetch_build.FULL_BUILDER:
raise NotImplementedError('Cannot yet get non-perf builds.') return LINUX_TRY_SERVER_URL
raise NotImplementedError('Unsupported builder type "%s".' % builder_type)
def GetBuildStatus(build_num, bot_name, builder_type): def GetBuildStatus(build_num, bot_name, builder_type):
...@@ -234,6 +183,8 @@ def GetBuildStatus(build_num, bot_name, builder_type): ...@@ -234,6 +183,8 @@ def GetBuildStatus(build_num, bot_name, builder_type):
A pair which consists of build status (SUCCESS, FAILED or PENDING) and a A pair which consists of build status (SUCCESS, FAILED or PENDING) and a
link to build status page on the waterfall. link to build status page on the waterfall.
""" """
# TODO(prasadv, qyearsley): Make this a method of BuildArchive
# (which may be renamed to BuilderTryBot or Builder).
results_url = None results_url = None
if build_num: if build_num:
# Get the URL for requesting JSON data with status information. # Get the URL for requesting JSON data with status information.
...@@ -277,6 +228,8 @@ def GetBuildNumFromBuilder(build_reason, bot_name, builder_type): ...@@ -277,6 +228,8 @@ def GetBuildNumFromBuilder(build_reason, bot_name, builder_type):
Returns: Returns:
A build number as a string if found, otherwise None. A build number as a string if found, otherwise None.
""" """
# TODO(prasadv, qyearsley): Make this a method of BuildArchive
# (which may be renamed to BuilderTryBot or Builder).
# Gets the buildbot url for the given host and port. # Gets the buildbot url for the given host and port.
server_url = _GetBuildBotUrl(builder_type) server_url = _GetBuildBotUrl(builder_type)
buildbot_url = BUILDER_JSON_URL % { buildbot_url = BUILDER_JSON_URL % {
...@@ -291,77 +244,3 @@ def GetBuildNumFromBuilder(build_reason, bot_name, builder_type): ...@@ -291,77 +244,3 @@ def GetBuildNumFromBuilder(build_reason, bot_name, builder_type):
if builds_data[current_build].get('reason') == build_reason: if builds_data[current_build].get('reason') == build_reason:
return builds_data[current_build].get('number') return builds_data[current_build].get('number')
return None return None
def _GetRequestParams(options):
"""Extracts request parameters which will be passed to PostTryJob.
Args:
options: The options object parsed from the command line.
Returns:
A dictionary with parameters to pass to PostTryJob.
"""
params = {
'user': options.user,
'name': options.name,
}
# Add other parameters if they're available in the options object.
for key in ['email', 'revision', 'root', 'bot', 'patch']:
option = getattr(options, key)
if option:
params[key] = option
return params
def _GenParser():
"""Returns a parser for getting command line arguments."""
usage = ('%prog [options]\n'
'Post a build request to the try server for the given revision.')
parser = optparse.OptionParser(usage=usage)
parser.add_option('-H', '--host',
help='Host address of the try server (required).')
parser.add_option('-P', '--port', type='int',
help='HTTP port of the try server (required).')
parser.add_option('-u', '--user', default=getpass.getuser(),
dest='user',
help='Owner user name [default: %default]')
parser.add_option('-e', '--email',
default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS',
os.environ.get('EMAIL_ADDRESS')),
help=('Email address where to send the results. Use either '
'the TRYBOT_RESULTS_EMAIL_ADDRESS environment '
'variable or EMAIL_ADDRESS to set the email address '
'the try bots report results to [default: %default]'))
parser.add_option('-n', '--name',
default='try_job_http',
help='Descriptive name of the try job')
parser.add_option('-b', '--bot',
help=('Only one builder per run may be specified; to post '
'jobs on on multiple builders, run script for each '
'builder separately.'))
parser.add_option('-r', '--revision',
help=('Revision to use for the try job. The revision may '
'be determined by the try server; see its waterfall '
'for more info.'))
parser.add_option('--root',
help=('Root to use for the patch; base subdirectory for '
'patch created in a subdirectory.'))
parser.add_option('--patch',
help='Patch information.')
return parser
def Main(_):
"""Posts a try job based on command line parameters."""
parser = _GenParser()
options, _ = parser.parse_args()
if not options.host or not options.port:
parser.print_help()
return 1
params = _GetRequestParams(options)
PostTryJob(options.host, options.port, params)
if __name__ == '__main__':
sys.exit(Main(sys.argv))
...@@ -148,13 +148,7 @@ def _ValidatePerfConfigFile(config_contents): ...@@ -148,13 +148,7 @@ def _ValidatePerfConfigFile(config_contents):
Returns: Returns:
True if valid. True if valid.
""" """
required_parameters = [ return _ValidateConfigFile(config_contents, required_parameters=['command'])
'command',
'repeat_count',
'truncate_percent',
'max_time_minutes',
]
return _ValidateConfigFile(config_contents, required_parameters)
def _ValidateBisectConfigFile(config_contents): def _ValidateBisectConfigFile(config_contents):
...@@ -169,16 +163,9 @@ def _ValidateBisectConfigFile(config_contents): ...@@ -169,16 +163,9 @@ def _ValidateBisectConfigFile(config_contents):
Returns: Returns:
True if valid. True if valid.
""" """
required_params = [ return _ValidateConfigFile(
'command', config_contents,
'good_revision', required_parameters=['command', 'good_revision', 'bad_revision'])
'bad_revision',
'metric',
'repeat_count',
'truncate_percent',
'max_time_minutes',
]
return _ValidateConfigFile(config_contents, required_params)
def _OutputFailedResults(text_to_print): def _OutputFailedResults(text_to_print):
...@@ -498,13 +485,13 @@ def _RunBisectionScript( ...@@ -498,13 +485,13 @@ def _RunBisectionScript(
'--command', config['command'], '--command', config['command'],
'--good_revision', config['good_revision'], '--good_revision', config['good_revision'],
'--bad_revision', config['bad_revision'], '--bad_revision', config['bad_revision'],
'--metric', config['metric'],
'--working_directory', working_directory, '--working_directory', working_directory,
'--output_buildbot_annotations' '--output_buildbot_annotations'
] ]
# Add flags for any optional config parameters if given in the config. # Add flags for any optional config parameters if given in the config.
options = [ options = [
('metric', '--metric'),
('repeat_count', '--repeat_test_count'), ('repeat_count', '--repeat_test_count'),
('truncate_percent', '--truncate_percent'), ('truncate_percent', '--truncate_percent'),
('max_time_minutes', '--max_time_minutes'), ('max_time_minutes', '--max_time_minutes'),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment