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

Side by Side Diff: Tools/Scripts/webkitpy/tool/commands/rebaseline.py

Issue 302003009: Make rebaselining not use gigabytes of memory. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: address review comments Created 6 years, 6 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 | Annotate | Revision Log
OLDNEW
1 # Copyright (c) 2010 Google Inc. All rights reserved. 1 # Copyright (c) 2010 Google Inc. All rights reserved.
2 # 2 #
3 # Redistribution and use in source and binary forms, with or without 3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are 4 # modification, are permitted provided that the following conditions are
5 # met: 5 # met:
6 # 6 #
7 # * Redistributions of source code must retain the above copyright 7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer. 8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above 9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer 10 # copyright notice, this list of conditions and the following disclaimer
(...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 fallback_path = port.baseline_search_path() 361 fallback_path = port.baseline_search_path()
362 if fallback_path not in builders_to_fallback_paths.values(): 362 if fallback_path not in builders_to_fallback_paths.values():
363 builders_to_fallback_paths[builder] = fallback_path 363 builders_to_fallback_paths[builder] = fallback_path
364 return builders_to_fallback_paths.keys() 364 return builders_to_fallback_paths.keys()
365 365
366 def _rebaseline_commands(self, test_prefix_list, options): 366 def _rebaseline_commands(self, test_prefix_list, options):
367 path_to_webkit_patch = self._tool.path() 367 path_to_webkit_patch = self._tool.path()
368 cwd = self._tool.scm().checkout_root 368 cwd = self._tool.scm().checkout_root
369 copy_baseline_commands = [] 369 copy_baseline_commands = []
370 rebaseline_commands = [] 370 rebaseline_commands = []
371 lines_to_remove = {}
371 port = self._tool.port_factory.get() 372 port = self._tool.port_factory.get()
372 373
373 for test_prefix in test_prefix_list: 374 for test_prefix in test_prefix_list:
374 for test in port.tests([test_prefix]): 375 for test in port.tests([test_prefix]):
375 for builder in self._builders_to_fetch_from(test_prefix_list[tes t_prefix]): 376 for builder in self._builders_to_fetch_from(test_prefix_list[tes t_prefix]):
376 actual_failures_suffixes = self._suffixes_for_actual_failure s(test, builder, test_prefix_list[test_prefix][builder]) 377 actual_failures_suffixes = self._suffixes_for_actual_failure s(test, builder, test_prefix_list[test_prefix][builder])
377 if not actual_failures_suffixes: 378 if not actual_failures_suffixes:
379 # If we're not going to rebaseline the test because it's passing on this
380 # builder, we still want to remove the line from TestExp ectations.
381 if test not in lines_to_remove:
382 lines_to_remove[test] = []
383 lines_to_remove[test].append(builder)
378 continue 384 continue
379 385
380 suffixes = ','.join(actual_failures_suffixes) 386 suffixes = ','.join(actual_failures_suffixes)
381 cmd_line = ['--suffixes', suffixes, '--builder', builder, '- -test', test] 387 cmd_line = ['--suffixes', suffixes, '--builder', builder, '- -test', test]
382 if options.results_directory: 388 if options.results_directory:
383 cmd_line.extend(['--results-directory', options.results_ directory]) 389 cmd_line.extend(['--results-directory', options.results_ directory])
384 if options.verbose: 390 if options.verbose:
385 cmd_line.append('--verbose') 391 cmd_line.append('--verbose')
386 copy_baseline_commands.append(tuple([[path_to_webkit_patch, 'copy-existing-baselines-internal'] + cmd_line, cwd])) 392 copy_baseline_commands.append(tuple([[path_to_webkit_patch, 'copy-existing-baselines-internal'] + cmd_line, cwd]))
387 rebaseline_commands.append(tuple([[path_to_webkit_patch, 're baseline-test-internal'] + cmd_line, cwd])) 393 rebaseline_commands.append(tuple([[path_to_webkit_patch, 're baseline-test-internal'] + cmd_line, cwd]))
388 return copy_baseline_commands, rebaseline_commands 394 return copy_baseline_commands, rebaseline_commands, lines_to_remove
389 395
390 def _serial_commands(self, command_results): 396 def _serial_commands(self, command_results):
391 files_to_add = set() 397 files_to_add = set()
392 files_to_delete = set() 398 files_to_delete = set()
393 lines_to_remove = {} 399 lines_to_remove = {}
394 for output in [result[1].split('\n') for result in command_results]: 400 for output in [result[1].split('\n') for result in command_results]:
395 file_added = False 401 file_added = False
396 for line in output: 402 for line in output:
397 try: 403 try:
398 if line: 404 if line:
(...skipping 29 matching lines...) Expand all
428 cmd_line = ['--no-modify-scm', '--suffixes', ','.join(all_suffixes), test] 434 cmd_line = ['--no-modify-scm', '--suffixes', ','.join(all_suffixes), test]
429 if verbose: 435 if verbose:
430 cmd_line.append('--verbose') 436 cmd_line.append('--verbose')
431 437
432 path_to_webkit_patch = self._tool.path() 438 path_to_webkit_patch = self._tool.path()
433 cwd = self._tool.scm().checkout_root 439 cwd = self._tool.scm().checkout_root
434 optimize_commands.append(tuple([[path_to_webkit_patch, 'optimize-bas elines'] + cmd_line, cwd])) 440 optimize_commands.append(tuple([[path_to_webkit_patch, 'optimize-bas elines'] + cmd_line, cwd]))
435 return optimize_commands 441 return optimize_commands
436 442
437 def _update_expectations_files(self, lines_to_remove): 443 def _update_expectations_files(self, lines_to_remove):
438 # FIXME: This routine is way too expensive. We're creating N ports and N TestExpectations 444 # FIXME: This routine is way too expensive. We're creating O(n ports) Te stExpectations objects.
439 # objects and (re-)writing the actual expectations file N times, for eac h test we update. 445 # This is slow and uses a lot of memory.
440 # We should be able to update everything in memory, once, and then write the file out a single time. 446 tests = lines_to_remove.keys()
447 to_remove = []
448
449 # This is so we remove lines for builders that skip this test, e.g. Andr oid skips most
450 # tests and we don't want to leave stray [ Android ] lines in TestExpect ations..
451 # FIXME: Is this necessary at all now that _rebaseline_commands includes the builders that
452 # used to be skipped because the result wasn't failing on tip of tree?
453 for port_name in self._tool.port_factory.all_port_names():
454 port = self._tool.port_factory.get(port_name)
455 generic_expectations = TestExpectations(port, tests=tests, include_o verrides=False)
456 full_expectations = TestExpectations(port, tests=tests, include_over rides=True)
457 for test in tests:
458 if self._port_skips_test(port, test, generic_expectations, full_ expectations):
459 for test_configuration in port.all_test_configurations():
460 if test_configuration.version == port.test_configuration ().version:
461 to_remove.append((test, test_configuration))
462
441 for test in lines_to_remove: 463 for test in lines_to_remove:
442 for builder in lines_to_remove[test]: 464 for builder in lines_to_remove[test]:
443 port = self._tool.port_factory.get_from_builder_name(builder) 465 port = self._tool.port_factory.get_from_builder_name(builder)
444 path = port.path_to_generic_test_expectations_file()
445 expectations = TestExpectations(port, include_overrides=False)
446 for test_configuration in port.all_test_configurations(): 466 for test_configuration in port.all_test_configurations():
447 if test_configuration.version == port.test_configuration().v ersion: 467 if test_configuration.version == port.test_configuration().v ersion:
448 expectationsString = expectations.remove_configuration_f rom_test(test, test_configuration) 468 to_remove.append((test, test_configuration))
449 self._tool.filesystem.write_text_file(path, expectationsString)
450 469
451 for port_name in self._tool.port_factory.all_port_names(): 470 port = self._tool.port_factory.get()
452 port = self._tool.port_factory.get(port_name) 471 expectations = TestExpectations(port, include_overrides=False)
453 generic_expectations = TestExpectations(port, tests=[test], incl ude_overrides=False) 472 expectationsString = expectations.remove_configurations(to_remove)
454 if self._port_skips_test(port, test, generic_expectations): 473 path = port.path_to_generic_test_expectations_file()
455 for test_configuration in port.all_test_configurations(): 474 self._tool.filesystem.write_text_file(path, expectationsString)
456 if test_configuration.version == port.test_configuration ().version:
457 expectationsString = generic_expectations.remove_con figuration_from_test(test, test_configuration)
458 generic_path = port.path_to_generic_test_expectations_file()
459 self._tool.filesystem.write_text_file(generic_path, expectat ionsString)
460 475
461 def _port_skips_test(self, port, test, generic_expectations): 476 def _port_skips_test(self, port, test, generic_expectations, full_expectatio ns):
462 fs = port.host.filesystem 477 fs = port.host.filesystem
463 if port.default_smoke_test_only(): 478 if port.default_smoke_test_only():
464 smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests') 479 smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests')
465 if fs.exists(smoke_test_filename) and test not in fs.read_text_file( smoke_test_filename): 480 if fs.exists(smoke_test_filename) and test not in fs.read_text_file( smoke_test_filename):
466 return True 481 return True
467 482
468 full_expectations = TestExpectations(port, tests=[test], include_overrid es=True)
469 return (SKIP in full_expectations.get_expectations(test) and 483 return (SKIP in full_expectations.get_expectations(test) and
470 SKIP not in generic_expectations.get_expectations(test)) 484 SKIP not in generic_expectations.get_expectations(test))
471 485
472 def _run_in_parallel_and_update_scm(self, commands): 486 def _run_in_parallel_and_update_scm(self, commands):
473 command_results = self._tool.executive.run_in_parallel(commands) 487 command_results = self._tool.executive.run_in_parallel(commands)
474 log_output = '\n'.join(result[2] for result in command_results).replace( '\n\n', '\n') 488 log_output = '\n'.join(result[2] for result in command_results).replace( '\n\n', '\n')
475 for line in log_output.split('\n'): 489 for line in log_output.split('\n'):
476 if line: 490 if line:
477 print >> sys.stderr, line # FIXME: Figure out how to log proper ly. 491 print >> sys.stderr, line # FIXME: Figure out how to log proper ly.
478 492
479 files_to_add, files_to_delete, lines_to_remove = self._serial_commands(c ommand_results) 493 files_to_add, files_to_delete, lines_to_remove = self._serial_commands(c ommand_results)
480 if files_to_delete: 494 if files_to_delete:
481 self._tool.scm().delete_list(files_to_delete) 495 self._tool.scm().delete_list(files_to_delete)
482 if files_to_add: 496 if files_to_add:
483 self._tool.scm().add_list(files_to_add) 497 self._tool.scm().add_list(files_to_add)
484 if lines_to_remove: 498 return lines_to_remove
485 self._update_expectations_files(lines_to_remove)
486 499
487 def _rebaseline(self, options, test_prefix_list): 500 def _rebaseline(self, options, test_prefix_list):
488 for test, builders_to_check in sorted(test_prefix_list.items()): 501 for test, builders_to_check in sorted(test_prefix_list.items()):
489 _log.info("Rebaselining %s" % test) 502 _log.info("Rebaselining %s" % test)
490 for builder, suffixes in sorted(builders_to_check.items()): 503 for builder, suffixes in sorted(builders_to_check.items()):
491 _log.debug(" %s: %s" % (builder, ",".join(suffixes))) 504 _log.debug(" %s: %s" % (builder, ",".join(suffixes)))
492 505
493 copy_baseline_commands, rebaseline_commands = self._rebaseline_commands( test_prefix_list, options) 506 copy_baseline_commands, rebaseline_commands, extra_lines_to_remove = sel f._rebaseline_commands(test_prefix_list, options)
507 lines_to_remove = {}
508
494 if copy_baseline_commands: 509 if copy_baseline_commands:
495 self._run_in_parallel_and_update_scm(copy_baseline_commands) 510 self._run_in_parallel_and_update_scm(copy_baseline_commands)
496 if rebaseline_commands: 511 if rebaseline_commands:
497 self._run_in_parallel_and_update_scm(rebaseline_commands) 512 lines_to_remove = self._run_in_parallel_and_update_scm(rebaseline_co mmands)
513
514 for test in extra_lines_to_remove:
515 if test in lines_to_remove:
516 lines_to_remove[test] = lines_to_remove[test] + extra_lines_to_r emove[test]
517 else:
518 lines_to_remove[test] = extra_lines_to_remove[test]
519
520 if lines_to_remove:
521 self._update_expectations_files(lines_to_remove)
522
498 if options.optimize: 523 if options.optimize:
499 self._run_in_parallel_and_update_scm(self._optimize_baselines(test_p refix_list, options.verbose)) 524 self._run_in_parallel_and_update_scm(self._optimize_baselines(test_p refix_list, options.verbose))
500 525
501 def _suffixes_for_actual_failures(self, test, builder_name, existing_suffixe s): 526 def _suffixes_for_actual_failures(self, test, builder_name, existing_suffixe s):
502 actual_results = self.builder_data()[builder_name].actual_results(test) 527 actual_results = self.builder_data()[builder_name].actual_results(test)
503 if not actual_results: 528 if not actual_results:
504 return set() 529 return set()
505 return set(existing_suffixes) & TestExpectations.suffixes_for_actual_exp ectations_string(actual_results) 530 return set(existing_suffixes) & TestExpectations.suffixes_for_actual_exp ectations_string(actual_results)
506 531
507 532
(...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after
858 883
859 try: 884 try:
860 old_branch_name = tool.scm().current_branch() 885 old_branch_name = tool.scm().current_branch()
861 tool.scm().delete_branch(self.AUTO_REBASELINE_BRANCH_NAME) 886 tool.scm().delete_branch(self.AUTO_REBASELINE_BRANCH_NAME)
862 tool.scm().create_clean_branch(self.AUTO_REBASELINE_BRANCH_NAME) 887 tool.scm().create_clean_branch(self.AUTO_REBASELINE_BRANCH_NAME)
863 888
864 # If the tests are passing everywhere, then this list will be empty. We don't need 889 # If the tests are passing everywhere, then this list will be empty. We don't need
865 # to rebaseline, but we'll still need to update TestExpectations. 890 # to rebaseline, but we'll still need to update TestExpectations.
866 if test_prefix_list: 891 if test_prefix_list:
867 self._rebaseline(options, test_prefix_list) 892 self._rebaseline(options, test_prefix_list)
868 # If a test is not failing on the bot, we don't try to rebaseline it , but we still
869 # want to remove the NeedsRebaseline line.
870 self._update_expectations_files(lines_to_remove)
871 893
872 tool.scm().commit_locally_with_message(self.commit_message(author, r evision, bugs)) 894 tool.scm().commit_locally_with_message(self.commit_message(author, r evision, bugs))
873 895
874 # FIXME: It would be nice if we could dcommit the patch without uplo ading, but still 896 # FIXME: It would be nice if we could dcommit the patch without uplo ading, but still
875 # go through all the precommit hooks. For rebaselines with lots of f iles, uploading 897 # go through all the precommit hooks. For rebaselines with lots of f iles, uploading
876 # takes a long time and sometimes fails, but we don't want to commit if, e.g. the 898 # takes a long time and sometimes fails, but we don't want to commit if, e.g. the
877 # tree is closed. 899 # tree is closed.
878 did_finish = self._run_git_cl_command(options, ['upload', '-f']) 900 did_finish = self._run_git_cl_command(options, ['upload', '-f'])
879 901
880 if did_finish: 902 if did_finish:
(...skipping 26 matching lines...) Expand all
907 if options.verbose: 929 if options.verbose:
908 rebaseline_command.append('--verbose') 930 rebaseline_command.append('--verbose')
909 # Use call instead of run_command so that stdout doesn't get swa llowed. 931 # Use call instead of run_command so that stdout doesn't get swa llowed.
910 tool.executive.call(rebaseline_command) 932 tool.executive.call(rebaseline_command)
911 except: 933 except:
912 traceback.print_exc(file=sys.stderr) 934 traceback.print_exc(file=sys.stderr)
913 # Sometimes git crashes and leaves us on a detached head. 935 # Sometimes git crashes and leaves us on a detached head.
914 tool.scm().checkout_branch(old_branch_name) 936 tool.scm().checkout_branch(old_branch_name)
915 937
916 time.sleep(self.SLEEP_TIME_IN_SECONDS) 938 time.sleep(self.SLEEP_TIME_IN_SECONDS)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698