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

Side by Side Diff: tools/auto_bisect/bisect_perf_regression.py

Issue 554283003: Refactored bisect results dicts into a separate class (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase AND Added tests for new classes Created 6 years, 2 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
« no previous file with comments | « no previous file | tools/auto_bisect/bisect_perf_regression_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 18 matching lines...) Expand all
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
40 import optparse 39 import optparse
41 import os 40 import os
42 import re 41 import re
43 import shlex 42 import shlex
44 import shutil 43 import shutil
45 import StringIO 44 import StringIO
46 import sys 45 import sys
47 import time 46 import time
48 import zipfile 47 import zipfile
49 48
50 sys.path.append(os.path.join( 49 sys.path.append(os.path.join(
51 os.path.dirname(__file__), os.path.pardir, 'telemetry')) 50 os.path.dirname(__file__), os.path.pardir, 'telemetry'))
52 51
52 from bisect_results import BisectResults
53 import bisect_utils 53 import bisect_utils
54 import builder 54 import builder
55 import math_utils 55 import math_utils
56 import request_build 56 import request_build
57 import source_control as source_control_module 57 import source_control as source_control_module
58 import ttest
59 from telemetry.util import cloud_storage 58 from telemetry.util import cloud_storage
60 59
61 # Below is the map of "depot" names to information about each depot. Each depot 60 # Below is the map of "depot" names to information about each depot. Each depot
62 # is a repository, and in the process of bisecting, revision ranges in these 61 # is a repository, and in the process of bisecting, revision ranges in these
63 # repositories may also be bisected. 62 # repositories may also be bisected.
64 # 63 #
65 # Each depot information dictionary may contain: 64 # Each depot information dictionary may contain:
66 # src: Path to the working directory. 65 # src: Path to the working directory.
67 # recurse: True if this repository will get bisected. 66 # recurse: True if this repository will get bisected.
68 # depends: A list of other repositories that are actually part of the same 67 # depends: A list of other repositories that are actually part of the same
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 268
270 269
271 def _AddAdditionalDepotInfo(depot_info): 270 def _AddAdditionalDepotInfo(depot_info):
272 """Adds additional depot info to the global depot variables.""" 271 """Adds additional depot info to the global depot variables."""
273 global DEPOT_DEPS_NAME 272 global DEPOT_DEPS_NAME
274 global DEPOT_NAMES 273 global DEPOT_NAMES
275 DEPOT_DEPS_NAME = dict(DEPOT_DEPS_NAME.items() + depot_info.items()) 274 DEPOT_DEPS_NAME = dict(DEPOT_DEPS_NAME.items() + depot_info.items())
276 DEPOT_NAMES = DEPOT_DEPS_NAME.keys() 275 DEPOT_NAMES = DEPOT_DEPS_NAME.keys()
277 276
278 277
279 def ConfidenceScore(good_results_lists, bad_results_lists):
280 """Calculates a confidence score.
281
282 This score is a percentage which represents our degree of confidence in the
283 proposition that the good results and bad results are distinct groups, and
284 their differences aren't due to chance alone.
285
286
287 Args:
288 good_results_lists: A list of lists of "good" result numbers.
289 bad_results_lists: A list of lists of "bad" result numbers.
290
291 Returns:
292 A number in the range [0, 100].
293 """
294 # If there's only one item in either list, this means only one revision was
295 # classified good or bad; this isn't good enough evidence to make a decision.
296 # If an empty list was passed, that also implies zero confidence.
297 if len(good_results_lists) <= 1 or len(bad_results_lists) <= 1:
298 return 0.0
299
300 # Flatten the lists of results lists.
301 sample1 = sum(good_results_lists, [])
302 sample2 = sum(bad_results_lists, [])
303
304 # If there were only empty lists in either of the lists (this is unexpected
305 # and normally shouldn't happen), then we also want to return 0.
306 if not sample1 or not sample2:
307 return 0.0
308
309 # The p-value is approximately the probability of obtaining the given set
310 # of good and bad values just by chance.
311 _, _, p_value = ttest.WelchsTTest(sample1, sample2)
312 return 100.0 * (1.0 - p_value)
313
314
315 def GetSHA1HexDigest(contents): 278 def GetSHA1HexDigest(contents):
316 """Returns SHA1 hex digest of the given string.""" 279 """Returns SHA1 hex digest of the given string."""
317 return hashlib.sha1(contents).hexdigest() 280 return hashlib.sha1(contents).hexdigest()
318 281
319 282
320 def GetZipFileName(build_revision=None, target_arch='ia32', patch_sha=None): 283 def GetZipFileName(build_revision=None, target_arch='ia32', patch_sha=None):
321 """Gets the archive file name for the given revision.""" 284 """Gets the archive file name for the given revision."""
322 def PlatformName(): 285 def PlatformName():
323 """Return a string to be used in paths for the platform.""" 286 """Return a string to be used in paths for the platform."""
324 if bisect_utils.IsWindowsHost(): 287 if bisect_utils.IsWindowsHost():
(...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after
861 if step_count: 824 if step_count:
862 step_perf_time_avg = step_perf_time_avg / step_count 825 step_perf_time_avg = step_perf_time_avg / step_count
863 step_build_time_avg = step_build_time_avg / step_count 826 step_build_time_avg = step_build_time_avg / step_count
864 print 827 print
865 print 'Average build time : %s' % datetime.timedelta( 828 print 'Average build time : %s' % datetime.timedelta(
866 seconds=int(step_build_time_avg)) 829 seconds=int(step_build_time_avg))
867 print 'Average test time : %s' % datetime.timedelta( 830 print 'Average test time : %s' % datetime.timedelta(
868 seconds=int(step_perf_time_avg)) 831 seconds=int(step_perf_time_avg))
869 832
870 833
871 def _FindOtherRegressions(revision_data_sorted, bad_greater_than_good): 834 class DepotDirectoryRegistry(object):
872 """Compiles a list of other possible regressions from the revision data. 835 """Manages depots (code repositories) and their directories."""
ojan 2014/09/26 17:34:30 IMO, this comment doesn't say anything the name of
873 836
874 Args: 837 def __init__(self):
875 revision_data_sorted: Sorted list of (revision, revision data) pairs. 838 self.depot_cwd = {}
876 bad_greater_than_good: Whether the result value at the "bad" revision is 839 self.cros_cwd = os.path.join(SRC_DIR, 'tools', 'cros')
877 numerically greater than the result value at the "good" revision. 840 for depot in DEPOT_NAMES:
841 # The working directory of each depot is just the path to the depot, but
842 # since we're already in 'src', we can skip that part.
843 self.depot_cwd[depot] = os.path.join(
844 SRC_DIR, DEPOT_DEPS_NAME[depot]['src'][4:])
878 845
879 Returns: 846 def AddDepot(self, depot_name, depot_dir):
880 A list of [current_rev, previous_rev, confidence] for other places where 847 self.depot_cwd[depot_name] = depot_dir
881 there may have been a regression.
882 """
883 other_regressions = []
884 previous_values = []
885 previous_id = None
886 for current_id, current_data in revision_data_sorted:
887 current_values = current_data['value']
888 if current_values:
889 current_values = current_values['values']
890 if previous_values:
891 confidence = ConfidenceScore(previous_values, [current_values])
892 mean_of_prev_runs = math_utils.Mean(sum(previous_values, []))
893 mean_of_current_runs = math_utils.Mean(current_values)
894 848
895 # Check that the potential regression is in the same direction as 849 def GetDepotDir(self, depot_name):
896 # the overall regression. If the mean of the previous runs < the 850 if depot_name == 'chromium':
897 # mean of the current runs, this local regression is in same 851 return SRC_DIR
898 # direction. 852 elif depot_name == 'cros':
899 prev_less_than_current = mean_of_prev_runs < mean_of_current_runs 853 return self.cros_cwd
900 is_same_direction = (prev_less_than_current if 854 elif depot_name in DEPOT_NAMES:
901 bad_greater_than_good else not prev_less_than_current) 855 return self.depot_cwd[depot_name]
856 else:
857 assert False, ('Unknown depot [ %s ] encountered. Possibly a new one '
858 'was added without proper support?' % depot_name)
902 859
903 # Only report potential regressions with high confidence. 860 def ChangeToDepotDir(self, depot_name):
904 if is_same_direction and confidence > 50: 861 """Given a depot, changes to the appropriate working directory.
905 other_regressions.append([current_id, previous_id, confidence]) 862
906 previous_values.append(current_values) 863 Args:
907 previous_id = current_id 864 depot_name: The name of the depot (see DEPOT_NAMES).
908 return other_regressions 865 """
866 os.chdir(self.GetDepotDir(depot_name))
909 867
910 868
911 class BisectPerformanceMetrics(object): 869 class BisectPerformanceMetrics(object):
912 """This class contains functionality to perform a bisection of a range of 870 """This class contains functionality to perform a bisection of a range of
913 revisions to narrow down where performance regressions may have occurred. 871 revisions to narrow down where performance regressions may have occurred.
914 872
915 The main entry-point is the Run method. 873 The main entry-point is the Run method.
916 """ 874 """
917 875
918 def __init__(self, source_control, opts): 876 def __init__(self, source_control, opts):
919 super(BisectPerformanceMetrics, self).__init__() 877 super(BisectPerformanceMetrics, self).__init__()
920 878
921 self.opts = opts 879 self.opts = opts
922 self.source_control = source_control 880 self.source_control = source_control
923 self.cros_cwd = os.path.join(SRC_DIR, 'tools', 'cros') 881 self.depot_manager = DepotDirectoryRegistry()
924 self.depot_cwd = {}
925 self.cleanup_commands = [] 882 self.cleanup_commands = []
926 self.warnings = [] 883 self.warnings = []
927 self.builder = builder.Builder.FromOpts(opts) 884 self.builder = builder.Builder.FromOpts(opts)
928 885
929 for depot in DEPOT_NAMES:
930 # The working directory of each depot is just the path to the depot, but
931 # since we're already in 'src', we can skip that part.
932 self.depot_cwd[depot] = os.path.join(
933 SRC_DIR, DEPOT_DEPS_NAME[depot]['src'][4:])
934 886
935 def PerformCleanup(self): 887 def PerformCleanup(self):
936 """Performs cleanup when script is finished.""" 888 """Performs cleanup when script is finished."""
937 os.chdir(SRC_DIR) 889 os.chdir(SRC_DIR)
938 for c in self.cleanup_commands: 890 for c in self.cleanup_commands:
939 if c[0] == 'mv': 891 if c[0] == 'mv':
940 shutil.move(c[1], c[2]) 892 shutil.move(c[1], c[2])
941 else: 893 else:
942 assert False, 'Invalid cleanup command.' 894 assert False, 'Invalid cleanup command.'
943 895
944 def GetRevisionList(self, depot, bad_revision, good_revision): 896 def GetRevisionList(self, depot, bad_revision, good_revision):
945 """Retrieves a list of all the commits between the bad revision and 897 """Retrieves a list of all the commits between the bad revision and
946 last known good revision.""" 898 last known good revision."""
947 899
948 revision_work_list = [] 900 revision_work_list = []
949 901
950 if depot == 'cros': 902 if depot == 'cros':
951 revision_range_start = good_revision 903 revision_range_start = good_revision
952 revision_range_end = bad_revision 904 revision_range_end = bad_revision
953 905
954 cwd = os.getcwd() 906 cwd = os.getcwd()
955 self.ChangeToDepotWorkingDirectory('cros') 907 self.depot_manager.ChangeToDepotDir('cros')
956 908
957 # Print the commit timestamps for every commit in the revision time 909 # Print the commit timestamps for every commit in the revision time
958 # range. We'll sort them and bisect by that. There is a remote chance that 910 # range. We'll sort them and bisect by that. There is a remote chance that
959 # 2 (or more) commits will share the exact same timestamp, but it's 911 # 2 (or more) commits will share the exact same timestamp, but it's
960 # probably safe to ignore that case. 912 # probably safe to ignore that case.
961 cmd = ['repo', 'forall', '-c', 913 cmd = ['repo', 'forall', '-c',
962 'git log --format=%%ct --before=%d --after=%d' % ( 914 'git log --format=%%ct --before=%d --after=%d' % (
963 revision_range_end, revision_range_start)] 915 revision_range_end, revision_range_start)]
964 output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd) 916 output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd)
965 917
966 assert not return_code, ('An error occurred while running ' 918 assert not return_code, ('An error occurred while running '
967 '"%s"' % ' '.join(cmd)) 919 '"%s"' % ' '.join(cmd))
968 920
969 os.chdir(cwd) 921 os.chdir(cwd)
970 922
971 revision_work_list = list(set( 923 revision_work_list = list(set(
972 [int(o) for o in output.split('\n') if bisect_utils.IsStringInt(o)])) 924 [int(o) for o in output.split('\n') if bisect_utils.IsStringInt(o)]))
973 revision_work_list = sorted(revision_work_list, reverse=True) 925 revision_work_list = sorted(revision_work_list, reverse=True)
974 else: 926 else:
975 cwd = self._GetDepotDirectory(depot) 927 cwd = self.depot_manager.GetDepotDir(depot)
976 revision_work_list = self.source_control.GetRevisionList(bad_revision, 928 revision_work_list = self.source_control.GetRevisionList(bad_revision,
977 good_revision, cwd=cwd) 929 good_revision, cwd=cwd)
978 930
979 return revision_work_list 931 return revision_work_list
980 932
981 def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision): 933 def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision):
982 commit_position = self.source_control.GetCommitPosition(revision) 934 commit_position = self.source_control.GetCommitPosition(revision)
983 935
984 if bisect_utils.IsStringInt(commit_position): 936 if bisect_utils.IsStringInt(commit_position):
985 # V8 is tricky to bisect, in that there are only a few instances when 937 # V8 is tricky to bisect, in that there are only a few instances when
986 # we can dive into bleeding_edge and get back a meaningful result. 938 # we can dive into bleeding_edge and get back a meaningful result.
987 # Try to detect a V8 "business as usual" case, which is when: 939 # Try to detect a V8 "business as usual" case, which is when:
988 # 1. trunk revision N has description "Version X.Y.Z" 940 # 1. trunk revision N has description "Version X.Y.Z"
989 # 2. bleeding_edge revision (N-1) has description "Prepare push to 941 # 2. bleeding_edge revision (N-1) has description "Prepare push to
990 # trunk. Now working on X.Y.(Z+1)." 942 # trunk. Now working on X.Y.(Z+1)."
991 # 943 #
992 # As of 01/24/2014, V8 trunk descriptions are formatted: 944 # As of 01/24/2014, V8 trunk descriptions are formatted:
993 # "Version 3.X.Y (based on bleeding_edge revision rZ)" 945 # "Version 3.X.Y (based on bleeding_edge revision rZ)"
994 # So we can just try parsing that out first and fall back to the old way. 946 # So we can just try parsing that out first and fall back to the old way.
995 v8_dir = self._GetDepotDirectory('v8') 947 v8_dir = self.depot_manager.GetDepotDir('v8')
996 v8_bleeding_edge_dir = self._GetDepotDirectory('v8_bleeding_edge') 948 v8_bleeding_edge_dir = self.depot_manager.GetDepotDir('v8_bleeding_edge')
997 949
998 revision_info = self.source_control.QueryRevisionInfo(revision, 950 revision_info = self.source_control.QueryRevisionInfo(revision,
999 cwd=v8_dir) 951 cwd=v8_dir)
1000 952
1001 version_re = re.compile("Version (?P<values>[0-9,.]+)") 953 version_re = re.compile("Version (?P<values>[0-9,.]+)")
1002 954
1003 regex_results = version_re.search(revision_info['subject']) 955 regex_results = version_re.search(revision_info['subject'])
1004 956
1005 if regex_results: 957 if regex_results:
1006 git_revision = None 958 git_revision = None
(...skipping 19 matching lines...) Expand all
1026 978
1027 if git_revision: 979 if git_revision:
1028 revision_info = self.source_control.QueryRevisionInfo(git_revision, 980 revision_info = self.source_control.QueryRevisionInfo(git_revision,
1029 cwd=v8_bleeding_edge_dir) 981 cwd=v8_bleeding_edge_dir)
1030 982
1031 if 'Prepare push to trunk' in revision_info['subject']: 983 if 'Prepare push to trunk' in revision_info['subject']:
1032 return git_revision 984 return git_revision
1033 return None 985 return None
1034 986
1035 def _GetNearestV8BleedingEdgeFromTrunk(self, revision, search_forward=True): 987 def _GetNearestV8BleedingEdgeFromTrunk(self, revision, search_forward=True):
1036 cwd = self._GetDepotDirectory('v8') 988 cwd = self.depot_manager.GetDepotDir('v8')
1037 cmd = ['log', '--format=%ct', '-1', revision] 989 cmd = ['log', '--format=%ct', '-1', revision]
1038 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 990 output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
1039 commit_time = int(output) 991 commit_time = int(output)
1040 commits = [] 992 commits = []
1041 993
1042 if search_forward: 994 if search_forward:
1043 cmd = ['log', '--format=%H', '-10', '--after=%d' % commit_time, 995 cmd = ['log', '--format=%H', '-10', '--after=%d' % commit_time,
1044 'origin/master'] 996 'origin/master']
1045 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 997 output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
1046 output = output.split() 998 output = output.split()
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
1088 results = {} 1040 results = {}
1089 for depot_name, depot_data in DEPOT_DEPS_NAME.iteritems(): 1041 for depot_name, depot_data in DEPOT_DEPS_NAME.iteritems():
1090 if (depot_data.get('platform') and 1042 if (depot_data.get('platform') and
1091 depot_data.get('platform') != os.name): 1043 depot_data.get('platform') != os.name):
1092 continue 1044 continue
1093 1045
1094 if (depot_data.get('recurse') and depot in depot_data.get('from')): 1046 if (depot_data.get('recurse') and depot in depot_data.get('from')):
1095 depot_data_src = depot_data.get('src') or depot_data.get('src_old') 1047 depot_data_src = depot_data.get('src') or depot_data.get('src_old')
1096 src_dir = deps_data.get(depot_data_src) 1048 src_dir = deps_data.get(depot_data_src)
1097 if src_dir: 1049 if src_dir:
1098 self.depot_cwd[depot_name] = os.path.join( 1050 self.depot_manager.AddDepot(
1099 SRC_DIR, depot_data_src[4:]) 1051 depot_name, os.path.join(SRC_DIR, depot_data_src[4:]))
1100 re_results = rxp.search(src_dir) 1052 re_results = rxp.search(src_dir)
1101 if re_results: 1053 if re_results:
1102 results[depot_name] = re_results.group('revision') 1054 results[depot_name] = re_results.group('revision')
1103 else: 1055 else:
1104 warning_text = ('Could not parse revision for %s while bisecting ' 1056 warning_text = ('Could not parse revision for %s while bisecting '
1105 '%s' % (depot_name, depot)) 1057 '%s' % (depot_name, depot))
1106 if not warning_text in self.warnings: 1058 if not warning_text in self.warnings:
1107 self.warnings.append(warning_text) 1059 self.warnings.append(warning_text)
1108 else: 1060 else:
1109 results[depot_name] = None 1061 results[depot_name] = None
(...skipping 16 matching lines...) Expand all
1126 def _Get3rdPartyRevisions(self, depot): 1078 def _Get3rdPartyRevisions(self, depot):
1127 """Parses the DEPS file to determine WebKit/v8/etc... versions. 1079 """Parses the DEPS file to determine WebKit/v8/etc... versions.
1128 1080
1129 Args: 1081 Args:
1130 depot: A depot name. Should be in the DEPOT_NAMES list. 1082 depot: A depot name. Should be in the DEPOT_NAMES list.
1131 1083
1132 Returns: 1084 Returns:
1133 A dict in the format {depot: revision} if successful, otherwise None. 1085 A dict in the format {depot: revision} if successful, otherwise None.
1134 """ 1086 """
1135 cwd = os.getcwd() 1087 cwd = os.getcwd()
1136 self.ChangeToDepotWorkingDirectory(depot) 1088 self.depot_manager.ChangeToDepotDir(depot)
1137 1089
1138 results = {} 1090 results = {}
1139 1091
1140 if depot == 'chromium' or depot == 'android-chrome': 1092 if depot == 'chromium' or depot == 'android-chrome':
1141 results = self._ParseRevisionsFromDEPSFile(depot) 1093 results = self._ParseRevisionsFromDEPSFile(depot)
1142 os.chdir(cwd) 1094 os.chdir(cwd)
1143 1095
1144 if depot == 'cros': 1096 if depot == 'cros':
1145 cmd = [ 1097 cmd = [
1146 bisect_utils.CROS_SDK_PATH, 1098 bisect_utils.CROS_SDK_PATH,
(...skipping 20 matching lines...) Expand all
1167 1119
1168 version = contents[2] 1120 version = contents[2]
1169 1121
1170 if contents[3] != '0': 1122 if contents[3] != '0':
1171 warningText = ('Chrome version: %s.%s but using %s.0 to bisect.' % 1123 warningText = ('Chrome version: %s.%s but using %s.0 to bisect.' %
1172 (version, contents[3], version)) 1124 (version, contents[3], version))
1173 if not warningText in self.warnings: 1125 if not warningText in self.warnings:
1174 self.warnings.append(warningText) 1126 self.warnings.append(warningText)
1175 1127
1176 cwd = os.getcwd() 1128 cwd = os.getcwd()
1177 self.ChangeToDepotWorkingDirectory('chromium') 1129 self.depot_manager.ChangeToDepotDir('chromium')
1178 cmd = ['log', '-1', '--format=%H', 1130 cmd = ['log', '-1', '--format=%H',
1179 '--author=chrome-release@google.com', 1131 '--author=chrome-release@google.com',
1180 '--grep=to %s' % version, 'origin/master'] 1132 '--grep=to %s' % version, 'origin/master']
1181 return_code = bisect_utils.CheckRunGit(cmd) 1133 return_code = bisect_utils.CheckRunGit(cmd)
1182 os.chdir(cwd) 1134 os.chdir(cwd)
1183 1135
1184 results['chromium'] = output.strip() 1136 results['chromium'] = output.strip()
1185 1137
1186 if depot == 'v8': 1138 if depot == 'v8':
1187 # We can't try to map the trunk revision to bleeding edge yet, because 1139 # We can't try to map the trunk revision to bleeding edge yet, because
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
1405 Returns: 1357 Returns:
1406 Updated DEPS content as string if deps key is found, otherwise None. 1358 Updated DEPS content as string if deps key is found, otherwise None.
1407 """ 1359 """
1408 # Check whether the depot and revision pattern in DEPS file vars 1360 # Check whether the depot and revision pattern in DEPS file vars
1409 # e.g. for webkit the format is "webkit_revision": "12345". 1361 # e.g. for webkit the format is "webkit_revision": "12345".
1410 deps_revision = re.compile(r'(?<="%s": ")([0-9]+)(?=")' % deps_key, 1362 deps_revision = re.compile(r'(?<="%s": ")([0-9]+)(?=")' % deps_key,
1411 re.MULTILINE) 1363 re.MULTILINE)
1412 new_data = None 1364 new_data = None
1413 if re.search(deps_revision, deps_contents): 1365 if re.search(deps_revision, deps_contents):
1414 commit_position = self.source_control.GetCommitPosition( 1366 commit_position = self.source_control.GetCommitPosition(
1415 git_revision, self._GetDepotDirectory(depot)) 1367 git_revision, self.depot_manager.GetDepotDir(depot))
1416 if not commit_position: 1368 if not commit_position:
1417 print 'Could not determine commit position for %s' % git_revision 1369 print 'Could not determine commit position for %s' % git_revision
1418 return None 1370 return None
1419 # Update the revision information for the given depot 1371 # Update the revision information for the given depot
1420 new_data = re.sub(deps_revision, str(commit_position), deps_contents) 1372 new_data = re.sub(deps_revision, str(commit_position), deps_contents)
1421 else: 1373 else:
1422 # Check whether the depot and revision pattern in DEPS file vars 1374 # Check whether the depot and revision pattern in DEPS file vars
1423 # e.g. for webkit the format is "webkit_revision": "559a6d4ab7a84c539..". 1375 # e.g. for webkit the format is "webkit_revision": "559a6d4ab7a84c539..".
1424 deps_revision = re.compile( 1376 deps_revision = re.compile(
1425 r'(?<=["\']%s["\']: ["\'])([a-fA-F0-9]{40})(?=["\'])' % deps_key, 1377 r'(?<=["\']%s["\']: ["\'])([a-fA-F0-9]{40})(?=["\'])' % deps_key,
(...skipping 343 matching lines...) Expand 10 before | Expand all | Expand 10 after
1769 # Some SVN depots were split into multiple git depots, so we need to 1721 # Some SVN depots were split into multiple git depots, so we need to
1770 # figure out for each mirror which git revision to grab. There's no 1722 # figure out for each mirror which git revision to grab. There's no
1771 # guarantee that the SVN revision will exist for each of the dependent 1723 # guarantee that the SVN revision will exist for each of the dependent
1772 # depots, so we have to grep the git logs and grab the next earlier one. 1724 # depots, so we have to grep the git logs and grab the next earlier one.
1773 if (not is_base 1725 if (not is_base
1774 and DEPOT_DEPS_NAME[depot]['depends'] 1726 and DEPOT_DEPS_NAME[depot]['depends']
1775 and self.source_control.IsGit()): 1727 and self.source_control.IsGit()):
1776 commit_position = self.source_control.GetCommitPosition(revision) 1728 commit_position = self.source_control.GetCommitPosition(revision)
1777 1729
1778 for d in DEPOT_DEPS_NAME[depot]['depends']: 1730 for d in DEPOT_DEPS_NAME[depot]['depends']:
1779 self.ChangeToDepotWorkingDirectory(d) 1731 self.depot_manager.ChangeToDepotDir(d)
1780 1732
1781 dependant_rev = self.source_control.ResolveToRevision( 1733 dependant_rev = self.source_control.ResolveToRevision(
1782 commit_position, d, DEPOT_DEPS_NAME, -1000) 1734 commit_position, d, DEPOT_DEPS_NAME, -1000)
1783 1735
1784 if dependant_rev: 1736 if dependant_rev:
1785 revisions_to_sync.append([d, dependant_rev]) 1737 revisions_to_sync.append([d, dependant_rev])
1786 1738
1787 num_resolved = len(revisions_to_sync) 1739 num_resolved = len(revisions_to_sync)
1788 num_needed = len(DEPOT_DEPS_NAME[depot]['depends']) 1740 num_needed = len(DEPOT_DEPS_NAME[depot]['depends'])
1789 1741
1790 self.ChangeToDepotWorkingDirectory(depot) 1742 self.depot_manager.ChangeToDepotDir(depot)
1791 1743
1792 if not ((num_resolved - 1) == num_needed): 1744 if not ((num_resolved - 1) == num_needed):
1793 return None 1745 return None
1794 1746
1795 return revisions_to_sync 1747 return revisions_to_sync
1796 1748
1797 @staticmethod 1749 @staticmethod
1798 def PerformPreBuildCleanup(): 1750 def PerformPreBuildCleanup():
1799 """Performs cleanup between runs.""" 1751 """Performs cleanup between runs."""
1800 print 'Cleaning up between runs.' 1752 print 'Cleaning up between runs.'
1801 print 1753 print
1802 1754
1803 # Leaving these .pyc files around between runs may disrupt some perf tests. 1755 # Leaving these .pyc files around between runs may disrupt some perf tests.
1804 for (path, _, files) in os.walk(SRC_DIR): 1756 for (path, _, files) in os.walk(SRC_DIR):
1805 for cur_file in files: 1757 for cur_file in files:
1806 if cur_file.endswith('.pyc'): 1758 if cur_file.endswith('.pyc'):
1807 path_to_file = os.path.join(path, cur_file) 1759 path_to_file = os.path.join(path, cur_file)
1808 os.remove(path_to_file) 1760 os.remove(path_to_file)
1809 1761
1810 def PerformCrosChrootCleanup(self): 1762 def PerformCrosChrootCleanup(self):
1811 """Deletes the chroot. 1763 """Deletes the chroot.
1812 1764
1813 Returns: 1765 Returns:
1814 True if successful. 1766 True if successful.
1815 """ 1767 """
1816 cwd = os.getcwd() 1768 cwd = os.getcwd()
1817 self.ChangeToDepotWorkingDirectory('cros') 1769 self.depot_manager.ChangeToDepotDir('cros')
1818 cmd = [bisect_utils.CROS_SDK_PATH, '--delete'] 1770 cmd = [bisect_utils.CROS_SDK_PATH, '--delete']
1819 return_code = bisect_utils.RunProcess(cmd) 1771 return_code = bisect_utils.RunProcess(cmd)
1820 os.chdir(cwd) 1772 os.chdir(cwd)
1821 return not return_code 1773 return not return_code
1822 1774
1823 def CreateCrosChroot(self): 1775 def CreateCrosChroot(self):
1824 """Creates a new chroot. 1776 """Creates a new chroot.
1825 1777
1826 Returns: 1778 Returns:
1827 True if successful. 1779 True if successful.
1828 """ 1780 """
1829 cwd = os.getcwd() 1781 cwd = os.getcwd()
1830 self.ChangeToDepotWorkingDirectory('cros') 1782 self.depot_manager.ChangeToDepotDir('cros')
1831 cmd = [bisect_utils.CROS_SDK_PATH, '--create'] 1783 cmd = [bisect_utils.CROS_SDK_PATH, '--create']
1832 return_code = bisect_utils.RunProcess(cmd) 1784 return_code = bisect_utils.RunProcess(cmd)
1833 os.chdir(cwd) 1785 os.chdir(cwd)
1834 return not return_code 1786 return not return_code
1835 1787
1836 def _PerformPreSyncCleanup(self, depot): 1788 def _PerformPreSyncCleanup(self, depot):
1837 """Performs any necessary cleanup before syncing. 1789 """Performs any necessary cleanup before syncing.
1838 1790
1839 Args: 1791 Args:
1840 depot: Depot name. 1792 depot: Depot name.
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
1982 """Syncs multiple depots to particular revisions. 1934 """Syncs multiple depots to particular revisions.
1983 1935
1984 Args: 1936 Args:
1985 revisions_to_sync: A list of (depot, revision) pairs to be synced. 1937 revisions_to_sync: A list of (depot, revision) pairs to be synced.
1986 sync_client: Program used to sync, e.g. "gclient", "repo". Can be None. 1938 sync_client: Program used to sync, e.g. "gclient", "repo". Can be None.
1987 1939
1988 Returns: 1940 Returns:
1989 True if successful, False otherwise. 1941 True if successful, False otherwise.
1990 """ 1942 """
1991 for depot, revision in revisions_to_sync: 1943 for depot, revision in revisions_to_sync:
1992 self.ChangeToDepotWorkingDirectory(depot) 1944 self.depot_manager.ChangeToDepotDir(depot)
1993 1945
1994 if sync_client: 1946 if sync_client:
1995 self.PerformPreBuildCleanup() 1947 self.PerformPreBuildCleanup()
1996 1948
1997 # When using gclient to sync, you need to specify the depot you 1949 # When using gclient to sync, you need to specify the depot you
1998 # want so that all the dependencies sync properly as well. 1950 # want so that all the dependencies sync properly as well.
1999 # i.e. gclient sync src@<SHA1> 1951 # i.e. gclient sync src@<SHA1>
2000 if sync_client == 'gclient': 1952 if sync_client == 'gclient':
2001 revision = '%s@%s' % (DEPOT_DEPS_NAME[depot]['src'], revision) 1953 revision = '%s@%s' % (DEPOT_DEPS_NAME[depot]['src'], revision)
2002 1954
(...skipping 20 matching lines...) Expand all
2023 dist_to_good_value = abs(current_value['std_dev'] - 1975 dist_to_good_value = abs(current_value['std_dev'] -
2024 known_good_value['std_dev']) 1976 known_good_value['std_dev'])
2025 dist_to_bad_value = abs(current_value['std_dev'] - 1977 dist_to_bad_value = abs(current_value['std_dev'] -
2026 known_bad_value['std_dev']) 1978 known_bad_value['std_dev'])
2027 else: 1979 else:
2028 dist_to_good_value = abs(current_value['mean'] - known_good_value['mean']) 1980 dist_to_good_value = abs(current_value['mean'] - known_good_value['mean'])
2029 dist_to_bad_value = abs(current_value['mean'] - known_bad_value['mean']) 1981 dist_to_bad_value = abs(current_value['mean'] - known_bad_value['mean'])
2030 1982
2031 return dist_to_good_value < dist_to_bad_value 1983 return dist_to_good_value < dist_to_bad_value
2032 1984
2033 def _GetDepotDirectory(self, depot_name):
2034 if depot_name == 'chromium':
2035 return SRC_DIR
2036 elif depot_name == 'cros':
2037 return self.cros_cwd
2038 elif depot_name in DEPOT_NAMES:
2039 return self.depot_cwd[depot_name]
2040 else:
2041 assert False, ('Unknown depot [ %s ] encountered. Possibly a new one '
2042 'was added without proper support?' % depot_name)
2043
2044 def ChangeToDepotWorkingDirectory(self, depot_name):
2045 """Given a depot, changes to the appropriate working directory.
2046
2047 Args:
2048 depot_name: The name of the depot (see DEPOT_NAMES).
2049 """
2050 os.chdir(self._GetDepotDirectory(depot_name))
2051
2052 def _FillInV8BleedingEdgeInfo(self, min_revision_data, max_revision_data): 1985 def _FillInV8BleedingEdgeInfo(self, min_revision_data, max_revision_data):
2053 r1 = self._GetNearestV8BleedingEdgeFromTrunk(min_revision_data['revision'], 1986 r1 = self._GetNearestV8BleedingEdgeFromTrunk(min_revision_data['revision'],
2054 search_forward=True) 1987 search_forward=True)
2055 r2 = self._GetNearestV8BleedingEdgeFromTrunk(max_revision_data['revision'], 1988 r2 = self._GetNearestV8BleedingEdgeFromTrunk(max_revision_data['revision'],
2056 search_forward=False) 1989 search_forward=False)
2057 min_revision_data['external']['v8_bleeding_edge'] = r1 1990 min_revision_data['external']['v8_bleeding_edge'] = r1
2058 max_revision_data['external']['v8_bleeding_edge'] = r2 1991 max_revision_data['external']['v8_bleeding_edge'] = r2
2059 1992
2060 if (not self._GetV8BleedingEdgeFromV8TrunkIfMappable( 1993 if (not self._GetV8BleedingEdgeFromV8TrunkIfMappable(
2061 min_revision_data['revision']) 1994 min_revision_data['revision'])
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
2117 end_revision: End of the revision range. 2050 end_revision: End of the revision range.
2118 start_revision: Start of the revision range. 2051 start_revision: Start of the revision range.
2119 previous_revision: The last revision we synced to on |previous_depot|. 2052 previous_revision: The last revision we synced to on |previous_depot|.
2120 2053
2121 Returns: 2054 Returns:
2122 A list containing the revisions between |start_revision| and 2055 A list containing the revisions between |start_revision| and
2123 |end_revision| inclusive. 2056 |end_revision| inclusive.
2124 """ 2057 """
2125 # Change into working directory of external library to run 2058 # Change into working directory of external library to run
2126 # subsequent commands. 2059 # subsequent commands.
2127 self.ChangeToDepotWorkingDirectory(current_depot) 2060 self.depot_manager.ChangeToDepotDir(current_depot)
2128 2061
2129 # V8 (and possibly others) is merged in periodically. Bisecting 2062 # V8 (and possibly others) is merged in periodically. Bisecting
2130 # this directory directly won't give much good info. 2063 # this directory directly won't give much good info.
2131 if DEPOT_DEPS_NAME[current_depot].has_key('custom_deps'): 2064 if DEPOT_DEPS_NAME[current_depot].has_key('custom_deps'):
2132 config_path = os.path.join(SRC_DIR, '..') 2065 config_path = os.path.join(SRC_DIR, '..')
2133 if bisect_utils.RunGClientAndCreateConfig(self.opts, 2066 if bisect_utils.RunGClientAndCreateConfig(self.opts,
2134 DEPOT_DEPS_NAME[current_depot]['custom_deps'], cwd=config_path): 2067 DEPOT_DEPS_NAME[current_depot]['custom_deps'], cwd=config_path):
2135 return [] 2068 return []
2136 if bisect_utils.RunGClient( 2069 if bisect_utils.RunGClient(
2137 ['sync', '--revision', previous_revision], cwd=SRC_DIR): 2070 ['sync', '--revision', previous_revision], cwd=SRC_DIR):
2138 return [] 2071 return []
2139 2072
2140 if current_depot == 'v8_bleeding_edge': 2073 if current_depot == 'v8_bleeding_edge':
2141 self.ChangeToDepotWorkingDirectory('chromium') 2074 self.depot_manager.ChangeToDepotDir('chromium')
2142 2075
2143 shutil.move('v8', 'v8.bak') 2076 shutil.move('v8', 'v8.bak')
2144 shutil.move('v8_bleeding_edge', 'v8') 2077 shutil.move('v8_bleeding_edge', 'v8')
2145 2078
2146 self.cleanup_commands.append(['mv', 'v8', 'v8_bleeding_edge']) 2079 self.cleanup_commands.append(['mv', 'v8', 'v8_bleeding_edge'])
2147 self.cleanup_commands.append(['mv', 'v8.bak', 'v8']) 2080 self.cleanup_commands.append(['mv', 'v8.bak', 'v8'])
2148 2081
2149 self.depot_cwd['v8_bleeding_edge'] = os.path.join(SRC_DIR, 'v8') 2082 self.depot_manager.AddDepot(
2150 self.depot_cwd['v8'] = os.path.join(SRC_DIR, 'v8.bak') 2083 'v8_bleeding_edge', os.path.join(SRC_DIR, 'v8'))
2084 self.depot_manager.AddDepot('v8', os.path.join(SRC_DIR, 'v8.bak'))
2151 2085
2152 self.ChangeToDepotWorkingDirectory(current_depot) 2086 self.depot_manager.ChangeToDepotDir(current_depot)
2153 2087
2154 depot_revision_list = self.GetRevisionList(current_depot, 2088 depot_revision_list = self.GetRevisionList(current_depot,
2155 end_revision, 2089 end_revision,
2156 start_revision) 2090 start_revision)
2157 2091
2158 self.ChangeToDepotWorkingDirectory('chromium') 2092 self.depot_manager.ChangeToDepotDir('chromium')
2159 2093
2160 return depot_revision_list 2094 return depot_revision_list
2161 2095
2162 def GatherReferenceValues(self, good_rev, bad_rev, cmd, metric, target_depot): 2096 def GatherReferenceValues(self, good_rev, bad_rev, cmd, metric, target_depot):
2163 """Gathers reference values by running the performance tests on the 2097 """Gathers reference values by running the performance tests on the
2164 known good and bad revisions. 2098 known good and bad revisions.
2165 2099
2166 Args: 2100 Args:
2167 good_rev: The last known good revision where the performance regression 2101 good_rev: The last known good revision where the performance regression
2168 has not occurred yet. 2102 has not occurred yet.
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
2255 2189
2256 Args: 2190 Args:
2257 good_revision: Number/tag of the known good revision. 2191 good_revision: Number/tag of the known good revision.
2258 bad_revision: Number/tag of the known bad revision. 2192 bad_revision: Number/tag of the known bad revision.
2259 2193
2260 Returns: 2194 Returns:
2261 True if the revisions are in the proper order (good earlier than bad). 2195 True if the revisions are in the proper order (good earlier than bad).
2262 """ 2196 """
2263 if self.source_control.IsGit() and target_depot != 'cros': 2197 if self.source_control.IsGit() and target_depot != 'cros':
2264 cmd = ['log', '--format=%ct', '-1', good_revision] 2198 cmd = ['log', '--format=%ct', '-1', good_revision]
2265 cwd = self._GetDepotDirectory(target_depot) 2199 cwd = self.depot_manager.GetDepotDir(target_depot)
2266 2200
2267 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 2201 output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
2268 good_commit_time = int(output) 2202 good_commit_time = int(output)
2269 2203
2270 cmd = ['log', '--format=%ct', '-1', bad_revision] 2204 cmd = ['log', '--format=%ct', '-1', bad_revision]
2271 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 2205 output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
2272 bad_commit_time = int(output) 2206 bad_commit_time = int(output)
2273 2207
2274 return good_commit_time <= bad_commit_time 2208 return good_commit_time <= bad_commit_time
2275 else: 2209 else:
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
2321 intermediate revisions to determine the CL where the performance regression 2255 intermediate revisions to determine the CL where the performance regression
2322 occurred. 2256 occurred.
2323 2257
2324 Args: 2258 Args:
2325 command_to_run: Specify the command to execute the performance test. 2259 command_to_run: Specify the command to execute the performance test.
2326 good_revision: Number/tag of the known good revision. 2260 good_revision: Number/tag of the known good revision.
2327 bad_revision: Number/tag of the known bad revision. 2261 bad_revision: Number/tag of the known bad revision.
2328 metric: The performance metric to monitor. 2262 metric: The performance metric to monitor.
2329 2263
2330 Returns: 2264 Returns:
2331 A dict with 2 members, 'revision_data' and 'error'. On success, 2265 A BisectResults object.
2332 'revision_data' will contain a dict mapping revision ids to
2333 data about that revision. Each piece of revision data consists of a
2334 dict with the following keys:
2335
2336 'passed': Represents whether the performance test was successful at
2337 that revision. Possible values include: 1 (passed), 0 (failed),
2338 '?' (skipped), 'F' (build failed).
2339 'depot': The depot that this revision is from (i.e. WebKit)
2340 'external': If the revision is a 'src' revision, 'external' contains
2341 the revisions of each of the external libraries.
2342 'sort': A sort value for sorting the dict in order of commits.
2343
2344 For example:
2345 {
2346 'error':None,
2347 'revision_data':
2348 {
2349 'CL #1':
2350 {
2351 'passed': False,
2352 'depot': 'chromium',
2353 'external': None,
2354 'sort': 0
2355 }
2356 }
2357 }
2358
2359 If an error occurred, the 'error' field will contain the message and
2360 'revision_data' will be empty.
2361 """ 2266 """
2362 results = { 2267 results = BisectResults(self.depot_manager, self.source_control)
2363 'revision_data' : {},
2364 'error' : None,
2365 }
2366 2268
2367 # Choose depot to bisect first 2269 # Choose depot to bisect first
2368 target_depot = 'chromium' 2270 target_depot = 'chromium'
2369 if self.opts.target_platform == 'cros': 2271 if self.opts.target_platform == 'cros':
2370 target_depot = 'cros' 2272 target_depot = 'cros'
2371 elif self.opts.target_platform == 'android-chrome': 2273 elif self.opts.target_platform == 'android-chrome':
2372 target_depot = 'android-chrome' 2274 target_depot = 'android-chrome'
2373 2275
2374 cwd = os.getcwd() 2276 cwd = os.getcwd()
2375 self.ChangeToDepotWorkingDirectory(target_depot) 2277 self.depot_manager.ChangeToDepotDir(target_depot)
2376 2278
2377 # If they passed SVN revisions, we can try match them to git SHA1 hashes. 2279 # If they passed SVN revisions, we can try match them to git SHA1 hashes.
2378 bad_revision = self.source_control.ResolveToRevision( 2280 bad_revision = self.source_control.ResolveToRevision(
2379 bad_revision_in, target_depot, DEPOT_DEPS_NAME, 100) 2281 bad_revision_in, target_depot, DEPOT_DEPS_NAME, 100)
2380 good_revision = self.source_control.ResolveToRevision( 2282 good_revision = self.source_control.ResolveToRevision(
2381 good_revision_in, target_depot, DEPOT_DEPS_NAME, -100) 2283 good_revision_in, target_depot, DEPOT_DEPS_NAME, -100)
2382 2284
2383 os.chdir(cwd) 2285 os.chdir(cwd)
2384 if bad_revision is None: 2286 if bad_revision is None:
2385 results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % bad_revision_in 2287 results.error = 'Couldn\'t resolve [%s] to SHA1.' % bad_revision_in
2386 return results 2288 return results
2387 2289
2388 if good_revision is None: 2290 if good_revision is None:
2389 results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % good_revision_in 2291 results.error = 'Couldn\'t resolve [%s] to SHA1.' % good_revision_in
2390 return results 2292 return results
2391 2293
2392 # Check that they didn't accidentally swap good and bad revisions. 2294 # Check that they didn't accidentally swap good and bad revisions.
2393 if not self.CheckIfRevisionsInProperOrder( 2295 if not self.CheckIfRevisionsInProperOrder(
2394 target_depot, good_revision, bad_revision): 2296 target_depot, good_revision, bad_revision):
2395 results['error'] = ('bad_revision < good_revision, did you swap these ' 2297 results.error = ('bad_revision < good_revision, did you swap these '
2396 'by mistake?') 2298 'by mistake?')
2397 return results 2299 return results
2398 bad_revision, good_revision = self.NudgeRevisionsIfDEPSChange( 2300 bad_revision, good_revision = self.NudgeRevisionsIfDEPSChange(
2399 bad_revision, good_revision, good_revision_in) 2301 bad_revision, good_revision, good_revision_in)
2400 if self.opts.output_buildbot_annotations: 2302 if self.opts.output_buildbot_annotations:
2401 bisect_utils.OutputAnnotationStepStart('Gathering Revisions') 2303 bisect_utils.OutputAnnotationStepStart('Gathering Revisions')
2402 2304
2403 cannot_bisect = self.CanPerformBisect(good_revision, bad_revision) 2305 cannot_bisect = self.CanPerformBisect(good_revision, bad_revision)
2404 if cannot_bisect: 2306 if cannot_bisect:
2405 results['error'] = cannot_bisect.get('error') 2307 results.error = cannot_bisect.get('error')
2406 return results 2308 return results
2407 2309
2408 print 'Gathering revision range for bisection.' 2310 print 'Gathering revision range for bisection.'
2409 # Retrieve a list of revisions to do bisection on. 2311 # Retrieve a list of revisions to do bisection on.
2410 src_revision_list = self.GetRevisionList( 2312 src_revision_list = self.GetRevisionList(
2411 target_depot, bad_revision, good_revision) 2313 target_depot, bad_revision, good_revision)
2412 2314
2413 if self.opts.output_buildbot_annotations: 2315 if self.opts.output_buildbot_annotations:
2414 bisect_utils.OutputAnnotationStepClosed() 2316 bisect_utils.OutputAnnotationStepClosed()
2415 2317
2416 if src_revision_list: 2318 if src_revision_list:
2417 # revision_data will store information about a revision such as the 2319 # revision_data will store information about a revision such as the
2418 # depot it came from, the webkit/V8 revision at that time, 2320 # depot it came from, the webkit/V8 revision at that time,
2419 # performance timing, build state, etc... 2321 # performance timing, build state, etc...
2420 revision_data = results['revision_data'] 2322 revision_data = results.revision_data
2421 2323
2422 # revision_list is the list we're binary searching through at the moment. 2324 # revision_list is the list we're binary searching through at the moment.
2423 revision_list = [] 2325 revision_list = []
2424 2326
2425 sort_key_ids = 0 2327 sort_key_ids = 0
2426 2328
2427 for current_revision_id in src_revision_list: 2329 for current_revision_id in src_revision_list:
2428 sort_key_ids += 1 2330 sort_key_ids += 1
2429 2331
2430 revision_data[current_revision_id] = { 2332 revision_data[current_revision_id] = {
(...skipping 22 matching lines...) Expand all
2453 bad_results, good_results = self.GatherReferenceValues(good_revision, 2355 bad_results, good_results = self.GatherReferenceValues(good_revision,
2454 bad_revision, 2356 bad_revision,
2455 command_to_run, 2357 command_to_run,
2456 metric, 2358 metric,
2457 target_depot) 2359 target_depot)
2458 2360
2459 if self.opts.output_buildbot_annotations: 2361 if self.opts.output_buildbot_annotations:
2460 bisect_utils.OutputAnnotationStepClosed() 2362 bisect_utils.OutputAnnotationStepClosed()
2461 2363
2462 if bad_results[1]: 2364 if bad_results[1]:
2463 results['error'] = ('An error occurred while building and running ' 2365 results.error = ('An error occurred while building and running '
2464 'the \'bad\' reference value. The bisect cannot continue without ' 2366 'the \'bad\' reference value. The bisect cannot continue without '
2465 'a working \'bad\' revision to start from.\n\nError: %s' % 2367 'a working \'bad\' revision to start from.\n\nError: %s' %
2466 bad_results[0]) 2368 bad_results[0])
2467 return results 2369 return results
2468 2370
2469 if good_results[1]: 2371 if good_results[1]:
2470 results['error'] = ('An error occurred while building and running ' 2372 results.error = ('An error occurred while building and running '
2471 'the \'good\' reference value. The bisect cannot continue without ' 2373 'the \'good\' reference value. The bisect cannot continue without '
2472 'a working \'good\' revision to start from.\n\nError: %s' % 2374 'a working \'good\' revision to start from.\n\nError: %s' %
2473 good_results[0]) 2375 good_results[0])
2474 return results 2376 return results
2475 2377
2476 2378
2477 # We need these reference values to determine if later runs should be 2379 # We need these reference values to determine if later runs should be
2478 # classified as pass or fail. 2380 # classified as pass or fail.
2479 known_bad_value = bad_results[0] 2381 known_bad_value = bad_results[0]
2480 known_good_value = good_results[0] 2382 known_good_value = good_results[0]
2481 2383
2482 # Can just mark the good and bad revisions explicitly here since we 2384 # Can just mark the good and bad revisions explicitly here since we
2483 # already know the results. 2385 # already know the results.
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
2528 break 2430 break
2529 2431
2530 earliest_revision = max_revision_data['external'][external_depot] 2432 earliest_revision = max_revision_data['external'][external_depot]
2531 latest_revision = min_revision_data['external'][external_depot] 2433 latest_revision = min_revision_data['external'][external_depot]
2532 2434
2533 new_revision_list = self.PrepareToBisectOnDepot( 2435 new_revision_list = self.PrepareToBisectOnDepot(
2534 external_depot, latest_revision, earliest_revision, 2436 external_depot, latest_revision, earliest_revision,
2535 previous_revision) 2437 previous_revision)
2536 2438
2537 if not new_revision_list: 2439 if not new_revision_list:
2538 results['error'] = ('An error occurred attempting to retrieve ' 2440 results.error = ('An error occurred attempting to retrieve '
2539 'revision range: [%s..%s]' % 2441 'revision range: [%s..%s]' %
2540 (earliest_revision, latest_revision)) 2442 (earliest_revision, latest_revision))
2541 return results 2443 return results
2542 2444
2543 _AddRevisionsIntoRevisionData( 2445 _AddRevisionsIntoRevisionData(
2544 new_revision_list, external_depot, min_revision_data['sort'], 2446 new_revision_list, external_depot, min_revision_data['sort'],
2545 revision_data) 2447 revision_data)
2546 2448
2547 # Reset the bisection and perform it on the newly inserted 2449 # Reset the bisection and perform it on the newly inserted
2548 # changelists. 2450 # changelists.
2549 revision_list = new_revision_list 2451 revision_list = new_revision_list
2550 min_revision = 0 2452 min_revision = 0
2551 max_revision = len(revision_list) - 1 2453 max_revision = len(revision_list) - 1
2552 sort_key_ids += len(revision_list) 2454 sort_key_ids += len(revision_list)
2553 2455
2554 print ('Regression in metric %s appears to be the result of ' 2456 print ('Regression in metric %s appears to be the result of '
2555 'changes in [%s].' % (metric, external_depot)) 2457 'changes in [%s].' % (metric, external_depot))
2556 2458
2557 self.PrintRevisionsToBisectMessage(revision_list, external_depot) 2459 self.PrintRevisionsToBisectMessage(revision_list, external_depot)
2558 2460
2559 continue 2461 continue
2560 else: 2462 else:
2561 break 2463 break
2562 else: 2464 else:
2563 next_revision_index = (int((max_revision - min_revision) / 2) + 2465 next_revision_index = (int((max_revision - min_revision) / 2) +
2564 min_revision) 2466 min_revision)
2565 2467
2566 next_revision_id = revision_list[next_revision_index] 2468 next_revision_id = revision_list[next_revision_index]
2567 next_revision_data = revision_data[next_revision_id] 2469 next_revision_data = revision_data[next_revision_id]
2568 next_revision_depot = next_revision_data['depot'] 2470 next_revision_depot = next_revision_data['depot']
2569 2471
2570 self.ChangeToDepotWorkingDirectory(next_revision_depot) 2472 self.depot_manager.ChangeToDepotDir(next_revision_depot)
2571 2473
2572 if self.opts.output_buildbot_annotations: 2474 if self.opts.output_buildbot_annotations:
2573 step_name = 'Working on [%s]' % next_revision_id 2475 step_name = 'Working on [%s]' % next_revision_id
2574 bisect_utils.OutputAnnotationStepStart(step_name) 2476 bisect_utils.OutputAnnotationStepStart(step_name)
2575 2477
2576 print 'Working on revision: [%s]' % next_revision_id 2478 print 'Working on revision: [%s]' % next_revision_id
2577 2479
2578 run_results = self.RunTest( 2480 run_results = self.RunTest(
2579 next_revision_id, next_revision_depot, command_to_run, metric, 2481 next_revision_id, next_revision_depot, command_to_run, metric,
2580 skippable=True) 2482 skippable=True)
(...skipping 28 matching lines...) Expand all
2609 # If the build is broken, remove it and redo search. 2511 # If the build is broken, remove it and redo search.
2610 revision_list.pop(next_revision_index) 2512 revision_list.pop(next_revision_index)
2611 2513
2612 max_revision -= 1 2514 max_revision -= 1
2613 2515
2614 if self.opts.output_buildbot_annotations: 2516 if self.opts.output_buildbot_annotations:
2615 self._PrintPartialResults(results) 2517 self._PrintPartialResults(results)
2616 bisect_utils.OutputAnnotationStepClosed() 2518 bisect_utils.OutputAnnotationStepClosed()
2617 else: 2519 else:
2618 # Weren't able to sync and retrieve the revision range. 2520 # Weren't able to sync and retrieve the revision range.
2619 results['error'] = ('An error occurred attempting to retrieve revision ' 2521 results.error = ('An error occurred attempting to retrieve revision '
2620 'range: [%s..%s]' % (good_revision, bad_revision)) 2522 'range: [%s..%s]' % (good_revision, bad_revision))
2621 2523
2622 return results 2524 return results
2623 2525
2624 def _PrintPartialResults(self, results_dict): 2526 def _PrintPartialResults(self, results):
2625 revision_data = results_dict['revision_data'] 2527 results_dict = results.GetResultsDict()
2626 revision_data_sorted = sorted(revision_data.iteritems(), 2528 self._PrintTestedCommitsTable(results_dict['revision_data_sorted'],
2627 key = lambda x: x[1]['sort'])
2628 results_dict = self._GetResultsDict(revision_data, revision_data_sorted)
2629
2630 self._PrintTestedCommitsTable(revision_data_sorted,
2631 results_dict['first_working_revision'], 2529 results_dict['first_working_revision'],
2632 results_dict['last_broken_revision'], 2530 results_dict['last_broken_revision'],
2633 100, final_step=False) 2531 100, final_step=False)
2634 2532
2635 def _ConfidenceLevelStatus(self, results_dict): 2533 def _ConfidenceLevelStatus(self, results_dict):
2636 if not results_dict['confidence']: 2534 if not results_dict['confidence']:
2637 return None 2535 return None
2638 confidence_status = 'Successful with %(level)s confidence%(warning)s.' 2536 confidence_status = 'Successful with %(level)s confidence%(warning)s.'
2639 if results_dict['confidence'] >= HIGH_CONFIDENCE: 2537 if results_dict['confidence'] >= HIGH_CONFIDENCE:
2640 level = 'high' 2538 level = 'high'
2641 else: 2539 else:
2642 level = 'low' 2540 level = 'low'
2643 warning = ' and warnings' 2541 warning = ' and warnings'
2644 if not self.warnings: 2542 if not self.warnings:
2645 warning = '' 2543 warning = ''
2646 return confidence_status % {'level': level, 'warning': warning} 2544 return confidence_status % {'level': level, 'warning': warning}
2647 2545
2648 def _GetViewVCLinkFromDepotAndHash(self, cl, depot): 2546 def _GetViewVCLinkFromDepotAndHash(self, cl, depot):
2649 info = self.source_control.QueryRevisionInfo(cl, 2547 info = self.source_control.QueryRevisionInfo(cl,
2650 self._GetDepotDirectory(depot)) 2548 self.depot_manager.GetDepotDir(depot))
2651 if depot and DEPOT_DEPS_NAME[depot].has_key('viewvc'): 2549 if depot and DEPOT_DEPS_NAME[depot].has_key('viewvc'):
2652 try: 2550 try:
2653 # Format is "git-svn-id: svn://....@123456 <other data>" 2551 # Format is "git-svn-id: svn://....@123456 <other data>"
2654 svn_line = [i for i in info['body'].splitlines() if 'git-svn-id:' in i] 2552 svn_line = [i for i in info['body'].splitlines() if 'git-svn-id:' in i]
2655 svn_revision = svn_line[0].split('@') 2553 svn_revision = svn_line[0].split('@')
2656 svn_revision = svn_revision[1].split(' ')[0] 2554 svn_revision = svn_revision[1].split(' ')[0]
2657 return DEPOT_DEPS_NAME[depot]['viewvc'] + svn_revision 2555 return DEPOT_DEPS_NAME[depot]['viewvc'] + svn_revision
2658 except IndexError: 2556 except IndexError:
2659 return '' 2557 return ''
2660 return '' 2558 return ''
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
2793 if not previous_link: 2691 if not previous_link:
2794 previous_link = previous_id 2692 previous_link = previous_id
2795 2693
2796 print ' %8s %70s %s' % ( 2694 print ' %8s %70s %s' % (
2797 current_data['depot'], current_link, 2695 current_data['depot'], current_link,
2798 ('%d%%' % confidence).center(10, ' ')) 2696 ('%d%%' % confidence).center(10, ' '))
2799 print ' %8s %70s' % ( 2697 print ' %8s %70s' % (
2800 previous_data['depot'], previous_link) 2698 previous_data['depot'], previous_link)
2801 print 2699 print
2802 2700
2803 def _GetResultsDict(self, revision_data, revision_data_sorted):
2804 # Find range where it possibly broke.
2805 first_working_revision = None
2806 first_working_revision_index = -1
2807 last_broken_revision = None
2808 last_broken_revision_index = -1
2809
2810 culprit_revisions = []
2811 other_regressions = []
2812 regression_size = 0.0
2813 regression_std_err = 0.0
2814 confidence = 0.0
2815
2816 for i in xrange(len(revision_data_sorted)):
2817 k, v = revision_data_sorted[i]
2818 if v['passed'] == 1:
2819 if not first_working_revision:
2820 first_working_revision = k
2821 first_working_revision_index = i
2822
2823 if not v['passed']:
2824 last_broken_revision = k
2825 last_broken_revision_index = i
2826
2827 if last_broken_revision != None and first_working_revision != None:
2828 broken_means = []
2829 for i in xrange(0, last_broken_revision_index + 1):
2830 if revision_data_sorted[i][1]['value']:
2831 broken_means.append(revision_data_sorted[i][1]['value']['values'])
2832
2833 working_means = []
2834 for i in xrange(first_working_revision_index, len(revision_data_sorted)):
2835 if revision_data_sorted[i][1]['value']:
2836 working_means.append(revision_data_sorted[i][1]['value']['values'])
2837
2838 # Flatten the lists to calculate mean of all values.
2839 working_mean = sum(working_means, [])
2840 broken_mean = sum(broken_means, [])
2841
2842 # Calculate the approximate size of the regression
2843 mean_of_bad_runs = math_utils.Mean(broken_mean)
2844 mean_of_good_runs = math_utils.Mean(working_mean)
2845
2846 regression_size = 100 * math_utils.RelativeChange(mean_of_good_runs,
2847 mean_of_bad_runs)
2848 if math.isnan(regression_size):
2849 regression_size = 'zero-to-nonzero'
2850
2851 regression_std_err = math.fabs(math_utils.PooledStandardError(
2852 [working_mean, broken_mean]) /
2853 max(0.0001, min(mean_of_good_runs, mean_of_bad_runs))) * 100.0
2854
2855 # Give a "confidence" in the bisect. At the moment we use how distinct the
2856 # values are before and after the last broken revision, and how noisy the
2857 # overall graph is.
2858 confidence = ConfidenceScore(working_means, broken_means)
2859
2860 culprit_revisions = []
2861
2862 cwd = os.getcwd()
2863 self.ChangeToDepotWorkingDirectory(
2864 revision_data[last_broken_revision]['depot'])
2865
2866 if revision_data[last_broken_revision]['depot'] == 'cros':
2867 # Want to get a list of all the commits and what depots they belong
2868 # to so that we can grab info about each.
2869 cmd = ['repo', 'forall', '-c',
2870 'pwd ; git log --pretty=oneline --before=%d --after=%d' % (
2871 last_broken_revision, first_working_revision + 1)]
2872 output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd)
2873
2874 changes = []
2875 assert not return_code, ('An error occurred while running '
2876 '"%s"' % ' '.join(cmd))
2877 last_depot = None
2878 cwd = os.getcwd()
2879 for l in output.split('\n'):
2880 if l:
2881 # Output will be in form:
2882 # /path_to_depot
2883 # /path_to_other_depot
2884 # <SHA1>
2885 # /path_again
2886 # <SHA1>
2887 # etc.
2888 if l[0] == '/':
2889 last_depot = l
2890 else:
2891 contents = l.split(' ')
2892 if len(contents) > 1:
2893 changes.append([last_depot, contents[0]])
2894 for c in changes:
2895 os.chdir(c[0])
2896 info = self.source_control.QueryRevisionInfo(c[1])
2897 culprit_revisions.append((c[1], info, None))
2898 else:
2899 for i in xrange(last_broken_revision_index, len(revision_data_sorted)):
2900 k, v = revision_data_sorted[i]
2901 if k == first_working_revision:
2902 break
2903 self.ChangeToDepotWorkingDirectory(v['depot'])
2904 info = self.source_control.QueryRevisionInfo(k)
2905 culprit_revisions.append((k, info, v['depot']))
2906 os.chdir(cwd)
2907
2908 # Check for any other possible regression ranges.
2909 other_regressions = _FindOtherRegressions(
2910 revision_data_sorted, mean_of_bad_runs > mean_of_good_runs)
2911
2912 return {
2913 'first_working_revision': first_working_revision,
2914 'last_broken_revision': last_broken_revision,
2915 'culprit_revisions': culprit_revisions,
2916 'other_regressions': other_regressions,
2917 'regression_size': regression_size,
2918 'regression_std_err': regression_std_err,
2919 'confidence': confidence,
2920 }
2921
2922 def _CheckForWarnings(self, results_dict): 2701 def _CheckForWarnings(self, results_dict):
2923 if len(results_dict['culprit_revisions']) > 1: 2702 if len(results_dict['culprit_revisions']) > 1:
2924 self.warnings.append('Due to build errors, regression range could ' 2703 self.warnings.append('Due to build errors, regression range could '
2925 'not be narrowed down to a single commit.') 2704 'not be narrowed down to a single commit.')
2926 if self.opts.repeat_test_count == 1: 2705 if self.opts.repeat_test_count == 1:
2927 self.warnings.append('Tests were only set to run once. This may ' 2706 self.warnings.append('Tests were only set to run once. This may '
2928 'be insufficient to get meaningful results.') 2707 'be insufficient to get meaningful results.')
2929 if 0 < results_dict['confidence'] < HIGH_CONFIDENCE: 2708 if 0 < results_dict['confidence'] < HIGH_CONFIDENCE:
2930 self.warnings.append('Confidence is not high. Try bisecting again ' 2709 self.warnings.append('Confidence is not high. Try bisecting again '
2931 'with increased repeat_count, larger range, or ' 2710 'with increased repeat_count, larger range, or '
2932 'on another metric.') 2711 'on another metric.')
2933 if not results_dict['confidence']: 2712 if not results_dict['confidence']:
2934 self.warnings.append('Confidence score is 0%. Try bisecting again on ' 2713 self.warnings.append('Confidence score is 0%. Try bisecting again on '
2935 'another platform or another metric.') 2714 'another platform or another metric.')
2936 2715
2937 def FormatAndPrintResults(self, bisect_results): 2716 def FormatAndPrintResults(self, bisect_results):
2938 """Prints the results from a bisection run in a readable format. 2717 """Prints the results from a bisection run in a readable format.
2939 2718
2940 Args: 2719 Args:
2941 bisect_results: The results from a bisection test run. 2720 bisect_results: The results from a bisection test run.
2942 """ 2721 """
2943 revision_data = bisect_results['revision_data'] 2722 results_dict = bisect_results.GetResultsDict()
2944 revision_data_sorted = sorted(revision_data.iteritems(),
2945 key = lambda x: x[1]['sort'])
2946 results_dict = self._GetResultsDict(revision_data, revision_data_sorted)
2947 2723
2948 self._CheckForWarnings(results_dict) 2724 self._CheckForWarnings(results_dict)
2949 2725
2950 if self.opts.output_buildbot_annotations: 2726 if self.opts.output_buildbot_annotations:
2951 bisect_utils.OutputAnnotationStepStart('Build Status Per Revision') 2727 bisect_utils.OutputAnnotationStepStart('Build Status Per Revision')
2952 2728
2953 print 2729 print
2954 print 'Full results of bisection:' 2730 print 'Full results of bisection:'
2955 for current_id, current_data in revision_data_sorted: 2731 for current_id, current_data in results_dict['revision_data_sorted']:
2956 build_status = current_data['passed'] 2732 build_status = current_data['passed']
2957 2733
2958 if type(build_status) is bool: 2734 if type(build_status) is bool:
2959 if build_status: 2735 if build_status:
2960 build_status = 'Good' 2736 build_status = 'Good'
2961 else: 2737 else:
2962 build_status = 'Bad' 2738 build_status = 'Bad'
2963 2739
2964 print ' %20s %40s %s' % (current_data['depot'], 2740 print ' %20s %40s %s' % (current_data['depot'],
2965 current_id, build_status) 2741 current_id, build_status)
2966 print 2742 print
2967 2743
2968 if self.opts.output_buildbot_annotations: 2744 if self.opts.output_buildbot_annotations:
2969 bisect_utils.OutputAnnotationStepClosed() 2745 bisect_utils.OutputAnnotationStepClosed()
2970 # The perf dashboard scrapes the "results" step in order to comment on 2746 # The perf dashboard scrapes the "results" step in order to comment on
2971 # bugs. If you change this, please update the perf dashboard as well. 2747 # bugs. If you change this, please update the perf dashboard as well.
2972 bisect_utils.OutputAnnotationStepStart('Results') 2748 bisect_utils.OutputAnnotationStepStart('Results')
2973 2749
2974 self._PrintBanner(results_dict) 2750 self._PrintBanner(results_dict)
2975 self._PrintWarnings() 2751 self._PrintWarnings()
2976 2752
2977 if results_dict['culprit_revisions'] and results_dict['confidence']: 2753 if results_dict['culprit_revisions'] and results_dict['confidence']:
2978 for culprit in results_dict['culprit_revisions']: 2754 for culprit in results_dict['culprit_revisions']:
2979 cl, info, depot = culprit 2755 cl, info, depot = culprit
2980 self._PrintRevisionInfo(cl, info, depot) 2756 self._PrintRevisionInfo(cl, info, depot)
2981 if results_dict['other_regressions']: 2757 if results_dict['other_regressions']:
2982 self._PrintOtherRegressions(results_dict['other_regressions'], 2758 self._PrintOtherRegressions(results_dict['other_regressions'],
2983 revision_data) 2759 results_dict['revision_data'])
2984 self._PrintTestedCommitsTable(revision_data_sorted, 2760 self._PrintTestedCommitsTable(results_dict['revision_data_sorted'],
2985 results_dict['first_working_revision'], 2761 results_dict['first_working_revision'],
2986 results_dict['last_broken_revision'], 2762 results_dict['last_broken_revision'],
2987 results_dict['confidence']) 2763 results_dict['confidence'])
2988 _PrintStepTime(revision_data_sorted) 2764 _PrintStepTime(results_dict['revision_data_sorted'])
2989 self._PrintReproSteps() 2765 self._PrintReproSteps()
2990 _PrintThankYou() 2766 _PrintThankYou()
2991 if self.opts.output_buildbot_annotations: 2767 if self.opts.output_buildbot_annotations:
2992 bisect_utils.OutputAnnotationStepClosed() 2768 bisect_utils.OutputAnnotationStepClosed()
2993 2769
2994 def _PrintBanner(self, results_dict): 2770 def _PrintBanner(self, results_dict):
2995 if self._IsBisectModeReturnCode(): 2771 if self._IsBisectModeReturnCode():
2996 metrics = 'N/A' 2772 metrics = 'N/A'
2997 change = 'Yes' 2773 change = 'Yes'
2998 else: 2774 else:
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after
3389 if (not source_control.IsInProperBranch() and 3165 if (not source_control.IsInProperBranch() and
3390 not opts.debug_ignore_sync and 3166 not opts.debug_ignore_sync and
3391 not opts.working_directory): 3167 not opts.working_directory):
3392 raise RuntimeError('You must switch to master branch to run bisection.') 3168 raise RuntimeError('You must switch to master branch to run bisection.')
3393 bisect_test = BisectPerformanceMetrics(source_control, opts) 3169 bisect_test = BisectPerformanceMetrics(source_control, opts)
3394 try: 3170 try:
3395 bisect_results = bisect_test.Run(opts.command, 3171 bisect_results = bisect_test.Run(opts.command,
3396 opts.bad_revision, 3172 opts.bad_revision,
3397 opts.good_revision, 3173 opts.good_revision,
3398 opts.metric) 3174 opts.metric)
3399 if bisect_results['error']: 3175 if bisect_results.error:
3400 raise RuntimeError(bisect_results['error']) 3176 raise RuntimeError(bisect_results.error)
3401 bisect_test.FormatAndPrintResults(bisect_results) 3177 bisect_test.FormatAndPrintResults(bisect_results)
3402 return 0 3178 return 0
3403 finally: 3179 finally:
3404 bisect_test.PerformCleanup() 3180 bisect_test.PerformCleanup()
3405 except RuntimeError, e: 3181 except RuntimeError, e:
3406 if opts.output_buildbot_annotations: 3182 if opts.output_buildbot_annotations:
3407 # The perf dashboard scrapes the "results" step in order to comment on 3183 # The perf dashboard scrapes the "results" step in order to comment on
3408 # bugs. If you change this, please update the perf dashboard as well. 3184 # bugs. If you change this, please update the perf dashboard as well.
3409 bisect_utils.OutputAnnotationStepStart('Results') 3185 bisect_utils.OutputAnnotationStepStart('Results')
3410 print 'Error: %s' % e.message 3186 print 'Error: %s' % e.message
3411 if opts.output_buildbot_annotations: 3187 if opts.output_buildbot_annotations:
3412 bisect_utils.OutputAnnotationStepClosed() 3188 bisect_utils.OutputAnnotationStepClosed()
3413 return 1 3189 return 1
3414 3190
3415 3191
3416 if __name__ == '__main__': 3192 if __name__ == '__main__':
3417 sys.exit(main()) 3193 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | tools/auto_bisect/bisect_perf_regression_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698