| # Copyright 2015 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import base64 |
| import gzip |
| import io |
| import json |
| import logging |
| import os |
| import platform |
| import subprocess |
| import tempfile |
| import urllib |
| import urllib2 |
| |
| |
| from core import path_util |
| |
| from telemetry import benchmark |
| from telemetry import decorators |
| from telemetry.core import discover |
| from telemetry.util import command_line |
| from telemetry.util import matching |
| |
| |
| # Unsupported Perf bisect bots. |
| EXCLUDED_BOTS = { |
| 'win_xp_perf_bisect', # Goma issues: crbug.com/330900 |
| 'win_perf_bisect_builder', |
| 'win64_nv_tester', |
| 'winx64_bisect_builder', |
| 'linux_perf_bisect_builder', |
| 'mac_perf_bisect_builder', |
| 'android_perf_bisect_builder', |
| 'android_arm64_perf_bisect_builder', |
| # Bisect FYI bots are not meant for testing actual perf regressions. |
| # Hardware configuration on these bots is different from actual bisect bot |
| # and these bots runs E2E integration tests for auto-bisect |
| # using dummy benchmarks. |
| 'linux_fyi_perf_bisect', |
| 'mac_fyi_perf_bisect', |
| 'win_fyi_perf_bisect', |
| 'winx64_fyi_perf_bisect', |
| } |
| |
| INCLUDE_BOTS = [ |
| 'all', |
| 'all-win', |
| 'all-mac', |
| 'all-linux', |
| 'all-android' |
| ] |
| |
| # Default try bot to use incase builbot is unreachable. |
| DEFAULT_TRYBOTS = [ |
| 'linux_perf_bisect', |
| 'mac_10_11_perf_bisect', |
| 'winx64_10_perf_bisect', |
| 'android_s5_perf_bisect', |
| ] |
| |
| CHROMIUM_SRC_PATH = path_util.GetChromiumSrcDir() |
| # Mapping of repo to its root path and git remote URL. |
| # Values for 'src' key in the map are related to path to the repo in the |
| # DEPS file, These values are to used create the DEPS patch along with patch |
| # that is being tried. |
| REPO_INFO_MAP = { |
| 'src': { |
| 'src': 'src', |
| 'url': 'https://chromium.googlesource.com/chromium/src.git', |
| }, |
| 'v8': { |
| 'src': 'src/v8', |
| 'url': 'https://chromium.googlesource.com/v8/v8.git', |
| }, |
| 'skia': { |
| 'src': 'src/third_party/skia', |
| 'url': 'https://chromium.googlesource.com/skia.git', |
| }, |
| 'angle': { |
| 'src': 'src/third_party/angle', |
| 'url': 'https://chromium.googlesource.com/angle/angle.git', |
| }, |
| 'catapult': { |
| 'src': 'src/third_party/catapult', |
| 'url': ('https://chromium.googlesource.com/external/github.com/' |
| 'catapult-project/catapult.git') |
| } |
| } |
| |
| _MILO_MASTER_ENDPOINT = ('https://luci-milo.appspot.com/prpc/milo.Buildbot/' |
| 'GetCompressedMasterJSON') |
| |
| _MILO_RESPONSE_PREFIX = ')]}\'\n' |
| |
| |
| assert not set(DEFAULT_TRYBOTS) & set(EXCLUDED_BOTS), ( |
| 'A trybot cannot present in both Default as well as Excluded bots lists.') |
| |
| |
| class TrybotError(Exception): |
| |
| def __str__(self): |
| return '(ERROR) Perf Try Job: %s' % self.args[0] |
| |
| |
| def _ProcessMiloData(data): |
| if not data.startswith(_MILO_RESPONSE_PREFIX): |
| return None |
| data = data[len(_MILO_RESPONSE_PREFIX):] |
| |
| try: |
| response_data = json.loads(data) |
| except Exception: |
| return None |
| |
| try: |
| decoded_data = base64.b64decode(response_data.get('data')) |
| except Exception: |
| return None |
| |
| try: |
| with io.BytesIO(decoded_data) as compressed_file: |
| with gzip.GzipFile(fileobj=compressed_file) as decompressed_file: |
| data_json = decompressed_file.read() |
| except Exception: |
| return None |
| |
| return json.loads(data_json) |
| |
| |
| def _GetTrybotList(builders): |
| builders = ['%s' % bot.replace('_perf_bisect', '').replace('_', '-') |
| for bot in builders] |
| builders.extend(INCLUDE_BOTS) |
| return sorted(builders) |
| |
| |
| def _GetBotPlatformFromTrybotName(trybot_name): |
| os_names = ['linux', 'android', 'mac', 'win'] |
| try: |
| return next(b for b in os_names if b in trybot_name) |
| except StopIteration: |
| raise TrybotError('Trybot "%s" unsupported for tryjobs.' % trybot_name) |
| |
| |
| def _GetBuilderNames(trybot_name, builders): |
| """Return platform and its available bot name as dictionary.""" |
| os_names = ['linux', 'android', 'mac', 'win'] |
| if 'all' not in trybot_name: |
| bot = ['%s_perf_bisect' % trybot_name.replace('-', '_')] |
| bot_platform = _GetBotPlatformFromTrybotName(trybot_name) |
| if 'x64' in trybot_name: |
| bot_platform += '-x64' |
| return {bot_platform: bot} |
| |
| platform_and_bots = {} |
| for os_name in os_names: |
| platform_and_bots[os_name] = [bot for bot in builders if os_name in bot] |
| |
| # Special case for Windows x64, consider it as separate platform |
| # config config should contain target_arch=x64 and --browser=release_x64. |
| win_x64_bots = [ |
| win_bot for win_bot in platform_and_bots['win'] |
| if 'x64' in win_bot] |
| # Separate out non x64 bits win bots |
| platform_and_bots['win'] = list( |
| set(platform_and_bots['win']) - set(win_x64_bots)) |
| platform_and_bots['win-x64'] = win_x64_bots |
| |
| if 'all-win' in trybot_name: |
| return {'win': platform_and_bots['win'], |
| 'win-x64': platform_and_bots['win-x64']} |
| if 'all-mac' in trybot_name: |
| return {'mac': platform_and_bots['mac']} |
| if 'all-android' in trybot_name: |
| return {'android': platform_and_bots['android']} |
| if 'all-linux' in trybot_name: |
| return {'linux': platform_and_bots['linux']} |
| |
| return platform_and_bots |
| |
| |
| _GIT_CMD = 'git' |
| |
| |
| if platform.system() == 'Windows': |
| # On windows, the git command is installed as 'git.bat' |
| _GIT_CMD = 'git.bat' |
| |
| |
| def RunGit(cmd, msg_on_error='', ignore_return_code=False): |
| """Runs the git command with the given arguments. |
| |
| Args: |
| cmd: git command arguments. |
| msg_on_error: Message to be displayed on git command error. |
| ignore_return_code: Ignores the return code for git command. |
| |
| Returns: |
| The output of the git command as string. |
| |
| Raises: |
| TrybotError: This exception is raised when git command fails. |
| """ |
| proc = subprocess.Popen( |
| [_GIT_CMD] + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| output, err = proc.communicate() |
| returncode = proc.poll() |
| if returncode: |
| if ignore_return_code: |
| return None |
| raise TrybotError('%s. \n%s \n%s' % (msg_on_error, err, output)) |
| |
| return output.strip() |
| |
| |
| class Trybot(command_line.ArgParseCommand): |
| """Run telemetry perf benchmark on trybot.""" |
| |
| usage = 'botname benchmark_name [<benchmark run options>]' |
| _builders = None |
| |
| def __init__(self): |
| self._builder_names = None |
| |
| @classmethod |
| def _GetBuilderList(cls): |
| if not cls._builders: |
| try: |
| headers = { |
| 'Accept': 'application/json', |
| 'Content-Type': 'application/json', |
| } |
| values = {'name': 'tryserver.chromium.perf'} |
| data = urllib.urlencode(values) |
| req = urllib2.Request(_MILO_MASTER_ENDPOINT, None, headers) |
| f = urllib2.urlopen(req, json.dumps(values), timeout=10) |
| # In case of any kind of exception, allow tryjobs to use default trybots. |
| # Possible exception are ssl.SSLError, urllib2.URLError, |
| # socket.timeout, socket.error. |
| except Exception: # pylint: disable=broad-except |
| # Incase of any exception return default trybots. |
| print ('WARNING: Unable to reach builbot to retrieve trybot ' |
| 'information, tryjob will use default trybots.') |
| cls._builders = DEFAULT_TRYBOTS |
| else: |
| data = _ProcessMiloData(f.read()) |
| builders = data.get('builders', {}).keys() |
| # Exclude unsupported bots like win xp and some dummy bots. |
| cls._builders = [bot for bot in builders if bot not in EXCLUDED_BOTS] |
| |
| return cls._builders |
| |
| def _InitializeBuilderNames(self, trybot): |
| self._builder_names = _GetBuilderNames(trybot, self._GetBuilderList()) |
| |
| @classmethod |
| def CreateParser(cls): |
| parser = argparse.ArgumentParser( |
| ('Run telemetry benchmarks on trybot. You can add all the benchmark ' |
| 'options available except the --browser option'), |
| formatter_class=argparse.RawTextHelpFormatter) |
| return parser |
| |
| @classmethod |
| def ProcessCommandLineArgs(cls, parser, options, extra_args, environment): |
| del environment # unused |
| for arg in extra_args: |
| if arg == '--browser' or arg.startswith('--browser='): |
| parser.error('--browser=... is not allowed when running trybot.') |
| all_benchmarks = discover.DiscoverClasses( |
| start_dir=path_util.GetPerfBenchmarksDir(), |
| top_level_dir=path_util.GetPerfDir(), |
| base_class=benchmark.Benchmark).values() |
| all_benchmark_names = [b.Name() for b in all_benchmarks] |
| all_benchmarks_by_names = {b.Name(): b for b in all_benchmarks} |
| benchmark_class = all_benchmarks_by_names.get(options.benchmark_name, None) |
| if not benchmark_class: |
| possible_benchmark_names = matching.GetMostLikelyMatchedObject( |
| all_benchmark_names, options.benchmark_name) |
| parser.error( |
| 'No benchmark named "%s". Do you mean any of those benchmarks ' |
| 'below?\n%s' % ( |
| options.benchmark_name, '\n'.join(possible_benchmark_names))) |
| is_benchmark_disabled, reason = cls.IsBenchmarkDisabledOnTrybotPlatform( |
| benchmark_class, options.trybot) |
| also_run_disabled_option = '--also-run-disabled-tests' |
| if is_benchmark_disabled and also_run_disabled_option not in extra_args: |
| parser.error('%s To run the benchmark on trybot anyway, add ' |
| '%s option.' % (reason, also_run_disabled_option)) |
| |
| @classmethod |
| def IsBenchmarkDisabledOnTrybotPlatform(cls, benchmark_class, trybot_name): |
| """Return whether benchmark will be disabled on trybot platform. |
| |
| Note that we cannot tell with certainty whether the benchmark will be |
| disabled on the trybot platform since the disable logic in ShouldDisable() |
| can be very dynamic and can only be verified on the trybot server platform. |
| |
| We are biased on the side of enabling the benchmark, and attempt to |
| early discover whether the benchmark will be disabled as our best. |
| |
| It should never be the case that the benchmark will be enabled on the test |
| platform but this method returns True. |
| |
| Returns: |
| A tuple (is_benchmark_disabled, reason) whereas |is_benchmark_disabled| is |
| a boolean that tells whether we are sure that the benchmark will be |
| disabled, and |reason| is a string that shows the reason why we think the |
| benchmark is disabled for sure. |
| """ |
| benchmark_name = benchmark_class.Name() |
| benchmark_disabled_strings = decorators.GetDisabledAttributes( |
| benchmark_class) |
| if 'all' in benchmark_disabled_strings: |
| return True, 'Benchmark %s is disabled on all platform.' % benchmark_name |
| if trybot_name == 'all': |
| return False, '' |
| trybot_platform = _GetBotPlatformFromTrybotName(trybot_name) |
| if trybot_platform in benchmark_disabled_strings: |
| return True, ( |
| "Benchmark %s is disabled on %s, and trybot's platform is %s." % |
| (benchmark_name, ', '.join(benchmark_disabled_strings), |
| trybot_platform)) |
| benchmark_enabled_strings = decorators.GetEnabledAttributes(benchmark_class) |
| if (benchmark_enabled_strings and |
| trybot_platform not in benchmark_enabled_strings and |
| 'all' not in benchmark_enabled_strings): |
| return True, ( |
| "Benchmark %s is only enabled on %s, and trybot's platform is %s." % |
| (benchmark_name, ', '.join(benchmark_enabled_strings), |
| trybot_platform)) |
| if benchmark_class.ShouldDisable != benchmark.Benchmark.ShouldDisable: |
| logging.warning( |
| 'Benchmark %s has ShouldDisable() method defined. If your trybot run ' |
| 'does not produce any results, it is possible that the benchmark ' |
| 'is disabled on the target trybot platform.', benchmark_name) |
| return False, '' |
| |
| @classmethod |
| def AddCommandLineArgs(cls, parser, environment): |
| del environment # unused |
| available_bots = _GetTrybotList(cls._GetBuilderList()) |
| parser.add_argument( |
| 'trybot', choices=available_bots, |
| help=('specify which bots to run telemetry benchmarks on. ' |
| ' Allowed values are:\n' + '\n'.join(available_bots)), |
| metavar='<trybot name>') |
| parser.add_argument( |
| 'benchmark_name', type=str, |
| help=('specify which benchmark to run. To see all available benchmarks,' |
| ' run `run_benchmark list`'), |
| metavar='<benchmark name>') |
| parser.add_argument( |
| '--repo_path', type=str, default=CHROMIUM_SRC_PATH, |
| help=("""specify the repo path where the patch is created.' |
| This argument should only be used if the changes are made outside chromium repo. |
| E.g., |
| 1) Assume you are running run_benchmarks command from $HOME/cr/src/ direcory:' |
| a) If your changes are in $HOME/cr/src/v8, then --repo_path=v8 or |
| --repo-path=$HOME/cr/src/v8 |
| b) If your changes are in $HOME/cr/src/third_party/catapult, then |
| --repo_path=third_party/catapult or |
| --repo_path = $HOME/cr/src/third_party/catapult' |
| c) If your changes are not relative to src/ e.g. you created changes in some |
| other direcotry say $HOME/mydir/v8/v8/, then the |
| --repo_path=$HOME/mydir/v8/v8 |
| 2) Assume you are running run_benchmarks command not relatvie to src i.e., |
| you are running from $HOME/mydir/ direcory:' |
| a) If your changes are in $HOME/cr/src/v8, then --repo-path=$HOME/cr/src/v8 |
| b) If your changes are in $HOME/cr/src/third_party/catapult, then |
| --repo_path=$HOME/cr/src/third_party/catapult' |
| c) If your changes are in $HOME/mydir/v8/v8/, then the |
| --repo_path=$HOME/mydir/v8/v8 or --repo_path=v8/v8"""), |
| metavar='<repo path>') |
| parser.add_argument( |
| '--deps_revision', type=str, default=None, |
| help=('specify DEPS revision to modify DEPS entry in Chromium to a ' |
| 'certain pushed revision.\n' |
| 'This revision overrides value in DEPS on TOT Chromium for the ' |
| 'repo specified in --repo_path.\nIt is applied for both with and ' |
| 'wihout patch.'), |
| metavar='<deps revision>') |
| |
| def Run(self, options, extra_args=None): |
| """Sends a tryjob to a perf trybot. |
| |
| This creates a branch, telemetry-tryjob, switches to that branch, edits |
| the bisect config, commits it, uploads the CL to rietveld, and runs a |
| tryjob on the given bot. |
| """ |
| if extra_args is None: |
| extra_args = [] |
| self._InitializeBuilderNames(options.trybot) |
| |
| return self._AttemptTryjob(options, extra_args) |
| |
| def _GetPerfConfig(self, bot_platform, arguments): |
| """Generates the perf config for try job. |
| |
| Args: |
| bot_platform: Name of the platform to be generated. |
| arguments: Command line arguments. |
| |
| Returns: |
| A dictionary with perf config parameters. |
| """ |
| # To make sure that we don't mutate the original args |
| arguments = arguments[:] |
| |
| # Always set verbose logging for later debugging |
| if '-v' not in arguments and '--verbose' not in arguments: |
| arguments.append('--verbose') |
| |
| # Generate the command line for the perf trybots |
| target_arch = 'ia32' |
| if any(arg == '--chrome-root' or arg.startswith('--chrome-root=') for arg |
| in arguments): |
| raise ValueError( |
| 'Trybot does not suport --chrome-root option set directly ' |
| 'through command line since it may contain references to your local ' |
| 'directory') |
| |
| arguments.insert(0, 'src/tools/perf/run_benchmark') |
| if bot_platform == 'android': |
| arguments.insert(1, '--browser=android-chromium') |
| elif any('x64' in bot for bot in self._builder_names[bot_platform]): |
| arguments.insert(1, '--browser=release_x64') |
| target_arch = 'x64' |
| else: |
| |
| arguments.insert(1, '--browser=release') |
| |
| command = ' '.join(arguments) |
| |
| return { |
| 'command': command, |
| 'repeat_count': '1', |
| 'max_time_minutes': '120', |
| 'truncate_percent': '0', |
| 'target_arch': target_arch, |
| } |
| |
| def _GetRepoAndBranchName(self, repo_path): |
| """Gets the repository name and working branch name. |
| |
| Args: |
| repo_path: Path to the repository. |
| |
| Returns: |
| Repository name and branch name as tuple. |
| |
| Raises: |
| TrybotError: This exception is raised for the following cases: |
| 1. Try job is for non-git repository or in invalid branch. |
| 2. Un-committed changes in the current branch. |
| 3. No local commits in the current branch. |
| """ |
| # If command runs successfully, then the output will be repo root path. |
| # and current branch name. |
| output = RunGit(['rev-parse', '--abbrev-ref', '--show-toplevel', 'HEAD'], |
| ('%s is not a git repository, must be in a git repository ' |
| 'to send changes to trybots' % os.getcwd())) |
| |
| repo_info = output.split() |
| # Assuming the base directory name is same as repo project name set in |
| # codereviews.settings file. |
| repo_name = os.path.basename(repo_info[0]).strip() |
| branch_name = repo_info[1].strip() |
| |
| if branch_name == 'HEAD': |
| raise TrybotError('Not on a valid branch, looks like branch ' |
| 'is dettached. [branch:%s]' % branch_name) |
| |
| # Check if the tree is dirty: make sure the index is up to date and then |
| # run diff-index |
| RunGit(['update-index', '--refresh', '-q'], ignore_return_code=True) |
| output = RunGit(['diff-index', 'HEAD']) |
| if output: |
| raise TrybotError( |
| 'Cannot send a try job with a dirty tree.\nPlease commit locally and ' |
| 'upload your changes to rietveld in %s repository.' % repo_path) |
| |
| return (repo_name, branch_name) |
| |
| def _GetBaseGitHashForRepo(self, branch_name, git_url): |
| """Gets the base revision for the repo on which local changes are made. |
| |
| Finds the upstream of the current branch that it is set to and gets |
| the HEAD revision from upstream. This also checks if the remote URL on |
| the upstream is supported by Perf Try job. |
| |
| Args: |
| branch_name: Current working branch name. |
| git_url: Remote URL of the repo. |
| |
| Returns: |
| Git hash of the HEAD revision from the upstream branch. |
| |
| Raises: |
| TrybotError: This exception is raised when a GIT command fails or if the |
| remote URL of the repo found is not supported. |
| """ |
| # Check if there is any upstream branch associated with current working |
| # branch, Once the upstream branch is found i.e., then validates the |
| # remote URL and then returns the HEAD revision from the remote branch. |
| while not self._IsRepoSupported(branch_name, git_url): |
| branch_name = RunGit( |
| ['rev-parse', '--abbrev-ref', '%s@{upstream}' % branch_name], |
| 'Failed to get upstream branch name.') |
| |
| return RunGit( |
| ['rev-parse', '%s@{upstream}' % branch_name], |
| 'Failed to get base revision hash on upstream.') |
| |
| def _IsRepoSupported(self, current_branch, repo_git_url): |
| cur_remote = RunGit( |
| ['config', 'branch.%s.remote'% current_branch], |
| 'Failed to get branch.%s.remote from git config' % current_branch) |
| cur_remote = cur_remote.strip() |
| if cur_remote == '.': |
| return False |
| cur_remote_url = RunGit( |
| ['config', 'remote.%s.url' % cur_remote], |
| 'Failed to get remote.%s.url from git config' % cur_remote) |
| if cur_remote_url.lower() == repo_git_url: |
| return True |
| raise TrybotError('URL %s on remote %s is not recognized on branch.'% ( |
| cur_remote_url, cur_remote)) |
| |
| def _GetChangeList(self): |
| """Gets the codereview URL for the current changes.""" |
| temp_file = None |
| json_output = None |
| try: |
| fd, temp_file = tempfile.mkstemp(suffix='.json', prefix='perf_try_cl') |
| os.close(fd) |
| RunGit(['cl', 'issue', '--json', temp_file], |
| 'Failed to run "git cl issue" command.') |
| with open(temp_file, 'r') as f: |
| json_output = json.load(f) |
| finally: |
| try: |
| if temp_file: |
| os.remove(temp_file) |
| except OSError: |
| pass |
| |
| # Make sure the local commits are uploaded to rietveld. |
| if not json_output.get('issue'): |
| raise TrybotError( |
| 'PLEASE NOTE: The workflow for Perf Try jobs is changed. ' |
| 'In order to run the perf try job, you must first upload your ' |
| 'changes to rietveld.') |
| return json_output.get('issue_url') |
| |
| def _AttemptTryjob(self, options, extra_args): |
| """Attempts to run a tryjob from a repo directory. |
| |
| Args: |
| options: Command line arguments to run benchmark. |
| extra_args: Extra arugments to run benchmark. |
| |
| Returns: |
| If successful returns 0, otherwise 1. |
| """ |
| original_workdir = os.getcwd() |
| repo_path = os.path.abspath(options.repo_path) |
| try: |
| # Check the existence of repo path. |
| if not os.path.exists(repo_path): |
| raise TrybotError('Repository path "%s" does not exist, please check ' |
| 'the value of <repo_path> argument.' % repo_path) |
| # Change to the repo directory. |
| os.chdir(repo_path) |
| repo_name, branch_name = self._GetRepoAndBranchName(repo_path) |
| |
| repo_info = REPO_INFO_MAP.get(repo_name, None) |
| if not repo_info: |
| raise TrybotError('Unsupported repository %s' % repo_name) |
| |
| deps_override = None |
| if repo_name != 'src': |
| if not options.deps_revision: |
| options.deps_revision = self._GetBaseGitHashForRepo( |
| branch_name, repo_info.get('url')) |
| deps_override = {repo_info.get('src'): options.deps_revision} |
| |
| rietveld_url = self._GetChangeList() |
| print ('\nRunning try job....\nview progress here %s.' |
| '\n\tRepo Name: %s\n\tPath: %s\n\tBranch: %s' % ( |
| rietveld_url, repo_name, repo_path, branch_name)) |
| |
| for bot_platform in self._builder_names: |
| if not self._builder_names[bot_platform]: |
| logging.warning('No builder is found for %s', bot_platform) |
| continue |
| try: |
| arguments = [options.benchmark_name] + extra_args |
| self._RunTryJob(bot_platform, arguments, deps_override) |
| # Even if git cl try throws TrybotError exception for any platform, |
| # keep sending try jobs to other platforms. |
| except TrybotError, err: |
| print err |
| except TrybotError, error: |
| print error |
| return 1 |
| finally: |
| # Restore to original working directory. |
| os.chdir(original_workdir) |
| return 0 |
| |
| def _RunTryJob(self, bot_platform, arguments, deps_override): |
| """Executes perf try job with benchmark test properties. |
| |
| Args: |
| bot_platform: Name of the platform to be generated. |
| arguments: Command line arguments. |
| deps_override: DEPS revision if needs to be overridden. |
| |
| Raises: |
| TrybotError: When trybot fails to upload CL or run git try. |
| """ |
| config = self._GetPerfConfig(bot_platform, arguments) |
| |
| # Generate git try command for available bots. |
| git_try_command = ['cl', 'try', '-m', 'tryserver.chromium.perf'] |
| |
| # Add Perf Test config to git try --properties arg. |
| git_try_command.extend(['-p', 'perf_try_config=%s' % json.dumps(config)]) |
| |
| error_msg_on_fail = 'Could not try CL for %s' % bot_platform |
| # Add deps overrides to git try --properties arg. |
| if deps_override: |
| git_try_command.extend([ |
| '-p', 'deps_revision_overrides=%s' % json.dumps(deps_override)]) |
| error_msg_on_fail += ' with DEPS override (%s)' % deps_override |
| for bot in self._builder_names[bot_platform]: |
| git_try_command.extend(['-b', bot]) |
| |
| RunGit(git_try_command, error_msg_on_fail) |
| print 'Perf Try job sent to rietveld for %s platform.' % bot_platform |