Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import argparse | 5 import argparse |
| 6 import json | |
| 7 import logging | |
| 6 import os | 8 import os |
| 7 import logging | |
| 8 import platform | 9 import platform |
| 9 import re | 10 import re |
| 10 import subprocess | 11 import subprocess |
| 11 import urllib2 | 12 import urllib2 |
| 12 import json | 13 |
| 13 | 14 |
| 14 from core import path_util | 15 from core import path_util |
| 15 | 16 |
| 16 from telemetry import benchmark | 17 from telemetry import benchmark |
| 17 from telemetry import decorators | 18 from telemetry import decorators |
| 18 from telemetry.core import discover | 19 from telemetry.core import discover |
| 19 from telemetry.util import command_line | 20 from telemetry.util import command_line |
| 20 from telemetry.util import matching | 21 from telemetry.util import matching |
| 21 | 22 |
| 22 | 23 |
| 23 CHROMIUM_CONFIG_FILENAME = 'tools/run-perf-test.cfg' | |
| 24 BLINK_CONFIG_FILENAME = 'Tools/run-perf-test.cfg' | |
| 25 SUCCESS, NO_CHANGES, ERROR = range(3) | |
| 26 # Unsupported Perf bisect bots. | 24 # Unsupported Perf bisect bots. |
| 27 EXCLUDED_BOTS = { | 25 EXCLUDED_BOTS = { |
| 28 'win_xp_perf_bisect', # Goma issues: crbug.com/330900 | 26 'win_xp_perf_bisect', # Goma issues: crbug.com/330900 |
| 29 'win_perf_bisect_builder', | 27 'win_perf_bisect_builder', |
| 30 'win64_nv_tester', | 28 'win64_nv_tester', |
| 31 'winx64_bisect_builder', | 29 'winx64_bisect_builder', |
| 32 'linux_perf_bisect_builder', | 30 'linux_perf_bisect_builder', |
| 33 'mac_perf_bisect_builder', | 31 'mac_perf_bisect_builder', |
| 34 'android_perf_bisect_builder', | 32 'android_perf_bisect_builder', |
| 35 'android_arm64_perf_bisect_builder', | 33 'android_arm64_perf_bisect_builder', |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 50 | 48 |
| 51 INCLUDE_BOTS = [ | 49 INCLUDE_BOTS = [ |
| 52 'all', | 50 'all', |
| 53 'all-win', | 51 'all-win', |
| 54 'all-mac', | 52 'all-mac', |
| 55 'all-linux', | 53 'all-linux', |
| 56 'all-android' | 54 'all-android' |
| 57 ] | 55 ] |
| 58 | 56 |
| 59 # Default try bot to use incase builbot is unreachable. | 57 # Default try bot to use incase builbot is unreachable. |
| 60 DEFAULT_TRYBOTS = [ | 58 DEFAULT_TRYBOTS = [ |
| 61 'linux_perf_bisect', | 59 'linux_perf_bisect', |
| 62 'mac_10_11_perf_bisect', | 60 'mac_10_11_perf_bisect', |
| 63 'winx64_10_perf_bisect', | 61 'winx64_10_perf_bisect', |
| 64 'android_s5_perf_bisect', | 62 'android_s5_perf_bisect', |
| 65 ] | 63 ] |
| 66 | 64 |
| 67 assert not set(DEFAULT_TRYBOTS) & set(EXCLUDED_BOTS), ( 'A trybot cannot ' | 65 CHROMIUM_SRC_PATH = path_util.GetChromiumSrcDir() |
| 68 'present in both Default as well as Excluded bots lists.') | 66 |
| 67 assert not set(DEFAULT_TRYBOTS) & set(EXCLUDED_BOTS), ( | |
| 68 'A trybot cannot present in both Default as well as Excluded bots lists.') | |
| 69 | |
| 69 | 70 |
| 70 class TrybotError(Exception): | 71 class TrybotError(Exception): |
| 71 | 72 |
| 72 def __str__(self): | 73 def __str__(self): |
| 73 return '%s\nError running tryjob.' % self.args[0] | 74 return '(ERROR) Perf Try Job: %s' % self.args[0] |
| 74 | 75 |
| 75 | 76 |
| 76 def _GetTrybotList(builders): | 77 def _GetTrybotList(builders): |
| 77 builders = ['%s' % bot.replace('_perf_bisect', '').replace('_', '-') | 78 builders = ['%s' % bot.replace('_perf_bisect', '').replace('_', '-') |
| 78 for bot in builders] | 79 for bot in builders] |
| 79 builders.extend(INCLUDE_BOTS) | 80 builders.extend(INCLUDE_BOTS) |
| 80 return sorted(builders) | 81 return sorted(builders) |
| 81 | 82 |
| 82 | 83 |
| 83 def _GetBotPlatformFromTrybotName(trybot_name): | 84 def _GetBotPlatformFromTrybotName(trybot_name): |
| 84 os_names = ['linux', 'android', 'mac', 'win'] | 85 os_names = ['linux', 'android', 'mac', 'win'] |
| 85 try: | 86 try: |
| 86 return next(b for b in os_names if b in trybot_name) | 87 return next(b for b in os_names if b in trybot_name) |
| 87 except StopIteration: | 88 except StopIteration: |
| 88 raise TrybotError('Trybot "%s" unsupported for tryjobs.' % trybot_name) | 89 raise TrybotError('Trybot "%s" unsupported for tryjobs.' % trybot_name) |
| 89 | 90 |
| 90 | 91 |
| 91 def _GetBuilderNames(trybot_name, builders): | 92 def _GetBuilderNames(trybot_name, builders): |
| 92 """ Return platform and its available bot name as dictionary.""" | 93 """Return platform and its available bot name as dictionary.""" |
| 93 os_names = ['linux', 'android', 'mac', 'win'] | 94 os_names = ['linux', 'android', 'mac', 'win'] |
| 94 if 'all' not in trybot_name: | 95 if 'all' not in trybot_name: |
| 95 bot = ['%s_perf_bisect' % trybot_name.replace('-', '_')] | 96 bot = ['%s_perf_bisect' % trybot_name.replace('-', '_')] |
| 96 bot_platform = _GetBotPlatformFromTrybotName(trybot_name) | 97 bot_platform = _GetBotPlatformFromTrybotName(trybot_name) |
| 97 if 'x64' in trybot_name: | 98 if 'x64' in trybot_name: |
| 98 bot_platform += '-x64' | 99 bot_platform += '-x64' |
| 99 return {bot_platform: bot} | 100 return {bot_platform: bot} |
| 100 | 101 |
| 101 platform_and_bots = {} | 102 platform_and_bots = {} |
| 102 for os_name in os_names: | 103 for os_name in os_names: |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 118 if 'all-mac' in trybot_name: | 119 if 'all-mac' in trybot_name: |
| 119 return {'mac': platform_and_bots['mac']} | 120 return {'mac': platform_and_bots['mac']} |
| 120 if 'all-android' in trybot_name: | 121 if 'all-android' in trybot_name: |
| 121 return {'android': platform_and_bots['android']} | 122 return {'android': platform_and_bots['android']} |
| 122 if 'all-linux' in trybot_name: | 123 if 'all-linux' in trybot_name: |
| 123 return {'linux': platform_and_bots['linux']} | 124 return {'linux': platform_and_bots['linux']} |
| 124 | 125 |
| 125 return platform_and_bots | 126 return platform_and_bots |
| 126 | 127 |
| 127 | 128 |
| 128 def _RunProcess(cmd): | 129 _GIT_CMD = 'git' |
| 129 logging.debug('Running process: "%s"', ' '.join(cmd)) | |
| 130 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| 131 out, err = proc.communicate() | |
| 132 returncode = proc.poll() | |
| 133 return (returncode, out, err) | |
| 134 | 130 |
| 135 | 131 |
| 136 _GIT_CMD = 'git' | |
| 137 if platform.system() == 'Windows': | 132 if platform.system() == 'Windows': |
| 138 # On windows, the git command is installed as 'git.bat' | 133 # On windows, the git command is installed as 'git.bat' |
| 139 _GIT_CMD = 'git.bat' | 134 _GIT_CMD = 'git.bat' |
| 140 | 135 |
| 141 | 136 |
| 137 def RunGit(cmd, msg_on_error='', ignore_return_code=False): | |
| 138 """Runs the GIT command with the given arguments. | |
|
eakuefner
2016/08/30 17:33:49
nit: s/GIT/git/
prasadv
2016/08/30 18:49:40
Done.
| |
| 139 | |
| 140 Args: | |
| 141 cmd: GIT command arguments. | |
| 142 msg_on_error: Message to be displayed on GIT command error. | |
| 143 ignore_return_code: Ignores the return code for GIT command | |
|
eakuefner
2016/08/30 17:33:49
nit: end with period?
prasadv
2016/08/30 18:49:40
Done.
| |
| 144 | |
| 145 Returns: | |
| 146 The output of the GIT command as string. | |
| 147 | |
| 148 Raises: | |
| 149 TrybotError: This exception is raised when GIT command fails. | |
| 150 """ | |
| 151 proc = subprocess.Popen( | |
| 152 [_GIT_CMD] + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
|
eakuefner
2016/08/30 17:33:50
Can you just write [_GIT_CMD, cmd]?
prasadv
2016/08/30 18:49:40
cmd is a list of arguments for git command and I t
| |
| 153 output, err = proc.communicate() | |
| 154 returncode = proc.poll() | |
| 155 if returncode: | |
| 156 if ignore_return_code: | |
| 157 return None | |
| 158 raise TrybotError('%s. \n%s' % (msg_on_error, err)) | |
| 159 | |
| 160 return output | |
| 161 | |
| 162 | |
| 142 class Trybot(command_line.ArgParseCommand): | 163 class Trybot(command_line.ArgParseCommand): |
| 143 """ Run telemetry perf benchmark on trybot """ | 164 """Run telemetry perf benchmark on trybot.""" |
| 144 | 165 |
| 145 usage = 'botname benchmark_name [<benchmark run options>]' | 166 usage = 'botname benchmark_name [<benchmark run options>]' |
| 146 _builders = None | 167 _builders = None |
| 147 | 168 |
| 148 def __init__(self): | 169 def __init__(self): |
| 149 self._builder_names = None | 170 self._builder_names = None |
| 150 | 171 |
| 151 @classmethod | 172 @classmethod |
| 152 def _GetBuilderList(cls): | 173 def _GetBuilderList(cls): |
| 153 if not cls._builders: | 174 if not cls._builders: |
| 154 try: | 175 try: |
| 155 f = urllib2.urlopen( | 176 f = urllib2.urlopen( |
| 156 ('https://build.chromium.org/p/tryserver.chromium.perf/json/' | 177 ('https://build.chromium.org/p/tryserver.chromium.perf/json/' |
| 157 'builders'), | 178 'builders'), |
| 158 timeout=5) | 179 timeout=5) |
| 159 # In case of any kind of exception, allow tryjobs to use default trybots. | 180 # In case of any kind of exception, allow tryjobs to use default trybots. |
| 160 # Possible exception are ssl.SSLError, urllib2.URLError, | 181 # Possible exception are ssl.SSLError, urllib2.URLError, |
| 161 # socket.timeout, socket.error. | 182 # socket.timeout, socket.error. |
| 162 except Exception: | 183 except Exception: # pylint: disable=broad-except |
| 163 # Incase of any exception return default trybots. | 184 # Incase of any exception return default trybots. |
| 164 print ('WARNING: Unable to reach builbot to retrieve trybot ' | 185 print ('WARNING: Unable to reach builbot to retrieve trybot ' |
| 165 'information, tryjob will use default trybots.') | 186 'information, tryjob will use default trybots.') |
| 166 cls._builders = DEFAULT_TRYBOTS | 187 cls._builders = DEFAULT_TRYBOTS |
| 167 else: | 188 else: |
| 168 builders = json.loads(f.read()).keys() | 189 builders = json.loads(f.read()).keys() |
| 169 # Exclude unsupported bots like win xp and some dummy bots. | 190 # Exclude unsupported bots like win xp and some dummy bots. |
| 170 cls._builders = [bot for bot in builders if bot not in EXCLUDED_BOTS] | 191 cls._builders = [bot for bot in builders if bot not in EXCLUDED_BOTS] |
| 171 | 192 |
| 172 return cls._builders | 193 return cls._builders |
| 173 | 194 |
| 174 def _InitializeBuilderNames(self, trybot): | 195 def _InitializeBuilderNames(self, trybot): |
| 175 self._builder_names = _GetBuilderNames(trybot, self._GetBuilderList()) | 196 self._builder_names = _GetBuilderNames(trybot, self._GetBuilderList()) |
| 176 | 197 |
| 177 @classmethod | 198 @classmethod |
| 178 def CreateParser(cls): | 199 def CreateParser(cls): |
| 179 parser = argparse.ArgumentParser( | 200 parser = argparse.ArgumentParser( |
| 180 ('Run telemetry benchmarks on trybot. You can add all the benchmark ' | 201 ('Run telemetry benchmarks on trybot. You can add all the benchmark ' |
| 181 'options available except the --browser option'), | 202 'options available except the --browser option'), |
| 182 formatter_class=argparse.RawTextHelpFormatter) | 203 formatter_class=argparse.RawTextHelpFormatter) |
| 183 return parser | 204 return parser |
| 184 | 205 |
| 185 @classmethod | 206 @classmethod |
| 186 def ProcessCommandLineArgs(cls, parser, options, extra_args, environment): | 207 def ProcessCommandLineArgs(cls, parser, options, extra_args, environment): |
| 187 del environment # unused | 208 del environment # unused |
| 188 for arg in extra_args: | 209 for arg in extra_args: |
| 189 if arg == '--browser' or arg.startswith('--browser='): | 210 if arg == '--browser' or arg.startswith('--browser='): |
| 190 parser.error('--browser=... is not allowed when running trybot.') | 211 parser.error('--browser=... is not allowed when running trybot.') |
| 191 all_benchmarks = discover.DiscoverClasses( | 212 all_benchmarks = discover.DiscoverClasses( |
| 192 start_dir=path_util.GetPerfBenchmarksDir(), | 213 start_dir=path_util.GetPerfBenchmarksDir(), |
| 193 top_level_dir=path_util.GetPerfDir(), | 214 top_level_dir=path_util.GetPerfDir(), |
| 194 base_class=benchmark.Benchmark).values() | 215 base_class=benchmark.Benchmark).values() |
| 195 all_benchmark_names = [b.Name() for b in all_benchmarks] | 216 all_benchmark_names = [b.Name() for b in all_benchmarks] |
| 196 all_benchmarks_by_names = {b.Name(): b for b in all_benchmarks} | 217 all_benchmarks_by_names = {b.Name(): b for b in all_benchmarks} |
| 197 benchmark_class = all_benchmarks_by_names.get(options.benchmark_name, None) | 218 benchmark_class = all_benchmarks_by_names.get(options.benchmark_name, None) |
| 198 if not benchmark_class: | 219 if not benchmark_class: |
| 199 possible_benchmark_names = matching.GetMostLikelyMatchedObject( | 220 possible_benchmark_names = matching.GetMostLikelyMatchedObject( |
| 200 all_benchmark_names, options.benchmark_name) | 221 all_benchmark_names, options.benchmark_name) |
| 201 parser.error( | 222 parser.error( |
| 202 'No benchmark named "%s". Do you mean any of those benchmarks ' | 223 'No benchmark named "%s". Do you mean any of those benchmarks ' |
| 203 'below?\n%s' % | 224 'below?\n%s' % ( |
| 204 (options.benchmark_name, '\n'.join(possible_benchmark_names))) | 225 options.benchmark_name, '\n'.join(possible_benchmark_names))) |
| 205 is_benchmark_disabled, reason = cls.IsBenchmarkDisabledOnTrybotPlatform( | 226 is_benchmark_disabled, reason = cls.IsBenchmarkDisabledOnTrybotPlatform( |
| 206 benchmark_class, options.trybot) | 227 benchmark_class, options.trybot) |
| 207 also_run_disabled_option = '--also-run-disabled-tests' | 228 also_run_disabled_option = '--also-run-disabled-tests' |
| 208 if is_benchmark_disabled and also_run_disabled_option not in extra_args: | 229 if is_benchmark_disabled and also_run_disabled_option not in extra_args: |
| 209 parser.error('%s To run the benchmark on trybot anyway, add ' | 230 parser.error('%s To run the benchmark on trybot anyway, add ' |
| 210 '%s option.' % (reason, also_run_disabled_option)) | 231 '%s option.' % (reason, also_run_disabled_option)) |
| 211 | 232 |
| 212 @classmethod | 233 @classmethod |
| 213 def IsBenchmarkDisabledOnTrybotPlatform(cls, benchmark_class, trybot_name): | 234 def IsBenchmarkDisabledOnTrybotPlatform(cls, benchmark_class, trybot_name): |
| 214 """ Return whether benchmark will be disabled on trybot platform. | 235 """Return whether benchmark will be disabled on trybot platform. |
| 215 | 236 |
| 216 Note that we cannot tell with certainty whether the benchmark will be | 237 Note that we cannot tell with certainty whether the benchmark will be |
| 217 disabled on the trybot platform since the disable logic in ShouldDisable() | 238 disabled on the trybot platform since the disable logic in ShouldDisable() |
| 218 can be very dynamic and can only be verified on the trybot server platform. | 239 can be very dynamic and can only be verified on the trybot server platform. |
| 219 | 240 |
| 220 We are biased on the side of enabling the benchmark, and attempt to | 241 We are biased on the side of enabling the benchmark, and attempt to |
| 221 early discover whether the benchmark will be disabled as our best. | 242 early discover whether the benchmark will be disabled as our best. |
| 222 | 243 |
| 223 It should never be the case that the benchmark will be enabled on the test | 244 It should never be the case that the benchmark will be enabled on the test |
| 224 platform but this method returns True. | 245 platform but this method returns True. |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 275 def Run(self, options, extra_args=None): | 296 def Run(self, options, extra_args=None): |
| 276 """Sends a tryjob to a perf trybot. | 297 """Sends a tryjob to a perf trybot. |
| 277 | 298 |
| 278 This creates a branch, telemetry-tryjob, switches to that branch, edits | 299 This creates a branch, telemetry-tryjob, switches to that branch, edits |
| 279 the bisect config, commits it, uploads the CL to rietveld, and runs a | 300 the bisect config, commits it, uploads the CL to rietveld, and runs a |
| 280 tryjob on the given bot. | 301 tryjob on the given bot. |
| 281 """ | 302 """ |
| 282 if extra_args is None: | 303 if extra_args is None: |
| 283 extra_args = [] | 304 extra_args = [] |
| 284 self._InitializeBuilderNames(options.trybot) | 305 self._InitializeBuilderNames(options.trybot) |
| 285 | 306 try: |
| 286 arguments = [options.benchmark_name] + extra_args | 307 self._AttemptTryjob(CHROMIUM_SRC_PATH, options, extra_args) |
| 287 | 308 except TrybotError, error: |
| 288 # First check if there are chromium changes to upload. | 309 print error |
|
nednguyen
2016/08/29 23:15:22
Why print error then return 1? Wouldn't not catchi
prasadv
2016/08/30 18:49:40
Most of the TryjobError exceptions are raised when
| |
| 289 status = self._AttemptTryjob(CHROMIUM_CONFIG_FILENAME, arguments) | 310 return 1 |
| 290 if status not in [SUCCESS, ERROR]: | |
| 291 # If we got here, there are no chromium changes to upload. Try blink. | |
| 292 os.chdir('third_party/WebKit/') | |
| 293 status = self._AttemptTryjob(BLINK_CONFIG_FILENAME, arguments) | |
| 294 os.chdir('../..') | |
| 295 if status not in [SUCCESS, ERROR]: | |
| 296 logging.error('No local changes found in chromium or blink trees. ' | |
| 297 'browser=%s argument sends local changes to the ' | |
| 298 'perf trybot(s): %s.', options.trybot, | |
| 299 self._builder_names.values()) | |
| 300 return 1 | |
| 301 return 0 | 311 return 0 |
| 302 | 312 |
| 303 def _UpdateConfigAndRunTryjob(self, bot_platform, cfg_file_path, arguments): | |
| 304 """Updates perf config file, uploads changes and excutes perf try job. | |
| 305 | |
| 306 Args: | |
| 307 bot_platform: Name of the platform to be generated. | |
| 308 cfg_file_path: Perf config file path. | |
| 309 | |
| 310 Returns: | |
| 311 (result, msg) where result is one of: | |
| 312 SUCCESS if a tryjob was sent | |
| 313 NO_CHANGES if there was nothing to try, | |
| 314 ERROR if a tryjob was attempted but an error encountered | |
| 315 and msg is an error message if an error was encountered, or rietveld | |
| 316 url if success, otherwise throws TrybotError exception. | |
| 317 """ | |
| 318 config = self._GetPerfConfig(bot_platform, arguments) | |
| 319 config_to_write = 'config = %s' % json.dumps( | |
| 320 config, sort_keys=True, indent=2, separators=(',', ': ')) | |
| 321 | |
| 322 try: | |
| 323 with open(cfg_file_path, 'r') as config_file: | |
| 324 if config_to_write == config_file.read(): | |
| 325 return NO_CHANGES, '' | |
| 326 except IOError: | |
| 327 msg = 'Cannot find %s. Please run from src dir.' % cfg_file_path | |
| 328 return (ERROR, msg) | |
| 329 | |
| 330 with open(cfg_file_path, 'w') as config_file: | |
| 331 config_file.write(config_to_write) | |
| 332 # Commit the config changes locally. | |
| 333 returncode, out, err = _RunProcess( | |
| 334 [_GIT_CMD, 'commit', '-a', '-m', 'bisect config: %s' % bot_platform]) | |
| 335 if returncode: | |
| 336 raise TrybotError('Could not commit bisect config change for %s,' | |
| 337 ' error %s' % (bot_platform, err)) | |
| 338 # Upload the CL to rietveld and run a try job. | |
| 339 returncode, out, err = _RunProcess([ | |
| 340 _GIT_CMD, 'cl', 'upload', '-f', '--bypass-hooks', '-m', | |
| 341 'CL for perf tryjob on %s' % bot_platform | |
| 342 ]) | |
| 343 if returncode: | |
| 344 raise TrybotError('Could not upload to rietveld for %s, error %s' % | |
| 345 (bot_platform, err)) | |
| 346 | |
| 347 match = re.search(r'https://codereview.chromium.org/[\d]+', out) | |
| 348 if not match: | |
| 349 raise TrybotError('Could not upload CL to rietveld for %s! Output %s' % | |
| 350 (bot_platform, out)) | |
| 351 rietveld_url = match.group(0) | |
| 352 # Generate git try command for available bots. | |
| 353 git_try_command = [_GIT_CMD, 'cl', 'try', '-m', 'tryserver.chromium.perf'] | |
| 354 for bot in self._builder_names[bot_platform]: | |
| 355 git_try_command.extend(['-b', bot]) | |
| 356 returncode, out, err = _RunProcess(git_try_command) | |
| 357 if returncode: | |
| 358 raise TrybotError('Could not try CL for %s, error %s' % | |
| 359 (bot_platform, err)) | |
| 360 | |
| 361 return (SUCCESS, rietveld_url) | |
| 362 | |
| 363 def _GetPerfConfig(self, bot_platform, arguments): | 313 def _GetPerfConfig(self, bot_platform, arguments): |
| 364 """Generates the perf config for try job. | 314 """Generates the perf config for try job. |
| 365 | 315 |
| 366 Args: | 316 Args: |
| 367 bot_platform: Name of the platform to be generated. | 317 bot_platform: Name of the platform to be generated. |
| 318 arguments: Command line arguments. | |
| 368 | 319 |
| 369 Returns: | 320 Returns: |
| 370 A dictionary with perf config parameters. | 321 A dictionary with perf config parameters. |
| 371 """ | 322 """ |
| 372 # To make sure that we don't mutate the original args | 323 # To make sure that we don't mutate the original args |
| 373 arguments = arguments[:] | 324 arguments = arguments[:] |
| 374 | 325 |
| 375 # Always set verbose logging for later debugging | 326 # Always set verbose logging for later debugging |
| 376 if '-v' not in arguments and '--verbose' not in arguments: | 327 if '-v' not in arguments and '--verbose' not in arguments: |
| 377 arguments.append('--verbose') | 328 arguments.append('--verbose') |
| 378 | 329 |
| 379 # Generate the command line for the perf trybots | 330 # Generate the command line for the perf trybots |
| 380 target_arch = 'ia32' | 331 target_arch = 'ia32' |
| 381 if any(arg == '--chrome-root' or arg.startswith('--chrome-root=') for arg | 332 if any(arg == '--chrome-root' or arg.startswith('--chrome-root=') for arg |
| 382 in arguments): | 333 in arguments): |
| 383 raise ValueError( | 334 raise ValueError( |
| 384 'Trybot does not suport --chrome-root option set directly ' | 335 'Trybot does not suport --chrome-root option set directly ' |
| 385 'through command line since it may contain references to your local ' | 336 'through command line since it may contain references to your local ' |
| 386 'directory') | 337 'directory') |
| 387 if bot_platform in ['win', 'win-x64']: | |
| 388 arguments.insert(0, 'python tools\\perf\\run_benchmark') | |
| 389 else: | |
| 390 arguments.insert(0, './tools/perf/run_benchmark') | |
| 391 | 338 |
| 339 arguments.insert(0, 'src/tools/perf/run_benchmark') | |
| 392 if bot_platform == 'android': | 340 if bot_platform == 'android': |
| 393 arguments.insert(1, '--browser=android-chromium') | 341 arguments.insert(1, '--browser=android-chromium') |
| 394 elif any('x64' in bot for bot in self._builder_names[bot_platform]): | 342 elif any('x64' in bot for bot in self._builder_names[bot_platform]): |
| 395 arguments.insert(1, '--browser=release_x64') | 343 arguments.insert(1, '--browser=release_x64') |
| 396 target_arch = 'x64' | 344 target_arch = 'x64' |
| 397 else: | 345 else: |
| 346 | |
| 398 arguments.insert(1, '--browser=release') | 347 arguments.insert(1, '--browser=release') |
| 399 | 348 |
| 400 command = ' '.join(arguments) | 349 command = ' '.join(arguments) |
| 401 | 350 |
| 402 return { | 351 return { |
| 403 'command': command, | 352 'command': command, |
| 404 'repeat_count': '1', | 353 'repeat_count': '1', |
| 405 'max_time_minutes': '120', | 354 'max_time_minutes': '120', |
| 406 'truncate_percent': '0', | 355 'truncate_percent': '0', |
| 407 'target_arch': target_arch, | 356 'target_arch': target_arch, |
| 408 } | 357 } |
| 409 | 358 |
| 410 def _AttemptTryjob(self, cfg_file_path, arguments): | 359 def _GetRepoAndBranchName(self, repo_path): |
| 411 """Attempts to run a tryjob from the current directory. | 360 """Gets the repository name and working branch name. |
| 412 | |
| 413 This is run once for chromium, and if it returns NO_CHANGES, once for blink. | |
| 414 | 361 |
| 415 Args: | 362 Args: |
| 416 cfg_file_path: Path to the config file for the try job. | 363 repo_path: Path to the repository. |
| 417 | 364 |
| 418 Returns: | 365 Returns: |
| 419 Returns SUCCESS if a tryjob was sent, NO_CHANGES if there was nothing to | 366 Repository name and branch name as tuple. |
| 420 try, ERROR if a tryjob was attempted but an error encountered. | 367 |
| 368 Raises: | |
| 369 TrybotError: This exception is raised for the following cases, | |
|
eakuefner
2016/08/30 17:33:49
nit: cases:
prasadv
2016/08/30 18:49:40
Done.
| |
| 370 1. Try job is for non-git repository or in invalid branch. | |
| 371 2. Un-committed changes in the current branch. | |
| 372 3. No local commits in the current branch. | |
| 421 """ | 373 """ |
| 422 source_repo = 'chromium' | 374 # If command runs successfully, then the output will be repo root path. |
| 423 if cfg_file_path == BLINK_CONFIG_FILENAME: | 375 # and current branch name. |
| 424 source_repo = 'blink' | 376 output = RunGit(['rev-parse', '--abbrev-ref', '--show-toplevel', 'HEAD'], |
| 377 ('%s is not a git repository, must be in a git repository ' | |
| 378 'to send changes to trybots' % os.getcwd())) | |
| 425 | 379 |
| 426 # TODO(prasadv): This method is quite long, we should consider refactor | 380 repo_info = output.split() |
| 427 # this by extracting to helper methods. | 381 # Assuming the base directory name is same as repo project name set in |
| 428 returncode, original_branchname, err = _RunProcess( | 382 # codereviews.settings file. |
| 429 [_GIT_CMD, 'rev-parse', '--abbrev-ref', 'HEAD']) | 383 repo_name = os.path.basename(repo_info[0]).strip() |
| 430 if returncode: | 384 branch_name = repo_info[1].strip() |
| 431 msg = 'Must be in a git repository to send changes to trybots.' | |
| 432 if err: | |
| 433 msg += '\nGit error: %s' % err | |
| 434 logging.error(msg) | |
| 435 return ERROR | |
| 436 | 385 |
| 437 original_branchname = original_branchname.strip() | 386 if branch_name == 'HEAD': |
| 387 raise TrybotError('Not on a valid branch, looks like branch ' | |
| 388 'is dettached. [branch:%s]' % branch_name) | |
| 438 | 389 |
| 439 # Check if the tree is dirty: make sure the index is up to date and then | 390 # Check if the tree is dirty: make sure the index is up to date and then |
| 440 # run diff-index | 391 # run diff-index |
| 441 _RunProcess([_GIT_CMD, 'update-index', '--refresh', '-q']) | 392 RunGit(['update-index', '--refresh', '-q'], ignore_return_code=True) |
| 442 returncode, out, err = _RunProcess([_GIT_CMD, 'diff-index', 'HEAD']) | 393 output = RunGit(['diff-index', 'HEAD']) |
| 443 if out: | 394 if output: |
| 444 logging.error( | 395 raise TrybotError( |
| 445 'Cannot send a try job with a dirty tree. Commit locally first.') | 396 'Cannot send a try job with a dirty tree. Please commit ' |
| 446 return ERROR | 397 'your changes locally first in %s repository.' % repo_path) |
| 447 | 398 |
| 448 # Make sure the tree does have local commits. | 399 # Make sure the tree does have local commits. |
| 449 returncode, out, err = _RunProcess( | 400 output = RunGit(['footers', 'HEAD']) |
| 450 [_GIT_CMD, 'log', 'origin/master..HEAD']) | 401 if output: |
| 451 if not out: | 402 raise TrybotError('No local changes found in %s repository.' % repo_path) |
| 452 return NO_CHANGES | |
| 453 | 403 |
| 454 # Create/check out the telemetry-tryjob branch, and edit the configs | 404 return (repo_name, branch_name) |
| 455 # for the tryjob there. | 405 |
| 456 returncode, out, err = _RunProcess( | 406 def _AttemptTryjob(self, repo_path, options, extra_args): |
| 457 [_GIT_CMD, 'checkout', '-b', 'telemetry-tryjob']) | 407 """Attempts to run a tryjob from a repo directory. |
| 458 if returncode: | 408 |
| 459 logging.error('Error creating branch telemetry-tryjob. ' | 409 Args: |
| 460 'Please delete it if it exists.\n%s', err) | 410 repo_path: Path to the repository. |
| 461 return ERROR | 411 options: Command line arguments to run benchmark. |
| 462 try: | 412 extra_args: Extra arugments to run benchmark. |
| 463 returncode, out, err = _RunProcess( | 413 """ |
| 464 [_GIT_CMD, 'branch', '--set-upstream-to', 'origin/master']) | 414 repo_name, branch_name = self._GetRepoAndBranchName(repo_path) |
| 465 if returncode: | 415 |
| 466 logging.error('Error in git branch --set-upstream-to: %s', err) | 416 arguments = [options.benchmark_name] + extra_args |
| 467 return ERROR | 417 |
| 468 for bot_platform in self._builder_names: | 418 rietveld_url = self._UploadPatchToRietveld(repo_name) |
| 469 if not self._builder_names[bot_platform]: | 419 print ('\nUploaded try job to rietveld.\nview progress here %s.' |
| 470 logging.warning('No builder is found for %s', bot_platform) | 420 '\n\tRepo Name: %s\n\tPath: %s\n\tBranch: %s' % ( |
| 471 continue | 421 rietveld_url, repo_name, repo_path, branch_name)) |
| 472 try: | 422 |
| 473 results, output = self._UpdateConfigAndRunTryjob( | 423 for bot_platform in self._builder_names: |
| 474 bot_platform, cfg_file_path, arguments) | 424 if not self._builder_names[bot_platform]: |
| 475 if results == ERROR: | 425 logging.warning('No builder is found for %s', bot_platform) |
| 476 logging.error(output) | 426 continue |
| 477 return ERROR | 427 try: |
| 478 elif results == NO_CHANGES: | 428 self._RunTryJob(bot_platform, arguments) |
| 479 print ('Skip the try job run on %s because it has been tried in ' | 429 except TrybotError, err: |
| 480 'previous try job run. ' % bot_platform) | 430 print err |
| 481 else: | 431 |
| 482 print ('Uploaded %s try job to rietveld for %s platform. ' | 432 def _UploadPatchToRietveld(self, repo_name): |
| 483 'View progress at %s' % (source_repo, bot_platform, output)) | 433 """Uploads the patch to rietveld and returns rietveld URL.""" |
| 484 except TrybotError, err: | 434 output = RunGit(['cl', 'upload', '-f', '--bypass-hooks', '-m', |
| 485 print err | 435 'CL for %s perf tryjob' % repo_name], |
|
nednguyen
2016/08/29 23:15:22
nits: would be nice to include benchmark name & bo
eakuefner
2016/08/30 17:33:50
The title should be the same as the first/only lin
prasadv
2016/08/30 18:49:40
Yes, CL title will be the first line of the messag
nednguyen
2016/08/30 18:55:23
It seems not very ideal that this overwrites exist
| |
| 486 logging.error(err) | 436 'Could not upload to rietveld for %s' % repo_name) |
| 487 finally: | 437 |
| 488 # Checkout original branch and delete telemetry-tryjob branch. | 438 match = re.search(r'https://codereview.chromium.org/[\d]+', output) |
| 489 # TODO(prasadv): This finally block could be extracted out to be a | 439 if not match: |
| 490 # separate function called _CleanupBranch. | 440 raise TrybotError('Could not upload CL to rietveld for %s! Output %s' % |
| 491 returncode, out, err = _RunProcess( | 441 (repo_name, output)) |
| 492 [_GIT_CMD, 'checkout', original_branchname]) | 442 return match.group(0) |
| 493 if returncode: | 443 |
| 494 logging.error('Could not check out %s. Please check it out and ' | 444 def _RunTryJob(self, bot_platform, arguments): |
| 495 'manually delete the telemetry-tryjob branch. ' | 445 """Executes perf try job with benchmark test properties. |
| 496 ': %s', original_branchname, err) | 446 |
| 497 return ERROR # pylint: disable=lost-exception | 447 Args: |
| 498 logging.info('Checked out original branch: %s', original_branchname) | 448 bot_platform: Name of the platform to be generated. |
| 499 returncode, out, err = _RunProcess( | 449 arguments: Command line arguments. |
| 500 [_GIT_CMD, 'branch', '-D', 'telemetry-tryjob']) | 450 |
| 501 if returncode: | 451 Raises: |
| 502 logging.error('Could not delete telemetry-tryjob branch. ' | 452 TrybotError: When trybot fails to upload CL or run git try. |
| 503 'Please delete it manually: %s', err) | 453 """ |
| 504 return ERROR # pylint: disable=lost-exception | 454 config = self._GetPerfConfig(bot_platform, arguments) |
| 505 logging.info('Deleted temp branch: telemetry-tryjob') | 455 |
| 506 return SUCCESS | 456 # Generate git try command for available bots. |
| 457 git_try_command = ['cl', 'try', '-m', 'tryserver.chromium.perf'] | |
| 458 | |
| 459 # Add Perf Test config to git try --properties arg. | |
| 460 git_try_command.extend(['-p', 'perf_try_config=%s' % json.dumps(config)]) | |
| 461 | |
| 462 for bot in self._builder_names[bot_platform]: | |
| 463 git_try_command.extend(['-b', bot]) | |
| 464 | |
| 465 RunGit(git_try_command, 'Could not try CL for %s' % bot_platform) | |
| 466 print 'Perf Try job sent to rietveld for %s platform.' % bot_platform | |
| OLD | NEW |