136 lines
4.1 KiB
Python
Executable file
136 lines
4.1 KiB
Python
Executable file
#!/usr/bin/env python
|
|
# Copyright 2015 The Chromium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""Script to plot the results of a bisect run."""
|
|
|
|
import argparse
|
|
import json
|
|
import math
|
|
import re
|
|
import urllib2
|
|
|
|
from matplotlib import cm # pylint: disable=import-error
|
|
from matplotlib import pyplot # pylint: disable=import-error
|
|
import numpy
|
|
|
|
|
|
_PLOT_WIDTH_INCHES = 8
|
|
_PLOT_HEIGHT_INCHES = 6
|
|
_PERCENTILES = (0, 0.05, 0.25, 0.5, 0.75, 0.95, 1)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('bisect_url_or_debug_info_file',
|
|
help='The Buildbot URL of a bisect run, or a file '
|
|
'containing the output from the Debug Info step.')
|
|
parser.add_argument('output', nargs='?', help='File path to save a PNG to.')
|
|
args = parser.parse_args()
|
|
|
|
url = (args.bisect_url_or_debug_info_file +
|
|
'/steps/Debug%20Info/logs/Debug%20Info/text')
|
|
try:
|
|
f = urllib2.urlopen(url)
|
|
except ValueError: # Not a valid URL.
|
|
f = open(args.bisect_url_or_debug_info_file, 'r')
|
|
|
|
results = []
|
|
for line in f.readlines():
|
|
regex = (r'(?:(?:[a-z0-9-]+@)?[a-z0-9]+,)*'
|
|
r'(?:[a-z0-9-]+@)?(?P<commit>[a-z0-9]+)\s*'
|
|
r'(?P<values>\[(?:-?[0-9.]+, )*-?[0-9.]*\])')
|
|
match = re.match(regex, line)
|
|
if not match:
|
|
continue
|
|
|
|
commit = match.group('commit')
|
|
values = json.loads(match.group('values'))
|
|
if not values:
|
|
continue
|
|
|
|
print commit, values
|
|
results.append((commit, values))
|
|
|
|
_SavePlots(results, args.output)
|
|
|
|
|
|
def _SavePlots(results, file_path=None):
|
|
"""Saves histograms and empirial distribution plots showing the diff.
|
|
|
|
Args:
|
|
file_path: The location to save the plots go.
|
|
"""
|
|
figsize = (_PLOT_WIDTH_INCHES * 2, _PLOT_HEIGHT_INCHES)
|
|
_, (axis0, axis1) = pyplot.subplots(nrows=1, ncols=2, figsize=figsize)
|
|
|
|
_DrawHistogram(axis0, results)
|
|
_DrawEmpiricalCdf(axis1, results)
|
|
|
|
if file_path:
|
|
pyplot.savefig(file_path)
|
|
pyplot.show()
|
|
pyplot.close()
|
|
|
|
|
|
def _DrawHistogram(axis, results):
|
|
values_per_commit = [values for _, values in results]
|
|
|
|
# Calculate bounds and bins.
|
|
combined_values = sum(values_per_commit, [])
|
|
lower_bound = min(combined_values)
|
|
upper_bound = max(combined_values)
|
|
if lower_bound == upper_bound:
|
|
lower_bound -= 0.5
|
|
upper_bound += 0.5
|
|
bins = numpy.linspace(lower_bound, upper_bound,
|
|
math.log(len(combined_values)) * 4)
|
|
|
|
# Histograms.
|
|
colors = cm.rainbow(numpy.linspace( # pylint: disable=no-member
|
|
1, 0, len(results) + 1))
|
|
for (commit, values), color in zip(results, colors):
|
|
axis.hist(values, bins, alpha=0.5, normed=True, histtype='stepfilled',
|
|
label='%s (n=%d)' % (commit, len(values)), color=color)
|
|
|
|
# Vertical lines denoting the medians.
|
|
medians = tuple(numpy.percentile(values, 50) for values in values_per_commit)
|
|
axis.set_xticks(medians, minor=True)
|
|
axis.grid(which='minor', axis='x', linestyle='--')
|
|
|
|
# Axis labels and legend.
|
|
#axis.set_xlabel(step.metric_name)
|
|
axis.set_ylabel('Relative probability')
|
|
axis.legend(loc='upper right')
|
|
|
|
|
|
def _DrawEmpiricalCdf(axis, results):
|
|
colors = cm.rainbow(numpy.linspace( # pylint: disable=no-member
|
|
1, 0, len(results) + 1))
|
|
for (commit, values), color in zip(results, colors):
|
|
# Empirical distribution function.
|
|
levels = numpy.linspace(0, 1, len(values) + 1)
|
|
axis.step(sorted(values) + [max(values)], levels,
|
|
label='%s (n=%d)' % (commit, len(values)), color=color)
|
|
|
|
# Dots denoting the percentiles.
|
|
axis.plot(numpy.percentile(values, tuple(p * 100 for p in _PERCENTILES)),
|
|
_PERCENTILES, '.', color=color)
|
|
|
|
axis.set_yticks(_PERCENTILES)
|
|
|
|
# Vertical lines denoting the medians.
|
|
values_per_commit = [values for _, values in results]
|
|
medians = tuple(numpy.percentile(values, 50) for values in values_per_commit)
|
|
axis.set_xticks(medians, minor=True)
|
|
axis.grid(which='minor', axis='x', linestyle='--')
|
|
|
|
# Axis labels and legend.
|
|
#axis.set_xlabel(step.metric_name)
|
|
axis.set_ylabel('Cumulative probability')
|
|
axis.legend(loc='lower right')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|