OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 | 5 |
6 # pylint: disable=W0201 | 6 # pylint: disable=W0201 |
7 | 7 |
8 | 8 |
9 import json | 9 import json |
10 import os | 10 import os |
11 import re | 11 import re |
12 import sys | 12 import sys |
13 | 13 |
14 from recipe_engine import recipe_api | 14 from recipe_engine import recipe_api |
15 from recipe_engine import config_types | 15 from recipe_engine import config_types |
16 | 16 |
17 from . import fake_specs | 17 from . import fake_specs |
18 | 18 |
19 | 19 |
20 TEST_EXPECTED_SKP_VERSION = '42' | |
21 TEST_EXPECTED_SK_IMAGE_VERSION = '42' | |
22 | |
23 VERSION_FILE_SK_IMAGE = 'SK_IMAGE_VERSION' | |
24 VERSION_FILE_SKP = 'SKP_VERSION' | |
25 | |
26 VERSION_NONE = -1 | |
27 | |
28 | |
29 class SkiaApi(recipe_api.RecipeApi): | 20 class SkiaApi(recipe_api.RecipeApi): |
30 | 21 |
31 def get_builder_spec(self, skia_dir, builder_name): | 22 def get_builder_spec(self, skia_dir, builder_name): |
32 """Obtain the buildbot spec for the given builder.""" | 23 """Obtain the buildbot spec for the given builder.""" |
33 fake_spec = None | 24 fake_spec = None |
34 if self._test_data.enabled: | 25 if self._test_data.enabled: |
35 fake_spec = fake_specs.FAKE_SPECS[builder_name] | 26 fake_spec = fake_specs.FAKE_SPECS[builder_name] |
36 builder_spec = self.m.run.json_from_file( | 27 builder_spec = self.m.run.json_from_file( |
37 skia_dir.join('tools', 'buildbot_spec.py'), | 28 skia_dir.join('tools', 'buildbot_spec.py'), |
38 skia_dir, | 29 skia_dir, |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 revert=False, | 160 revert=False, |
170 **checkout_kwargs) | 161 **checkout_kwargs) |
171 | 162 |
172 self.m.vars.got_revision = ( | 163 self.m.vars.got_revision = ( |
173 update_step.presentation.properties['got_revision']) | 164 update_step.presentation.properties['got_revision']) |
174 self.m.tryserver.maybe_apply_issue() | 165 self.m.tryserver.maybe_apply_issue() |
175 | 166 |
176 if self.m.vars.need_chromium_checkout: | 167 if self.m.vars.need_chromium_checkout: |
177 self.m.gclient.runhooks(cwd=self.m.vars.checkout_root, | 168 self.m.gclient.runhooks(cwd=self.m.vars.checkout_root, |
178 env=self.m.vars.gclient_env) | 169 env=self.m.vars.gclient_env) |
179 | |
180 def copy_dir(self, host_version, version_file, tmp_dir, | |
181 host_path, device_path, test_expected_version, | |
182 test_actual_version): | |
183 actual_version_file = self.m.path.join(tmp_dir, version_file) | |
184 # Copy to device. | |
185 device_version_file = self.m.flavor.device_path_join( | |
186 self.m.flavor.device_dirs.tmp_dir, version_file) | |
187 if str(actual_version_file) != str(device_version_file): | |
188 try: | |
189 device_version = ( | |
190 self.m.flavor.read_file_on_device(device_version_file)) | |
191 except self.m.step.StepFailure: | |
192 device_version = VERSION_NONE | |
193 if device_version != host_version: | |
194 self.m.flavor.remove_file_on_device(device_version_file) | |
195 self.m.flavor.create_clean_device_dir(device_path) | |
196 self.m.flavor.copy_directory_contents_to_device( | |
197 host_path, device_path) | |
198 | |
199 # Copy the new version file. | |
200 self.m.flavor.copy_file_to_device(actual_version_file, | |
201 device_version_file) | |
202 | |
203 def _copy_images(self): | |
204 """Download and copy test images if needed.""" | |
205 version_file = self.m.vars.infrabots_dir.join( | |
206 'assets', 'skimage', 'VERSION') | |
207 test_data = self.m.properties.get( | |
208 'test_downloaded_sk_image_version', TEST_EXPECTED_SK_IMAGE_VERSION) | |
209 version = self.m.run.readfile( | |
210 version_file, | |
211 name='Get downloaded skimage VERSION', | |
212 test_data=test_data).rstrip() | |
213 self.m.run.writefile( | |
214 self.m.path.join(self.m.vars.tmp_dir, VERSION_FILE_SK_IMAGE), | |
215 version) | |
216 self.copy_dir( | |
217 version, | |
218 VERSION_FILE_SK_IMAGE, | |
219 self.m.vars.tmp_dir, | |
220 self.m.vars.images_dir, | |
221 self.m.flavor.device_dirs.images_dir, | |
222 test_expected_version=self.m.properties.get( | |
223 'test_downloaded_sk_image_version', | |
224 TEST_EXPECTED_SK_IMAGE_VERSION), | |
225 test_actual_version=self.m.properties.get( | |
226 'test_downloaded_sk_image_version', | |
227 TEST_EXPECTED_SK_IMAGE_VERSION)) | |
228 return version | |
229 | |
230 def _copy_skps(self): | |
231 """Download and copy the SKPs if needed.""" | |
232 version_file = self.m.vars.infrabots_dir.join( | |
233 'assets', 'skp', 'VERSION') | |
234 test_data = self.m.properties.get( | |
235 'test_downloaded_skp_version', TEST_EXPECTED_SKP_VERSION) | |
236 version = self.m.run.readfile( | |
237 version_file, | |
238 name='Get downloaded SKP VERSION', | |
239 test_data=test_data).rstrip() | |
240 self.m.run.writefile( | |
241 self.m.path.join(self.m.vars.tmp_dir, VERSION_FILE_SKP), | |
242 version) | |
243 self.copy_dir( | |
244 version, | |
245 VERSION_FILE_SKP, | |
246 self.m.vars.tmp_dir, | |
247 self.m.vars.local_skp_dir, | |
248 self.m.flavor.device_dirs.skp_dir, | |
249 test_expected_version=self.m.properties.get( | |
250 'test_downloaded_skp_version', TEST_EXPECTED_SKP_VERSION), | |
251 test_actual_version=self.m.properties.get( | |
252 'test_downloaded_skp_version', TEST_EXPECTED_SKP_VERSION)) | |
253 return version | |
254 | |
255 def install(self): | |
256 """Copy the required executables and files to the device.""" | |
257 # Run any device-specific installation. | |
258 self.m.flavor.install() | |
259 | |
260 # TODO(borenet): Only copy files which have changed. | |
261 # Resources | |
262 self.m.flavor.copy_directory_contents_to_device( | |
263 self.m.vars.resource_dir, | |
264 self.m.flavor.device_dirs.resource_dir) | |
265 | |
266 def test_steps(self): | |
267 """Run the DM test.""" | |
268 self.m.run.run_once(self.install) | |
269 self.m.run.run_once(self._copy_skps) | |
270 self.m.run.run_once(self._copy_images) | |
271 | |
272 use_hash_file = False | |
273 if self.m.vars.upload_dm_results: | |
274 # This must run before we write anything into | |
275 # self.m.flavor.device_dirs.dm_dir or we may end up deleting our | |
276 # output on machines where they're the same. | |
277 self.m.flavor.create_clean_host_dir(self.m.vars.dm_dir) | |
278 host_dm_dir = str(self.m.vars.dm_dir) | |
279 device_dm_dir = str(self.m.flavor.device_dirs.dm_dir) | |
280 if host_dm_dir != device_dm_dir: | |
281 self.m.flavor.create_clean_device_dir(device_dm_dir) | |
282 | |
283 # Obtain the list of already-generated hashes. | |
284 hash_filename = 'uninteresting_hashes.txt' | |
285 | |
286 # Ensure that the tmp_dir exists. | |
287 self.m.run.run_once(self.m.file.makedirs, | |
288 'tmp_dir', | |
289 self.m.vars.tmp_dir, | |
290 infra_step=True) | |
291 | |
292 host_hashes_file = self.m.vars.tmp_dir.join(hash_filename) | |
293 hashes_file = self.m.flavor.device_path_join( | |
294 self.m.flavor.device_dirs.tmp_dir, hash_filename) | |
295 self.m.run( | |
296 self.m.python.inline, | |
297 'get uninteresting hashes', | |
298 program=""" | |
299 import contextlib | |
300 import math | |
301 import socket | |
302 import sys | |
303 import time | |
304 import urllib2 | |
305 | |
306 HASHES_URL = 'https://gold.skia.org/_/hashes' | |
307 RETRIES = 5 | |
308 TIMEOUT = 60 | |
309 WAIT_BASE = 15 | |
310 | |
311 socket.setdefaulttimeout(TIMEOUT) | |
312 for retry in range(RETRIES): | |
313 try: | |
314 with contextlib.closing( | |
315 urllib2.urlopen(HASHES_URL, timeout=TIMEOUT)) as w: | |
316 hashes = w.read() | |
317 with open(sys.argv[1], 'w') as f: | |
318 f.write(hashes) | |
319 break | |
320 except Exception as e: | |
321 print 'Failed to get uninteresting hashes from %s:' % HASHES_URL | |
322 print e | |
323 if retry == RETRIES: | |
324 raise | |
325 waittime = WAIT_BASE * math.pow(2, retry) | |
326 print 'Retry in %d seconds.' % waittime | |
327 time.sleep(waittime) | |
328 """, | |
329 args=[host_hashes_file], | |
330 cwd=self.m.vars.skia_dir, | |
331 abort_on_failure=False, | |
332 fail_build_on_failure=False, | |
333 infra_step=True) | |
334 | |
335 if self.m.path.exists(host_hashes_file): | |
336 self.m.flavor.copy_file_to_device(host_hashes_file, hashes_file) | |
337 use_hash_file = True | |
338 | |
339 # Run DM. | |
340 properties = [ | |
341 'gitHash', self.m.vars.got_revision, | |
342 'master', self.m.vars.master_name, | |
343 'builder', self.m.vars.builder_name, | |
344 'build_number', self.m.vars.build_number, | |
345 ] | |
346 if self.m.vars.is_trybot: | |
347 properties.extend([ | |
348 'issue', self.m.vars.issue, | |
349 'patchset', self.m.vars.patchset, | |
350 ]) | |
351 | |
352 args = [ | |
353 'dm', | |
354 '--undefok', # This helps branches that may not know new flags. | |
355 '--resourcePath', self.m.flavor.device_dirs.resource_dir, | |
356 '--skps', self.m.flavor.device_dirs.skp_dir, | |
357 '--images', self.m.flavor.device_path_join( | |
358 self.m.flavor.device_dirs.images_dir, 'dm'), | |
359 '--colorImages', self.m.flavor.device_path_join( | |
360 self.m.flavor.device_dirs.images_dir, 'colorspace'), | |
361 '--nameByHash', | |
362 '--properties' | |
363 ] + properties | |
364 | |
365 args.append('--key') | |
366 args.extend(self._KeyParams()) | |
367 if use_hash_file: | |
368 args.extend(['--uninterestingHashesFile', hashes_file]) | |
369 if self.m.vars.upload_dm_results: | |
370 args.extend(['--writePath', self.m.flavor.device_dirs.dm_dir]) | |
371 | |
372 skip_flag = None | |
373 if self.m.vars.builder_cfg.get('cpu_or_gpu') == 'CPU': | |
374 skip_flag = '--nogpu' | |
375 elif self.m.vars.builder_cfg.get('cpu_or_gpu') == 'GPU': | |
376 skip_flag = '--nocpu' | |
377 if skip_flag: | |
378 args.append(skip_flag) | |
379 args.extend(self.m.vars.dm_flags) | |
380 | |
381 self.m.run(self.m.flavor.step, 'dm', cmd=args, | |
382 abort_on_failure=False, | |
383 env=self.m.vars.default_env) | |
384 | |
385 if self.m.vars.upload_dm_results: | |
386 # Copy images and JSON to host machine if needed. | |
387 self.m.flavor.copy_directory_contents_to_host( | |
388 self.m.flavor.device_dirs.dm_dir, self.m.vars.dm_dir) | |
389 | |
390 # See skia:2789. | |
391 if ('Valgrind' in self.m.vars.builder_name and | |
392 self.m.vars.builder_cfg.get('cpu_or_gpu') == 'GPU'): | |
393 abandonGpuContext = list(args) | |
394 abandonGpuContext.append('--abandonGpuContext') | |
395 self.m.run(self.m.flavor.step, 'dm --abandonGpuContext', | |
396 cmd=abandonGpuContext, abort_on_failure=False) | |
397 preAbandonGpuContext = list(args) | |
398 preAbandonGpuContext.append('--preAbandonGpuContext') | |
399 self.m.run(self.m.flavor.step, 'dm --preAbandonGpuContext', | |
400 cmd=preAbandonGpuContext, abort_on_failure=False, | |
401 env=self.m.vars.default_env) | |
402 | |
403 def perf_steps(self): | |
404 """Run Skia benchmarks.""" | |
405 self.m.run.run_once(self.install) | |
406 self.m.run.run_once(self._copy_skps) | |
407 self.m.run.run_once(self._copy_images) | |
408 | |
409 if self.m.vars.upload_perf_results: | |
410 self.m.flavor.create_clean_device_dir( | |
411 self.m.flavor.device_dirs.perf_data_dir) | |
412 | |
413 # Run nanobench. | |
414 properties = [ | |
415 '--properties', | |
416 'gitHash', self.m.vars.got_revision, | |
417 'build_number', self.m.vars.build_number, | |
418 ] | |
419 if self.m.vars.is_trybot: | |
420 properties.extend([ | |
421 'issue', self.m.vars.issue, | |
422 'patchset', self.m.vars.patchset, | |
423 ]) | |
424 | |
425 target = 'nanobench' | |
426 if 'VisualBench' in self.m.vars.builder_name: | |
427 target = 'visualbench' | |
428 args = [ | |
429 target, | |
430 '--undefok', # This helps branches that may not know new flags. | |
431 '-i', self.m.flavor.device_dirs.resource_dir, | |
432 '--skps', self.m.flavor.device_dirs.skp_dir, | |
433 '--images', self.m.flavor.device_path_join( | |
434 self.m.flavor.device_dirs.images_dir, 'nanobench'), | |
435 ] | |
436 | |
437 skip_flag = None | |
438 if self.m.vars.builder_cfg.get('cpu_or_gpu') == 'CPU': | |
439 skip_flag = '--nogpu' | |
440 elif self.m.vars.builder_cfg.get('cpu_or_gpu') == 'GPU': | |
441 skip_flag = '--nocpu' | |
442 if skip_flag: | |
443 args.append(skip_flag) | |
444 args.extend(self.m.vars.nanobench_flags) | |
445 | |
446 if self.m.vars.upload_perf_results: | |
447 json_path = self.m.flavor.device_path_join( | |
448 self.m.flavor.device_dirs.perf_data_dir, | |
449 'nanobench_%s.json' % self.m.vars.got_revision) | |
450 args.extend(['--outResultsFile', json_path]) | |
451 args.extend(properties) | |
452 | |
453 keys_blacklist = ['configuration', 'role', 'is_trybot'] | |
454 args.append('--key') | |
455 for k in sorted(self.m.vars.builder_cfg.keys()): | |
456 if not k in keys_blacklist: | |
457 args.extend([k, self.m.vars.builder_cfg[k]]) | |
458 | |
459 self.m.run(self.m.flavor.step, target, cmd=args, | |
460 abort_on_failure=False, | |
461 env=self.m.vars.default_env) | |
462 | |
463 # See skia:2789. | |
464 if ('Valgrind' in self.m.vars.builder_name and | |
465 self.m.vars.builder_cfg.get('cpu_or_gpu') == 'GPU'): | |
466 abandonGpuContext = list(args) | |
467 abandonGpuContext.extend(['--abandonGpuContext', '--nocpu']) | |
468 self.m.run(self.m.flavor.step, | |
469 '%s --abandonGpuContext' % target, | |
470 cmd=abandonGpuContext, abort_on_failure=False, | |
471 env=self.m.vars.default_env) | |
472 | |
473 # Upload results. | |
474 if self.m.vars.upload_perf_results: | |
475 self.m.file.makedirs('perf_dir', self.m.vars.perf_data_dir) | |
476 self.m.flavor.copy_directory_contents_to_host( | |
477 self.m.flavor.device_dirs.perf_data_dir, | |
478 self.m.vars.perf_data_dir) | |
479 | |
480 def cleanup_steps(self): | |
481 """Run any cleanup steps.""" | |
482 self.m.flavor.cleanup_steps() | |
483 | |
484 def _KeyParams(self): | |
485 """Build a unique key from the builder name (as a list). | |
486 | |
487 E.g. arch x86 gpu GeForce320M mode MacMini4.1 os Mac10.6 | |
488 """ | |
489 # Don't bother to include role, which is always Test. | |
490 # TryBots are uploaded elsewhere so they can use the same key. | |
491 blacklist = ['role', 'is_trybot'] | |
492 | |
493 flat = [] | |
494 for k in sorted(self.m.vars.builder_cfg.keys()): | |
495 if k not in blacklist: | |
496 flat.append(k) | |
497 flat.append(self.m.vars.builder_cfg[k]) | |
498 return flat | |
OLD | NEW |