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

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