| 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 |