| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Performance Test Bisect Tool | 6 """Performance Test Bisect Tool |
| 7 | 7 |
| 8 This script bisects a series of changelists using binary search. It starts at | 8 This script bisects a series of changelists using binary search. It starts at |
| 9 a bad revision where a performance metric has regressed, and asks for a last | 9 a bad revision where a performance metric has regressed, and asks for a last |
| 10 known-good revision. It will then binary search across this revision range by | 10 known-good revision. It will then binary search across this revision range by |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 47 import zipfile | 47 import zipfile |
| 48 | 48 |
| 49 sys.path.append(os.path.join( | 49 sys.path.append(os.path.join( |
| 50 os.path.dirname(__file__), os.path.pardir, 'telemetry')) | 50 os.path.dirname(__file__), os.path.pardir, 'telemetry')) |
| 51 | 51 |
| 52 from bisect_printer import BisectPrinter | 52 from bisect_printer import BisectPrinter |
| 53 from bisect_results import BisectResults | 53 from bisect_results import BisectResults |
| 54 from bisect_state import BisectState | 54 from bisect_state import BisectState |
| 55 import bisect_utils | 55 import bisect_utils |
| 56 import builder | 56 import builder |
| 57 import crbug_query |
| 57 import math_utils | 58 import math_utils |
| 58 import request_build | 59 import request_build |
| 59 import source_control | 60 import source_control |
| 60 from telemetry.util import cloud_storage | 61 from telemetry.util import cloud_storage |
| 61 | 62 |
| 62 # The script is in chromium/src/tools/auto_bisect. Throughout this script, | 63 # The script is in chromium/src/tools/auto_bisect. Throughout this script, |
| 63 # we use paths to other things in the chromium/src repository. | 64 # we use paths to other things in the chromium/src repository. |
| 64 | 65 |
| 65 # Possible return values from BisectPerformanceMetrics.RunTest. | 66 # Possible return values from BisectPerformanceMetrics.RunTest. |
| 66 BUILD_RESULT_SUCCEED = 0 | 67 BUILD_RESULT_SUCCEED = 0 |
| (...skipping 2070 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2137 | 2138 |
| 2138 Args: | 2139 Args: |
| 2139 command_to_run: Specify the command to execute the performance test. | 2140 command_to_run: Specify the command to execute the performance test. |
| 2140 good_revision: Number/tag of the known good revision. | 2141 good_revision: Number/tag of the known good revision. |
| 2141 bad_revision: Number/tag of the known bad revision. | 2142 bad_revision: Number/tag of the known bad revision. |
| 2142 metric: The performance metric to monitor. | 2143 metric: The performance metric to monitor. |
| 2143 | 2144 |
| 2144 Returns: | 2145 Returns: |
| 2145 A BisectResults object. | 2146 A BisectResults object. |
| 2146 """ | 2147 """ |
| 2148 |
| 2147 # Choose depot to bisect first | 2149 # Choose depot to bisect first |
| 2148 target_depot = 'chromium' | 2150 target_depot = 'chromium' |
| 2149 if self.opts.target_platform == 'android-chrome': | 2151 if self.opts.target_platform == 'android-chrome': |
| 2150 target_depot = 'android-chrome' | 2152 target_depot = 'android-chrome' |
| 2151 | 2153 |
| 2152 cwd = os.getcwd() | 2154 cwd = os.getcwd() |
| 2153 self.depot_registry.ChangeToDepotDir(target_depot) | 2155 self.depot_registry.ChangeToDepotDir(target_depot) |
| 2154 | 2156 |
| 2155 # If they passed SVN revisions, we can try match them to git SHA1 hashes. | 2157 # If they passed SVN revisions, we can try match them to git SHA1 hashes. |
| 2156 bad_revision = source_control.ResolveToRevision( | 2158 bad_revision = source_control.ResolveToRevision( |
| (...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2477 self.debug_ignore_perf_test = None | 2479 self.debug_ignore_perf_test = None |
| 2478 self.debug_ignore_regression_confidence = None | 2480 self.debug_ignore_regression_confidence = None |
| 2479 self.debug_fake_first_test_mean = 0 | 2481 self.debug_fake_first_test_mean = 0 |
| 2480 self.gs_bucket = None | 2482 self.gs_bucket = None |
| 2481 self.target_arch = 'ia32' | 2483 self.target_arch = 'ia32' |
| 2482 self.target_build_type = 'Release' | 2484 self.target_build_type = 'Release' |
| 2483 self.builder_host = None | 2485 self.builder_host = None |
| 2484 self.builder_port = None | 2486 self.builder_port = None |
| 2485 self.bisect_mode = bisect_utils.BISECT_MODE_MEAN | 2487 self.bisect_mode = bisect_utils.BISECT_MODE_MEAN |
| 2486 self.improvement_direction = 0 | 2488 self.improvement_direction = 0 |
| 2489 self.bug_id = '' |
| 2487 | 2490 |
| 2488 @staticmethod | 2491 @staticmethod |
| 2489 def _AddBisectOptionsGroup(parser): | 2492 def _AddBisectOptionsGroup(parser): |
| 2490 group = parser.add_argument_group('Bisect options') | 2493 group = parser.add_argument_group('Bisect options') |
| 2491 group.add_argument('-c', '--command', required=True, | 2494 group.add_argument('-c', '--command', required=True, |
| 2492 help='A command to execute your performance test at ' | 2495 help='A command to execute your performance test at ' |
| 2493 'each point in the bisection.') | 2496 'each point in the bisection.') |
| 2494 group.add_argument('-b', '--bad_revision', required=True, | 2497 group.add_argument('-b', '--bad_revision', required=True, |
| 2495 help='A bad revision to start bisection. Must be later ' | 2498 help='A bad revision to start bisection. Must be later ' |
| 2496 'than good revision. May be either a git or svn ' | 2499 'than good revision. May be either a git or svn ' |
| (...skipping 27 matching lines...) Expand all Loading... |
| 2524 help='The highest/lowest % are discarded to form a ' | 2527 help='The highest/lowest % are discarded to form a ' |
| 2525 'truncated mean. Values will be clamped to range ' | 2528 'truncated mean. Values will be clamped to range ' |
| 2526 '[0, 25]. Default value is 25 (highest/lowest 25% ' | 2529 '[0, 25]. Default value is 25 (highest/lowest 25% ' |
| 2527 'will be discarded).') | 2530 'will be discarded).') |
| 2528 group.add_argument('--bisect_mode', default=bisect_utils.BISECT_MODE_MEAN, | 2531 group.add_argument('--bisect_mode', default=bisect_utils.BISECT_MODE_MEAN, |
| 2529 choices=[bisect_utils.BISECT_MODE_MEAN, | 2532 choices=[bisect_utils.BISECT_MODE_MEAN, |
| 2530 bisect_utils.BISECT_MODE_STD_DEV, | 2533 bisect_utils.BISECT_MODE_STD_DEV, |
| 2531 bisect_utils.BISECT_MODE_RETURN_CODE], | 2534 bisect_utils.BISECT_MODE_RETURN_CODE], |
| 2532 help='The bisect mode. Choices are to bisect on the ' | 2535 help='The bisect mode. Choices are to bisect on the ' |
| 2533 'difference in mean, std_dev, or return_code.') | 2536 'difference in mean, std_dev, or return_code.') |
| 2537 group.add_argument('--bug_id', type=str, default='', |
| 2538 help='The id for the bug associated with this bisect. ' + |
| 2539 'If this number is given, bisect will attempt to ' + |
| 2540 ' verify that the bug is not closed before starting.') |
| 2534 | 2541 |
| 2535 @staticmethod | 2542 @staticmethod |
| 2536 def _AddBuildOptionsGroup(parser): | 2543 def _AddBuildOptionsGroup(parser): |
| 2537 group = parser.add_argument_group('Build options') | 2544 group = parser.add_argument_group('Build options') |
| 2538 group.add_argument('-w', '--working_directory', | 2545 group.add_argument('-w', '--working_directory', |
| 2539 help='Path to the working directory where the script ' | 2546 help='Path to the working directory where the script ' |
| 2540 'will do an initial checkout of the chromium depot. The ' | 2547 'will do an initial checkout of the chromium depot. The ' |
| 2541 'files will be placed in a subdirectory "bisect" under ' | 2548 'files will be placed in a subdirectory "bisect" under ' |
| 2542 'working_directory and that will be used to perform the ' | 2549 'working_directory and that will be used to perform the ' |
| 2543 'bisection. This parameter is optional, if it is not ' | 2550 'bisection. This parameter is optional, if it is not ' |
| 2544 'supplied, the script will work from the current depot.') | 2551 'supplied, the script will work from the current depot.') |
| 2545 group.add_argument('--build_preference', type='choice', | 2552 group.add_argument('--build_preference', type=str, |
| 2546 choices=['msvs', 'ninja', 'make'], | 2553 choices=['msvs', 'ninja', 'make'], |
| 2547 help='The preferred build system to use. On linux/mac ' | 2554 help='The preferred build system to use. On linux/mac ' |
| 2548 'the options are make/ninja. On Windows, the ' | 2555 'the options are make/ninja. On Windows, the ' |
| 2549 'options are msvs/ninja.') | 2556 'options are msvs/ninja.') |
| 2550 group.add_argument('--target_platform', type='choice', default='chromium', | 2557 group.add_argument('--target_platform', type=str, default='chromium', |
| 2551 choices=['chromium', 'android', 'android-chrome'], | 2558 choices=['chromium', 'android', 'android-chrome'], |
| 2552 help='The target platform. Choices are "chromium" ' | 2559 help='The target platform. Choices are "chromium" ' |
| 2553 '(current platform), or "android". If you specify ' | 2560 '(current platform), or "android". If you specify ' |
| 2554 'something other than "chromium", you must be ' | 2561 'something other than "chromium", you must be ' |
| 2555 'properly set up to build that platform.') | 2562 'properly set up to build that platform.') |
| 2556 group.add_argument('--no_custom_deps', dest='no_custom_deps', | 2563 group.add_argument('--no_custom_deps', dest='no_custom_deps', |
| 2557 action='store_true', default=False, | 2564 action='store_true', default=False, |
| 2558 help='Run the script with custom_deps or not.') | 2565 help='Run the script with custom_deps or not.') |
| 2559 group.add_argument('--extra_src', | 2566 group.add_argument('--extra_src', |
| 2560 help='Path to a script which can be used to modify the ' | 2567 help='Path to a script which can be used to modify the ' |
| 2561 'bisect script\'s behavior.') | 2568 'bisect script\'s behavior.') |
| 2562 group.add_argument('--use_goma', action='store_true', | 2569 group.add_argument('--use_goma', action='store_true', |
| 2563 help='Add a bunch of extra threads for goma, and enable ' | 2570 help='Add a bunch of extra threads for goma, and enable ' |
| 2564 'goma') | 2571 'goma') |
| 2565 group.add_argument('--goma_dir', | 2572 group.add_argument('--goma_dir', |
| 2566 help='Path to goma tools (or system default if not ' | 2573 help='Path to goma tools (or system default if not ' |
| 2567 'specified).') | 2574 'specified).') |
| 2568 group.add_argument('--goma_threads', type=int, default='64', | 2575 group.add_argument('--goma_threads', type=int, default='64', |
| 2569 help='Number of threads for goma, only if using goma.') | 2576 help='Number of threads for goma, only if using goma.') |
| 2570 group.add_argument('--output_buildbot_annotations', action='store_true', | 2577 group.add_argument('--output_buildbot_annotations', action='store_true', |
| 2571 help='Add extra annotation output for buildbot.') | 2578 help='Add extra annotation output for buildbot.') |
| 2572 group.add_argument('--gs_bucket', default='', dest='gs_bucket', | 2579 group.add_argument('--gs_bucket', default='', dest='gs_bucket', |
| 2573 help='Name of Google Storage bucket to upload or ' | 2580 help='Name of Google Storage bucket to upload or ' |
| 2574 'download build. e.g., chrome-perf') | 2581 'download build. e.g., chrome-perf') |
| 2575 group.add_argument('--target_arch', type='choice', default='ia32', | 2582 group.add_argument('--target_arch', type=str, default='ia32', |
| 2576 dest='target_arch', choices=['ia32', 'x64', 'arm'], | 2583 dest='target_arch', choices=['ia32', 'x64', 'arm'], |
| 2577 help='The target build architecture. Choices are "ia32" ' | 2584 help='The target build architecture. Choices are "ia32" ' |
| 2578 '(default), "x64" or "arm".') | 2585 '(default), "x64" or "arm".') |
| 2579 group.add_argument('--target_build_type', type='choice', default='Release', | 2586 group.add_argument('--target_build_type', type=str, default='Release', |
| 2580 choices=['Release', 'Debug'], | 2587 choices=['Release', 'Debug'], |
| 2581 help='The target build type. Choices are "Release" ' | 2588 help='The target build type. Choices are "Release" ' |
| 2582 '(default), or "Debug".') | 2589 '(default), or "Debug".') |
| 2583 group.add_argument('--builder_host', dest='builder_host', | 2590 group.add_argument('--builder_host', dest='builder_host', |
| 2584 help='Host address of server to produce build by ' | 2591 help='Host address of server to produce build by ' |
| 2585 'posting try job request.') | 2592 'posting try job request.') |
| 2586 group.add_argument('--builder_port', dest='builder_port', type='int', | 2593 group.add_argument('--builder_port', dest='builder_port', type=int, |
| 2587 help='HTTP port of the server to produce build by ' | 2594 help='HTTP port of the server to produce build by ' |
| 2588 'posting try job request.') | 2595 'posting try job request.') |
| 2589 | 2596 |
| 2590 @staticmethod | 2597 @staticmethod |
| 2591 def _AddDebugOptionsGroup(parser): | 2598 def _AddDebugOptionsGroup(parser): |
| 2592 group = parser.add_argument_group('Debug options') | 2599 group = parser.add_argument_group('Debug options') |
| 2593 group.add_argument('--debug_ignore_build', action='store_true', | 2600 group.add_argument('--debug_ignore_build', action='store_true', |
| 2594 help='DEBUG: Don\'t perform builds.') | 2601 help='DEBUG: Don\'t perform builds.') |
| 2595 group.add_argument('--debug_ignore_sync', action='store_true', | 2602 group.add_argument('--debug_ignore_sync', action='store_true', |
| 2596 help='DEBUG: Don\'t perform syncs.') | 2603 help='DEBUG: Don\'t perform syncs.') |
| 2597 group.add_argument('--debug_ignore_perf_test', action='store_true', | 2604 group.add_argument('--debug_ignore_perf_test', action='store_true', |
| 2598 help='DEBUG: Don\'t perform performance tests.') | 2605 help='DEBUG: Don\'t perform performance tests.') |
| 2599 group.add_argument('--debug_ignore_regression_confidence', | 2606 group.add_argument('--debug_ignore_regression_confidence', |
| 2600 action='store_true', | 2607 action='store_true', |
| 2601 help='DEBUG: Don\'t score the confidence of the initial ' | 2608 help='DEBUG: Don\'t score the confidence of the initial ' |
| 2602 'good and bad revisions\' test results.') | 2609 'good and bad revisions\' test results.') |
| 2603 group.add_argument('--debug_fake_first_test_mean', type='int', default='0', | 2610 group.add_argument('--debug_fake_first_test_mean', type=int, default='0', |
| 2604 help='DEBUG: When faking performance tests, return this ' | 2611 help='DEBUG: When faking performance tests, return this ' |
| 2605 'value as the mean of the first performance test, ' | 2612 'value as the mean of the first performance test, ' |
| 2606 'and return a mean of 0.0 for further tests.') | 2613 'and return a mean of 0.0 for further tests.') |
| 2607 return group | 2614 return group |
| 2608 | 2615 |
| 2609 @classmethod | 2616 @classmethod |
| 2610 def _CreateCommandLineParser(cls): | 2617 def _CreateCommandLineParser(cls): |
| 2611 """Creates a parser with bisect options. | 2618 """Creates a parser with bisect options. |
| 2612 | 2619 |
| 2613 Returns: | 2620 Returns: |
| 2614 An instance of optparse.OptionParser. | 2621 An instance of optparse.OptionParser. |
| 2615 """ | 2622 """ |
| 2616 usage = ('%prog [options] [-- chromium-options]\n' | 2623 usage = ('%prog [options] [-- chromium-options]\n' |
| 2617 'Perform binary search on revision history to find a minimal ' | 2624 'Perform binary search on revision history to find a minimal ' |
| 2618 'range of revisions where a performance metric regressed.\n') | 2625 'range of revisions where a performance metric regressed.\n') |
| 2619 | 2626 |
| 2620 parser = argparse.ArgumentParser(usage=usage) | 2627 parser = argparse.ArgumentParser(usage=usage) |
| 2621 cls._AddBisectOptionsGroup(parser) | 2628 cls._AddBisectOptionsGroup(parser) |
| 2622 cls._AddBuildOptionsGroup(parser) | 2629 cls._AddBuildOptionsGroup(parser) |
| 2623 cls._AddDebugOptionsGroup(parser) | 2630 cls._AddDebugOptionsGroup(parser) |
| 2624 return parser | 2631 return parser |
| 2625 | 2632 |
| 2626 def ParseCommandLine(self): | 2633 def ParseCommandLine(self): |
| 2627 """Parses the command line for bisect options.""" | 2634 """Parses the command line for bisect options.""" |
| 2628 parser = self._CreateCommandLineParser() | 2635 parser = self._CreateCommandLineParser() |
| 2629 opts, _ = parser.parse_args() | 2636 opts = parser.parse_args() |
| 2630 | 2637 |
| 2631 try: | 2638 try: |
| 2632 if (not opts.metric and | 2639 if (not opts.metric and |
| 2633 opts.bisect_mode != bisect_utils.BISECT_MODE_RETURN_CODE): | 2640 opts.bisect_mode != bisect_utils.BISECT_MODE_RETURN_CODE): |
| 2634 raise RuntimeError('missing required parameter: --metric') | 2641 raise RuntimeError('missing required parameter: --metric') |
| 2635 | 2642 |
| 2636 if opts.gs_bucket: | 2643 if opts.gs_bucket: |
| 2637 if not cloud_storage.List(opts.gs_bucket): | 2644 if not cloud_storage.List(opts.gs_bucket): |
| 2638 raise RuntimeError('Invalid Google Storage: gs://%s' % opts.gs_bucket) | 2645 raise RuntimeError('Invalid Google Storage: gs://%s' % opts.gs_bucket) |
| 2639 if not opts.builder_host: | 2646 if not opts.builder_host: |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2700 logging.basicConfig( | 2707 logging.basicConfig( |
| 2701 stream=logging.sys.stdout, level=logging.INFO, format=logging_format) | 2708 stream=logging.sys.stdout, level=logging.INFO, format=logging_format) |
| 2702 | 2709 |
| 2703 | 2710 |
| 2704 def main(): | 2711 def main(): |
| 2705 _ConfigureLogging() | 2712 _ConfigureLogging() |
| 2706 try: | 2713 try: |
| 2707 opts = BisectOptions() | 2714 opts = BisectOptions() |
| 2708 opts.ParseCommandLine() | 2715 opts.ParseCommandLine() |
| 2709 | 2716 |
| 2717 if opts.bug_id: |
| 2718 if opts.output_buildbot_annotations: |
| 2719 bisect_utils.OutputAnnotationStepStart('Checking Issue Tracker') |
| 2720 issue_closed = crbug_query.checkIssueClosed(opts.bug_id) |
| 2721 if issue_closed: |
| 2722 print 'Aborting bisect because bug is closed' |
| 2723 else: |
| 2724 print 'Could not confirm bug is closed, proceeding.' |
| 2725 if opts.output_buildbot_annotations: |
| 2726 bisect_utils.OutputAnnotationStepClosed() |
| 2727 if issue_closed: |
| 2728 results = BisectResults(abort_reason='the bug is closed.') |
| 2729 bisect_test = BisectPerformanceMetrics(opts, os.getcwd()) |
| 2730 bisect_test.printer.FormatAndPrintResults(results) |
| 2731 return 0 |
| 2732 |
| 2733 |
| 2710 if opts.extra_src: | 2734 if opts.extra_src: |
| 2711 extra_src = bisect_utils.LoadExtraSrc(opts.extra_src) | 2735 extra_src = bisect_utils.LoadExtraSrc(opts.extra_src) |
| 2712 if not extra_src: | 2736 if not extra_src: |
| 2713 raise RuntimeError('Invalid or missing --extra_src.') | 2737 raise RuntimeError('Invalid or missing --extra_src.') |
| 2714 bisect_utils.AddAdditionalDepotInfo(extra_src.GetAdditionalDepotInfo()) | 2738 bisect_utils.AddAdditionalDepotInfo(extra_src.GetAdditionalDepotInfo()) |
| 2715 | 2739 |
| 2716 if opts.working_directory: | 2740 if opts.working_directory: |
| 2717 custom_deps = bisect_utils.DEFAULT_GCLIENT_CUSTOM_DEPS | 2741 custom_deps = bisect_utils.DEFAULT_GCLIENT_CUSTOM_DEPS |
| 2718 if opts.no_custom_deps: | 2742 if opts.no_custom_deps: |
| 2719 custom_deps = None | 2743 custom_deps = None |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2753 bisect_utils.OutputAnnotationStepStart('Results') | 2777 bisect_utils.OutputAnnotationStepStart('Results') |
| 2754 print 'Error: ', e.message | 2778 print 'Error: ', e.message |
| 2755 logging.warn('A RuntimeError was caught: %s', e.message) | 2779 logging.warn('A RuntimeError was caught: %s', e.message) |
| 2756 if opts.output_buildbot_annotations: | 2780 if opts.output_buildbot_annotations: |
| 2757 bisect_utils.OutputAnnotationStepClosed() | 2781 bisect_utils.OutputAnnotationStepClosed() |
| 2758 return 1 | 2782 return 1 |
| 2759 | 2783 |
| 2760 | 2784 |
| 2761 if __name__ == '__main__': | 2785 if __name__ == '__main__': |
| 2762 sys.exit(main()) | 2786 sys.exit(main()) |
| OLD | NEW |