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 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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()) |
OLD | NEW |