1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Finds perf trybots that can run telemetry tests."""
15 from telemetry import decorators
16 from telemetry.core import platform
17 from telemetry.core import possible_browser
19 CHROMIUM_CONFIG_FILENAME = 'tools/run-perf-test.cfg'
20 BLINK_CONFIG_FILENAME = 'Tools/run-perf-test.cfg'
21 SUCCESS, NO_CHANGES, ERROR = range(3)
24 class PossibleTrybotBrowser(possible_browser.PossibleBrowser):
25 """A script that sends a job to a trybot."""
27 def __init__(self, browser_type, finder_options):
28 target_os = browser_type.split('-')[1]
29 self._buildername = '%s_perf_bisect' % browser_type.replace(
30 'trybot-', '').replace('-', '_')
31 super(PossibleTrybotBrowser, self).__init__(browser_type, target_os,
35 raise NotImplementedError()
37 def SupportsOptions(self, finder_options):
38 if (finder_options.android_device or
39 finder_options.chrome_root or
40 finder_options.cros_remote or
41 finder_options.extensions_to_load or
42 finder_options.interactive or
43 finder_options.profile_dir):
50 def _RunProcess(self, cmd):
51 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
52 out, err = proc.communicate()
53 returncode = proc.poll()
54 return (returncode, out, err)
56 def _AttemptTryjob(self, cfg_file_path):
57 """Attempts to run a tryjob from the current directory.
59 This is run once for chromium, and if it returns NO_CHANGES, once for blink.
62 cfg_file_path: Path to the config file for the try job.
65 (result, msg) where result is one of:
66 SUCCESS if a tryjob was sent
67 NO_CHANGES if there was nothing to try,
68 ERROR if a tryjob was attempted but an error encountered
69 and msg is an error message if an error was encountered, or rietveld
72 returncode, original_branchname, err = self._RunProcess(
73 ['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
75 msg = 'Must be in a git repository to send changes to trybots.'
77 msg += '\nGit error: %s' % err
79 original_branchname = original_branchname.strip()
81 # Check if the tree is dirty: make sure the index is up to date and then
83 self._RunProcess(['git', 'update-index', '--refresh', '-q'])
84 returncode, out, err = self._RunProcess(['git', 'diff-index', 'HEAD'])
86 msg = 'Cannot send a try job with a dirty tree. Commit locally first.'
89 # Make sure the tree does have local commits.
90 returncode, out, err = self._RunProcess(
91 ['git', 'log', 'origin/master..HEAD'])
93 return (NO_CHANGES, None)
95 # Create/check out the telemetry-tryjob branch, and edit the configs
96 # for the tryjob there.
97 returncode, out, err = self._RunProcess(
98 ['git', 'checkout', '-b', 'telemetry-tryjob'])
100 msg = ('Error creating branch telemetry-tryjob. '
101 'Please delete it if it exists.\n%s' % err)
103 returncode, out, err = self._RunProcess(
104 ['git', 'branch', '--set-upstream-to', 'origin/master'])
106 return (ERROR, 'Error in git branch --set-upstream-to: %s' % err)
108 # Generate the command line for the perf trybots
110 if self._target_os == 'win':
111 arguments[0] = 'python tools\\perf\\run_measurement'
113 arguments[0] = './tools/perf/run_measurement'
114 for index, arg in enumerate(arguments):
115 if arg.startswith('--browser='):
116 if self._target_os == 'android':
117 arguments[index] = '--browser=android-chrome-shell'
119 arguments[index] = '--browser=release'
120 command = ' '.join(arguments)
122 # Add the correct command to the config file and commit it.
126 'max_time_minutes': '120',
127 'truncate_percent': '0',
130 config_file = open(cfg_file_path, 'w')
132 msg = 'Cannot find %s. Please run from src dir.' % cfg_file_path
134 config_file.write('config = %s' % json.dumps(
135 config, sort_keys=True, indent=2, separators=(',', ': ')))
137 returncode, out, err = self._RunProcess(
138 ['git', 'commit', '-a', '-m', 'bisect config'])
140 msg = 'Could not commit bisect config change, error %s' % err
143 # Upload the CL to rietveld and run a try job.
144 returncode, out, err = self._RunProcess([
145 'git', 'cl', 'upload', '-f', '--bypass-hooks', '-m',
149 msg = 'Could upload to rietveld, error %s' % err
151 match = re.search(r'https://codereview.chromium.org/[\d]+', out)
153 msg = 'Could not upload CL to rietveld! Output %s' % out
155 rietveld_url = match.group(0)
156 returncode, out, err = self._RunProcess([
157 'git', 'cl', 'try', '-m', 'tryserver.chromium.perf', '-b',
160 msg = 'Could not try CL, error %s' % err
163 # Checkout original branch and delete telemetry-tryjob branch.
164 returncode, out, err = self._RunProcess(
165 ['git', 'checkout', original_branchname])
168 ('Could not check out %s. Please check it out and manually '
169 'delete the telemetry-tryjob branch. Error message: %s') %
170 (original_branchname, err))
172 returncode, out, err = self._RunProcess(
173 ['git', 'branch', '-D', 'telemetry-tryjob'])
175 msg = (('Could not delete telemetry-tryjob branch. '
176 'Please delete it manually. Error %s') % err)
178 return (SUCCESS, rietveld_url)
181 """Sends a tryjob to a perf trybot.
183 This creates a branch, telemetry-tryjob, switches to that branch, edits
184 the bisect config, commits it, uploads the CL to rietveld, and runs a
185 tryjob on the given bot.
187 # First check if there are chromium changes to upload.
188 status, msg = self._AttemptTryjob(CHROMIUM_CONFIG_FILENAME)
189 if status == SUCCESS:
190 print 'Uploaded chromium try job to rietveld. View progress at %s' % msg
192 elif status == ERROR:
196 # If we got here, there are no chromium changes to upload. Try blink.
197 os.chdir('third_party/WebKit/')
198 status, msg = self._AttemptTryjob(BLINK_CONFIG_FILENAME)
200 if status == SUCCESS:
201 print 'Uploaded blink try job to rietveld. View progress at %s' % msg
203 elif status == ERROR:
207 logging.error('No local changes found in chromium or blink trees. '
208 'browser=%s argument sends local changes to the %s '
209 'perf trybot.', self._browser_type, self._buildername)
212 def _InitPlatformIfNeeded(self):
216 self._platform = platform.GetHostPlatform()
218 # pylint: disable=W0212
219 self._platform_backend = self._platform._platform_backend
222 def SelectDefaultBrowser(_):
226 def CanFindAvailableBrowsers():
231 def _GetTrybotList():
233 'http://build.chromium.org/p/tryserver.chromium.perf/json')
234 builders = json.loads(f.read()).get('builders', {}).keys()
235 builders = ['trybot-%s' % b.replace('_perf_bisect', '').replace('_', '-')
236 for b in builders if not b.endswith('_perf_bisect_builder')]
237 # Perf try jobs do not work on Windows XP
238 if 'trybot-win-xp' in builders:
239 builders.remove('trybot-win-xp')
243 def FindAllBrowserTypes(finder_options):
244 # Listing browsers requires an http request; only do this if the user is
245 # running with browser=list or a browser=trybot-* argument.
246 if (finder_options.browser_type and
247 (finder_options.browser_type == 'list' or
248 finder_options.browser_type.startswith('trybot'))):
249 return _GetTrybotList()
253 def FindAllAvailableBrowsers(finder_options):
254 """Find all perf trybots on tryserver.chromium.perf."""
255 return [PossibleTrybotBrowser(b, finder_options) for b in
256 FindAllBrowserTypes(finder_options)]