X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;f=cirros-testvm%2Fsrc-cirros%2Fbuildroot-2015.05%2Fsupport%2Fscripts%2Fgraph-build-time;fp=cirros-testvm%2Fsrc-cirros%2Fbuildroot-2015.05%2Fsupport%2Fscripts%2Fgraph-build-time;h=7eb3e479532698db4400f7a01e352627aa001d63;hb=b0a0f15dfaa205161a7fcb20cf1b8cd4948c2ef3;hp=0000000000000000000000000000000000000000;hpb=c6ac3cd55ee2da956195eee393b0882105dfad4e;p=packages%2Ftrusty%2Fcirros-testvm.git diff --git a/cirros-testvm/src-cirros/buildroot-2015.05/support/scripts/graph-build-time b/cirros-testvm/src-cirros/buildroot-2015.05/support/scripts/graph-build-time new file mode 100755 index 0000000..7eb3e47 --- /dev/null +++ b/cirros-testvm/src-cirros/buildroot-2015.05/support/scripts/graph-build-time @@ -0,0 +1,306 @@ +#!/usr/bin/env python + +# Copyright (C) 2011 by Thomas Petazzoni +# Copyright (C) 2013 by Yann E. MORIN +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# This script generates graphs of packages build time, from the timing +# data generated by Buildroot in the $(O)/build-time.log file. +# +# Example usage: +# +# cat $(O)/build-time.log | ./support/scripts/graph-build-time --type=histogram --output=foobar.pdf +# +# Three graph types are available : +# +# * histogram, which creates an histogram of the build time for each +# package, decomposed by each step (extract, patch, configure, +# etc.). The order in which the packages are shown is +# configurable: by package name, by build order, or by duration +# order. See the --order option. +# +# * pie-packages, which creates a pie chart of the build time of +# each package (without decomposition in steps). Packages that +# contributed to less than 1% of the overall build time are all +# grouped together in an "Other" entry. +# +# * pie-steps, which creates a pie chart of the time spent globally +# on each step (extract, patch, configure, etc...) +# +# The default is to generate an histogram ordered by package name. +# +# Requirements: +# +# * matplotlib (python-matplotlib on Debian/Ubuntu systems) +# * numpy (python-numpy on Debian/Ubuntu systems) +# * argparse (by default in Python 2.7, requires python-argparse if +# Python 2.6 is used) + +import sys + +try: + import matplotlib as mpl + import numpy +except ImportError: + sys.stderr.write("You need python-matplotlib and python-numpy to generate build graphs\n") + exit(1) + +# Use the Agg backend (which produces a PNG output, see +# http://matplotlib.org/faq/usage_faq.html#what-is-a-backend), +# otherwise an incorrect backend is used on some host machines). +# Note: matplotlib.use() must be called *before* matplotlib.pyplot. +mpl.use('Agg') + +import matplotlib.pyplot as plt +import matplotlib.font_manager as fm +import csv +import argparse + +steps = [ 'extract', 'patch', 'configure', 'build', + 'install-target', 'install-staging', 'install-images', + 'install-host'] + +default_colors = ['#e60004', '#009836', '#2e1d86', '#ffed00', + '#0068b5', '#f28e00', '#940084', '#97c000'] + +alternate_colors = ['#00e0e0', '#3f7f7f', '#ff0000', '#00c000', + '#0080ff', '#c000ff', '#00eeee', '#e0e000'] + +class Package: + def __init__(self, name): + self.name = name + self.steps_duration = {} + self.steps_start = {} + self.steps_end = {} + + def add_step(self, step, state, time): + if state == "start": + self.steps_start[step] = time + else: + self.steps_end[step] = time + if step in self.steps_start and step in self.steps_end: + self.steps_duration[step] = self.steps_end[step] - self.steps_start[step] + + def get_duration(self, step=None): + if step is None: + duration = 0 + for step in list(self.steps_duration.keys()): + duration += self.steps_duration[step] + return duration + if step in self.steps_duration: + return self.steps_duration[step] + return 0 + +# Generate an histogram of the time spent in each step of each +# package. +def pkg_histogram(data, output, order="build"): + n_pkgs = len(data) + ind = numpy.arange(n_pkgs) + + if order == "duration": + data = sorted(data, key=lambda p: p.get_duration(), reverse=True) + elif order == "name": + data = sorted(data, key=lambda p: p.name, reverse=False) + + # Prepare the vals array, containing one entry for each step + vals = [] + for step in steps: + val = [] + for p in data: + val.append(p.get_duration(step)) + vals.append(val) + + bottom = [0] * n_pkgs + legenditems = [] + + plt.figure() + + # Draw the bars, step by step + for i in range(0, len(vals)): + b = plt.bar(ind+0.1, vals[i], width=0.8, color=colors[i], bottom=bottom, linewidth=0.25) + legenditems.append(b[0]) + bottom = [ bottom[j] + vals[i][j] for j in range(0, len(vals[i])) ] + + # Draw the package names + plt.xticks(ind + .6, [ p.name for p in data ], rotation=-60, rotation_mode="anchor", fontsize=8, ha='left') + + # Adjust size of graph depending on the number of packages + # Ensure a minimal size twice as the default + # Magic Numbers do Magic Layout! + ratio = max(((n_pkgs + 10) / 48, 2)) + borders = 0.1 / ratio + sz = plt.gcf().get_figwidth() + plt.gcf().set_figwidth(sz * ratio) + + # Adjust space at borders, add more space for the + # package names at the bottom + plt.gcf().subplots_adjust(bottom=0.2, left=borders, right=1-borders) + + # Remove ticks in the graph for each package + axes = plt.gcf().gca() + for line in axes.get_xticklines(): + line.set_markersize(0) + + axes.set_ylabel('Time (seconds)') + + # Reduce size of legend text + leg_prop = fm.FontProperties(size=6) + + # Draw legend + plt.legend(legenditems, steps, prop=leg_prop) + + if order == "name": + plt.title('Build time of packages\n') + elif order == "build": + plt.title('Build time of packages, by build order\n') + elif order == "duration": + plt.title('Build time of packages, by duration order\n') + + # Save graph + plt.savefig(output) + +# Generate a pie chart with the time spent building each package. +def pkg_pie_time_per_package(data, output): + # Compute total build duration + total = 0 + for p in data: + total += p.get_duration() + + # Build the list of labels and values, and filter the packages + # that account for less than 1% of the build time. + labels = [] + values = [] + other_value = 0 + for p in data: + if p.get_duration() < (total * 0.01): + other_value += p.get_duration() + else: + labels.append(p.name) + values.append(p.get_duration()) + + labels.append('Other') + values.append(other_value) + + plt.figure() + + # Draw pie graph + patches, texts, autotexts = plt.pie(values, labels=labels, + autopct='%1.1f%%', shadow=True, + colors=colors) + + # Reduce text size + proptease = fm.FontProperties() + proptease.set_size('xx-small') + plt.setp(autotexts, fontproperties=proptease) + plt.setp(texts, fontproperties=proptease) + + plt.title('Build time per package') + plt.savefig(output) + +# Generate a pie chart with a portion for the overall time spent in +# each step for all packages. +def pkg_pie_time_per_step(data, output): + steps_values = [] + for step in steps: + val = 0 + for p in data: + val += p.get_duration(step) + steps_values.append(val) + + plt.figure() + + # Draw pie graph + patches, texts, autotexts = plt.pie(steps_values, labels=steps, + autopct='%1.1f%%', shadow=True, + colors=colors) + + # Reduce text size + proptease = fm.FontProperties() + proptease.set_size('xx-small') + plt.setp(autotexts, fontproperties=proptease) + plt.setp(texts, fontproperties=proptease) + + plt.title('Build time per step') + plt.savefig(output) + +# Parses the csv file passed on standard input and returns a list of +# Package objects, filed with the duration of each step and the total +# duration of the package. +def read_data(input_file): + if input_file is None: + input_file = sys.stdin + else: + input_file = open(input_file) + reader = csv.reader(input_file, delimiter=':') + pkgs = [] + + # Auxilliary function to find a package by name in the list. + def getpkg(name): + for p in pkgs: + if p.name == name: + return p + return None + + for row in reader: + time = int(row[0].strip()) + state = row[1].strip() + step = row[2].strip() + pkg = row[3].strip() + + p = getpkg(pkg) + if p is None: + p = Package(pkg) + pkgs.append(p) + + p.add_step(step, state, time) + + return pkgs + +parser = argparse.ArgumentParser(description='Draw build time graphs') +parser.add_argument("--type", '-t', metavar="GRAPH_TYPE", + help="Type of graph (histogram, pie-packages, pie-steps)") +parser.add_argument("--order", '-O', metavar="GRAPH_ORDER", + help="Ordering of packages: build or duration (for histogram only)") +parser.add_argument("--alternate-colors", '-c', action="store_true", + help="Use alternate colour-scheme") +parser.add_argument("--input", '-i', metavar="OUTPUT", + help="Input file (usually $(O)/build/build-time.log)") +parser.add_argument("--output", '-o', metavar="OUTPUT", required=True, + help="Output file (.pdf or .png extension)") +args = parser.parse_args() + +d = read_data(args.input) + +if args.alternate_colors: + colors = alternate_colors +else: + colors = default_colors + +if args.type == "histogram" or args.type is None: + if args.order == "build" or args.order == "duration" or args.order == "name": + pkg_histogram(d, args.output, args.order) + elif args.order is None: + pkg_histogram(d, args.output, "name") + else: + sys.stderr.write("Unknown ordering: %s\n" % args.order) + exit(1) +elif args.type == "pie-packages": + pkg_pie_time_per_package(d, args.output) +elif args.type == "pie-steps": + pkg_pie_time_per_step(d, args.output) +else: + sys.stderr.write("Unknown type: %s\n" % args.type) + exit(1)