diff --git a/README.md b/README.md
index 8a73191..c12efce 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,10 @@
# bootchart
+Bootchart is a tool for performance analysis and visualization of the GNU/Linux boot process.
+Resource utilization and process information are collected during the boot process and are later rendered in a PNG, SVG or EPS encoded chart.
+Bootchart provides a shell script to be run by the kernel in the init phase.
+The script will run in background and collect process information, CPU statistics and disk usage statistics from the /proc file system.
+The performance data are stored in memory and are written to disk once the boot process completes.
+The boot log file is later processed using a Java application (or the web form) which builds the process tree and renders a performance chart in different formats: SVG e EPS.
+The chart can then be analyzed to examine process dependency and overall resource utilization.
+
diff --git a/bootchart-0.9-nojavafunctions.patch b/bootchart-0.9-nojavafunctions.patch
new file mode 100644
index 0000000..10d4b94
--- /dev/null
+++ b/bootchart-0.9-nojavafunctions.patch
@@ -0,0 +1,37 @@
+--- bootchart-0.9/script/bootchart.orig 2006-06-26 16:03:51.000000000 +0200
++++ bootchart-0.9/script/bootchart 2006-06-26 16:07:21.000000000 +0200
+@@ -4,21 +4,23 @@
+ # JPackage Project
+
+ # Source functions library
+-if [ -f /usr/share/java-utils/java-functions ] ; then
+- . /usr/share/java-utils/java-functions
+-else
+- echo "Can't find functions library, aborting"
+- exit 1
+-fi
++#if [ -f /usr/share/java-utils/java-functions ] ; then
++# . /usr/share/java-utils/java-functions
++#else
++# echo "Can't find functions library, aborting"
++# exit 1
++#fi
+
+ # Configuration
+ MAIN_CLASS="org.bootchart.Main"
+-BASE_JARS="commons-cli.jar bootchart.jar"
+-FLAGS="-Djava.awt.headless=true -Dgnu.java.awt.peer.gtk.Graphics=Graphics2D"
++BASE_JARS="/usr/share/java/bootchart.jar"
++#BASE_JARS="commons-cli.jar bootchart.jar"
++#FLAGS="-Djava.awt.headless=true -Dgnu.java.awt.peer.gtk.Graphics=Graphics2D"
+
+ # Set parameters
+-set_jvm
+-set_classpath $BASE_JARS
++#set_jvm
++#set_classpath $BASE_JARS
+
+ # Let's start
+-run "$@"
++#run "$@"
++java -cp $BASE_JARS $MAIN_CLASS $@
diff --git a/bootchart-0.9-svg_path.patch b/bootchart-0.9-svg_path.patch
new file mode 100644
index 0000000..4c58f9e
--- /dev/null
+++ b/bootchart-0.9-svg_path.patch
@@ -0,0 +1,15 @@
+--- bootchart-0.9/bootchart.pl.orig 2006-06-26 11:33:31.000000000 +0200
++++ bootchart-0.9/bootchart.pl 2006-06-26 11:35:22.000000000 +0200
+@@ -37,9 +37,9 @@
+ my %ps_info;
+ my %ps_by_parent;
+
+-my $chart_svg_template = `cat svg/bootchart.svg.template` || die;
+-my $process_svg_template = `cat svg/process.svg.template` || die;
+-my $svg_css_file = `cat svg/style.css` || die;
++my $chart_svg_template = `cat /usr/share/bootchart/svg/bootchart.svg.template` || die;
++my $process_svg_template = `cat /usr/share/bootchart/svg/process.svg.template` || die;
++my $svg_css_file = `cat /usr/share/bootchart/svg/style.css` || die;
+ $svg_css_file =~ s/^/\t\t\t/mg;
+ $svg_css_file =~ s/^\s+//g;
+
diff --git a/bootchart-grub2 b/bootchart-grub2
new file mode 100644
index 0000000..a417be3
--- /dev/null
+++ b/bootchart-grub2
@@ -0,0 +1,120 @@
+#! /bin/sh -e
+
+# update-grub helper script, modified for bootchart entry.
+# Copyright (C) 2006,2007,2008 Free Software Foundation, Inc.
+#
+# GRUB 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 3 of the License, or
+# (at your option) any later version.
+#
+# GRUB 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 GRUB. If not, see .
+
+prefix=/usr
+exec_prefix=/usr
+libdir=/usr/lib
+. ${libdir}/grub/grub-mkconfig_lib
+
+if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then
+ OS=GNU/Linux
+else
+ OS="${GRUB_DISTRIBUTOR} GNU/Linux"
+fi
+
+test_numeric ()
+{
+ local a=$1
+ local cmp=$2
+ local b=$3
+ if [ "$a" = "$b" ] ; then
+ case $cmp in
+ ge|eq|le) return 0 ;;
+ gt|lt) return 1 ;;
+ esac
+ fi
+ if [ "$cmp" = "lt" ] ; then
+ c=$a
+ a=$b
+ b=$c
+ fi
+ if (echo $a ; echo $b) | sort -n | head -n 1 | grep -qx $b ; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+test_gt ()
+{
+ local a=`echo $1 | sed -e "s/vmlinu[zx]-//g"`
+ local b=`echo $2 | sed -e "s/vmlinu[zx]-//g"`
+ local cmp=gt
+ if [ "x$b" = "x" ] ; then
+ return 0
+ fi
+ case $a:$b in
+ *.old:*.old) ;;
+ *.old:*) a=`echo -n $a | sed -e s/\.old$//g` ; cmp=gt ;;
+ *:*.old) b=`echo -n $b | sed -e s/\.old$//g` ; cmp=ge ;;
+ esac
+ test_numeric $a $cmp $b
+ return $?
+}
+
+find_latest ()
+{
+ local a=""
+ for i in $@ ; do
+ if test_gt "$i" "$a" ; then
+ a="$i"
+ fi
+ done
+ echo "$a"
+}
+
+list=`for i in /boot/vmlinu[xz]-* /vmlinu[xz]-* ; do
+ if grub_file_is_not_garbage "$i" ; then echo -n "$i " ; fi
+ done`
+
+while [ "x$list" != "x" ] ; do
+ linux=`find_latest $list`
+ echo "Found linux image: $linux" >&2
+ basename=`basename $linux`
+ dirname=`dirname $linux`
+ grub_dirname=`echo ${dirname} | sed -e "s%^/boot%${GRUB_DRIVE_BOOT}%g"`
+ version=`echo $basename | sed -e "s,^[^0-9]*-,,g"`
+ alt_version=`echo $version | sed -e "s,\.old$,,g"`
+
+ initrd=
+ for i in "initramfs.img-${version}" "initramfs-${version}.img" \
+ "initramfs.img-${alt_version}" "initramfs-${alt_version}.img"; do
+ if test -e "${dirname}/${i}" ; then
+ initrd="$i"
+ break
+ fi
+ done
+ if test -n "${initrd}" ; then
+ echo "Found initrd image: ${dirname}/${initrd}" >&2
+ fi
+
+ cat << EOF
+menuentry "${OS}, linux ${version} (bootchart)" {
+ linux ${grub_dirname}/${basename} root=${GRUB_DEVICE} ro ${GRUB_CMDLINE_LINUX} init=/sbin/bootchartd
+EOF
+ if test -n "${initrd}" ; then
+ cat << EOF
+ initrd ${grub_dirname}/${initrd}
+EOF
+ fi
+ cat << EOF
+}
+EOF
+
+ list=`echo $list | tr ' ' '\n' | grep -vx $linux | tr '\n' ' '`
+done
diff --git a/bootchart.sh b/bootchart.sh
new file mode 100644
index 0000000..e991be1
--- /dev/null
+++ b/bootchart.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# Bootchart shell script - use the web renderer for bootchart
+#
+# Copyright (c) 2006 by Stefano Cotta Ramusino
+
+if [ $# -eq 0 ]; then
+ format="png"
+ output_dir="."
+else
+ if [[ "$1" == "png" || "$1" == "svg" || "$1" == "eps" ]]; then
+ format=$1
+ else
+ echo "$0 : invalid format. Try: png, svg or eps"
+ exit 1
+ fi
+ if [[ -d "$2" ]]; then
+ output_dir=$2
+ else
+ echo "$0: output directory not found"
+ exit 1
+ fi
+fi
+
+bootchart_log="/var/log/bootchart.tgz"
+webrenderer_url="http://render.bootchart.org:8080/bootchart/render"
+
+if [ ! -f "$bootchart_log" ]; then
+ echo "$0: bootchart log not found."
+ echo "Try adding \`init=/sbin/bootchartd' to your kernel entries in grub configuration file"
+ exit 1
+fi
+
+if [ $format == "svg" ]; then
+ output_ext_file="svgz"
+else
+ if [ $format == "eps" ]; then
+ output_ext_file="eps.gz"
+ else
+ output_ext_file=$format
+ fi
+fi
+
+curl \
+ --form format=$format \
+ --form log=@$bootchart_log \
+ $webrenderer_url > $output_dir/bootchart.$output_ext_file
+
+exit $?
diff --git a/bootchart.spec b/bootchart.spec
new file mode 100644
index 0000000..5286894
--- /dev/null
+++ b/bootchart.spec
@@ -0,0 +1,203 @@
+Name: bootchart
+Version: 1.20
+Release: 1mamba
+Summary: Bootchart is a tool for performance analysis and visualization of the GNU/Linux boot process
+Group: System/Benchmarks
+Vendor: openmamba
+Distribution: openmamba
+Packager: Stefano Cotta Ramusino
+URL: http://www.bootchart.org/
+Source: http://foo-projects.org/~sofar/bootchart/bootchart-%{version}.tar.gz
+#Source: http://downloads.sourceforge.net/sourceforge/bootchart/bootchart-%{version}.tar.bz2
+Source1: http://zerodeux.net/misc/bootchart/bootchart_pl
+Source2: bootchart.sh
+Source3: kbootchart
+Source4: kbootchart.png
+Source5: bootchart-grub2
+Patch0: %{name}-0.9-nojavafunctions.patch
+Patch1: %{name}-0.9-svg_path.patch
+License: GPL
+Requires: curl
+Requires: %{name}-logger
+BuildRequires: apache-ant
+BuildRequires: jdk
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+
+%description
+Bootchart is a tool for performance analysis and visualization of the GNU/Linux boot process.
+Resource utilization and process information are collected during the boot process and are later rendered in a PNG, SVG or EPS encoded chart.
+Bootchart provides a shell script to be run by the kernel in the init phase.
+The script will run in background and collect process information, CPU statistics and disk usage statistics from the /proc file system.
+The performance data are stored in memory and are written to disk once the boot process completes.
+The boot log file is later processed using a Java application (or the web form) which builds the process tree and renders a performance chart in different formats: SVG e EPS.
+The chart can then be analyzed to examine process dependency and overall resource utilization.
+
+%package logger
+Summary: Boot logging script for %{name}
+Group: System/Kernel and Hardware
+
+%description logger
+Boot logging script for %{name}.
+
+%prep
+%setup -q
+#%patch0 -p1
+
+cp %{S:1} %{name}.pl
+%patch1 -p1
+
+%build
+#JAVA_HOME=%{_jvmdir}/jdk ANT_HOME=/opt/java/ant ant
+%configure
+%make
+
+%install
+[ "%{buildroot}" != / ] && rm -rf "%{buildroot}"
+%makeinstall
+
+## install scripts
+#install -D -m 755 script/%{name} \
+# %{buildroot}%{_bindir}/%{name}
+#install -D -m 755 %{name}.pl \
+# %{buildroot}%{_bindir}/%{name}.pl
+#install -D -m 755 %{S:2} \
+# %{buildroot}%{_bindir}/%{name}.sh
+#install -d %{buildroot}%{_datadir}/%{name}/svg
+#install -m 644 svg/* \
+# %{buildroot}%{_datadir}/%{name}/svg
+#
+## install java files
+#install -D -m 644 %{name}.jar \
+# %{buildroot}%{_javadir}/%{name}-%{version}.jar
+#ln -s %{name}-%{version}.jar %{buildroot}%{_javadir}/%{name}.jar
+#install -d -m 755 %{buildroot}%{_javadocdir}/%{name}-%{version}
+#cp -pr javadoc/* \
+# %{buildroot}%{_javadocdir}/%{name}-%{version}
+#ln -s %{name}-%{version} %{buildroot}%{_javadocdir}/%{name}
+
+## install logger
+#install -D -m 755 script/bootchartd \
+# %{buildroot}/sbin/bootchartd
+##install -D -m 644 script/bootchartd.conf \
+# %{buildroot}%{_sysconfdir}/bootchartd.conf
+#
+# install kde frontend
+install -D -m 755 %{S:3} \
+ %{buildroot}%{_bindir}/k%{name}
+install -D -m 644 %{S:4} \
+ %{buildroot}%{_datadir}/icons/crystalsvg/32x32/apps/k%{name}.png
+
+# install grub.d file
+install -D -m 0755 %{S:5} %{buildroot}%{_sysconfdir}/grub.d/15_bootchart
+
+# create KDE menu link
+install -d %{buildroot}/%{_datadir}/applications
+cat > %{buildroot}/%{_datadir}/applications/k%{name}.desktop </dev/null); do
+ if [ $(grep init=/sbin/%{name}d $i >> /dev/null; echo $?) -ne 0 ]; then
+ sed -i "s|^kernel\(.*\)|kernel \1 init=/sbin/%{name}d|" $i 2> /dev/null
+ fi
+ done
+ %endif
+ %if "%{_target_cpu}" == "ppc"
+ for i in $(ls %{_sysconfdir}/yaboot/conf.d/%{distribution}-* 2>/dev/null); do
+ if [ $(grep init=/sbin/%{name}d $i >> /dev/null; echo $?) -ne 0 ]; then
+ sed -i "s|^append=\"\(.*\)\"|append=\"\1 init=/sbin/%{name}d\"|" $i 2> /dev/null
+ fi
+ done
+ %endif
+fi
+
+if [ $1 -eq 1 ]; then
+# new install
+ %if "%{_target_cpu}" == "i586"
+ [ -x /usr/sbin/update-grub ] && /usr/sbin/update-grub
+ %endif
+ %if "%{_target_cpu}" == "ppc"
+ [ -x /usr/sbin/yaboot-update ] && /usr/sbin/yaboot-update
+ %endif
+fi
+exit 0
+
+%postun logger
+if [ $1 -eq 0 ]; then
+# uninstall
+%if "%{_target_cpu}" == "i586"
+for i in $(ls %{_sysconfdir}/grub/conf.d/%{distribution}-* 2>/dev/null); do
+ if [ $(grep init=/sbin/%{name}d $i >> /dev/null; echo $?) -eq 0 ]; then
+ sed -i "s|^kernel\(.*\) init=/sbin/%{name}d\(.*\)|kernel \1\2|" $i 2> /dev/null
+ fi
+done
+
+%endif
+%if "%{_target_cpu}" == "ppc"
+for i in $(ls %{_sysconfdir}/yaboot/conf.d/%{distribution}-* 2>/dev/null); do
+ if [ $(grep init=/sbin/%{name}d $i >> /dev/null; echo $?) -eq 0 ]; then
+ sed -i "s|^append=\"\(.*\) init=/sbin/%{name}d\(.*\)\"|append=\"\1\2\"|" $i 2> /dev/null
+ fi
+done
+%endif
+fi
+exit 0
+
+%files
+%defattr(-,root,root)
+%{_bindir}/kbootchart
+#%{_datadir}/%{name}/svg
+#%{_javadir}/*
+%{_datadir}/applications/k%{name}.desktop
+%{_datadir}/icons/crystalsvg/32x32/apps/k%{name}.png
+#%{_javadocdir}/*
+%doc COPYING README
+#%doc TODO
+
+%files logger
+%defattr(-,root,root)
+%{_sysconfdir}/grub.d/15_bootchart
+%{_sbindir}/bootchartd
+%{_datadir}/doc/bootchart/bootchartd.conf.example
+#%config(noreplace) %{_sysconfdir}/%{name}d.conf
+#%doc README.logger
+
+%changelog
+* Sat Dec 01 2012 Automatic Build System 1.20-1mamba
+- update to 1.20
+
+* Mon May 09 2011 Silvan Calarco 0.9-7mamba
+- updated grub2 configuration file to use grub-mkconfig_lib instead of deprecated update-grub_lib
+
+* Fri Feb 05 2010 Silvan Calarco 0.9-6mamba
+- rebuilt to remove executable requirements
+
+* Tue Oct 14 2008 Silvan Calarco 0.9-5mamba
+- updated for grub2
+
+* Tue Oct 14 2008 Silvan Calarco 0.9-4mamba
+- rebuilt
+
+* Mon Feb 19 2007 Silvan Calarco 0.9-3qilnx
+- add an exit 0 to scripts
+
+* Wed Nov 15 2006 Stefano Cotta Ramusino 0.9-2qilnx
+- added %%post and %%postun scriplets
+
+* Mon Jun 26 2006 Stefano Cotta Ramusino 0.9-1qilnx
+- package created by autospec
diff --git a/bootchart_pl b/bootchart_pl
new file mode 100644
index 0000000..63860d6
--- /dev/null
+++ b/bootchart_pl
@@ -0,0 +1,467 @@
+#!/usr/bin/perl -w
+
+# A basic perl port of the bootchart renderer, SVG only
+# (c) 2005 Vincent Caron
+
+# 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
+
+# BUGS
+# - ignore number of CPUs (disk util.)
+
+# 2005-06-29 - fixes
+# - process name is latest sample argv value
+# - sibling processes are properly PID-sorted from top to bottom
+# - process start is now parsed from proc_ps
+# - hiding bootchartd-related processes
+#
+# 2005-06-28
+# - initial release
+
+use strict;
+
+my %headers;
+my @disk_samples;
+my @cpu_samples;
+my %ps_info;
+my %ps_by_parent;
+
+my $chart_svg_template = `cat svg/bootchart.svg.template` || die;
+my $process_svg_template = `cat svg/process.svg.template` || die;
+my $svg_css_file = `cat svg/style.css` || die;
+$svg_css_file =~ s/^/\t\t\t/mg;
+$svg_css_file =~ s/^\s+//g;
+
+my $HZ = 100;
+my $min_img_w = 800;
+my $sec_w = 25;
+my $proc_h = 16;
+my $header_h = 300;
+my $img_w;
+my $img_h;
+my $t_begin;
+my $t_end;
+my $t_dur;
+
+
+sub parse_logs {
+ my $logfile = shift;
+ my $tmp = "/tmp/bootchart-$$";
+
+ mkdir $tmp or die;
+ system "cd $tmp && tar xzf $logfile";
+
+ parse_log_header($tmp);
+ parse_log_cpu($tmp);
+ parse_log_disk($tmp);
+ parse_log_ps($tmp);
+
+ system('rm', '-rf', $tmp);
+ print STDERR "Disk samples: ".scalar(@disk_samples)."\n";
+ print STDERR "CPU samples : ".scalar(@cpu_samples)."\n";
+ print STDERR "Processes : ".scalar(keys %ps_info)."\n";
+}
+
+sub parse_log_header {
+ my $tmp = shift;
+
+ open(HEADER, "<$tmp/header");
+ while () {
+ chomp;
+ my ($key, $val) = split /\s*=\s*/, $_, 2;
+ $headers{$key} = $val;
+ }
+ close HEADER;
+}
+
+sub parse_log_cpu {
+ my $tmp = shift;
+
+ # Reads in: time, user, system, iowait
+ my ($time, $ltime);
+ my (@timings, @ltimings);
+
+ open(PROC_STAT, "<$tmp/proc_stat.log");
+ while () {
+ chomp;
+ $time = $1, next if /^(\d+)$/;
+
+ if (/^$/) {
+ if (defined $ltime) {
+ my $dtime = ($time - $ltime) / $HZ; # in seconds
+ my $user = ($timings[0] + $timings[1]) - ($ltimings[0] + $ltimings[1]);
+ my $system = ($timings[2] + $timings[5] + $timings[6]) - ($ltimings[2] + $ltimings[5] + $ltimings[6]);
+ my $idle = $timings[3] - $ltimings[3];
+ my $iowait = $timings[4] - $ltimings[4];
+ my $sum = $user + $system + $idle + $iowait;
+ my %sample;
+ $sample{time} = $time;
+ $sample{user} = $user / $sum;
+ $sample{system} = $system / $sum;
+ $sample{iowait} = $iowait / $sum;
+ push @cpu_samples, \%sample;
+ }
+
+ $ltime = $time;
+ @ltimings = @timings;
+ next;
+ }
+
+ @timings = split /\s+/ if s/^cpu\s+//;
+ }
+ close(PROC_STAT);
+}
+
+sub parse_log_disk {
+ my $tmp = shift;
+
+ # Reads in: time, read, write, use
+ my ($time, $ltime);
+ my ($read, $write, $util) = (0, 0, 0);
+ my ($lread, $lwrite, $lutil);
+
+ open(DISK_STAT, "<$tmp/proc_diskstats.log");
+ while () {
+ chomp;
+ $time = $1, next if /^(\d+)$/;
+
+ if (/^$/) {
+ if (defined $ltime) {
+ my $dtime = ($time - $ltime) / $HZ; # in seconds
+ my $dutil = ($util - $lutil) / (1000 * $dtime);
+
+ my %sample;
+ $sample{time} = $time;
+ $sample{read} = ($read - $lread) / 2 / $dtime; # in KB/s
+ $sample{write} = ($write - $lwrite) / 2 / $dtime; # in KB/s
+ $sample{util} = ($dutil > 1 ? 1 : $dutil);
+ push @disk_samples, \%sample;
+ }
+
+ $ltime = $time;
+ $lread = $read, $read = 0;
+ $lwrite = $write, $write = 0;
+ $lutil = $util, $util = 0;
+ next;
+ }
+
+ s/\s+//;
+ my @tokens = split /\s+/;
+ next if scalar(@tokens) != 14 || not $tokens[2] =~ /hd|sd/;
+
+ $read += $tokens[5];
+ $write += $tokens[9];
+ $util += $tokens[12];
+ }
+ close(DISK_STAT);
+}
+
+sub parse_log_ps {
+ my $tmp = shift;
+
+ my $time;
+
+ open(PS_STAT, "<$tmp/proc_ps.log");
+ while () {
+ chomp;
+ next if /^$/;
+ $time = $1, next if /^(\d+)$/;
+
+ my @tokens = split /\s+/;
+ my $pid = $tokens[0];
+
+ if (!defined $ps_info{$pid}) {
+ my %info;
+ my @empty;
+ my $ppid = $tokens[3];
+ my $start = $tokens[21];
+ $t_begin = $start if !defined $t_begin || $t_begin > $start;
+
+ $info{ppid} = $ppid;
+ $info{start} = $start;
+ $info{samples} = \@empty;
+ $ps_info{$pid} = \%info;
+
+ if (!defined $ps_by_parent{$ppid}) {
+ my @pidlist = ($pid);
+ $ps_by_parent{$ppid} = \@pidlist;
+ } else {
+ push @{$ps_by_parent{$ppid}}, $pid;
+ }
+ }
+
+ # argv may change, we'll store here the last sampled value
+ my $comm = $tokens[1];
+ $comm =~ s/[()]//g;
+ $ps_info{$pid}->{comm} = $comm;
+
+ my %sample;
+ $sample{time} = $time;
+ $sample{state} = $tokens[2];
+ $sample{sys} = $tokens[13];
+ $sample{user} = $tokens[14];
+ push @{$ps_info{$pid}->{samples}}, \%sample;
+ }
+ close PS_STAT;
+}
+
+sub unxml {
+ my $x = shift;
+ $x =~ s/</g;
+ $x =~ s/>/>/g;
+
+ return $x;
+}
+
+sub render_svg {
+ my $t_init = $cpu_samples[0]->{time};
+ $t_end = $cpu_samples[-1]->{time};
+ $t_dur = $t_end - $t_begin;
+ $img_w = $t_dur / $HZ * $sec_w;
+
+ my @subst;
+ $subst[1] = $svg_css_file;
+
+ #
+ # Headers
+ #
+
+ my $cpu = $headers{'system.cpu'};
+ $cpu =~ s/model name\s*:\s*//;
+ $subst[2] = unxml($headers{title});
+ $subst[3] = "uname: ".unxml($headers{'system.uname'});
+ $subst[4] = "release: ".unxml($headers{'system.release'});
+ $subst[5] = "CPU: ".unxml($cpu);
+ $subst[6] = "kernel options: ".unxml($headers{'system.kernel.options'});
+ $subst[7] = "boot time: ".int($t_end / $HZ)." seconds";
+
+ #
+ # CPU I/O chart
+ #
+
+ my $bar_h = 50;
+ my $cpu_ticks = '';
+ for (my $i = 0; $i < $t_dur / $HZ; $i++) {
+ my $x = $i * $sec_w;
+ $cpu_ticks .= "\n";
+ }
+ $cpu_ticks =~ s/^/\t\t\t/mg; $cpu_ticks =~ s/^\s+//g;
+
+ my $io_points = '';
+ my $cpu_points = '';
+
+ if (@cpu_samples) {
+ my $last_x = 0;
+
+ for (@cpu_samples) {
+ my $time = $_->{time};
+ my $iowait = $_->{iowait};
+ my $usersys = $_->{user} + $_->{system};
+
+ my $pos_x = int(($time - $t_begin) * $img_w / $t_dur);
+ my $pos_y = int($bar_h - ($usersys + $iowait) * $bar_h);
+ $io_points .= "$pos_x,$bar_h" if $io_points eq '';
+ $io_points .= " $pos_x,$pos_y";
+
+ $pos_y = int($bar_h - $usersys * $bar_h);
+ $cpu_points .= "$pos_x,$bar_h" if $cpu_points eq '';
+ $cpu_points .= " $pos_x,$pos_y";
+
+ $last_x = $pos_x;
+ }
+ $io_points .= " $last_x,$bar_h";
+ $cpu_points .= " $last_x,$bar_h";
+ }
+
+ $subst[8] = $cpu_ticks;
+ $subst[9] = $io_points;
+ $subst[10] = $cpu_points;
+
+ #
+ # Disk usage chart
+ #
+
+ my $util_points = '';
+ my $read_points = '';
+
+ if (@disk_samples) {
+ my $max_tput = 0;
+ my $max_tput_label = '';
+ for (@disk_samples) {
+ my $put = $_->{read} + $_->{write};
+ $max_tput = $put if $put > $max_tput;
+ }
+
+ my $last_x = 0;
+ my $last_y = 0;
+
+ for (@disk_samples) {
+ my $time = $_->{time};
+ my $read = $_->{read};
+ my $write = $_->{write};
+ my $util = $_->{util};
+
+ my $pos_x = int(($time - $t_begin) * $img_w / $t_dur);
+ my $pos_y = int($bar_h - $util * $bar_h);
+ $util_points .= "$pos_x,$bar_h" if $util_points eq '';
+ $util_points .= " $pos_x,$pos_y";
+
+ $pos_y = int($bar_h - ($read + $write) / $max_tput * $bar_h);
+ if ($read_points ne '') {
+ $read_points .= "\t\t\t";
+ }
+ $read_points .= "\n";
+
+ if ($max_tput_label eq '' && $read + $write == $max_tput) {
+ my $label = int($max_tput / 1024)." MB/s";
+ $max_tput_label = "\t\t\t$label\n";
+ }
+
+ $last_x = $pos_x;
+ $last_y = $pos_y;
+ }
+ $util_points .= " $last_x,$bar_h";
+ $read_points .= $max_tput_label;
+ }
+
+ $subst[11] = $cpu_ticks;
+ $subst[12] = $util_points;
+ $subst[13] = $read_points;
+ $subst[14] = ''; # open_points, no openfile parser implemented
+
+ #
+ # Process tree
+ #
+
+ my $tree_h = scalar(@cpu_samples) * $proc_h;
+ my $axis = '';
+ my $proc_ticks = '';
+ for (my $i = 0; $i < $t_dur / $HZ; $i++) {
+ my $x = $i * $sec_w;
+ $proc_ticks .= "\n";
+ if ($i > 0 && $i % 5 == 0) {
+ my $label = $i.'s';
+ $axis .= "$label\n";
+ }
+ }
+ $proc_ticks =~ s/^/\t\t\t/mg; $proc_ticks =~ s/^\s+//g;
+ $axis =~ s/^/\t\t\t/mg; $axis =~ s/^\s+//g;
+
+ my $proc_tree = '';
+ my @init = (1);
+ my $ps_count = 0;
+ $proc_tree = render_proc_tree(\@init, \$ps_count);
+
+ $subst[15] = $axis;
+ $subst[16] = $proc_ticks;
+ $subst[17] = $proc_tree;
+
+ $img_h = $proc_h * $ps_count + $header_h;
+ $subst[0] = 'width="'.($img_w > $min_img_w ? $img_w : $min_img_w).'px" height="'.($img_h + 1).'px"';
+
+ print template_subst($chart_svg_template, @subst);
+}
+
+sub render_proc_tree {
+ my $ps_list = shift;
+ my $ps_count = shift;
+ my $level = shift || 0;
+
+ my $proc_tree = '';
+ for (sort {$a <=> $b} @{$ps_list}) {
+ # Hide bootchartd and its children processes
+ next if $ps_info{$_}->{comm} eq 'bootchartd';
+
+ my $children = $ps_by_parent{$_};
+ $proc_tree .= render_proc($_, $ps_count, $level);
+ $proc_tree .= render_proc_tree($children, $ps_count, $level + 1) if defined $children;
+ }
+ return $proc_tree;
+}
+
+sub render_proc {
+ my $pid = shift;
+ my $ps_count = shift;
+ my $level = shift;
+
+ my $info = $ps_info{$pid};
+ return '' if defined $info->{done}; # Draw once
+
+ my @samples = @{$info->{samples}};
+ return '' if scalar(@samples) < 2; # Skip processes with only 1 sample
+
+ my $p_begin = ($pid == 1) ? $samples[0]->{time} : $info->{start};
+ my $p_period = $samples[1]->{time} - $p_begin;
+ my $p_end = $samples[-1]->{time};
+ my $p_dur = $p_end - $p_begin + $p_period;
+
+ my $x = ($p_begin - $t_begin) * $sec_w / $HZ;
+ my $y = ${$ps_count} * $proc_h;
+ my $w = $p_dur * $sec_w / $HZ;
+
+ my $position = "$x,$y";
+ my $border = "width=\"$w\" height=\"$proc_h\"";
+ my $timeline = "\n";
+
+ my ($last_tx, $last_sample);
+ for my $sample (@samples) {
+ my $time = $sample->{time};
+ my $state = $sample->{state};
+ my $tx = ($time - $p_begin) * $sec_w / $HZ;
+ $tx = 0 if $tx < 0;
+
+ if (defined $last_sample) {
+ my $tw = $tx - $last_tx;
+
+ if ($state ne 'S') {
+ my $sys = ($sample->{sys} - $last_sample->{sys}) / $HZ;
+ my $user = ($sample->{user} - $last_sample->{user}) / $HZ;
+
+ my %class = ('D' => 'UnintSleep', 'R' => 'Running', 'T' => 'Traced', 'Z' => 'Zombie');
+ my $opacity = ($state eq 'R') ? ' fill-opacity="'.($sys + $user).'"' : '';
+ $timeline .= "\n";
+ }
+ }
+
+ $last_tx = $tx;
+ $last_sample = $sample;
+ }
+ $timeline =~ s/^/\t\t/mg; $timeline =~ s/^\s+//g;
+
+ my $label = $info->{comm};
+ my $label_pos = ($w < 200 && ($x + $w + 200) < $img_w) ?
+ "dx=\"2\" dy=\"".($proc_h - 1)."\" x=\"".($w + 1)."\" y=\"0\"" :
+ "dx=\"".($w / 2)."\" dy=\"".($proc_h - 1)."\" x=\"0\" y=\"0\"";
+
+ my @subst = ($position, $timeline, $border, '', $label_pos, $label, '');
+
+ $info->{done} = 1;
+ ${$ps_count}++;
+ return template_subst($process_svg_template, @subst);
+}
+
+sub template_subst {
+ my $template = shift;
+
+ my $i = 0;
+ for(@_) {
+ $template =~ s/\Q{$i}\E/$_[$i]/g;
+ $i++;
+ }
+ return $template;
+}
+
+
+parse_logs('/var/log/bootchart.tgz');
+render_svg();
diff --git a/kbootchart b/kbootchart
new file mode 100644
index 0000000..ab4cbb3
--- /dev/null
+++ b/kbootchart
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# kbootchart - kde frontend for bootchart
+#
+# Copyright (c) 2006 by Stefano Cotta Ramusino
+
+lang="java"
+
+if [ -z "$JAVA_HOME" ]; then
+ JRE_HOME="/usr/java/jre"
+ JDK_HOME="/usr/java/jdk"
+ if [ -x "$JRE_HOME/bin/java" ]; then
+ JAVA_HOME=$JRE_HOME
+ elif [ -x "$JDK_HOME/bin/java" ]; then
+ JAVA_HOME=$JDK_HOME
+ elif [[ $(host www.qilinux.org &> /dev/null; echo $?) -eq 0 ]]; then
+ lang="bash"
+ elif [ -x "$(which perl 2> /dev/null)" ]; then
+ lang="perl"
+ else
+ kdialog --title "KBootchart" \
+ --icon $(basename $0) \
+ --error "Perl is missing in your system.\nTo create the bootchart image you need to install perl."
+ exit 1
+ fi
+fi
+
+if [[ "$lang" == "java" || "$lang" == "bash" ]]; then
+ format="$(kdialog --title "KBootchart" \
+ --icon $(basename $0) \
+ --radiolist "Select the output image format:" \
+ png "PNG" on svg "SVG" off eps "EPS" off)"
+
+ if [ -z "$format" ]; then
+ exit 1
+ fi
+else
+ format="svg"
+fi
+
+output_dir="$(kdialog --title "Select the directory where the image will be created" \
+ --icon $(basename $0) \
+ --getexistingdirectory ~)"
+
+if [ -z "$output_dir" ]; then
+ exit 1
+fi
+
+if [ $format == "svg" && $lang != "perl" ]]; then
+ output_ext_file="svgz"
+else
+ if [ $format == "eps" ]; then
+ output_ext_file="eps.gz"
+ else
+ output_ext_file=$format
+ fi
+fi
+
+if [ "$lang" == "java" ]; then
+ PATH=$JAVA_HOME/bin:$PATH bootchart -f $format -o $output_dir &> /dev/null
+elif [ "$lang" == "bash" ]; then
+ bootchart.sh $format $output_dir &> /dev/null
+else
+ bootchart.pl > $output_dir/bootchart.$output_ext_file 2> /dev/null
+fi
+
+if [ $? -ne 0 ]; then
+ kdialog --title "KBootchart" \
+ --icon $(basename $0) \
+ --error "Error during creation of image file."
+ exit 1
+fi
+
+kdialog --title "KBootchart" \
+ --icon $(basename $0) \
+ --msgbox "File bootchart.$output_ext_file successfully created"
+
+exit $?
diff --git a/kbootchart.png b/kbootchart.png
new file mode 100644
index 0000000..c735df6
Binary files /dev/null and b/kbootchart.png differ