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