Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(105)

Side by Side Diff: content/test/gpu/gather_swarming_json_results.py

Issue 2116593004: Add script which gathers JSON output from a Swarming task's shards. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Load WebGL 2.0 conformance test times from the isolate. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2016 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Script which gathers and merges the JSON results from multiple
7 swarming shards of a step on the waterfall.
8
9 This is used to feed in the per-test times of previous runs of tests
10 to the browser_test_runner's sharding algorithm, to improve shard
11 distribution.
12 """
13
14 import argparse
15 import json
16 import os
17 import shutil
18 import subprocess
19 import sys
20 import tempfile
21 import urllib
22 import urllib2
23
24 SWARMING_SERVICE = 'https://chromium-swarm.appspot.com'
25
26 THIS_DIR = os.path.dirname(os.path.abspath(__file__))
27 SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(THIS_DIR)))
28 SWARMING_CLIENT_DIR = os.path.join(SRC_DIR, 'tools', 'swarming_client')
29
30 class Swarming:
31 @staticmethod
32 def CheckAuth():
33 output = subprocess.check_output([
34 os.path.join(SWARMING_CLIENT_DIR, 'auth.py'),
35 'check',
36 '--service',
37 SWARMING_SERVICE])
38 if not output.startswith('user:'):
39 print 'Must run:'
40 print ' tools/swarming_client/auth.py login --service ' + \
41 SWARMING_SERVICE
42 print 'and authenticate with @google.com credentials.'
43 sys.exit(1)
44
45 @staticmethod
46 def Collect(hashes, output_dir, verbose):
47 cmd = [
48 os.path.join(SWARMING_CLIENT_DIR, 'swarming.py'),
49 'collect',
50 '-S',
51 SWARMING_SERVICE,
52 '--task-output-dir',
53 output_dir] + hashes
54 if verbose:
55 print 'Collecting Swarming results:'
56 print cmd
57 if verbose > 1:
58 # Print stdout from the collect command.
59 stdout = None
60 else:
61 fnull = open(os.devnull, 'w')
62 stdout = fnull
63 subprocess.check_call(cmd, stdout=stdout, stderr=subprocess.STDOUT)
64
65 @staticmethod
66 def ExtractShardHashes(urls):
67 SWARMING_URL = 'https://chromium-swarm.appspot.com/user/task/'
68 hashes = []
Vadim Sh. 2016/07/02 01:19:51 technically these are not hashes but task IDs
Ken Russell (switch to Gerrit) 2016/07/02 03:17:01 Thanks, renamed throughout.
69 for k,v in urls.iteritems():
70 if not k.startswith('shard'):
71 raise Exception('Illegally formatted \'urls\' key %s' % k)
72 if not v.startswith(SWARMING_URL):
73 raise Exception('Illegally formatted \'urls\' value %s' % v)
74 hashes.append(v[len(SWARMING_URL):])
75 return hashes
76
77 class Waterfall:
78 def __init__(self, waterfall):
79 self._waterfall = waterfall
80 self.BASE_URL = 'http://build.chromium.org/p/'
81 self.BASE_BUILD_URL = self.BASE_URL + '%s/builders/%s'
Zhenyao Mo 2016/07/01 23:29:38 unused variable
Ken Russell (switch to Gerrit) 2016/07/02 03:17:01 Removed.
82 self.BASE_JSON_BUILDERS_URL = self.BASE_URL + '%s/json/builders'
83 self.BASE_JSON_BUILDS_URL = self.BASE_JSON_BUILDERS_URL + '/%s/builds'
84
85 def GetJsonFromUrl(self, url):
86 conn = urllib2.urlopen(url)
87 result = conn.read()
88 conn.close()
89 return json.loads(result)
90
91 def GetBuildNumbersForBot(self, bot):
92 builds_json = self.GetJsonFromUrl(
93 self.BASE_JSON_BUILDS_URL %
94 (self._waterfall, urllib.quote(bot)))
95 build_numbers = [int(k) for k in builds_json.keys()]
96 build_numbers.sort()
97 return build_numbers
98
99 def GetMostRecentlyCompletedBuildNumberForBot(self, bot):
100 builds = self.GetBuildNumbersForBot(bot)
101 return builds[len(builds) - 1]
102
103 def GetJsonForBuild(self, bot, build):
104 return self.GetJsonFromUrl(
105 (self.BASE_JSON_BUILDS_URL + '/%d') %
106 (self._waterfall, urllib.quote(bot), build))
107
108
109 def JsonLoadStrippingUnicode(file, **kwargs):
110 def StripUnicode(obj):
111 if isinstance(obj, unicode):
112 try:
113 return obj.encode('ascii')
114 except UnicodeEncodeError:
115 return obj
116
117 if isinstance(obj, list):
118 return map(StripUnicode, obj)
119
120 if isinstance(obj, dict):
121 new_obj = type(obj)(
122 (StripUnicode(k), StripUnicode(v)) for k, v in obj.iteritems() )
123 return new_obj
124
125 return obj
126
127 return StripUnicode(json.load(file, **kwargs))
128
129
130 def Merge(dest, src):
131 if isinstance(dest, list):
132 if not isinstance(src, list):
133 raise Exception('Both must be lists: ' + dest + ' and ' + src)
134 return dest + src
135
136 if isinstance(dest, dict):
137 if not isinstance(src, dict):
138 raise Exception('Both must be dicts: ' + dest + ' and ' + src)
139 for k in src.iterkeys():
140 if k not in dest:
141 dest[k] = src[k]
142 else:
143 dest[k] = Merge(dest[k], src[k])
144 return dest
145
146 return src
147
148
149 def main():
150 rest_args = sys.argv[1:]
151 parser = argparse.ArgumentParser(
152 description='Gather JSON results from a run of a Swarming test.',
153 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
154 parser.add_argument('-v', '--verbose', action='count', default=0,
155 help='Enable verbose output (specify multiple times '
156 'for more output)')
157 parser.add_argument('--waterfall', type=str, default='chromium.gpu.fyi',
158 help='Which waterfall to examine')
159 parser.add_argument('--bot', type=str, default='Linux Release (NVIDIA)',
160 help='Which bot on the waterfall to examine')
161 parser.add_argument('--build', default=-1, type=int,
162 help='Which build to fetch (-1 means most recent)')
163 parser.add_argument('--step', type=str, default='webgl2_conformance_tests',
164 help='Which step to fetch (treated as a prefix)')
165 parser.add_argument('--output', type=str, default='output.json',
166 help='Name of output file')
167 parser.add_argument('--leak-temp-dir', action='store_true', default=False,
168 help='Deliberately leak temporary directory')
169
170 options = parser.parse_args(rest_args)
171
172 Swarming.CheckAuth()
173
174 waterfall = Waterfall(options.waterfall)
175 build = options.build
176 if build < 0:
177 build = waterfall.GetMostRecentlyCompletedBuildNumberForBot(options.bot)
178
179 build_json = waterfall.GetJsonForBuild(options.bot, build)
180
181 if options.verbose:
182 print 'Fetching information from %s, bot %s, build %s' % (
183 options.waterfall, options.bot, build)
184
185 hashes = []
186 for s in build_json['steps']:
187 if s['name'].startswith(options.step):
188 # Found the step. Now iterate down the URLs.
Zhenyao Mo 2016/07/01 23:29:38 URL name isn't apparent. Can you document why?
Ken Russell (switch to Gerrit) 2016/07/02 03:17:01 Added a comment.
189 if 'urls' not in s or not s['urls']:
190 # Note: we could also just download json.output if it exists.
191 print ('%s on waterfall %s, bot %s, build %s doesn\'t '
192 'look like a Swarmed task') % (
193 s['name'], options.waterfall, options.bot, build)
194 return 1
195 hashes = Swarming.ExtractShardHashes(s['urls'])
196 if options.verbose:
197 print 'Found Swarming hashes for step %s' % s['name']
198
199 break
200 if not hashes:
201 print 'Problem gathering the Swarming hashes for %s' % options.step
202 return 1
203
204 # Collect the results.
205 tmpdir = tempfile.mkdtemp()
206 Swarming.Collect(hashes, tmpdir, options.verbose)
207
208 # Shards' JSON outputs are in sequentially-numbered subdirectories
209 # of the output directory.
210 merged_json = None
211 for i in xrange(len(hashes)):
212 with open(os.path.join(tmpdir, str(i), 'output.json')) as f:
213 cur_json = JsonLoadStrippingUnicode(f)
214 if not merged_json:
215 merged_json = cur_json
216 else:
217 merged_json = Merge(merged_json, cur_json)
218
219 with open(options.output, 'w') as f:
220 json.dump(merged_json, f)
221
222 if options.leak_temp_dir:
223 print 'Temporary directory: %s' % tmpdir
224 else:
225 shutil.rmtree(tmpdir)
226
227 return 0
228
229
230 if __name__ == "__main__":
231 sys.exit(main())
OLDNEW
« no previous file with comments | « content/test/data/gpu/webgl2_conformance_tests_output.json ('k') | content/test/gpu/generate_buildbot_json.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698