Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / backends / remote / trybot_browser_finder.py
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.
4
5 """Finds perf trybots that can run telemetry tests."""
6
7 import json
8 import logging
9 import os
10 import re
11 import subprocess
12 import sys
13 import urllib2
14
15 from telemetry import decorators
16 from telemetry.core import platform
17 from telemetry.core import possible_browser
18
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)
22
23
24 class PossibleTrybotBrowser(possible_browser.PossibleBrowser):
25   """A script that sends a job to a trybot."""
26
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,
32                                                 finder_options, True)
33
34   def Create(self):
35     raise NotImplementedError()
36
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):
44       return False
45     return True
46
47   def IsRemote(self):
48     return True
49
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)
55
56   def _AttemptTryjob(self, cfg_file_path):
57     """Attempts to run a tryjob from the current directory.
58
59     This is run once for chromium, and if it returns NO_CHANGES, once for blink.
60
61     Args:
62       cfg_file_path: Path to the config file for the try job.
63
64     Returns:
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
70           url if success.
71     """
72     returncode, original_branchname, err = self._RunProcess(
73         ['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
74     if returncode:
75       msg = 'Must be in a git repository to send changes to trybots.'
76       if err:
77         msg += '\nGit error: %s' % err
78       return (ERROR, msg)
79     original_branchname = original_branchname.strip()
80
81     # Check if the tree is dirty: make sure the index is up to date and then
82     # run diff-index
83     self._RunProcess(['git', 'update-index', '--refresh', '-q'])
84     returncode, out, err = self._RunProcess(['git', 'diff-index', 'HEAD'])
85     if out:
86       msg = 'Cannot send a try job with a dirty tree. Commit locally first.'
87       return (ERROR, msg)
88
89     # Make sure the tree does have local commits.
90     returncode, out, err = self._RunProcess(
91         ['git', 'log', 'origin/master..HEAD'])
92     if not out:
93       return (NO_CHANGES, None)
94
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'])
99     if returncode:
100       msg = ('Error creating branch telemetry-tryjob. '
101              'Please delete it if it exists.\n%s' % err)
102       return (ERROR, msg)
103     returncode, out, err = self._RunProcess(
104         ['git', 'branch', '--set-upstream-to', 'origin/master'])
105     if returncode:
106       return (ERROR, 'Error in git branch --set-upstream-to: %s' % err)
107
108     # Generate the command line for the perf trybots
109     arguments = sys.argv
110     if self._target_os == 'win':
111       arguments[0] = 'python tools\\perf\\run_measurement'
112     else:
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'
118         else:
119           arguments[index] = '--browser=release'
120     command = ' '.join(arguments)
121
122     # Add the correct command to the config file and commit it.
123     config = {
124         'command': command,
125         'repeat_count': '1',
126         'max_time_minutes': '120',
127         'truncate_percent': '0',
128     }
129     try:
130       config_file = open(cfg_file_path, 'w')
131     except IOError:
132       msg = 'Cannot find %s. Please run from src dir.' % cfg_file_path
133       return (ERROR, msg)
134     config_file.write('config = %s' % json.dumps(
135         config, sort_keys=True, indent=2, separators=(',', ': ')))
136     config_file.close()
137     returncode, out, err = self._RunProcess(
138         ['git', 'commit', '-a', '-m', 'bisect config'])
139     if returncode:
140       msg = 'Could not commit bisect config change, error %s' % err
141       return (ERROR, msg)
142
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',
146         'CL for perf tryjob'
147     ])
148     if returncode:
149       msg = 'Could upload to rietveld, error %s' % err
150       return (ERROR, msg)
151     match = re.search(r'https://codereview.chromium.org/[\d]+', out)
152     if not match:
153       msg = 'Could not upload CL to rietveld! Output %s' % out
154       return (ERROR, msg)
155     rietveld_url = match.group(0)
156     returncode, out, err = self._RunProcess([
157         'git', 'cl', 'try', '-m', 'tryserver.chromium.perf', '-b',
158         self._buildername])
159     if returncode:
160       msg = 'Could not try CL, error %s' % err
161       return (ERROR, msg)
162
163     # Checkout original branch and delete telemetry-tryjob branch.
164     returncode, out, err = self._RunProcess(
165         ['git', 'checkout', original_branchname])
166     if returncode:
167       msg = (
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))
171       return (ERROR, msg)
172     returncode, out, err = self._RunProcess(
173         ['git', 'branch', '-D', 'telemetry-tryjob'])
174     if returncode:
175       msg = (('Could not delete telemetry-tryjob branch. '
176               'Please delete it manually. Error %s') % err)
177       return (ERROR, msg)
178     return (SUCCESS, rietveld_url)
179
180   def RunRemote(self):
181     """Sends a tryjob to a perf trybot.
182
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.
186     """
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
191       return
192     elif status == ERROR:
193       logging.error(msg)
194       return
195
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)
199     os.chdir('../..')
200     if status == SUCCESS:
201       print 'Uploaded blink try job to rietveld. View progress at %s' % msg
202       return
203     elif status == ERROR:
204       logging.error(msg)
205       return
206     else:
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)
210       return
211
212   def _InitPlatformIfNeeded(self):
213     if self._platform:
214       return
215
216     self._platform = platform.GetHostPlatform()
217
218     # pylint: disable=W0212
219     self._platform_backend = self._platform._platform_backend
220
221
222 def SelectDefaultBrowser(_):
223   return None
224
225
226 def CanFindAvailableBrowsers():
227   return True
228
229
230 @decorators.Cache
231 def _GetTrybotList():
232   f = urllib2.urlopen(
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')
240   return builders
241
242
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()
250   return []
251
252
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)]