| 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 |
| 11 syncing, building, and running a performance test. If the change is | 11 syncing, building, and running a performance test. If the change is |
| 12 suspected to occur as a result of WebKit/V8 changes, the script will | 12 suspected to occur as a result of WebKit/V8 changes, the script will |
| 13 further bisect changes to those depots and attempt to narrow down the revision | 13 further bisect changes to those depots and attempt to narrow down the revision |
| 14 range. | 14 range. |
| 15 | 15 |
| 16 Example usage using SVN revisions: | 16 Example usage using SVN revisions: |
| 17 | 17 |
| 18 ./tools/bisect-perf-regression.py -c\ | 18 ./tools/bisect_perf_regression.py -c\ |
| 19 "out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\ | 19 "out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\ |
| 20 -g 168222 -b 168232 -m shutdown/simple-user-quit | 20 -g 168222 -b 168232 -m shutdown/simple-user-quit |
| 21 | 21 |
| 22 Be aware that if you're using the git workflow and specify an SVN revision, | 22 Be aware that if you're using the git workflow and specify an SVN revision, |
| 23 the script will attempt to find the git SHA1 where SVN changes up to that | 23 the script will attempt to find the git SHA1 where SVN changes up to that |
| 24 revision were merged in. | 24 revision were merged in. |
| 25 | 25 |
| 26 Example usage using git hashes: | 26 Example usage using git hashes: |
| 27 | 27 |
| 28 ./tools/bisect-perf-regression.py -c\ | 28 ./tools/bisect_perf_regression.py -c\ |
| 29 "out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\ | 29 "out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\ |
| 30 -g 1f6e67861535121c5c819c16a666f2436c207e7b\ | 30 -g 1f6e67861535121c5c819c16a666f2436c207e7b\ |
| 31 -b b732f23b4f81c382db0b23b9035f3dadc7d925bb\ | 31 -b b732f23b4f81c382db0b23b9035f3dadc7d925bb\ |
| 32 -m shutdown/simple-user-quit | 32 -m shutdown/simple-user-quit |
| 33 """ | 33 """ |
| 34 | 34 |
| 35 import copy | 35 import copy |
| 36 import datetime | 36 import datetime |
| 37 import errno | 37 import errno |
| 38 import hashlib | 38 import hashlib |
| 39 import math | 39 import math |
| 40 import optparse | 40 import optparse |
| 41 import os | 41 import os |
| 42 import re | 42 import re |
| 43 import shlex | 43 import shlex |
| 44 import shutil | 44 import shutil |
| 45 import StringIO | 45 import StringIO |
| 46 import sys | 46 import sys |
| 47 import time | 47 import time |
| 48 import zipfile | 48 import zipfile |
| 49 | 49 |
| 50 sys.path.append(os.path.join(os.path.dirname(__file__), 'telemetry')) | 50 sys.path.append(os.path.join( |
| 51 os.path.dirname(__file__), os.path.pardir, 'telemetry')) |
| 51 | 52 |
| 52 from auto_bisect import bisect_utils | 53 import bisect_utils |
| 53 from auto_bisect import builder | 54 import builder |
| 54 from auto_bisect import math_utils | 55 import math_utils |
| 55 from auto_bisect import request_build | 56 import request_build |
| 56 from auto_bisect import source_control as source_control_module | 57 import source_control as source_control_module |
| 57 from auto_bisect import ttest | 58 import ttest |
| 58 from telemetry.util import cloud_storage | 59 from telemetry.util import cloud_storage |
| 59 | 60 |
| 60 # Below is the map of "depot" names to information about each depot. Each depot | 61 # Below is the map of "depot" names to information about each depot. Each depot |
| 61 # is a repository, and in the process of bisecting, revision ranges in these | 62 # is a repository, and in the process of bisecting, revision ranges in these |
| 62 # repositories may also be bisected. | 63 # repositories may also be bisected. |
| 63 # | 64 # |
| 64 # Each depot information dictionary may contain: | 65 # Each depot information dictionary may contain: |
| 65 # src: Path to the working directory. | 66 # src: Path to the working directory. |
| 66 # recurse: True if this repository will get bisected. | 67 # recurse: True if this repository will get bisected. |
| 67 # depends: A list of other repositories that are actually part of the same | 68 # depends: A list of other repositories that are actually part of the same |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 143 'svn': 'http://skia.googlecode.com/svn/trunk/gyp', | 144 'svn': 'http://skia.googlecode.com/svn/trunk/gyp', |
| 144 'depends': None, | 145 'depends': None, |
| 145 'from': ['chromium'], | 146 'from': ['chromium'], |
| 146 'viewvc': 'https://code.google.com/p/skia/source/detail?r=', | 147 'viewvc': 'https://code.google.com/p/skia/source/detail?r=', |
| 147 'deps_var': 'None' | 148 'deps_var': 'None' |
| 148 } | 149 } |
| 149 } | 150 } |
| 150 | 151 |
| 151 DEPOT_NAMES = DEPOT_DEPS_NAME.keys() | 152 DEPOT_NAMES = DEPOT_DEPS_NAME.keys() |
| 152 | 153 |
| 154 # The script is in chromium/src/tools/auto_bisect. Throughout this script, |
| 155 # we use paths to other things in the chromium/src repository. |
| 156 |
| 153 CROS_CHROMEOS_PATTERN = 'chromeos-base/chromeos-chrome' | 157 CROS_CHROMEOS_PATTERN = 'chromeos-base/chromeos-chrome' |
| 154 | 158 |
| 155 # Possible return values from BisectPerformanceMetrics.RunTest. | 159 # Possible return values from BisectPerformanceMetrics.RunTest. |
| 156 BUILD_RESULT_SUCCEED = 0 | 160 BUILD_RESULT_SUCCEED = 0 |
| 157 BUILD_RESULT_FAIL = 1 | 161 BUILD_RESULT_FAIL = 1 |
| 158 BUILD_RESULT_SKIPPED = 2 | 162 BUILD_RESULT_SKIPPED = 2 |
| 159 | 163 |
| 160 # Maximum time in seconds to wait after posting build request to the try server. | 164 # Maximum time in seconds to wait after posting build request to the try server. |
| 161 # TODO: Change these values based on the actual time taken by buildbots on | 165 # TODO: Change these values based on the actual time taken by buildbots on |
| 162 # the try server. | 166 # the try server. |
| (...skipping 743 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 906 revisions to narrow down where performance regressions may have occurred. | 910 revisions to narrow down where performance regressions may have occurred. |
| 907 | 911 |
| 908 The main entry-point is the Run method. | 912 The main entry-point is the Run method. |
| 909 """ | 913 """ |
| 910 | 914 |
| 911 def __init__(self, source_control, opts): | 915 def __init__(self, source_control, opts): |
| 912 super(BisectPerformanceMetrics, self).__init__() | 916 super(BisectPerformanceMetrics, self).__init__() |
| 913 | 917 |
| 914 self.opts = opts | 918 self.opts = opts |
| 915 self.source_control = source_control | 919 self.source_control = source_control |
| 920 |
| 921 # The src directory here is NOT the src/ directory for the repository |
| 922 # where the bisect script is running from. Instead, it's the src/ directory |
| 923 # inside the bisect/ directory which is created before running. |
| 916 self.src_cwd = os.getcwd() | 924 self.src_cwd = os.getcwd() |
| 917 self.cros_cwd = os.path.join(os.getcwd(), '..', 'cros') | 925 self.cros_cwd = os.path.join(os.getcwd(), '..', 'cros') |
| 918 self.depot_cwd = {} | 926 self.depot_cwd = {} |
| 919 self.cleanup_commands = [] | 927 self.cleanup_commands = [] |
| 920 self.warnings = [] | 928 self.warnings = [] |
| 921 self.builder = builder.Builder.FromOpts(opts) | 929 self.builder = builder.Builder.FromOpts(opts) |
| 922 | 930 |
| 923 for d in DEPOT_NAMES: | 931 for depot in DEPOT_NAMES: |
| 924 # The working directory of each depot is just the path to the depot, but | 932 # The working directory of each depot is just the path to the depot, but |
| 925 # since we're already in 'src', we can skip that part. | 933 # since we're already in 'src', we can skip that part. |
| 926 | 934 self.depot_cwd[depot] = os.path.join( |
| 927 self.depot_cwd[d] = os.path.join( | 935 self.src_cwd, DEPOT_DEPS_NAME[depot]['src'][4:]) |
| 928 self.src_cwd, DEPOT_DEPS_NAME[d]['src'][4:]) | |
| 929 | 936 |
| 930 def PerformCleanup(self): | 937 def PerformCleanup(self): |
| 931 """Performs cleanup when script is finished.""" | 938 """Performs cleanup when script is finished.""" |
| 932 os.chdir(self.src_cwd) | 939 os.chdir(self.src_cwd) |
| 933 for c in self.cleanup_commands: | 940 for c in self.cleanup_commands: |
| 934 if c[0] == 'mv': | 941 if c[0] == 'mv': |
| 935 shutil.move(c[1], c[2]) | 942 shutil.move(c[1], c[2]) |
| 936 else: | 943 else: |
| 937 assert False, 'Invalid cleanup command.' | 944 assert False, 'Invalid cleanup command.' |
| 938 | 945 |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1083 results = {} | 1090 results = {} |
| 1084 for depot_name, depot_data in DEPOT_DEPS_NAME.iteritems(): | 1091 for depot_name, depot_data in DEPOT_DEPS_NAME.iteritems(): |
| 1085 if (depot_data.get('platform') and | 1092 if (depot_data.get('platform') and |
| 1086 depot_data.get('platform') != os.name): | 1093 depot_data.get('platform') != os.name): |
| 1087 continue | 1094 continue |
| 1088 | 1095 |
| 1089 if (depot_data.get('recurse') and depot in depot_data.get('from')): | 1096 if (depot_data.get('recurse') and depot in depot_data.get('from')): |
| 1090 depot_data_src = depot_data.get('src') or depot_data.get('src_old') | 1097 depot_data_src = depot_data.get('src') or depot_data.get('src_old') |
| 1091 src_dir = deps_data.get(depot_data_src) | 1098 src_dir = deps_data.get(depot_data_src) |
| 1092 if src_dir: | 1099 if src_dir: |
| 1093 self.depot_cwd[depot_name] = os.path.join(self.src_cwd, | 1100 self.depot_cwd[depot_name] = os.path.join( |
| 1094 depot_data_src[4:]) | 1101 self.src_cwd, depot_data_src[4:]) |
| 1095 re_results = rxp.search(src_dir) | 1102 re_results = rxp.search(src_dir) |
| 1096 if re_results: | 1103 if re_results: |
| 1097 results[depot_name] = re_results.group('revision') | 1104 results[depot_name] = re_results.group('revision') |
| 1098 else: | 1105 else: |
| 1099 warning_text = ('Could not parse revision for %s while bisecting ' | 1106 warning_text = ('Could not parse revision for %s while bisecting ' |
| 1100 '%s' % (depot_name, depot)) | 1107 '%s' % (depot_name, depot)) |
| 1101 if not warning_text in self.warnings: | 1108 if not warning_text in self.warnings: |
| 1102 self.warnings.append(warning_text) | 1109 self.warnings.append(warning_text) |
| 1103 else: | 1110 else: |
| 1104 results[depot_name] = None | 1111 results[depot_name] = None |
| (...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1560 def _IsBisectModeUsingMetric(self): | 1567 def _IsBisectModeUsingMetric(self): |
| 1561 return self.opts.bisect_mode in [BISECT_MODE_MEAN, BISECT_MODE_STD_DEV] | 1568 return self.opts.bisect_mode in [BISECT_MODE_MEAN, BISECT_MODE_STD_DEV] |
| 1562 | 1569 |
| 1563 def _IsBisectModeReturnCode(self): | 1570 def _IsBisectModeReturnCode(self): |
| 1564 return self.opts.bisect_mode in [BISECT_MODE_RETURN_CODE] | 1571 return self.opts.bisect_mode in [BISECT_MODE_RETURN_CODE] |
| 1565 | 1572 |
| 1566 def _IsBisectModeStandardDeviation(self): | 1573 def _IsBisectModeStandardDeviation(self): |
| 1567 return self.opts.bisect_mode in [BISECT_MODE_STD_DEV] | 1574 return self.opts.bisect_mode in [BISECT_MODE_STD_DEV] |
| 1568 | 1575 |
| 1569 def GetCompatibleCommand(self, command_to_run, revision, depot): | 1576 def GetCompatibleCommand(self, command_to_run, revision, depot): |
| 1570 # Prior to crrev.com/274857 *only* android-chromium-testshell | 1577 """Return a possibly modified test command depending on the revision. |
| 1571 # Then until crrev.com/276628 *both* (android-chromium-testshell and | 1578 |
| 1572 # android-chrome-shell) work. After that rev 276628 *only* | 1579 Prior to crrev.com/274857 *only* android-chromium-testshell |
| 1573 # android-chrome-shell works. bisect-perf-regression.py script should | 1580 Then until crrev.com/276628 *both* (android-chromium-testshell and |
| 1574 # handle these cases and set appropriate browser type based on revision. | 1581 android-chrome-shell) work. After that rev 276628 *only* |
| 1582 android-chrome-shell works. The bisect_perf_regression.py script should |
| 1583 handle these cases and set appropriate browser type based on revision. |
| 1584 """ |
| 1575 if self.opts.target_platform in ['android']: | 1585 if self.opts.target_platform in ['android']: |
| 1576 # When its a third_party depot, get the chromium revision. | 1586 # When its a third_party depot, get the chromium revision. |
| 1577 if depot != 'chromium': | 1587 if depot != 'chromium': |
| 1578 revision = bisect_utils.CheckRunGit( | 1588 revision = bisect_utils.CheckRunGit( |
| 1579 ['rev-parse', 'HEAD'], cwd=self.src_cwd).strip() | 1589 ['rev-parse', 'HEAD'], cwd=self.src_cwd).strip() |
| 1580 commit_position = self.source_control.GetCommitPosition(revision, | 1590 commit_position = self.source_control.GetCommitPosition(revision, |
| 1581 cwd=self.src_cwd) | 1591 cwd=self.src_cwd) |
| 1582 if not commit_position: | 1592 if not commit_position: |
| 1583 return command_to_run | 1593 return command_to_run |
| 1584 cmd_re = re.compile('--browser=(?P<browser_type>\S+)') | 1594 cmd_re = re.compile('--browser=(?P<browser_type>\S+)') |
| (...skipping 691 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2276 | 2286 |
| 2277 Args: | 2287 Args: |
| 2278 good_revision: Known good revision. | 2288 good_revision: Known good revision. |
| 2279 bad_revision: Known bad revision. | 2289 bad_revision: Known bad revision. |
| 2280 | 2290 |
| 2281 Returns: | 2291 Returns: |
| 2282 A dictionary indicating the result. If revision is not bisectable, | 2292 A dictionary indicating the result. If revision is not bisectable, |
| 2283 this will contain the field "error", otherwise None. | 2293 this will contain the field "error", otherwise None. |
| 2284 """ | 2294 """ |
| 2285 if self.opts.target_platform == 'android': | 2295 if self.opts.target_platform == 'android': |
| 2286 revision_to_check = self.source_control.GetCommitPosition(good_revision) | 2296 good_revision = self.source_control.GetCommitPosition(good_revision) |
| 2287 if (bisect_utils.IsStringInt(good_revision) | 2297 if (bisect_utils.IsStringInt(good_revision) |
| 2288 and good_revision < 265549): | 2298 and good_revision < 265549): |
| 2289 return {'error': ( | 2299 return {'error': ( |
| 2290 'Bisect cannot continue for the given revision range.\n' | 2300 'Bisect cannot continue for the given revision range.\n' |
| 2291 'It is impossible to bisect Android regressions ' | 2301 'It is impossible to bisect Android regressions ' |
| 2292 'prior to r265549, which allows the bisect bot to ' | 2302 'prior to r265549, which allows the bisect bot to ' |
| 2293 'rely on Telemetry to do apk installation of the most recently ' | 2303 'rely on Telemetry to do apk installation of the most recently ' |
| 2294 'built local ChromeShell(refer to crbug.com/385324).\n' | 2304 'built local ChromeShell(refer to crbug.com/385324).\n' |
| 2295 'Please try bisecting revisions greater than or equal to r265549.')} | 2305 'Please try bisecting revisions greater than or equal to r265549.')} |
| 2296 | 2306 |
| (...skipping 1100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3397 # bugs. If you change this, please update the perf dashboard as well. | 3407 # bugs. If you change this, please update the perf dashboard as well. |
| 3398 bisect_utils.OutputAnnotationStepStart('Results') | 3408 bisect_utils.OutputAnnotationStepStart('Results') |
| 3399 print 'Error: %s' % e.message | 3409 print 'Error: %s' % e.message |
| 3400 if opts.output_buildbot_annotations: | 3410 if opts.output_buildbot_annotations: |
| 3401 bisect_utils.OutputAnnotationStepClosed() | 3411 bisect_utils.OutputAnnotationStepClosed() |
| 3402 return 1 | 3412 return 1 |
| 3403 | 3413 |
| 3404 | 3414 |
| 3405 if __name__ == '__main__': | 3415 if __name__ == '__main__': |
| 3406 sys.exit(main()) | 3416 sys.exit(main()) |
| OLD | NEW |