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

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: Added docstring Created 6 years, 3 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 850 matching lines...) Expand 10 before | Expand all | Expand 10 after
861 if step_count: 861 if step_count:
862 step_perf_time_avg = step_perf_time_avg / step_count 862 step_perf_time_avg = step_perf_time_avg / step_count
863 step_build_time_avg = step_build_time_avg / step_count 863 step_build_time_avg = step_build_time_avg / step_count
864 print 864 print
865 print 'Average build time : %s' % datetime.timedelta( 865 print 'Average build time : %s' % datetime.timedelta(
866 seconds=int(step_build_time_avg)) 866 seconds=int(step_build_time_avg))
867 print 'Average test time : %s' % datetime.timedelta( 867 print 'Average test time : %s' % datetime.timedelta(
868 seconds=int(step_perf_time_avg)) 868 seconds=int(step_perf_time_avg))
869 869
870 870
871 def _FindOtherRegressions(revision_data_sorted, bad_greater_than_good): 871 class BisectResults(object):
ojan 2014/09/19 18:17:00 All these classes should do in their own files. It
Sergiy Byelozyorov 2014/09/22 19:30:12 Done.
872 """Compiles a list of other possible regressions from the revision data. 872 """This class holds the results of the bisect."""
ojan 2014/09/19 18:17:00 +1 to removing comments like this. This doesn't pr
Sergiy Byelozyorov 2014/09/22 19:30:12 Done.
873 873
874 Args: 874 def __init__(self, depot_manager, source_control):
875 revision_data_sorted: Sorted list of (revision, revision data) pairs. 875 self._depot_manager = depot_manager
876 bad_greater_than_good: Whether the result value at the "bad" revision is 876 self.revision_data = {}
877 numerically greater than the result value at the "good" revision. 877 self.error = None
878 878 self._source_control = source_control
879 Returns: 879
880 A list of [current_rev, previous_rev, confidence] for other places where 880 @staticmethod
881 there may have been a regression. 881 def _FindOtherRegressions(revision_data_sorted, bad_greater_than_good):
882 """ 882 """Compiles a list of other possible regressions from the revision data.
883 other_regressions = [] 883
884 previous_values = [] 884 Args:
885 previous_id = None 885 revision_data_sorted: Sorted list of (revision, revision data) pairs.
886 for current_id, current_data in revision_data_sorted: 886 bad_greater_than_good: Whether the result value at the "bad" revision is
887 current_values = current_data['value'] 887 numerically greater than the result value at the "good" revision.
888 if current_values: 888
889 current_values = current_values['values'] 889 Returns:
890 if previous_values: 890 A list of [current_rev, previous_rev, confidence] for other places where
891 confidence = ConfidenceScore(previous_values, [current_values]) 891 there may have been a regression.
892 mean_of_prev_runs = math_utils.Mean(sum(previous_values, [])) 892 """
893 mean_of_current_runs = math_utils.Mean(current_values) 893 other_regressions = []
894 894 previous_values = []
895 # Check that the potential regression is in the same direction as 895 previous_id = None
896 # the overall regression. If the mean of the previous runs < the 896 for current_id, current_data in revision_data_sorted:
897 # mean of the current runs, this local regression is in same 897 current_values = current_data['value']
898 # direction. 898 if current_values:
899 prev_less_than_current = mean_of_prev_runs < mean_of_current_runs 899 current_values = current_values['values']
900 is_same_direction = (prev_less_than_current if 900 if previous_values:
901 bad_greater_than_good else not prev_less_than_current) 901 confidence = ConfidenceScore(previous_values, [current_values])
902 902 mean_of_prev_runs = math_utils.Mean(sum(previous_values, []))
903 # Only report potential regressions with high confidence. 903 mean_of_current_runs = math_utils.Mean(current_values)
904 if is_same_direction and confidence > 50: 904
905 other_regressions.append([current_id, previous_id, confidence]) 905 # Check that the potential regression is in the same direction as
906 previous_values.append(current_values) 906 # the overall regression. If the mean of the previous runs < the
907 previous_id = current_id 907 # mean of the current runs, this local regression is in same
908 return other_regressions 908 # direction.
909 prev_less_than_current = mean_of_prev_runs < mean_of_current_runs
910 is_same_direction = (prev_less_than_current if
911 bad_greater_than_good else not prev_less_than_current)
912
913 # Only report potential regressions with high confidence.
914 if is_same_direction and confidence > 50:
915 other_regressions.append([current_id, previous_id, confidence])
916 previous_values.append(current_values)
917 previous_id = current_id
918 return other_regressions
919
920 def GetResultsDict(self):
921 """Prepares and returns information about the final resulsts as a dict.
922
923 Returns:
924 A dictionary with the following fields
ojan 2014/09/19 18:17:00 In my experience, documentation like this almost i
Sergiy Byelozyorov 2014/09/22 19:30:12 It actually took me a while to gather this info an
925
926 'first_working_revision': First good revision.
927 'last_broken_revision': Last bad revision.
928 'culprit_revisions': A list of revisions, which contain the bad change
929 introducing the failure.
930 'other_regressions': A list of tuples representing other regressions,
931 which may have occured.
932 'regression_size': For performance bisects, this is a relative change of
933 the mean metric value. For other bisects this field always contains
934 'zero-to-nonzero'.
935 'regression_std_err': For performance bisects, it is a pooled standard
936 error for groups of good and bad runs. Not used for other bisects.
937 'confidence': For performance bisects, it is a confidence that the good
938 and bad runs are distinct groups. Not used for non-performance
939 bisects.
940 'revision_data_sorted': dict mapping revision ids to data about that
941 revision. Each piece of revision data consists of a dict with the
942 following keys:
943
944 'passed': Represents whether the performance test was successful at
945 that revision. Possible values include: 1 (passed), 0 (failed),
946 '?' (skipped), 'F' (build failed).
947 'depot': The depot that this revision is from (i.e. WebKit)
948 'external': If the revision is a 'src' revision, 'external' contains
949 the revisions of each of the external libraries.
950 'sort': A sort value for sorting the dict in order of commits.
951
952 For example:
953 {
954 'CL #1':
955 {
956 'passed': False,
957 'depot': 'chromium',
958 'external': None,
959 'sort': 0
960 }
961 }
962 """
963 revision_data_sorted = sorted(self.revision_data.iteritems(),
964 key = lambda x: x[1]['sort'])
965
966 # Find range where it possibly broke.
967 first_working_revision = None
968 first_working_revision_index = -1
969 last_broken_revision = None
970 last_broken_revision_index = -1
971
972 culprit_revisions = []
973 other_regressions = []
974 regression_size = 0.0
975 regression_std_err = 0.0
976 confidence = 0.0
977
978 for i in xrange(len(revision_data_sorted)):
979 k, v = revision_data_sorted[i]
980 if v['passed'] == 1:
981 if not first_working_revision:
982 first_working_revision = k
983 first_working_revision_index = i
984
985 if not v['passed']:
986 last_broken_revision = k
987 last_broken_revision_index = i
988
989 if last_broken_revision != None and first_working_revision != None:
990 broken_means = []
991 for i in xrange(0, last_broken_revision_index + 1):
992 if revision_data_sorted[i][1]['value']:
993 broken_means.append(revision_data_sorted[i][1]['value']['values'])
994
995 working_means = []
996 for i in xrange(first_working_revision_index, len(revision_data_sorted)):
997 if revision_data_sorted[i][1]['value']:
998 working_means.append(revision_data_sorted[i][1]['value']['values'])
999
1000 # Flatten the lists to calculate mean of all values.
1001 working_mean = sum(working_means, [])
1002 broken_mean = sum(broken_means, [])
1003
1004 # Calculate the approximate size of the regression
1005 mean_of_bad_runs = math_utils.Mean(broken_mean)
1006 mean_of_good_runs = math_utils.Mean(working_mean)
1007
1008 regression_size = 100 * math_utils.RelativeChange(mean_of_good_runs,
1009 mean_of_bad_runs)
1010 if math.isnan(regression_size):
1011 regression_size = 'zero-to-nonzero'
1012
1013 regression_std_err = math.fabs(math_utils.PooledStandardError(
1014 [working_mean, broken_mean]) /
1015 max(0.0001, min(mean_of_good_runs, mean_of_bad_runs))) * 100.0
1016
1017 # Give a "confidence" in the bisect. At the moment we use how distinct the
1018 # values are before and after the last broken revision, and how noisy the
1019 # overall graph is.
1020 confidence = ConfidenceScore(working_means, broken_means)
1021
1022 culprit_revisions = []
1023
1024 cwd = os.getcwd()
1025 self._depot_manager.ChangeToDepotDir(
1026 self.revision_data[last_broken_revision]['depot'])
1027
1028 if self.revision_data[last_broken_revision]['depot'] == 'cros':
1029 # Want to get a list of all the commits and what depots they belong
1030 # to so that we can grab info about each.
1031 cmd = ['repo', 'forall', '-c',
1032 'pwd ; git log --pretty=oneline --before=%d --after=%d' % (
1033 last_broken_revision, first_working_revision + 1)]
1034 output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd)
1035
1036 changes = []
1037 assert not return_code, ('An error occurred while running '
1038 '"%s"' % ' '.join(cmd))
1039 last_depot = None
1040 cwd = os.getcwd()
1041 for l in output.split('\n'):
1042 if l:
1043 # Output will be in form:
1044 # /path_to_depot
1045 # /path_to_other_depot
1046 # <SHA1>
1047 # /path_again
1048 # <SHA1>
1049 # etc.
1050 if l[0] == '/':
1051 last_depot = l
1052 else:
1053 contents = l.split(' ')
1054 if len(contents) > 1:
1055 changes.append([last_depot, contents[0]])
1056 for c in changes:
1057 os.chdir(c[0])
1058 info = self._source_control.QueryRevisionInfo(c[1])
1059 culprit_revisions.append((c[1], info, None))
1060 else:
1061 for i in xrange(last_broken_revision_index, len(revision_data_sorted)):
1062 k, v = revision_data_sorted[i]
1063 if k == first_working_revision:
1064 break
1065 self._depot_manager.ChangeToDepotDir(v['depot'])
1066 info = self._source_control.QueryRevisionInfo(k)
1067 culprit_revisions.append((k, info, v['depot']))
1068 os.chdir(cwd)
1069
1070 # Check for any other possible regression ranges.
1071 other_regressions = self._FindOtherRegressions(
1072 revision_data_sorted, mean_of_bad_runs > mean_of_good_runs)
1073
1074 return {
1075 'first_working_revision': first_working_revision,
1076 'last_broken_revision': last_broken_revision,
1077 'culprit_revisions': culprit_revisions,
1078 'other_regressions': other_regressions,
1079 'regression_size': regression_size,
1080 'regression_std_err': regression_std_err,
1081 'confidence': confidence,
1082 'revision_data_sorted': revision_data_sorted
1083 }
1084
1085
1086 class DepotManager(object):
ojan 2014/09/19 18:17:00 Don't call it a manager. A manager can literally d
Sergiy Byelozyorov 2014/09/22 19:30:12 How about DepotDirectoryRegistry? I'd like to keep
ojan 2014/09/26 17:34:30 SGTM. That's much more clear.
1087 """Manages depots (code repositories) and their directories."""
1088
1089 def __init__(self):
1090 self.depot_cwd = {}
1091 self.cros_cwd = os.path.join(SRC_DIR, 'tools', 'cros')
1092 for depot in DEPOT_NAMES:
1093 # The working directory of each depot is just the path to the depot, but
1094 # since we're already in 'src', we can skip that part.
1095 self.depot_cwd[depot] = os.path.join(
1096 SRC_DIR, DEPOT_DEPS_NAME[depot]['src'][4:])
1097
1098 def AddDepot(self, depot_name, depot_dir):
1099 self.depot_cwd[depot_name] = depot_dir
1100
1101 def GetDepotDir(self, depot_name):
1102 if depot_name == 'chromium':
1103 return SRC_DIR
1104 elif depot_name == 'cros':
1105 return self.cros_cwd
1106 elif depot_name in DEPOT_NAMES:
1107 return self.depot_cwd[depot_name]
1108 else:
1109 assert False, ('Unknown depot [ %s ] encountered. Possibly a new one '
1110 'was added without proper support?' % depot_name)
1111
1112 def ChangeToDepotDir(self, depot_name):
1113 """Given a depot, changes to the appropriate working directory.
1114
1115 Args:
1116 depot_name: The name of the depot (see DEPOT_NAMES).
1117 """
1118 os.chdir(self.GetDepotDir(depot_name))
909 1119
910 1120
911 class BisectPerformanceMetrics(object): 1121 class BisectPerformanceMetrics(object):
912 """This class contains functionality to perform a bisection of a range of 1122 """This class contains functionality to perform a bisection of a range of
913 revisions to narrow down where performance regressions may have occurred. 1123 revisions to narrow down where performance regressions may have occurred.
914 1124
915 The main entry-point is the Run method. 1125 The main entry-point is the Run method.
916 """ 1126 """
917 1127
918 def __init__(self, source_control, opts): 1128 def __init__(self, source_control, opts):
919 super(BisectPerformanceMetrics, self).__init__() 1129 super(BisectPerformanceMetrics, self).__init__()
920 1130
921 self.opts = opts 1131 self.opts = opts
922 self.source_control = source_control 1132 self.source_control = source_control
923 self.cros_cwd = os.path.join(SRC_DIR, 'tools', 'cros') 1133 self.depot_manager = DepotManager()
924 self.depot_cwd = {}
925 self.cleanup_commands = [] 1134 self.cleanup_commands = []
926 self.warnings = [] 1135 self.warnings = []
927 self.builder = builder.Builder.FromOpts(opts) 1136 self.builder = builder.Builder.FromOpts(opts)
928 1137
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 1138
935 def PerformCleanup(self): 1139 def PerformCleanup(self):
936 """Performs cleanup when script is finished.""" 1140 """Performs cleanup when script is finished."""
937 os.chdir(SRC_DIR) 1141 os.chdir(SRC_DIR)
938 for c in self.cleanup_commands: 1142 for c in self.cleanup_commands:
939 if c[0] == 'mv': 1143 if c[0] == 'mv':
940 shutil.move(c[1], c[2]) 1144 shutil.move(c[1], c[2])
941 else: 1145 else:
942 assert False, 'Invalid cleanup command.' 1146 assert False, 'Invalid cleanup command.'
943 1147
944 def GetRevisionList(self, depot, bad_revision, good_revision): 1148 def GetRevisionList(self, depot, bad_revision, good_revision):
945 """Retrieves a list of all the commits between the bad revision and 1149 """Retrieves a list of all the commits between the bad revision and
946 last known good revision.""" 1150 last known good revision."""
947 1151
948 revision_work_list = [] 1152 revision_work_list = []
949 1153
950 if depot == 'cros': 1154 if depot == 'cros':
951 revision_range_start = good_revision 1155 revision_range_start = good_revision
952 revision_range_end = bad_revision 1156 revision_range_end = bad_revision
953 1157
954 cwd = os.getcwd() 1158 cwd = os.getcwd()
955 self.ChangeToDepotWorkingDirectory('cros') 1159 self.depot_manager.ChangeToDepotDir('cros')
956 1160
957 # Print the commit timestamps for every commit in the revision time 1161 # 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 1162 # 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 1163 # 2 (or more) commits will share the exact same timestamp, but it's
960 # probably safe to ignore that case. 1164 # probably safe to ignore that case.
961 cmd = ['repo', 'forall', '-c', 1165 cmd = ['repo', 'forall', '-c',
962 'git log --format=%%ct --before=%d --after=%d' % ( 1166 'git log --format=%%ct --before=%d --after=%d' % (
963 revision_range_end, revision_range_start)] 1167 revision_range_end, revision_range_start)]
964 output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd) 1168 output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd)
965 1169
966 assert not return_code, ('An error occurred while running ' 1170 assert not return_code, ('An error occurred while running '
967 '"%s"' % ' '.join(cmd)) 1171 '"%s"' % ' '.join(cmd))
968 1172
969 os.chdir(cwd) 1173 os.chdir(cwd)
970 1174
971 revision_work_list = list(set( 1175 revision_work_list = list(set(
972 [int(o) for o in output.split('\n') if bisect_utils.IsStringInt(o)])) 1176 [int(o) for o in output.split('\n') if bisect_utils.IsStringInt(o)]))
973 revision_work_list = sorted(revision_work_list, reverse=True) 1177 revision_work_list = sorted(revision_work_list, reverse=True)
974 else: 1178 else:
975 cwd = self._GetDepotDirectory(depot) 1179 cwd = self.depot_manager.GetDepotDir(depot)
976 revision_work_list = self.source_control.GetRevisionList(bad_revision, 1180 revision_work_list = self.source_control.GetRevisionList(bad_revision,
977 good_revision, cwd=cwd) 1181 good_revision, cwd=cwd)
978 1182
979 return revision_work_list 1183 return revision_work_list
980 1184
981 def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision): 1185 def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision):
982 commit_position = self.source_control.GetCommitPosition(revision) 1186 commit_position = self.source_control.GetCommitPosition(revision)
983 1187
984 if bisect_utils.IsStringInt(commit_position): 1188 if bisect_utils.IsStringInt(commit_position):
985 # V8 is tricky to bisect, in that there are only a few instances when 1189 # 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. 1190 # 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: 1191 # Try to detect a V8 "business as usual" case, which is when:
988 # 1. trunk revision N has description "Version X.Y.Z" 1192 # 1. trunk revision N has description "Version X.Y.Z"
989 # 2. bleeding_edge revision (N-1) has description "Prepare push to 1193 # 2. bleeding_edge revision (N-1) has description "Prepare push to
990 # trunk. Now working on X.Y.(Z+1)." 1194 # trunk. Now working on X.Y.(Z+1)."
991 # 1195 #
992 # As of 01/24/2014, V8 trunk descriptions are formatted: 1196 # As of 01/24/2014, V8 trunk descriptions are formatted:
993 # "Version 3.X.Y (based on bleeding_edge revision rZ)" 1197 # "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. 1198 # So we can just try parsing that out first and fall back to the old way.
995 v8_dir = self._GetDepotDirectory('v8') 1199 v8_dir = self.depot_manager.GetDepotDir('v8')
996 v8_bleeding_edge_dir = self._GetDepotDirectory('v8_bleeding_edge') 1200 v8_bleeding_edge_dir = self.depot_manager.GetDepotDir('v8_bleeding_edge')
997 1201
998 revision_info = self.source_control.QueryRevisionInfo(revision, 1202 revision_info = self.source_control.QueryRevisionInfo(revision,
999 cwd=v8_dir) 1203 cwd=v8_dir)
1000 1204
1001 version_re = re.compile("Version (?P<values>[0-9,.]+)") 1205 version_re = re.compile("Version (?P<values>[0-9,.]+)")
1002 1206
1003 regex_results = version_re.search(revision_info['subject']) 1207 regex_results = version_re.search(revision_info['subject'])
1004 1208
1005 if regex_results: 1209 if regex_results:
1006 git_revision = None 1210 git_revision = None
(...skipping 19 matching lines...) Expand all
1026 1230
1027 if git_revision: 1231 if git_revision:
1028 revision_info = self.source_control.QueryRevisionInfo(git_revision, 1232 revision_info = self.source_control.QueryRevisionInfo(git_revision,
1029 cwd=v8_bleeding_edge_dir) 1233 cwd=v8_bleeding_edge_dir)
1030 1234
1031 if 'Prepare push to trunk' in revision_info['subject']: 1235 if 'Prepare push to trunk' in revision_info['subject']:
1032 return git_revision 1236 return git_revision
1033 return None 1237 return None
1034 1238
1035 def _GetNearestV8BleedingEdgeFromTrunk(self, revision, search_forward=True): 1239 def _GetNearestV8BleedingEdgeFromTrunk(self, revision, search_forward=True):
1036 cwd = self._GetDepotDirectory('v8') 1240 cwd = self.depot_manager.GetDepotDir('v8')
1037 cmd = ['log', '--format=%ct', '-1', revision] 1241 cmd = ['log', '--format=%ct', '-1', revision]
1038 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 1242 output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
1039 commit_time = int(output) 1243 commit_time = int(output)
1040 commits = [] 1244 commits = []
1041 1245
1042 if search_forward: 1246 if search_forward:
1043 cmd = ['log', '--format=%H', '-10', '--after=%d' % commit_time, 1247 cmd = ['log', '--format=%H', '-10', '--after=%d' % commit_time,
1044 'origin/master'] 1248 'origin/master']
1045 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 1249 output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
1046 output = output.split() 1250 output = output.split()
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
1088 results = {} 1292 results = {}
1089 for depot_name, depot_data in DEPOT_DEPS_NAME.iteritems(): 1293 for depot_name, depot_data in DEPOT_DEPS_NAME.iteritems():
1090 if (depot_data.get('platform') and 1294 if (depot_data.get('platform') and
1091 depot_data.get('platform') != os.name): 1295 depot_data.get('platform') != os.name):
1092 continue 1296 continue
1093 1297
1094 if (depot_data.get('recurse') and depot in depot_data.get('from')): 1298 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') 1299 depot_data_src = depot_data.get('src') or depot_data.get('src_old')
1096 src_dir = deps_data.get(depot_data_src) 1300 src_dir = deps_data.get(depot_data_src)
1097 if src_dir: 1301 if src_dir:
1098 self.depot_cwd[depot_name] = os.path.join( 1302 self.depot_manager.AddDepot(
1099 SRC_DIR, depot_data_src[4:]) 1303 depot_name, os.path.join(SRC_DIR, depot_data_src[4:]))
1100 re_results = rxp.search(src_dir) 1304 re_results = rxp.search(src_dir)
1101 if re_results: 1305 if re_results:
1102 results[depot_name] = re_results.group('revision') 1306 results[depot_name] = re_results.group('revision')
1103 else: 1307 else:
1104 warning_text = ('Could not parse revision for %s while bisecting ' 1308 warning_text = ('Could not parse revision for %s while bisecting '
1105 '%s' % (depot_name, depot)) 1309 '%s' % (depot_name, depot))
1106 if not warning_text in self.warnings: 1310 if not warning_text in self.warnings:
1107 self.warnings.append(warning_text) 1311 self.warnings.append(warning_text)
1108 else: 1312 else:
1109 results[depot_name] = None 1313 results[depot_name] = None
(...skipping 16 matching lines...) Expand all
1126 def _Get3rdPartyRevisions(self, depot): 1330 def _Get3rdPartyRevisions(self, depot):
1127 """Parses the DEPS file to determine WebKit/v8/etc... versions. 1331 """Parses the DEPS file to determine WebKit/v8/etc... versions.
1128 1332
1129 Args: 1333 Args:
1130 depot: A depot name. Should be in the DEPOT_NAMES list. 1334 depot: A depot name. Should be in the DEPOT_NAMES list.
1131 1335
1132 Returns: 1336 Returns:
1133 A dict in the format {depot: revision} if successful, otherwise None. 1337 A dict in the format {depot: revision} if successful, otherwise None.
1134 """ 1338 """
1135 cwd = os.getcwd() 1339 cwd = os.getcwd()
1136 self.ChangeToDepotWorkingDirectory(depot) 1340 self.depot_manager.ChangeToDepotDir(depot)
1137 1341
1138 results = {} 1342 results = {}
1139 1343
1140 if depot == 'chromium' or depot == 'android-chrome': 1344 if depot == 'chromium' or depot == 'android-chrome':
1141 results = self._ParseRevisionsFromDEPSFile(depot) 1345 results = self._ParseRevisionsFromDEPSFile(depot)
1142 os.chdir(cwd) 1346 os.chdir(cwd)
1143 1347
1144 if depot == 'cros': 1348 if depot == 'cros':
1145 cmd = [ 1349 cmd = [
1146 bisect_utils.CROS_SDK_PATH, 1350 bisect_utils.CROS_SDK_PATH,
(...skipping 20 matching lines...) Expand all
1167 1371
1168 version = contents[2] 1372 version = contents[2]
1169 1373
1170 if contents[3] != '0': 1374 if contents[3] != '0':
1171 warningText = ('Chrome version: %s.%s but using %s.0 to bisect.' % 1375 warningText = ('Chrome version: %s.%s but using %s.0 to bisect.' %
1172 (version, contents[3], version)) 1376 (version, contents[3], version))
1173 if not warningText in self.warnings: 1377 if not warningText in self.warnings:
1174 self.warnings.append(warningText) 1378 self.warnings.append(warningText)
1175 1379
1176 cwd = os.getcwd() 1380 cwd = os.getcwd()
1177 self.ChangeToDepotWorkingDirectory('chromium') 1381 self.depot_manager.ChangeToDepotDir('chromium')
1178 cmd = ['log', '-1', '--format=%H', 1382 cmd = ['log', '-1', '--format=%H',
1179 '--author=chrome-release@google.com', 1383 '--author=chrome-release@google.com',
1180 '--grep=to %s' % version, 'origin/master'] 1384 '--grep=to %s' % version, 'origin/master']
1181 return_code = bisect_utils.CheckRunGit(cmd) 1385 return_code = bisect_utils.CheckRunGit(cmd)
1182 os.chdir(cwd) 1386 os.chdir(cwd)
1183 1387
1184 results['chromium'] = output.strip() 1388 results['chromium'] = output.strip()
1185 1389
1186 if depot == 'v8': 1390 if depot == 'v8':
1187 # We can't try to map the trunk revision to bleeding edge yet, because 1391 # 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: 1609 Returns:
1406 Updated DEPS content as string if deps key is found, otherwise None. 1610 Updated DEPS content as string if deps key is found, otherwise None.
1407 """ 1611 """
1408 # Check whether the depot and revision pattern in DEPS file vars 1612 # Check whether the depot and revision pattern in DEPS file vars
1409 # e.g. for webkit the format is "webkit_revision": "12345". 1613 # e.g. for webkit the format is "webkit_revision": "12345".
1410 deps_revision = re.compile(r'(?<="%s": ")([0-9]+)(?=")' % deps_key, 1614 deps_revision = re.compile(r'(?<="%s": ")([0-9]+)(?=")' % deps_key,
1411 re.MULTILINE) 1615 re.MULTILINE)
1412 new_data = None 1616 new_data = None
1413 if re.search(deps_revision, deps_contents): 1617 if re.search(deps_revision, deps_contents):
1414 commit_position = self.source_control.GetCommitPosition( 1618 commit_position = self.source_control.GetCommitPosition(
1415 git_revision, self._GetDepotDirectory(depot)) 1619 git_revision, self.depot_manager.GetDepotDir(depot))
1416 if not commit_position: 1620 if not commit_position:
1417 print 'Could not determine commit position for %s' % git_revision 1621 print 'Could not determine commit position for %s' % git_revision
1418 return None 1622 return None
1419 # Update the revision information for the given depot 1623 # Update the revision information for the given depot
1420 new_data = re.sub(deps_revision, str(commit_position), deps_contents) 1624 new_data = re.sub(deps_revision, str(commit_position), deps_contents)
1421 else: 1625 else:
1422 # Check whether the depot and revision pattern in DEPS file vars 1626 # Check whether the depot and revision pattern in DEPS file vars
1423 # e.g. for webkit the format is "webkit_revision": "559a6d4ab7a84c539..". 1627 # e.g. for webkit the format is "webkit_revision": "559a6d4ab7a84c539..".
1424 deps_revision = re.compile( 1628 deps_revision = re.compile(
1425 r'(?<=["\']%s["\']: ["\'])([a-fA-F0-9]{40})(?=["\'])' % deps_key, 1629 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 1973 # 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 1974 # 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 1975 # 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. 1976 # depots, so we have to grep the git logs and grab the next earlier one.
1773 if (not is_base 1977 if (not is_base
1774 and DEPOT_DEPS_NAME[depot]['depends'] 1978 and DEPOT_DEPS_NAME[depot]['depends']
1775 and self.source_control.IsGit()): 1979 and self.source_control.IsGit()):
1776 commit_position = self.source_control.GetCommitPosition(revision) 1980 commit_position = self.source_control.GetCommitPosition(revision)
1777 1981
1778 for d in DEPOT_DEPS_NAME[depot]['depends']: 1982 for d in DEPOT_DEPS_NAME[depot]['depends']:
1779 self.ChangeToDepotWorkingDirectory(d) 1983 self.depot_manager.ChangeToDepotDir(d)
1780 1984
1781 dependant_rev = self.source_control.ResolveToRevision( 1985 dependant_rev = self.source_control.ResolveToRevision(
1782 commit_position, d, DEPOT_DEPS_NAME, -1000) 1986 commit_position, d, DEPOT_DEPS_NAME, -1000)
1783 1987
1784 if dependant_rev: 1988 if dependant_rev:
1785 revisions_to_sync.append([d, dependant_rev]) 1989 revisions_to_sync.append([d, dependant_rev])
1786 1990
1787 num_resolved = len(revisions_to_sync) 1991 num_resolved = len(revisions_to_sync)
1788 num_needed = len(DEPOT_DEPS_NAME[depot]['depends']) 1992 num_needed = len(DEPOT_DEPS_NAME[depot]['depends'])
1789 1993
1790 self.ChangeToDepotWorkingDirectory(depot) 1994 self.depot_manager.ChangeToDepotDir(depot)
1791 1995
1792 if not ((num_resolved - 1) == num_needed): 1996 if not ((num_resolved - 1) == num_needed):
1793 return None 1997 return None
1794 1998
1795 return revisions_to_sync 1999 return revisions_to_sync
1796 2000
1797 @staticmethod 2001 @staticmethod
1798 def PerformPreBuildCleanup(): 2002 def PerformPreBuildCleanup():
1799 """Performs cleanup between runs.""" 2003 """Performs cleanup between runs."""
1800 print 'Cleaning up between runs.' 2004 print 'Cleaning up between runs.'
1801 print 2005 print
1802 2006
1803 # Leaving these .pyc files around between runs may disrupt some perf tests. 2007 # Leaving these .pyc files around between runs may disrupt some perf tests.
1804 for (path, _, files) in os.walk(SRC_DIR): 2008 for (path, _, files) in os.walk(SRC_DIR):
1805 for cur_file in files: 2009 for cur_file in files:
1806 if cur_file.endswith('.pyc'): 2010 if cur_file.endswith('.pyc'):
1807 path_to_file = os.path.join(path, cur_file) 2011 path_to_file = os.path.join(path, cur_file)
1808 os.remove(path_to_file) 2012 os.remove(path_to_file)
1809 2013
1810 def PerformCrosChrootCleanup(self): 2014 def PerformCrosChrootCleanup(self):
1811 """Deletes the chroot. 2015 """Deletes the chroot.
1812 2016
1813 Returns: 2017 Returns:
1814 True if successful. 2018 True if successful.
1815 """ 2019 """
1816 cwd = os.getcwd() 2020 cwd = os.getcwd()
1817 self.ChangeToDepotWorkingDirectory('cros') 2021 self.depot_manager.ChangeToDepotDir('cros')
1818 cmd = [bisect_utils.CROS_SDK_PATH, '--delete'] 2022 cmd = [bisect_utils.CROS_SDK_PATH, '--delete']
1819 return_code = bisect_utils.RunProcess(cmd) 2023 return_code = bisect_utils.RunProcess(cmd)
1820 os.chdir(cwd) 2024 os.chdir(cwd)
1821 return not return_code 2025 return not return_code
1822 2026
1823 def CreateCrosChroot(self): 2027 def CreateCrosChroot(self):
1824 """Creates a new chroot. 2028 """Creates a new chroot.
1825 2029
1826 Returns: 2030 Returns:
1827 True if successful. 2031 True if successful.
1828 """ 2032 """
1829 cwd = os.getcwd() 2033 cwd = os.getcwd()
1830 self.ChangeToDepotWorkingDirectory('cros') 2034 self.depot_manager.ChangeToDepotDir('cros')
1831 cmd = [bisect_utils.CROS_SDK_PATH, '--create'] 2035 cmd = [bisect_utils.CROS_SDK_PATH, '--create']
1832 return_code = bisect_utils.RunProcess(cmd) 2036 return_code = bisect_utils.RunProcess(cmd)
1833 os.chdir(cwd) 2037 os.chdir(cwd)
1834 return not return_code 2038 return not return_code
1835 2039
1836 def _PerformPreSyncCleanup(self, depot): 2040 def _PerformPreSyncCleanup(self, depot):
1837 """Performs any necessary cleanup before syncing. 2041 """Performs any necessary cleanup before syncing.
1838 2042
1839 Args: 2043 Args:
1840 depot: Depot name. 2044 depot: Depot name.
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
1982 """Syncs multiple depots to particular revisions. 2186 """Syncs multiple depots to particular revisions.
1983 2187
1984 Args: 2188 Args:
1985 revisions_to_sync: A list of (depot, revision) pairs to be synced. 2189 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. 2190 sync_client: Program used to sync, e.g. "gclient", "repo". Can be None.
1987 2191
1988 Returns: 2192 Returns:
1989 True if successful, False otherwise. 2193 True if successful, False otherwise.
1990 """ 2194 """
1991 for depot, revision in revisions_to_sync: 2195 for depot, revision in revisions_to_sync:
1992 self.ChangeToDepotWorkingDirectory(depot) 2196 self.depot_manager.ChangeToDepotDir(depot)
1993 2197
1994 if sync_client: 2198 if sync_client:
1995 self.PerformPreBuildCleanup() 2199 self.PerformPreBuildCleanup()
1996 2200
1997 # When using gclient to sync, you need to specify the depot you 2201 # When using gclient to sync, you need to specify the depot you
1998 # want so that all the dependencies sync properly as well. 2202 # want so that all the dependencies sync properly as well.
1999 # i.e. gclient sync src@<SHA1> 2203 # i.e. gclient sync src@<SHA1>
2000 if sync_client == 'gclient': 2204 if sync_client == 'gclient':
2001 revision = '%s@%s' % (DEPOT_DEPS_NAME[depot]['src'], revision) 2205 revision = '%s@%s' % (DEPOT_DEPS_NAME[depot]['src'], revision)
2002 2206
(...skipping 20 matching lines...) Expand all
2023 dist_to_good_value = abs(current_value['std_dev'] - 2227 dist_to_good_value = abs(current_value['std_dev'] -
2024 known_good_value['std_dev']) 2228 known_good_value['std_dev'])
2025 dist_to_bad_value = abs(current_value['std_dev'] - 2229 dist_to_bad_value = abs(current_value['std_dev'] -
2026 known_bad_value['std_dev']) 2230 known_bad_value['std_dev'])
2027 else: 2231 else:
2028 dist_to_good_value = abs(current_value['mean'] - known_good_value['mean']) 2232 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']) 2233 dist_to_bad_value = abs(current_value['mean'] - known_bad_value['mean'])
2030 2234
2031 return dist_to_good_value < dist_to_bad_value 2235 return dist_to_good_value < dist_to_bad_value
2032 2236
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): 2237 def _FillInV8BleedingEdgeInfo(self, min_revision_data, max_revision_data):
2053 r1 = self._GetNearestV8BleedingEdgeFromTrunk(min_revision_data['revision'], 2238 r1 = self._GetNearestV8BleedingEdgeFromTrunk(min_revision_data['revision'],
2054 search_forward=True) 2239 search_forward=True)
2055 r2 = self._GetNearestV8BleedingEdgeFromTrunk(max_revision_data['revision'], 2240 r2 = self._GetNearestV8BleedingEdgeFromTrunk(max_revision_data['revision'],
2056 search_forward=False) 2241 search_forward=False)
2057 min_revision_data['external']['v8_bleeding_edge'] = r1 2242 min_revision_data['external']['v8_bleeding_edge'] = r1
2058 max_revision_data['external']['v8_bleeding_edge'] = r2 2243 max_revision_data['external']['v8_bleeding_edge'] = r2
2059 2244
2060 if (not self._GetV8BleedingEdgeFromV8TrunkIfMappable( 2245 if (not self._GetV8BleedingEdgeFromV8TrunkIfMappable(
2061 min_revision_data['revision']) 2246 min_revision_data['revision'])
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
2117 end_revision: End of the revision range. 2302 end_revision: End of the revision range.
2118 start_revision: Start of the revision range. 2303 start_revision: Start of the revision range.
2119 previous_revision: The last revision we synced to on |previous_depot|. 2304 previous_revision: The last revision we synced to on |previous_depot|.
2120 2305
2121 Returns: 2306 Returns:
2122 A list containing the revisions between |start_revision| and 2307 A list containing the revisions between |start_revision| and
2123 |end_revision| inclusive. 2308 |end_revision| inclusive.
2124 """ 2309 """
2125 # Change into working directory of external library to run 2310 # Change into working directory of external library to run
2126 # subsequent commands. 2311 # subsequent commands.
2127 self.ChangeToDepotWorkingDirectory(current_depot) 2312 self.depot_manager.ChangeToDepotDir(current_depot)
2128 2313
2129 # V8 (and possibly others) is merged in periodically. Bisecting 2314 # V8 (and possibly others) is merged in periodically. Bisecting
2130 # this directory directly won't give much good info. 2315 # this directory directly won't give much good info.
2131 if DEPOT_DEPS_NAME[current_depot].has_key('custom_deps'): 2316 if DEPOT_DEPS_NAME[current_depot].has_key('custom_deps'):
2132 config_path = os.path.join(SRC_DIR, '..') 2317 config_path = os.path.join(SRC_DIR, '..')
2133 if bisect_utils.RunGClientAndCreateConfig(self.opts, 2318 if bisect_utils.RunGClientAndCreateConfig(self.opts,
2134 DEPOT_DEPS_NAME[current_depot]['custom_deps'], cwd=config_path): 2319 DEPOT_DEPS_NAME[current_depot]['custom_deps'], cwd=config_path):
2135 return [] 2320 return []
2136 if bisect_utils.RunGClient( 2321 if bisect_utils.RunGClient(
2137 ['sync', '--revision', previous_revision], cwd=SRC_DIR): 2322 ['sync', '--revision', previous_revision], cwd=SRC_DIR):
2138 return [] 2323 return []
2139 2324
2140 if current_depot == 'v8_bleeding_edge': 2325 if current_depot == 'v8_bleeding_edge':
2141 self.ChangeToDepotWorkingDirectory('chromium') 2326 self.depot_manager.ChangeToDepotDir('chromium')
2142 2327
2143 shutil.move('v8', 'v8.bak') 2328 shutil.move('v8', 'v8.bak')
2144 shutil.move('v8_bleeding_edge', 'v8') 2329 shutil.move('v8_bleeding_edge', 'v8')
2145 2330
2146 self.cleanup_commands.append(['mv', 'v8', 'v8_bleeding_edge']) 2331 self.cleanup_commands.append(['mv', 'v8', 'v8_bleeding_edge'])
2147 self.cleanup_commands.append(['mv', 'v8.bak', 'v8']) 2332 self.cleanup_commands.append(['mv', 'v8.bak', 'v8'])
2148 2333
2149 self.depot_cwd['v8_bleeding_edge'] = os.path.join(SRC_DIR, 'v8') 2334 self.depot_manager.AddDepot(
2150 self.depot_cwd['v8'] = os.path.join(SRC_DIR, 'v8.bak') 2335 'v8_bleeding_edge', os.path.join(SRC_DIR, 'v8'))
2336 self.depot_manager.AddDepot('v8', os.path.join(SRC_DIR, 'v8.bak'))
2151 2337
2152 self.ChangeToDepotWorkingDirectory(current_depot) 2338 self.depot_manager.ChangeToDepotDir(current_depot)
2153 2339
2154 depot_revision_list = self.GetRevisionList(current_depot, 2340 depot_revision_list = self.GetRevisionList(current_depot,
2155 end_revision, 2341 end_revision,
2156 start_revision) 2342 start_revision)
2157 2343
2158 self.ChangeToDepotWorkingDirectory('chromium') 2344 self.depot_manager.ChangeToDepotDir('chromium')
2159 2345
2160 return depot_revision_list 2346 return depot_revision_list
2161 2347
2162 def GatherReferenceValues(self, good_rev, bad_rev, cmd, metric, target_depot): 2348 def GatherReferenceValues(self, good_rev, bad_rev, cmd, metric, target_depot):
2163 """Gathers reference values by running the performance tests on the 2349 """Gathers reference values by running the performance tests on the
2164 known good and bad revisions. 2350 known good and bad revisions.
2165 2351
2166 Args: 2352 Args:
2167 good_rev: The last known good revision where the performance regression 2353 good_rev: The last known good revision where the performance regression
2168 has not occurred yet. 2354 has not occurred yet.
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
2255 2441
2256 Args: 2442 Args:
2257 good_revision: Number/tag of the known good revision. 2443 good_revision: Number/tag of the known good revision.
2258 bad_revision: Number/tag of the known bad revision. 2444 bad_revision: Number/tag of the known bad revision.
2259 2445
2260 Returns: 2446 Returns:
2261 True if the revisions are in the proper order (good earlier than bad). 2447 True if the revisions are in the proper order (good earlier than bad).
2262 """ 2448 """
2263 if self.source_control.IsGit() and target_depot != 'cros': 2449 if self.source_control.IsGit() and target_depot != 'cros':
2264 cmd = ['log', '--format=%ct', '-1', good_revision] 2450 cmd = ['log', '--format=%ct', '-1', good_revision]
2265 cwd = self._GetDepotDirectory(target_depot) 2451 cwd = self.depot_manager.GetDepotDir(target_depot)
2266 2452
2267 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 2453 output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
2268 good_commit_time = int(output) 2454 good_commit_time = int(output)
2269 2455
2270 cmd = ['log', '--format=%ct', '-1', bad_revision] 2456 cmd = ['log', '--format=%ct', '-1', bad_revision]
2271 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) 2457 output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
2272 bad_commit_time = int(output) 2458 bad_commit_time = int(output)
2273 2459
2274 return good_commit_time <= bad_commit_time 2460 return good_commit_time <= bad_commit_time
2275 else: 2461 else:
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
2321 intermediate revisions to determine the CL where the performance regression 2507 intermediate revisions to determine the CL where the performance regression
2322 occurred. 2508 occurred.
2323 2509
2324 Args: 2510 Args:
2325 command_to_run: Specify the command to execute the performance test. 2511 command_to_run: Specify the command to execute the performance test.
2326 good_revision: Number/tag of the known good revision. 2512 good_revision: Number/tag of the known good revision.
2327 bad_revision: Number/tag of the known bad revision. 2513 bad_revision: Number/tag of the known bad revision.
2328 metric: The performance metric to monitor. 2514 metric: The performance metric to monitor.
2329 2515
2330 Returns: 2516 Returns:
2331 A dict with 2 members, 'revision_data' and 'error'. On success, 2517 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 """ 2518 """
2362 results = { 2519 results = BisectResults(self.depot_manager, self.source_control)
2363 'revision_data' : {},
2364 'error' : None,
2365 }
2366 2520
2367 # Choose depot to bisect first 2521 # Choose depot to bisect first
2368 target_depot = 'chromium' 2522 target_depot = 'chromium'
2369 if self.opts.target_platform == 'cros': 2523 if self.opts.target_platform == 'cros':
2370 target_depot = 'cros' 2524 target_depot = 'cros'
2371 elif self.opts.target_platform == 'android-chrome': 2525 elif self.opts.target_platform == 'android-chrome':
2372 target_depot = 'android-chrome' 2526 target_depot = 'android-chrome'
2373 2527
2374 cwd = os.getcwd() 2528 cwd = os.getcwd()
2375 self.ChangeToDepotWorkingDirectory(target_depot) 2529 self.depot_manager.ChangeToDepotDir(target_depot)
2376 2530
2377 # If they passed SVN revisions, we can try match them to git SHA1 hashes. 2531 # If they passed SVN revisions, we can try match them to git SHA1 hashes.
2378 bad_revision = self.source_control.ResolveToRevision( 2532 bad_revision = self.source_control.ResolveToRevision(
2379 bad_revision_in, target_depot, DEPOT_DEPS_NAME, 100) 2533 bad_revision_in, target_depot, DEPOT_DEPS_NAME, 100)
2380 good_revision = self.source_control.ResolveToRevision( 2534 good_revision = self.source_control.ResolveToRevision(
2381 good_revision_in, target_depot, DEPOT_DEPS_NAME, -100) 2535 good_revision_in, target_depot, DEPOT_DEPS_NAME, -100)
2382 2536
2383 os.chdir(cwd) 2537 os.chdir(cwd)
2384 if bad_revision is None: 2538 if bad_revision is None:
2385 results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % bad_revision_in 2539 results.error = 'Couldn\'t resolve [%s] to SHA1.' % bad_revision_in
2386 return results 2540 return results
2387 2541
2388 if good_revision is None: 2542 if good_revision is None:
2389 results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % good_revision_in 2543 results.error = 'Couldn\'t resolve [%s] to SHA1.' % good_revision_in
2390 return results 2544 return results
2391 2545
2392 # Check that they didn't accidentally swap good and bad revisions. 2546 # Check that they didn't accidentally swap good and bad revisions.
2393 if not self.CheckIfRevisionsInProperOrder( 2547 if not self.CheckIfRevisionsInProperOrder(
2394 target_depot, good_revision, bad_revision): 2548 target_depot, good_revision, bad_revision):
2395 results['error'] = ('bad_revision < good_revision, did you swap these ' 2549 results.error = ('bad_revision < good_revision, did you swap these '
2396 'by mistake?') 2550 'by mistake?')
2397 return results 2551 return results
2398 bad_revision, good_revision = self.NudgeRevisionsIfDEPSChange( 2552 bad_revision, good_revision = self.NudgeRevisionsIfDEPSChange(
2399 bad_revision, good_revision, good_revision_in) 2553 bad_revision, good_revision, good_revision_in)
2400 if self.opts.output_buildbot_annotations: 2554 if self.opts.output_buildbot_annotations:
2401 bisect_utils.OutputAnnotationStepStart('Gathering Revisions') 2555 bisect_utils.OutputAnnotationStepStart('Gathering Revisions')
2402 2556
2403 cannot_bisect = self.CanPerformBisect(good_revision, bad_revision) 2557 cannot_bisect = self.CanPerformBisect(good_revision, bad_revision)
2404 if cannot_bisect: 2558 if cannot_bisect:
2405 results['error'] = cannot_bisect.get('error') 2559 results.error = cannot_bisect.get('error')
2406 return results 2560 return results
2407 2561
2408 print 'Gathering revision range for bisection.' 2562 print 'Gathering revision range for bisection.'
2409 # Retrieve a list of revisions to do bisection on. 2563 # Retrieve a list of revisions to do bisection on.
2410 src_revision_list = self.GetRevisionList( 2564 src_revision_list = self.GetRevisionList(
2411 target_depot, bad_revision, good_revision) 2565 target_depot, bad_revision, good_revision)
2412 2566
2413 if self.opts.output_buildbot_annotations: 2567 if self.opts.output_buildbot_annotations:
2414 bisect_utils.OutputAnnotationStepClosed() 2568 bisect_utils.OutputAnnotationStepClosed()
2415 2569
2416 if src_revision_list: 2570 if src_revision_list:
2417 # revision_data will store information about a revision such as the 2571 # revision_data will store information about a revision such as the
2418 # depot it came from, the webkit/V8 revision at that time, 2572 # depot it came from, the webkit/V8 revision at that time,
2419 # performance timing, build state, etc... 2573 # performance timing, build state, etc...
2420 revision_data = results['revision_data'] 2574 revision_data = results.revision_data
2421 2575
2422 # revision_list is the list we're binary searching through at the moment. 2576 # revision_list is the list we're binary searching through at the moment.
2423 revision_list = [] 2577 revision_list = []
2424 2578
2425 sort_key_ids = 0 2579 sort_key_ids = 0
2426 2580
2427 for current_revision_id in src_revision_list: 2581 for current_revision_id in src_revision_list:
2428 sort_key_ids += 1 2582 sort_key_ids += 1
2429 2583
2430 revision_data[current_revision_id] = { 2584 revision_data[current_revision_id] = {
(...skipping 22 matching lines...) Expand all
2453 bad_results, good_results = self.GatherReferenceValues(good_revision, 2607 bad_results, good_results = self.GatherReferenceValues(good_revision,
2454 bad_revision, 2608 bad_revision,
2455 command_to_run, 2609 command_to_run,
2456 metric, 2610 metric,
2457 target_depot) 2611 target_depot)
2458 2612
2459 if self.opts.output_buildbot_annotations: 2613 if self.opts.output_buildbot_annotations:
2460 bisect_utils.OutputAnnotationStepClosed() 2614 bisect_utils.OutputAnnotationStepClosed()
2461 2615
2462 if bad_results[1]: 2616 if bad_results[1]:
2463 results['error'] = ('An error occurred while building and running ' 2617 results.error = ('An error occurred while building and running '
2464 'the \'bad\' reference value. The bisect cannot continue without ' 2618 'the \'bad\' reference value. The bisect cannot continue without '
2465 'a working \'bad\' revision to start from.\n\nError: %s' % 2619 'a working \'bad\' revision to start from.\n\nError: %s' %
2466 bad_results[0]) 2620 bad_results[0])
2467 return results 2621 return results
2468 2622
2469 if good_results[1]: 2623 if good_results[1]:
2470 results['error'] = ('An error occurred while building and running ' 2624 results.error = ('An error occurred while building and running '
2471 'the \'good\' reference value. The bisect cannot continue without ' 2625 'the \'good\' reference value. The bisect cannot continue without '
2472 'a working \'good\' revision to start from.\n\nError: %s' % 2626 'a working \'good\' revision to start from.\n\nError: %s' %
2473 good_results[0]) 2627 good_results[0])
2474 return results 2628 return results
2475 2629
2476 2630
2477 # We need these reference values to determine if later runs should be 2631 # We need these reference values to determine if later runs should be
2478 # classified as pass or fail. 2632 # classified as pass or fail.
2479 known_bad_value = bad_results[0] 2633 known_bad_value = bad_results[0]
2480 known_good_value = good_results[0] 2634 known_good_value = good_results[0]
2481 2635
2482 # Can just mark the good and bad revisions explicitly here since we 2636 # Can just mark the good and bad revisions explicitly here since we
2483 # already know the results. 2637 # already know the results.
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
2528 break 2682 break
2529 2683
2530 earliest_revision = max_revision_data['external'][external_depot] 2684 earliest_revision = max_revision_data['external'][external_depot]
2531 latest_revision = min_revision_data['external'][external_depot] 2685 latest_revision = min_revision_data['external'][external_depot]
2532 2686
2533 new_revision_list = self.PrepareToBisectOnDepot( 2687 new_revision_list = self.PrepareToBisectOnDepot(
2534 external_depot, latest_revision, earliest_revision, 2688 external_depot, latest_revision, earliest_revision,
2535 previous_revision) 2689 previous_revision)
2536 2690
2537 if not new_revision_list: 2691 if not new_revision_list:
2538 results['error'] = ('An error occurred attempting to retrieve ' 2692 results.error = ('An error occurred attempting to retrieve '
2539 'revision range: [%s..%s]' % 2693 'revision range: [%s..%s]' %
2540 (earliest_revision, latest_revision)) 2694 (earliest_revision, latest_revision))
2541 return results 2695 return results
2542 2696
2543 _AddRevisionsIntoRevisionData( 2697 _AddRevisionsIntoRevisionData(
2544 new_revision_list, external_depot, min_revision_data['sort'], 2698 new_revision_list, external_depot, min_revision_data['sort'],
2545 revision_data) 2699 revision_data)
2546 2700
2547 # Reset the bisection and perform it on the newly inserted 2701 # Reset the bisection and perform it on the newly inserted
2548 # changelists. 2702 # changelists.
2549 revision_list = new_revision_list 2703 revision_list = new_revision_list
2550 min_revision = 0 2704 min_revision = 0
2551 max_revision = len(revision_list) - 1 2705 max_revision = len(revision_list) - 1
2552 sort_key_ids += len(revision_list) 2706 sort_key_ids += len(revision_list)
2553 2707
2554 print ('Regression in metric %s appears to be the result of ' 2708 print ('Regression in metric %s appears to be the result of '
2555 'changes in [%s].' % (metric, external_depot)) 2709 'changes in [%s].' % (metric, external_depot))
2556 2710
2557 self.PrintRevisionsToBisectMessage(revision_list, external_depot) 2711 self.PrintRevisionsToBisectMessage(revision_list, external_depot)
2558 2712
2559 continue 2713 continue
2560 else: 2714 else:
2561 break 2715 break
2562 else: 2716 else:
2563 next_revision_index = (int((max_revision - min_revision) / 2) + 2717 next_revision_index = (int((max_revision - min_revision) / 2) +
2564 min_revision) 2718 min_revision)
2565 2719
2566 next_revision_id = revision_list[next_revision_index] 2720 next_revision_id = revision_list[next_revision_index]
2567 next_revision_data = revision_data[next_revision_id] 2721 next_revision_data = revision_data[next_revision_id]
2568 next_revision_depot = next_revision_data['depot'] 2722 next_revision_depot = next_revision_data['depot']
2569 2723
2570 self.ChangeToDepotWorkingDirectory(next_revision_depot) 2724 self.depot_manager.ChangeToDepotDir(next_revision_depot)
2571 2725
2572 if self.opts.output_buildbot_annotations: 2726 if self.opts.output_buildbot_annotations:
2573 step_name = 'Working on [%s]' % next_revision_id 2727 step_name = 'Working on [%s]' % next_revision_id
2574 bisect_utils.OutputAnnotationStepStart(step_name) 2728 bisect_utils.OutputAnnotationStepStart(step_name)
2575 2729
2576 print 'Working on revision: [%s]' % next_revision_id 2730 print 'Working on revision: [%s]' % next_revision_id
2577 2731
2578 run_results = self.RunTest( 2732 run_results = self.RunTest(
2579 next_revision_id, next_revision_depot, command_to_run, metric, 2733 next_revision_id, next_revision_depot, command_to_run, metric,
2580 skippable=True) 2734 skippable=True)
(...skipping 28 matching lines...) Expand all
2609 # If the build is broken, remove it and redo search. 2763 # If the build is broken, remove it and redo search.
2610 revision_list.pop(next_revision_index) 2764 revision_list.pop(next_revision_index)
2611 2765
2612 max_revision -= 1 2766 max_revision -= 1
2613 2767
2614 if self.opts.output_buildbot_annotations: 2768 if self.opts.output_buildbot_annotations:
2615 self._PrintPartialResults(results) 2769 self._PrintPartialResults(results)
2616 bisect_utils.OutputAnnotationStepClosed() 2770 bisect_utils.OutputAnnotationStepClosed()
2617 else: 2771 else:
2618 # Weren't able to sync and retrieve the revision range. 2772 # Weren't able to sync and retrieve the revision range.
2619 results['error'] = ('An error occurred attempting to retrieve revision ' 2773 results.error = ('An error occurred attempting to retrieve revision '
2620 'range: [%s..%s]' % (good_revision, bad_revision)) 2774 'range: [%s..%s]' % (good_revision, bad_revision))
2621 2775
2622 return results 2776 return results
2623 2777
2624 def _PrintPartialResults(self, results_dict): 2778 def _PrintPartialResults(self, results):
2625 revision_data = results_dict['revision_data'] 2779 results_dict = results.GetResultsDict()
2626 revision_data_sorted = sorted(revision_data.iteritems(), 2780 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'], 2781 results_dict['first_working_revision'],
2632 results_dict['last_broken_revision'], 2782 results_dict['last_broken_revision'],
2633 100, final_step=False) 2783 100, final_step=False)
2634 2784
2635 def _ConfidenceLevelStatus(self, results_dict): 2785 def _ConfidenceLevelStatus(self, results_dict):
2636 if not results_dict['confidence']: 2786 if not results_dict['confidence']:
2637 return None 2787 return None
2638 confidence_status = 'Successful with %(level)s confidence%(warning)s.' 2788 confidence_status = 'Successful with %(level)s confidence%(warning)s.'
2639 if results_dict['confidence'] >= HIGH_CONFIDENCE: 2789 if results_dict['confidence'] >= HIGH_CONFIDENCE:
2640 level = 'high' 2790 level = 'high'
2641 else: 2791 else:
2642 level = 'low' 2792 level = 'low'
2643 warning = ' and warnings' 2793 warning = ' and warnings'
2644 if not self.warnings: 2794 if not self.warnings:
2645 warning = '' 2795 warning = ''
2646 return confidence_status % {'level': level, 'warning': warning} 2796 return confidence_status % {'level': level, 'warning': warning}
2647 2797
2648 def _GetViewVCLinkFromDepotAndHash(self, cl, depot): 2798 def _GetViewVCLinkFromDepotAndHash(self, cl, depot):
2649 info = self.source_control.QueryRevisionInfo(cl, 2799 info = self.source_control.QueryRevisionInfo(cl,
2650 self._GetDepotDirectory(depot)) 2800 self.depot_manager.GetDepotDir(depot))
2651 if depot and DEPOT_DEPS_NAME[depot].has_key('viewvc'): 2801 if depot and DEPOT_DEPS_NAME[depot].has_key('viewvc'):
2652 try: 2802 try:
2653 # Format is "git-svn-id: svn://....@123456 <other data>" 2803 # 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] 2804 svn_line = [i for i in info['body'].splitlines() if 'git-svn-id:' in i]
2655 svn_revision = svn_line[0].split('@') 2805 svn_revision = svn_line[0].split('@')
2656 svn_revision = svn_revision[1].split(' ')[0] 2806 svn_revision = svn_revision[1].split(' ')[0]
2657 return DEPOT_DEPS_NAME[depot]['viewvc'] + svn_revision 2807 return DEPOT_DEPS_NAME[depot]['viewvc'] + svn_revision
2658 except IndexError: 2808 except IndexError:
2659 return '' 2809 return ''
2660 return '' 2810 return ''
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
2793 if not previous_link: 2943 if not previous_link:
2794 previous_link = previous_id 2944 previous_link = previous_id
2795 2945
2796 print ' %8s %70s %s' % ( 2946 print ' %8s %70s %s' % (
2797 current_data['depot'], current_link, 2947 current_data['depot'], current_link,
2798 ('%d%%' % confidence).center(10, ' ')) 2948 ('%d%%' % confidence).center(10, ' '))
2799 print ' %8s %70s' % ( 2949 print ' %8s %70s' % (
2800 previous_data['depot'], previous_link) 2950 previous_data['depot'], previous_link)
2801 print 2951 print
2802 2952
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): 2953 def _CheckForWarnings(self, results_dict):
2923 if len(results_dict['culprit_revisions']) > 1: 2954 if len(results_dict['culprit_revisions']) > 1:
2924 self.warnings.append('Due to build errors, regression range could ' 2955 self.warnings.append('Due to build errors, regression range could '
2925 'not be narrowed down to a single commit.') 2956 'not be narrowed down to a single commit.')
2926 if self.opts.repeat_test_count == 1: 2957 if self.opts.repeat_test_count == 1:
2927 self.warnings.append('Tests were only set to run once. This may ' 2958 self.warnings.append('Tests were only set to run once. This may '
2928 'be insufficient to get meaningful results.') 2959 'be insufficient to get meaningful results.')
2929 if 0 < results_dict['confidence'] < HIGH_CONFIDENCE: 2960 if 0 < results_dict['confidence'] < HIGH_CONFIDENCE:
2930 self.warnings.append('Confidence is not high. Try bisecting again ' 2961 self.warnings.append('Confidence is not high. Try bisecting again '
2931 'with increased repeat_count, larger range, or ' 2962 'with increased repeat_count, larger range, or '
2932 'on another metric.') 2963 'on another metric.')
2933 if not results_dict['confidence']: 2964 if not results_dict['confidence']:
2934 self.warnings.append('Confidence score is 0%. Try bisecting again on ' 2965 self.warnings.append('Confidence score is 0%. Try bisecting again on '
2935 'another platform or another metric.') 2966 'another platform or another metric.')
2936 2967
2937 def FormatAndPrintResults(self, bisect_results): 2968 def FormatAndPrintResults(self, bisect_results):
2938 """Prints the results from a bisection run in a readable format. 2969 """Prints the results from a bisection run in a readable format.
2939 2970
2940 Args: 2971 Args:
2941 bisect_results: The results from a bisection test run. 2972 bisect_results: The results from a bisection test run.
2942 """ 2973 """
2943 revision_data = bisect_results['revision_data'] 2974 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 2975
2948 self._CheckForWarnings(results_dict) 2976 self._CheckForWarnings(results_dict)
2949 2977
2950 if self.opts.output_buildbot_annotations: 2978 if self.opts.output_buildbot_annotations:
2951 bisect_utils.OutputAnnotationStepStart('Build Status Per Revision') 2979 bisect_utils.OutputAnnotationStepStart('Build Status Per Revision')
2952 2980
2953 print 2981 print
2954 print 'Full results of bisection:' 2982 print 'Full results of bisection:'
2955 for current_id, current_data in revision_data_sorted: 2983 for current_id, current_data in results_dict['revision_data_sorted']:
2956 build_status = current_data['passed'] 2984 build_status = current_data['passed']
2957 2985
2958 if type(build_status) is bool: 2986 if type(build_status) is bool:
2959 if build_status: 2987 if build_status:
2960 build_status = 'Good' 2988 build_status = 'Good'
2961 else: 2989 else:
2962 build_status = 'Bad' 2990 build_status = 'Bad'
2963 2991
2964 print ' %20s %40s %s' % (current_data['depot'], 2992 print ' %20s %40s %s' % (current_data['depot'],
2965 current_id, build_status) 2993 current_id, build_status)
2966 print 2994 print
2967 2995
2968 if self.opts.output_buildbot_annotations: 2996 if self.opts.output_buildbot_annotations:
2969 bisect_utils.OutputAnnotationStepClosed() 2997 bisect_utils.OutputAnnotationStepClosed()
2970 # The perf dashboard scrapes the "results" step in order to comment on 2998 # 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. 2999 # bugs. If you change this, please update the perf dashboard as well.
2972 bisect_utils.OutputAnnotationStepStart('Results') 3000 bisect_utils.OutputAnnotationStepStart('Results')
2973 3001
2974 self._PrintBanner(results_dict) 3002 self._PrintBanner(results_dict)
2975 self._PrintWarnings() 3003 self._PrintWarnings()
2976 3004
2977 if results_dict['culprit_revisions'] and results_dict['confidence']: 3005 if results_dict['culprit_revisions'] and results_dict['confidence']:
2978 for culprit in results_dict['culprit_revisions']: 3006 for culprit in results_dict['culprit_revisions']:
2979 cl, info, depot = culprit 3007 cl, info, depot = culprit
2980 self._PrintRevisionInfo(cl, info, depot) 3008 self._PrintRevisionInfo(cl, info, depot)
2981 if results_dict['other_regressions']: 3009 if results_dict['other_regressions']:
2982 self._PrintOtherRegressions(results_dict['other_regressions'], 3010 self._PrintOtherRegressions(results_dict['other_regressions'],
2983 revision_data) 3011 results_dict['revision_data'])
2984 self._PrintTestedCommitsTable(revision_data_sorted, 3012 self._PrintTestedCommitsTable(results_dict['revision_data_sorted'],
2985 results_dict['first_working_revision'], 3013 results_dict['first_working_revision'],
2986 results_dict['last_broken_revision'], 3014 results_dict['last_broken_revision'],
2987 results_dict['confidence']) 3015 results_dict['confidence'])
2988 _PrintStepTime(revision_data_sorted) 3016 _PrintStepTime(results_dict['revision_data_sorted'])
2989 self._PrintReproSteps() 3017 self._PrintReproSteps()
2990 _PrintThankYou() 3018 _PrintThankYou()
2991 if self.opts.output_buildbot_annotations: 3019 if self.opts.output_buildbot_annotations:
2992 bisect_utils.OutputAnnotationStepClosed() 3020 bisect_utils.OutputAnnotationStepClosed()
2993 3021
2994 def _PrintBanner(self, results_dict): 3022 def _PrintBanner(self, results_dict):
2995 if self._IsBisectModeReturnCode(): 3023 if self._IsBisectModeReturnCode():
2996 metrics = 'N/A' 3024 metrics = 'N/A'
2997 change = 'Yes' 3025 change = 'Yes'
2998 else: 3026 else:
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after
3389 if (not source_control.IsInProperBranch() and 3417 if (not source_control.IsInProperBranch() and
3390 not opts.debug_ignore_sync and 3418 not opts.debug_ignore_sync and
3391 not opts.working_directory): 3419 not opts.working_directory):
3392 raise RuntimeError('You must switch to master branch to run bisection.') 3420 raise RuntimeError('You must switch to master branch to run bisection.')
3393 bisect_test = BisectPerformanceMetrics(source_control, opts) 3421 bisect_test = BisectPerformanceMetrics(source_control, opts)
3394 try: 3422 try:
3395 bisect_results = bisect_test.Run(opts.command, 3423 bisect_results = bisect_test.Run(opts.command,
3396 opts.bad_revision, 3424 opts.bad_revision,
3397 opts.good_revision, 3425 opts.good_revision,
3398 opts.metric) 3426 opts.metric)
3399 if bisect_results['error']: 3427 if bisect_results.error:
3400 raise RuntimeError(bisect_results['error']) 3428 raise RuntimeError(bisect_results.error)
3401 bisect_test.FormatAndPrintResults(bisect_results) 3429 bisect_test.FormatAndPrintResults(bisect_results)
3402 return 0 3430 return 0
3403 finally: 3431 finally:
3404 bisect_test.PerformCleanup() 3432 bisect_test.PerformCleanup()
3405 except RuntimeError, e: 3433 except RuntimeError, e:
3406 if opts.output_buildbot_annotations: 3434 if opts.output_buildbot_annotations:
3407 # The perf dashboard scrapes the "results" step in order to comment on 3435 # 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. 3436 # bugs. If you change this, please update the perf dashboard as well.
3409 bisect_utils.OutputAnnotationStepStart('Results') 3437 bisect_utils.OutputAnnotationStepStart('Results')
3410 print 'Error: %s' % e.message 3438 print 'Error: %s' % e.message
3411 if opts.output_buildbot_annotations: 3439 if opts.output_buildbot_annotations:
3412 bisect_utils.OutputAnnotationStepClosed() 3440 bisect_utils.OutputAnnotationStepClosed()
3413 return 1 3441 return 1
3414 3442
3415 3443
3416 if __name__ == '__main__': 3444 if __name__ == '__main__':
3417 sys.exit(main()) 3445 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