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

Unified Diff: tools/auto_bisect/bisect_perf_regression.py

Issue 554283003: Refactored bisect results dicts into a separate class (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added docstring Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/auto_bisect/bisect_perf_regression_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/auto_bisect/bisect_perf_regression.py
diff --git a/tools/auto_bisect/bisect_perf_regression.py b/tools/auto_bisect/bisect_perf_regression.py
index 9d90d71f2ebe252e2d9632cdea071c5d443c6548..9d95cbd5ae81d17c398219123d0bbacae2788d40 100755
--- a/tools/auto_bisect/bisect_perf_regression.py
+++ b/tools/auto_bisect/bisect_perf_regression.py
@@ -868,44 +868,254 @@ def _PrintStepTime(revision_data_sorted):
seconds=int(step_perf_time_avg))
-def _FindOtherRegressions(revision_data_sorted, bad_greater_than_good):
- """Compiles a list of other possible regressions from the revision data.
+class BisectResults(object):
ojan 2014/09/19 18:17:00 All these classes should do in their own files. It
Sergiy Byelozyorov 2014/09/22 19:30:12 Done.
+ """This class holds the results of the bisect."""
ojan 2014/09/19 18:17:00 +1 to removing comments like this. This doesn't pr
Sergiy Byelozyorov 2014/09/22 19:30:12 Done.
- Args:
- revision_data_sorted: Sorted list of (revision, revision data) pairs.
- bad_greater_than_good: Whether the result value at the "bad" revision is
- numerically greater than the result value at the "good" revision.
+ def __init__(self, depot_manager, source_control):
+ self._depot_manager = depot_manager
+ self.revision_data = {}
+ self.error = None
+ self._source_control = source_control
- Returns:
- A list of [current_rev, previous_rev, confidence] for other places where
- there may have been a regression.
- """
- other_regressions = []
- previous_values = []
- previous_id = None
- for current_id, current_data in revision_data_sorted:
- current_values = current_data['value']
- if current_values:
- current_values = current_values['values']
- if previous_values:
- confidence = ConfidenceScore(previous_values, [current_values])
- mean_of_prev_runs = math_utils.Mean(sum(previous_values, []))
- mean_of_current_runs = math_utils.Mean(current_values)
-
- # Check that the potential regression is in the same direction as
- # the overall regression. If the mean of the previous runs < the
- # mean of the current runs, this local regression is in same
- # direction.
- prev_less_than_current = mean_of_prev_runs < mean_of_current_runs
- is_same_direction = (prev_less_than_current if
- bad_greater_than_good else not prev_less_than_current)
-
- # Only report potential regressions with high confidence.
- if is_same_direction and confidence > 50:
- other_regressions.append([current_id, previous_id, confidence])
- previous_values.append(current_values)
- previous_id = current_id
- return other_regressions
+ @staticmethod
+ def _FindOtherRegressions(revision_data_sorted, bad_greater_than_good):
+ """Compiles a list of other possible regressions from the revision data.
+
+ Args:
+ revision_data_sorted: Sorted list of (revision, revision data) pairs.
+ bad_greater_than_good: Whether the result value at the "bad" revision is
+ numerically greater than the result value at the "good" revision.
+
+ Returns:
+ A list of [current_rev, previous_rev, confidence] for other places where
+ there may have been a regression.
+ """
+ other_regressions = []
+ previous_values = []
+ previous_id = None
+ for current_id, current_data in revision_data_sorted:
+ current_values = current_data['value']
+ if current_values:
+ current_values = current_values['values']
+ if previous_values:
+ confidence = ConfidenceScore(previous_values, [current_values])
+ mean_of_prev_runs = math_utils.Mean(sum(previous_values, []))
+ mean_of_current_runs = math_utils.Mean(current_values)
+
+ # Check that the potential regression is in the same direction as
+ # the overall regression. If the mean of the previous runs < the
+ # mean of the current runs, this local regression is in same
+ # direction.
+ prev_less_than_current = mean_of_prev_runs < mean_of_current_runs
+ is_same_direction = (prev_less_than_current if
+ bad_greater_than_good else not prev_less_than_current)
+
+ # Only report potential regressions with high confidence.
+ if is_same_direction and confidence > 50:
+ other_regressions.append([current_id, previous_id, confidence])
+ previous_values.append(current_values)
+ previous_id = current_id
+ return other_regressions
+
+ def GetResultsDict(self):
+ """Prepares and returns information about the final resulsts as a dict.
+
+ Returns:
+ A dictionary with the following fields
ojan 2014/09/19 18:17:00 In my experience, documentation like this almost i
Sergiy Byelozyorov 2014/09/22 19:30:12 It actually took me a while to gather this info an
+
+ 'first_working_revision': First good revision.
+ 'last_broken_revision': Last bad revision.
+ 'culprit_revisions': A list of revisions, which contain the bad change
+ introducing the failure.
+ 'other_regressions': A list of tuples representing other regressions,
+ which may have occured.
+ 'regression_size': For performance bisects, this is a relative change of
+ the mean metric value. For other bisects this field always contains
+ 'zero-to-nonzero'.
+ 'regression_std_err': For performance bisects, it is a pooled standard
+ error for groups of good and bad runs. Not used for other bisects.
+ 'confidence': For performance bisects, it is a confidence that the good
+ and bad runs are distinct groups. Not used for non-performance
+ bisects.
+ 'revision_data_sorted': dict mapping revision ids to data about that
+ revision. Each piece of revision data consists of a dict with the
+ following keys:
+
+ 'passed': Represents whether the performance test was successful at
+ that revision. Possible values include: 1 (passed), 0 (failed),
+ '?' (skipped), 'F' (build failed).
+ 'depot': The depot that this revision is from (i.e. WebKit)
+ 'external': If the revision is a 'src' revision, 'external' contains
+ the revisions of each of the external libraries.
+ 'sort': A sort value for sorting the dict in order of commits.
+
+ For example:
+ {
+ 'CL #1':
+ {
+ 'passed': False,
+ 'depot': 'chromium',
+ 'external': None,
+ 'sort': 0
+ }
+ }
+ """
+ revision_data_sorted = sorted(self.revision_data.iteritems(),
+ key = lambda x: x[1]['sort'])
+
+ # Find range where it possibly broke.
+ first_working_revision = None
+ first_working_revision_index = -1
+ last_broken_revision = None
+ last_broken_revision_index = -1
+
+ culprit_revisions = []
+ other_regressions = []
+ regression_size = 0.0
+ regression_std_err = 0.0
+ confidence = 0.0
+
+ for i in xrange(len(revision_data_sorted)):
+ k, v = revision_data_sorted[i]
+ if v['passed'] == 1:
+ if not first_working_revision:
+ first_working_revision = k
+ first_working_revision_index = i
+
+ if not v['passed']:
+ last_broken_revision = k
+ last_broken_revision_index = i
+
+ if last_broken_revision != None and first_working_revision != None:
+ broken_means = []
+ for i in xrange(0, last_broken_revision_index + 1):
+ if revision_data_sorted[i][1]['value']:
+ broken_means.append(revision_data_sorted[i][1]['value']['values'])
+
+ working_means = []
+ for i in xrange(first_working_revision_index, len(revision_data_sorted)):
+ if revision_data_sorted[i][1]['value']:
+ working_means.append(revision_data_sorted[i][1]['value']['values'])
+
+ # Flatten the lists to calculate mean of all values.
+ working_mean = sum(working_means, [])
+ broken_mean = sum(broken_means, [])
+
+ # Calculate the approximate size of the regression
+ mean_of_bad_runs = math_utils.Mean(broken_mean)
+ mean_of_good_runs = math_utils.Mean(working_mean)
+
+ regression_size = 100 * math_utils.RelativeChange(mean_of_good_runs,
+ mean_of_bad_runs)
+ if math.isnan(regression_size):
+ regression_size = 'zero-to-nonzero'
+
+ regression_std_err = math.fabs(math_utils.PooledStandardError(
+ [working_mean, broken_mean]) /
+ max(0.0001, min(mean_of_good_runs, mean_of_bad_runs))) * 100.0
+
+ # Give a "confidence" in the bisect. At the moment we use how distinct the
+ # values are before and after the last broken revision, and how noisy the
+ # overall graph is.
+ confidence = ConfidenceScore(working_means, broken_means)
+
+ culprit_revisions = []
+
+ cwd = os.getcwd()
+ self._depot_manager.ChangeToDepotDir(
+ self.revision_data[last_broken_revision]['depot'])
+
+ if self.revision_data[last_broken_revision]['depot'] == 'cros':
+ # Want to get a list of all the commits and what depots they belong
+ # to so that we can grab info about each.
+ cmd = ['repo', 'forall', '-c',
+ 'pwd ; git log --pretty=oneline --before=%d --after=%d' % (
+ last_broken_revision, first_working_revision + 1)]
+ output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd)
+
+ changes = []
+ assert not return_code, ('An error occurred while running '
+ '"%s"' % ' '.join(cmd))
+ last_depot = None
+ cwd = os.getcwd()
+ for l in output.split('\n'):
+ if l:
+ # Output will be in form:
+ # /path_to_depot
+ # /path_to_other_depot
+ # <SHA1>
+ # /path_again
+ # <SHA1>
+ # etc.
+ if l[0] == '/':
+ last_depot = l
+ else:
+ contents = l.split(' ')
+ if len(contents) > 1:
+ changes.append([last_depot, contents[0]])
+ for c in changes:
+ os.chdir(c[0])
+ info = self._source_control.QueryRevisionInfo(c[1])
+ culprit_revisions.append((c[1], info, None))
+ else:
+ for i in xrange(last_broken_revision_index, len(revision_data_sorted)):
+ k, v = revision_data_sorted[i]
+ if k == first_working_revision:
+ break
+ self._depot_manager.ChangeToDepotDir(v['depot'])
+ info = self._source_control.QueryRevisionInfo(k)
+ culprit_revisions.append((k, info, v['depot']))
+ os.chdir(cwd)
+
+ # Check for any other possible regression ranges.
+ other_regressions = self._FindOtherRegressions(
+ revision_data_sorted, mean_of_bad_runs > mean_of_good_runs)
+
+ return {
+ 'first_working_revision': first_working_revision,
+ 'last_broken_revision': last_broken_revision,
+ 'culprit_revisions': culprit_revisions,
+ 'other_regressions': other_regressions,
+ 'regression_size': regression_size,
+ 'regression_std_err': regression_std_err,
+ 'confidence': confidence,
+ 'revision_data_sorted': revision_data_sorted
+ }
+
+
+class DepotManager(object):
ojan 2014/09/19 18:17:00 Don't call it a manager. A manager can literally d
Sergiy Byelozyorov 2014/09/22 19:30:12 How about DepotDirectoryRegistry? I'd like to keep
ojan 2014/09/26 17:34:30 SGTM. That's much more clear.
+ """Manages depots (code repositories) and their directories."""
+
+ def __init__(self):
+ self.depot_cwd = {}
+ self.cros_cwd = os.path.join(SRC_DIR, 'tools', 'cros')
+ for depot in DEPOT_NAMES:
+ # The working directory of each depot is just the path to the depot, but
+ # since we're already in 'src', we can skip that part.
+ self.depot_cwd[depot] = os.path.join(
+ SRC_DIR, DEPOT_DEPS_NAME[depot]['src'][4:])
+
+ def AddDepot(self, depot_name, depot_dir):
+ self.depot_cwd[depot_name] = depot_dir
+
+ def GetDepotDir(self, depot_name):
+ if depot_name == 'chromium':
+ return SRC_DIR
+ elif depot_name == 'cros':
+ return self.cros_cwd
+ elif depot_name in DEPOT_NAMES:
+ return self.depot_cwd[depot_name]
+ else:
+ assert False, ('Unknown depot [ %s ] encountered. Possibly a new one '
+ 'was added without proper support?' % depot_name)
+
+ def ChangeToDepotDir(self, depot_name):
+ """Given a depot, changes to the appropriate working directory.
+
+ Args:
+ depot_name: The name of the depot (see DEPOT_NAMES).
+ """
+ os.chdir(self.GetDepotDir(depot_name))
class BisectPerformanceMetrics(object):
@@ -920,17 +1130,11 @@ class BisectPerformanceMetrics(object):
self.opts = opts
self.source_control = source_control
- self.cros_cwd = os.path.join(SRC_DIR, 'tools', 'cros')
- self.depot_cwd = {}
+ self.depot_manager = DepotManager()
self.cleanup_commands = []
self.warnings = []
self.builder = builder.Builder.FromOpts(opts)
- for depot in DEPOT_NAMES:
- # The working directory of each depot is just the path to the depot, but
- # since we're already in 'src', we can skip that part.
- self.depot_cwd[depot] = os.path.join(
- SRC_DIR, DEPOT_DEPS_NAME[depot]['src'][4:])
def PerformCleanup(self):
"""Performs cleanup when script is finished."""
@@ -952,7 +1156,7 @@ class BisectPerformanceMetrics(object):
revision_range_end = bad_revision
cwd = os.getcwd()
- self.ChangeToDepotWorkingDirectory('cros')
+ self.depot_manager.ChangeToDepotDir('cros')
# Print the commit timestamps for every commit in the revision time
# range. We'll sort them and bisect by that. There is a remote chance that
@@ -972,7 +1176,7 @@ class BisectPerformanceMetrics(object):
[int(o) for o in output.split('\n') if bisect_utils.IsStringInt(o)]))
revision_work_list = sorted(revision_work_list, reverse=True)
else:
- cwd = self._GetDepotDirectory(depot)
+ cwd = self.depot_manager.GetDepotDir(depot)
revision_work_list = self.source_control.GetRevisionList(bad_revision,
good_revision, cwd=cwd)
@@ -992,8 +1196,8 @@ class BisectPerformanceMetrics(object):
# As of 01/24/2014, V8 trunk descriptions are formatted:
# "Version 3.X.Y (based on bleeding_edge revision rZ)"
# So we can just try parsing that out first and fall back to the old way.
- v8_dir = self._GetDepotDirectory('v8')
- v8_bleeding_edge_dir = self._GetDepotDirectory('v8_bleeding_edge')
+ v8_dir = self.depot_manager.GetDepotDir('v8')
+ v8_bleeding_edge_dir = self.depot_manager.GetDepotDir('v8_bleeding_edge')
revision_info = self.source_control.QueryRevisionInfo(revision,
cwd=v8_dir)
@@ -1033,7 +1237,7 @@ class BisectPerformanceMetrics(object):
return None
def _GetNearestV8BleedingEdgeFromTrunk(self, revision, search_forward=True):
- cwd = self._GetDepotDirectory('v8')
+ cwd = self.depot_manager.GetDepotDir('v8')
cmd = ['log', '--format=%ct', '-1', revision]
output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
commit_time = int(output)
@@ -1095,8 +1299,8 @@ class BisectPerformanceMetrics(object):
depot_data_src = depot_data.get('src') or depot_data.get('src_old')
src_dir = deps_data.get(depot_data_src)
if src_dir:
- self.depot_cwd[depot_name] = os.path.join(
- SRC_DIR, depot_data_src[4:])
+ self.depot_manager.AddDepot(
+ depot_name, os.path.join(SRC_DIR, depot_data_src[4:]))
re_results = rxp.search(src_dir)
if re_results:
results[depot_name] = re_results.group('revision')
@@ -1133,7 +1337,7 @@ class BisectPerformanceMetrics(object):
A dict in the format {depot: revision} if successful, otherwise None.
"""
cwd = os.getcwd()
- self.ChangeToDepotWorkingDirectory(depot)
+ self.depot_manager.ChangeToDepotDir(depot)
results = {}
@@ -1174,7 +1378,7 @@ class BisectPerformanceMetrics(object):
self.warnings.append(warningText)
cwd = os.getcwd()
- self.ChangeToDepotWorkingDirectory('chromium')
+ self.depot_manager.ChangeToDepotDir('chromium')
cmd = ['log', '-1', '--format=%H',
'--author=chrome-release@google.com',
'--grep=to %s' % version, 'origin/master']
@@ -1412,7 +1616,7 @@ class BisectPerformanceMetrics(object):
new_data = None
if re.search(deps_revision, deps_contents):
commit_position = self.source_control.GetCommitPosition(
- git_revision, self._GetDepotDirectory(depot))
+ git_revision, self.depot_manager.GetDepotDir(depot))
if not commit_position:
print 'Could not determine commit position for %s' % git_revision
return None
@@ -1776,7 +1980,7 @@ class BisectPerformanceMetrics(object):
commit_position = self.source_control.GetCommitPosition(revision)
for d in DEPOT_DEPS_NAME[depot]['depends']:
- self.ChangeToDepotWorkingDirectory(d)
+ self.depot_manager.ChangeToDepotDir(d)
dependant_rev = self.source_control.ResolveToRevision(
commit_position, d, DEPOT_DEPS_NAME, -1000)
@@ -1787,7 +1991,7 @@ class BisectPerformanceMetrics(object):
num_resolved = len(revisions_to_sync)
num_needed = len(DEPOT_DEPS_NAME[depot]['depends'])
- self.ChangeToDepotWorkingDirectory(depot)
+ self.depot_manager.ChangeToDepotDir(depot)
if not ((num_resolved - 1) == num_needed):
return None
@@ -1814,7 +2018,7 @@ class BisectPerformanceMetrics(object):
True if successful.
"""
cwd = os.getcwd()
- self.ChangeToDepotWorkingDirectory('cros')
+ self.depot_manager.ChangeToDepotDir('cros')
cmd = [bisect_utils.CROS_SDK_PATH, '--delete']
return_code = bisect_utils.RunProcess(cmd)
os.chdir(cwd)
@@ -1827,7 +2031,7 @@ class BisectPerformanceMetrics(object):
True if successful.
"""
cwd = os.getcwd()
- self.ChangeToDepotWorkingDirectory('cros')
+ self.depot_manager.ChangeToDepotDir('cros')
cmd = [bisect_utils.CROS_SDK_PATH, '--create']
return_code = bisect_utils.RunProcess(cmd)
os.chdir(cwd)
@@ -1989,7 +2193,7 @@ class BisectPerformanceMetrics(object):
True if successful, False otherwise.
"""
for depot, revision in revisions_to_sync:
- self.ChangeToDepotWorkingDirectory(depot)
+ self.depot_manager.ChangeToDepotDir(depot)
if sync_client:
self.PerformPreBuildCleanup()
@@ -2030,25 +2234,6 @@ class BisectPerformanceMetrics(object):
return dist_to_good_value < dist_to_bad_value
- def _GetDepotDirectory(self, depot_name):
- if depot_name == 'chromium':
- return SRC_DIR
- elif depot_name == 'cros':
- return self.cros_cwd
- elif depot_name in DEPOT_NAMES:
- return self.depot_cwd[depot_name]
- else:
- assert False, ('Unknown depot [ %s ] encountered. Possibly a new one '
- 'was added without proper support?' % depot_name)
-
- def ChangeToDepotWorkingDirectory(self, depot_name):
- """Given a depot, changes to the appropriate working directory.
-
- Args:
- depot_name: The name of the depot (see DEPOT_NAMES).
- """
- os.chdir(self._GetDepotDirectory(depot_name))
-
def _FillInV8BleedingEdgeInfo(self, min_revision_data, max_revision_data):
r1 = self._GetNearestV8BleedingEdgeFromTrunk(min_revision_data['revision'],
search_forward=True)
@@ -2124,7 +2309,7 @@ class BisectPerformanceMetrics(object):
"""
# Change into working directory of external library to run
# subsequent commands.
- self.ChangeToDepotWorkingDirectory(current_depot)
+ self.depot_manager.ChangeToDepotDir(current_depot)
# V8 (and possibly others) is merged in periodically. Bisecting
# this directory directly won't give much good info.
@@ -2138,7 +2323,7 @@ class BisectPerformanceMetrics(object):
return []
if current_depot == 'v8_bleeding_edge':
- self.ChangeToDepotWorkingDirectory('chromium')
+ self.depot_manager.ChangeToDepotDir('chromium')
shutil.move('v8', 'v8.bak')
shutil.move('v8_bleeding_edge', 'v8')
@@ -2146,16 +2331,17 @@ class BisectPerformanceMetrics(object):
self.cleanup_commands.append(['mv', 'v8', 'v8_bleeding_edge'])
self.cleanup_commands.append(['mv', 'v8.bak', 'v8'])
- self.depot_cwd['v8_bleeding_edge'] = os.path.join(SRC_DIR, 'v8')
- self.depot_cwd['v8'] = os.path.join(SRC_DIR, 'v8.bak')
+ self.depot_manager.AddDepot(
+ 'v8_bleeding_edge', os.path.join(SRC_DIR, 'v8'))
+ self.depot_manager.AddDepot('v8', os.path.join(SRC_DIR, 'v8.bak'))
- self.ChangeToDepotWorkingDirectory(current_depot)
+ self.depot_manager.ChangeToDepotDir(current_depot)
depot_revision_list = self.GetRevisionList(current_depot,
end_revision,
start_revision)
- self.ChangeToDepotWorkingDirectory('chromium')
+ self.depot_manager.ChangeToDepotDir('chromium')
return depot_revision_list
@@ -2262,7 +2448,7 @@ class BisectPerformanceMetrics(object):
"""
if self.source_control.IsGit() and target_depot != 'cros':
cmd = ['log', '--format=%ct', '-1', good_revision]
- cwd = self._GetDepotDirectory(target_depot)
+ cwd = self.depot_manager.GetDepotDir(target_depot)
output = bisect_utils.CheckRunGit(cmd, cwd=cwd)
good_commit_time = int(output)
@@ -2328,41 +2514,9 @@ class BisectPerformanceMetrics(object):
metric: The performance metric to monitor.
Returns:
- A dict with 2 members, 'revision_data' and 'error'. On success,
- 'revision_data' will contain a dict mapping revision ids to
- data about that revision. Each piece of revision data consists of a
- dict with the following keys:
-
- 'passed': Represents whether the performance test was successful at
- that revision. Possible values include: 1 (passed), 0 (failed),
- '?' (skipped), 'F' (build failed).
- 'depot': The depot that this revision is from (i.e. WebKit)
- 'external': If the revision is a 'src' revision, 'external' contains
- the revisions of each of the external libraries.
- 'sort': A sort value for sorting the dict in order of commits.
-
- For example:
- {
- 'error':None,
- 'revision_data':
- {
- 'CL #1':
- {
- 'passed': False,
- 'depot': 'chromium',
- 'external': None,
- 'sort': 0
- }
- }
- }
-
- If an error occurred, the 'error' field will contain the message and
- 'revision_data' will be empty.
+ A BisectResults object.
"""
- results = {
- 'revision_data' : {},
- 'error' : None,
- }
+ results = BisectResults(self.depot_manager, self.source_control)
# Choose depot to bisect first
target_depot = 'chromium'
@@ -2372,7 +2526,7 @@ class BisectPerformanceMetrics(object):
target_depot = 'android-chrome'
cwd = os.getcwd()
- self.ChangeToDepotWorkingDirectory(target_depot)
+ self.depot_manager.ChangeToDepotDir(target_depot)
# If they passed SVN revisions, we can try match them to git SHA1 hashes.
bad_revision = self.source_control.ResolveToRevision(
@@ -2382,18 +2536,18 @@ class BisectPerformanceMetrics(object):
os.chdir(cwd)
if bad_revision is None:
- results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % bad_revision_in
+ results.error = 'Couldn\'t resolve [%s] to SHA1.' % bad_revision_in
return results
if good_revision is None:
- results['error'] = 'Couldn\'t resolve [%s] to SHA1.' % good_revision_in
+ results.error = 'Couldn\'t resolve [%s] to SHA1.' % good_revision_in
return results
# Check that they didn't accidentally swap good and bad revisions.
if not self.CheckIfRevisionsInProperOrder(
target_depot, good_revision, bad_revision):
- results['error'] = ('bad_revision < good_revision, did you swap these '
- 'by mistake?')
+ results.error = ('bad_revision < good_revision, did you swap these '
+ 'by mistake?')
return results
bad_revision, good_revision = self.NudgeRevisionsIfDEPSChange(
bad_revision, good_revision, good_revision_in)
@@ -2402,7 +2556,7 @@ class BisectPerformanceMetrics(object):
cannot_bisect = self.CanPerformBisect(good_revision, bad_revision)
if cannot_bisect:
- results['error'] = cannot_bisect.get('error')
+ results.error = cannot_bisect.get('error')
return results
print 'Gathering revision range for bisection.'
@@ -2417,7 +2571,7 @@ class BisectPerformanceMetrics(object):
# revision_data will store information about a revision such as the
# depot it came from, the webkit/V8 revision at that time,
# performance timing, build state, etc...
- revision_data = results['revision_data']
+ revision_data = results.revision_data
# revision_list is the list we're binary searching through at the moment.
revision_list = []
@@ -2460,17 +2614,17 @@ class BisectPerformanceMetrics(object):
bisect_utils.OutputAnnotationStepClosed()
if bad_results[1]:
- results['error'] = ('An error occurred while building and running '
+ results.error = ('An error occurred while building and running '
'the \'bad\' reference value. The bisect cannot continue without '
'a working \'bad\' revision to start from.\n\nError: %s' %
- bad_results[0])
+ bad_results[0])
return results
if good_results[1]:
- results['error'] = ('An error occurred while building and running '
+ results.error = ('An error occurred while building and running '
'the \'good\' reference value. The bisect cannot continue without '
'a working \'good\' revision to start from.\n\nError: %s' %
- good_results[0])
+ good_results[0])
return results
@@ -2535,9 +2689,9 @@ class BisectPerformanceMetrics(object):
previous_revision)
if not new_revision_list:
- results['error'] = ('An error occurred attempting to retrieve '
- 'revision range: [%s..%s]' %
- (earliest_revision, latest_revision))
+ results.error = ('An error occurred attempting to retrieve '
+ 'revision range: [%s..%s]' %
+ (earliest_revision, latest_revision))
return results
_AddRevisionsIntoRevisionData(
@@ -2567,7 +2721,7 @@ class BisectPerformanceMetrics(object):
next_revision_data = revision_data[next_revision_id]
next_revision_depot = next_revision_data['depot']
- self.ChangeToDepotWorkingDirectory(next_revision_depot)
+ self.depot_manager.ChangeToDepotDir(next_revision_depot)
if self.opts.output_buildbot_annotations:
step_name = 'Working on [%s]' % next_revision_id
@@ -2616,18 +2770,14 @@ class BisectPerformanceMetrics(object):
bisect_utils.OutputAnnotationStepClosed()
else:
# Weren't able to sync and retrieve the revision range.
- results['error'] = ('An error occurred attempting to retrieve revision '
- 'range: [%s..%s]' % (good_revision, bad_revision))
+ results.error = ('An error occurred attempting to retrieve revision '
+ 'range: [%s..%s]' % (good_revision, bad_revision))
return results
- def _PrintPartialResults(self, results_dict):
- revision_data = results_dict['revision_data']
- revision_data_sorted = sorted(revision_data.iteritems(),
- key = lambda x: x[1]['sort'])
- results_dict = self._GetResultsDict(revision_data, revision_data_sorted)
-
- self._PrintTestedCommitsTable(revision_data_sorted,
+ def _PrintPartialResults(self, results):
+ results_dict = results.GetResultsDict()
+ self._PrintTestedCommitsTable(results_dict['revision_data_sorted'],
results_dict['first_working_revision'],
results_dict['last_broken_revision'],
100, final_step=False)
@@ -2647,7 +2797,7 @@ class BisectPerformanceMetrics(object):
def _GetViewVCLinkFromDepotAndHash(self, cl, depot):
info = self.source_control.QueryRevisionInfo(cl,
- self._GetDepotDirectory(depot))
+ self.depot_manager.GetDepotDir(depot))
if depot and DEPOT_DEPS_NAME[depot].has_key('viewvc'):
try:
# Format is "git-svn-id: svn://....@123456 <other data>"
@@ -2800,125 +2950,6 @@ class BisectPerformanceMetrics(object):
previous_data['depot'], previous_link)
print
- def _GetResultsDict(self, revision_data, revision_data_sorted):
- # Find range where it possibly broke.
- first_working_revision = None
- first_working_revision_index = -1
- last_broken_revision = None
- last_broken_revision_index = -1
-
- culprit_revisions = []
- other_regressions = []
- regression_size = 0.0
- regression_std_err = 0.0
- confidence = 0.0
-
- for i in xrange(len(revision_data_sorted)):
- k, v = revision_data_sorted[i]
- if v['passed'] == 1:
- if not first_working_revision:
- first_working_revision = k
- first_working_revision_index = i
-
- if not v['passed']:
- last_broken_revision = k
- last_broken_revision_index = i
-
- if last_broken_revision != None and first_working_revision != None:
- broken_means = []
- for i in xrange(0, last_broken_revision_index + 1):
- if revision_data_sorted[i][1]['value']:
- broken_means.append(revision_data_sorted[i][1]['value']['values'])
-
- working_means = []
- for i in xrange(first_working_revision_index, len(revision_data_sorted)):
- if revision_data_sorted[i][1]['value']:
- working_means.append(revision_data_sorted[i][1]['value']['values'])
-
- # Flatten the lists to calculate mean of all values.
- working_mean = sum(working_means, [])
- broken_mean = sum(broken_means, [])
-
- # Calculate the approximate size of the regression
- mean_of_bad_runs = math_utils.Mean(broken_mean)
- mean_of_good_runs = math_utils.Mean(working_mean)
-
- regression_size = 100 * math_utils.RelativeChange(mean_of_good_runs,
- mean_of_bad_runs)
- if math.isnan(regression_size):
- regression_size = 'zero-to-nonzero'
-
- regression_std_err = math.fabs(math_utils.PooledStandardError(
- [working_mean, broken_mean]) /
- max(0.0001, min(mean_of_good_runs, mean_of_bad_runs))) * 100.0
-
- # Give a "confidence" in the bisect. At the moment we use how distinct the
- # values are before and after the last broken revision, and how noisy the
- # overall graph is.
- confidence = ConfidenceScore(working_means, broken_means)
-
- culprit_revisions = []
-
- cwd = os.getcwd()
- self.ChangeToDepotWorkingDirectory(
- revision_data[last_broken_revision]['depot'])
-
- if revision_data[last_broken_revision]['depot'] == 'cros':
- # Want to get a list of all the commits and what depots they belong
- # to so that we can grab info about each.
- cmd = ['repo', 'forall', '-c',
- 'pwd ; git log --pretty=oneline --before=%d --after=%d' % (
- last_broken_revision, first_working_revision + 1)]
- output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd)
-
- changes = []
- assert not return_code, ('An error occurred while running '
- '"%s"' % ' '.join(cmd))
- last_depot = None
- cwd = os.getcwd()
- for l in output.split('\n'):
- if l:
- # Output will be in form:
- # /path_to_depot
- # /path_to_other_depot
- # <SHA1>
- # /path_again
- # <SHA1>
- # etc.
- if l[0] == '/':
- last_depot = l
- else:
- contents = l.split(' ')
- if len(contents) > 1:
- changes.append([last_depot, contents[0]])
- for c in changes:
- os.chdir(c[0])
- info = self.source_control.QueryRevisionInfo(c[1])
- culprit_revisions.append((c[1], info, None))
- else:
- for i in xrange(last_broken_revision_index, len(revision_data_sorted)):
- k, v = revision_data_sorted[i]
- if k == first_working_revision:
- break
- self.ChangeToDepotWorkingDirectory(v['depot'])
- info = self.source_control.QueryRevisionInfo(k)
- culprit_revisions.append((k, info, v['depot']))
- os.chdir(cwd)
-
- # Check for any other possible regression ranges.
- other_regressions = _FindOtherRegressions(
- revision_data_sorted, mean_of_bad_runs > mean_of_good_runs)
-
- return {
- 'first_working_revision': first_working_revision,
- 'last_broken_revision': last_broken_revision,
- 'culprit_revisions': culprit_revisions,
- 'other_regressions': other_regressions,
- 'regression_size': regression_size,
- 'regression_std_err': regression_std_err,
- 'confidence': confidence,
- }
-
def _CheckForWarnings(self, results_dict):
if len(results_dict['culprit_revisions']) > 1:
self.warnings.append('Due to build errors, regression range could '
@@ -2940,10 +2971,7 @@ class BisectPerformanceMetrics(object):
Args:
bisect_results: The results from a bisection test run.
"""
- revision_data = bisect_results['revision_data']
- revision_data_sorted = sorted(revision_data.iteritems(),
- key = lambda x: x[1]['sort'])
- results_dict = self._GetResultsDict(revision_data, revision_data_sorted)
+ results_dict = bisect_results.GetResultsDict()
self._CheckForWarnings(results_dict)
@@ -2952,7 +2980,7 @@ class BisectPerformanceMetrics(object):
print
print 'Full results of bisection:'
- for current_id, current_data in revision_data_sorted:
+ for current_id, current_data in results_dict['revision_data_sorted']:
build_status = current_data['passed']
if type(build_status) is bool:
@@ -2980,12 +3008,12 @@ class BisectPerformanceMetrics(object):
self._PrintRevisionInfo(cl, info, depot)
if results_dict['other_regressions']:
self._PrintOtherRegressions(results_dict['other_regressions'],
- revision_data)
- self._PrintTestedCommitsTable(revision_data_sorted,
+ results_dict['revision_data'])
+ self._PrintTestedCommitsTable(results_dict['revision_data_sorted'],
results_dict['first_working_revision'],
results_dict['last_broken_revision'],
results_dict['confidence'])
- _PrintStepTime(revision_data_sorted)
+ _PrintStepTime(results_dict['revision_data_sorted'])
self._PrintReproSteps()
_PrintThankYou()
if self.opts.output_buildbot_annotations:
@@ -3396,8 +3424,8 @@ def main():
opts.bad_revision,
opts.good_revision,
opts.metric)
- if bisect_results['error']:
- raise RuntimeError(bisect_results['error'])
+ if bisect_results.error:
+ raise RuntimeError(bisect_results.error)
bisect_test.FormatAndPrintResults(bisect_results)
return 0
finally:
« no previous file with comments | « no previous file | tools/auto_bisect/bisect_perf_regression_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698