[Buildroot] [PATCH 2/2] graph-build-time: generate graphs based on timing data

Thomas Petazzoni thomas.petazzoni at free-electrons.com
Sun Oct 9 16:17:28 UTC 2011


This script generates graphs of packages build time, from the timing
data generated by Buildroot in the $(O)/build-time.data file.

Example usage:

  cat $(O)/build-time.data | \
      ./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 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...)

Signed-off-by: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
---
 support/scripts/graph-build-time |  252 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 252 insertions(+), 0 deletions(-)
 create mode 100755 support/scripts/graph-build-time

diff --git a/support/scripts/graph-build-time b/support/scripts/graph-build-time
new file mode 100755
index 0000000..7fde852
--- /dev/null
+++ b/support/scripts/graph-build-time
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2011 by Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
+#
+# 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.data file.
+#
+# Example usage:
+#
+#   cat $(O)/build-time.data | ./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 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...)
+#
+# 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 matplotlib
+import numpy
+matplotlib.use('PDF')
+
+import matplotlib.pyplot as plt
+import matplotlib.font_manager as fm
+import csv
+import argparse
+import sys
+
+steps = [ 'extract', 'patch', 'configure', 'build',
+          'install-target', 'install-host', 'install-images',
+          'install-staging' ]
+
+histogram_colors = ['#e60004', '#009836', '#2e1d86', '#ffed00',
+                    '#0068b5', '#f28e00', '#940084', '#97c000',
+                    '#00469b', '#f9c000' ]
+
+class Package:
+    def __init__(self, name):
+        self.name = name
+        self.steps_duration = {}
+        self.duration = 0
+
+    def add_step(self, step, duration):
+        self.steps_duration[step] = duration
+        self.duration += duration
+
+    def get_duration(self, step):
+        if self.steps_duration.has_key(step):
+            return self.steps_duration[step]
+        else:
+            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.duration, reverse=True)
+
+    # 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, vals[i], 1, color=histogram_colors[i], bottom=bottom, linewidth=0)
+        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 + .5, [ p.name for p in data ], rotation=90, fontsize=4)
+
+    # Adjust size of graph (double the width)
+    sz = plt.gcf().get_size_inches()
+    plt.gcf().set_size_inches(sz[0] * 2, sz[1])
+
+    # Add more space for the package names at the bottom
+    plt.gcf().subplots_adjust(bottom=0.2)
+
+    # 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 == "build":
+        plt.title('Build time of packages, by build order')
+    elif order == "duration":
+        plt.title('Build time of packages, by duration order')
+
+    # 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.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.duration < (total * 0.01):
+            other_value += p.duration
+        else:
+            labels.append(p.name)
+            values.append(p.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)
+
+    # 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)
+
+    # 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():
+    reader = csv.reader(sys.stdin, 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:
+        pkg = row[0]
+        step = row[1]
+        duration = float(row[2]) / 1000.
+
+        p = getpkg(pkg)
+        if p is None:
+            p = Package(pkg)
+            pkgs.append(p)
+
+        p.add_step(step, duration)
+
+    return pkgs
+
+parser = argparse.ArgumentParser(description='Draw build time graphs')
+parser.add_argument("--type", metavar="GRAPH_TYPE", required=True,
+                    help="Type of graph (histogram, pie-packages, pie-steps)")
+parser.add_argument("--order", metavar="GRAPH_ORDER",
+                    help="Ordering of packages: build or duration (for histogram only)")
+parser.add_argument("--output", metavar="OUTPUT", required=True,
+                    help="Output file (PDF extension)")
+args = parser.parse_args()
+
+d = read_data()
+
+if args.type == "histogram":
+    if args.order == "build" or args.order == "duration":
+        pkg_histogram(d, args.output, args.order)
+    else:
+        print "Unknown graph order"
+        sys.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)
+
-- 
1.7.4.1




More information about the buildroot mailing list