Hello community,
here is the log from the commit of package bootchart for openSUSE:Factory
checked in at Thu Oct 28 15:32:45 CEST 2010.
--------
--- bootchart/bootchart.changes 2010-07-15 13:34:16.000000000 +0200
+++ bootchart/bootchart.changes 2010-10-27 10:53:56.000000000 +0200
@@ -1,0 +2,19 @@
+Wed Oct 27 08:52:23 UTC 2010 - aj@suse.de
+
+- Use RPM_OPT_FLAGS for building.
+
+-------------------------------------------------------------------
+Wed Oct 27 08:49:41 UTC 2010 - aj@suse.de
+
+- Update to bootchart2 0.12.4:
+ o various bugfixes
+ o display memory usage
+ o includes pthread patch.
+
+-------------------------------------------------------------------
+Wed Oct 27 08:44:17 UTC 2010 - aj@suse.de
+
+- Disable initrd support for now as the system will not boot with
+ it (bnc#649512).
+
+-------------------------------------------------------------------
calling whatdependson for head-i586
Old:
----
bootchart-pthread.patch
bootchart2-0.12.1.tar.bz2
New:
----
bootchart2-0.12.4.tar.bz2
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ bootchart.spec ++++++
--- /var/tmp/diff_new_pack.5Ph6GV/_old 2010-10-28 15:31:00.000000000 +0200
+++ /var/tmp/diff_new_pack.5Ph6GV/_new 2010-10-28 15:31:00.000000000 +0200
@@ -1,5 +1,5 @@
#
-# spec file for package bootchart (Version 2.0.12.1)
+# spec file for package bootchart (Version 2.0.12.4)
#
# Copyright (c) 2010 SUSE LINUX Products GmbH, Nuernberg, Germany.
#
@@ -17,7 +17,7 @@
# norootforbuild
-%define real_bootchart2_version 0.12.1
+%define real_bootchart2_version 0.12.4
%if 0%{?suse_version}
%define system_requires lsb-release python-cairo python-gtk
%else
@@ -39,7 +39,6 @@
Source: bootchart2-%{real_bootchart2_version}.tar.bz2
Source3: mkinitrd-boot.sh
Source4: mkinitrd-setup.sh
-Patch0: bootchart-pthread.patch
AutoReqProv: on
Requires: %system_requires
Requires(post): mkinitrd
@@ -50,43 +49,43 @@
%prep
%setup -q -n bootchart2-%{real_bootchart2_version}
-%patch0 -p1
%build
export PY_LIBDIR=%py_libdir
export PY_SITEDIR=%py_sitedir
-make
+make CFLAGS="$RPM_OPT_FLAGS"
%install
export PY_LIBDIR=%py_libdir
export PY_SITEDIR=%py_sitedir
make install DESTDIR=$RPM_BUILD_ROOT
# SUSE specific bits.
-install -m 755 -D %SOURCE3 $RPM_BUILD_ROOT/lib/mkinitrd/scripts/boot-bootchart.sh
-install -m 755 -D %SOURCE4 $RPM_BUILD_ROOT/lib/mkinitrd/scripts/setup-bootchart.sh
+## Remove for now since they are broken, see bnc#
+#install -m 755 -D %SOURCE3 $RPM_BUILD_ROOT/lib/mkinitrd/scripts/boot-bootchart.sh
+#install -m 755 -D %SOURCE4 $RPM_BUILD_ROOT/lib/mkinitrd/scripts/setup-bootchart.sh
%fdupes %{buildroot}/%{_datadir}
-%post
-[ -x /sbin/mkinitrd_setup ] && mkinitrd_setup
-if [ "$YAST_IS_RUNNING" != "instsys" -a \
- ! -e /.buildenv -a \
- -x /sbin/update-bootloader -a \
- -x /sbin/mkinitrd ]; then
- /sbin/mkinitrd
- /sbin/update-bootloader --refresh
-fi
-exit 0
-
-%postun
-[ -x /sbin/mkinitrd_setup ] && mkinitrd_setup
-if [ "$YAST_IS_RUNNING" != "instsys" -a \
- ! -e /.buildenv -a \
- -x /sbin/update-bootloader -a \
- -x /sbin/mkinitrd ]; then
- /sbin/mkinitrd
- /sbin/update-bootloader --refresh
-fi
-exit 0
+#%post
+#[ -x /sbin/mkinitrd_setup ] && mkinitrd_setup
+#if [ "$YAST_IS_RUNNING" != "instsys" -a \
+# ! -e /.buildenv -a \
+# -x /sbin/update-bootloader -a \
+# -x /sbin/mkinitrd ]; then
+# /sbin/mkinitrd
+# /sbin/update-bootloader --refresh
+#fi
+#exit 0
+
+#%postun
+#[ -x /sbin/mkinitrd_setup ] && mkinitrd_setup
+#if [ "$YAST_IS_RUNNING" != "instsys" -a \
+# ! -e /.buildenv -a \
+# -x /sbin/update-bootloader -a \
+# -x /sbin/mkinitrd ]; then
+# /sbin/mkinitrd
+# /sbin/update-bootloader --refresh
+#fi
+#exit 0
%clean
rm -rf $RPM_BUILD_ROOT
@@ -100,7 +99,7 @@
/sbin/*
/usr/bin/*
%config(noreplace) /etc/bootchartd.conf
-/lib/mkinitrd
+#/lib/mkinitrd
%py_sitedir/pybootchartgui
%changelog
++++++ bootchart2-0.12.1.tar.bz2 -> bootchart2-0.12.4.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/AUTHORS new/bootchart2-0.12.4/AUTHORS
--- old/bootchart2-0.12.1/AUTHORS 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/AUTHORS 2010-10-24 16:26:26.000000000 +0200
@@ -1,9 +1,10 @@
-Anders Norgaard
Michael Meeks
+Anders Norgaard
Scott James Remnant
Henning Niss
Contributors:
+ Riccardo Magliocchetti
Brian Ewins
Based on work by:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/Makefile new/bootchart2-0.12.4/Makefile
--- old/bootchart2-0.12.1/Makefile 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/Makefile 2010-10-24 16:26:26.000000000 +0200
@@ -1,9 +1,9 @@
-VER=0.12.1
+VER=0.12.4
PKG_NAME=bootchart2
PKG_TARBALL=$(PKG_NAME)-$(VER).tar.bz2
-CC = gcc
-CFLAGS = -g -Wall -O0
+CC ?= gcc
+CFLAGS ?= -g -Wall -O0
BINDIR ?= /usr/bin
PY_LIBDIR ?= /usr/lib/python2.6
@@ -17,15 +17,18 @@
collector/tasks-netlink.o \
collector/dump.o
-all: bootchart-collector
+all: bootchart-collector pybootchartgui/main.py
%.o:%.c
- $(CC) $(CFLAGS) -DVERSION=\"$(VER)\" -c $^ -o $@
+ $(CC) $(CFLAGS) -pthread -DVERSION=\"$(VER)\" -c $^ -o $@
bootchart-collector: $(COLLECTOR)
- $(CC) -lpthread -Icollector -o $@ $^
+ $(CC) -pthread -Icollector -o $@ $^
-py-install-compile:
+pybootchartgui/main.py: pybootchartgui/main.py.in
+ sed -s "s/@VER@/$(VER)/g" $^ > $@
+
+py-install-compile: pybootchartgui/main.py
install -d $(DESTDIR)$(PY_SITEDIR)/pybootchartgui
cp pybootchartgui/*.py $(DESTDIR)$(PY_SITEDIR)/pybootchartgui
install -D -m 755 pybootchartgui.py $(DESTDIR)$(BINDIR)/pybootchartgui
@@ -42,10 +45,10 @@
install -m 755 -D bootchart-collector $(DESTDIR)/lib/bootchart/bootchart-collector
install: all py-install-compile install-collector
- mkdir -p $RPM_BUILD_ROOT/lib/bootchart/mnt
clean:
- -rm -f bootchart-collector bootchart-collector-dynamic collector/*.o
+ -rm -f bootchart-collector bootchart-collector-dynamic \
+ collector/*.o pybootchartgui/main.py
dist:
COMMIT_HASH=`git show-ref -s -h | head -n 1` ; \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/NEWS new/bootchart2-0.12.4/NEWS
--- old/bootchart2-0.12.1/NEWS 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/NEWS 2010-10-24 16:26:26.000000000 +0200
@@ -1,3 +1,36 @@
+bootchart2 0.12.4:
+ + bootchartd
+ + reduce overhead caused by pidof (Riccardo Magliocchetti)
+ + collector
+ + attempt to retry ptrace to avoid bogus ENOSYS (Michael)
+ + add meminfo polling (Dave Martin)
+ + pybootchartgui
+ + handle dmesg timestamps with big delta (Riccardo)
+ + avoid divide by zero when rendering I/O utilization (Riccardo)
+ + add process grouping in the cumulative chart (Riccardo)
+ + fix cpu time calculation in cumulative chart (Riccardo)
+ + get i/o statistics for flash based devices (Riccardo)
+ + prettier coloring for the cumulative graphs (Michael)
+ + fix interactive CPU rendering (Michael)
+ + render memory usage graph (Dave Martin)
+
+bootchart2 0.12.3
+ + collector
+ + pclose after popen (Riccardo Magliocchetti (xrmx))
+ + fix buffer overflow (xrmx)
+ + count 'processor:' in /proc/cpuinfo for ARM (Michael)
+ + get model name from that line too for ARM (xrmx)
+ + store /proc/cpuinfo in the boot-chart archive (xrmx)
+ + try harder to detect missing TASKSTATS (Michael)
+ + sanity-check invalid domain names (Michael)
+ + detect missing PROC_EVENTS more reliably (Michael)
+ + README fixes (xrmx, Michael)
+ + pybootchartgui
+ + make num_cpu parsing robust (Michael)
+
+bootchart2 0.12.2
+ + fix pthread compile / linking bug
+
bootchart2 0.12.1
+ pybootchartgui
+ pylint cleanup
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/README new/bootchart2-0.12.4/README
--- old/bootchart2-0.12.1/README 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/README 2010-10-24 16:26:26.000000000 +0200
@@ -30,6 +30,10 @@
chart rendering tool. If you want to chart the initrd, add
rdinitrd=/sbin/bootchartd to the kernel command-line.
+ To make bootchart2 work best, please ensure your kernel is
+configured with CONFIG_PROC_EVENTS=y and CONFIG_TASKSTATS=y, without
+these we are slower, less accurate, and produce an uglier task
+hierarchy.
Why bootchart2 ?
------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/TODO new/bootchart2-0.12.4/TODO
--- old/bootchart2-0.12.1/TODO 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/TODO 2010-10-24 16:26:26.000000000 +0200
@@ -3,6 +3,10 @@
* FIXME: are we merging / glupping threads properly ?
+ how can sreadahead apparently be doing no I/O ? :-)
+ + extend rectangles in rendered output to note when
+ processes truly exit to improve duration rendering.
+ + expand the tasks API for this.
+
* Tracing metrics:
+ traced %d processes of $$ (!) :-)
+ consumed %d seconds of CPU [ render these ... ]
@@ -18,10 +22,6 @@
+ fix cumulative graphing - and boundary box bits
- + fix for profiling a running system ... [ get the origin right ! ]
- + the renderer (or collector ?) should subtract the initial
- times of each process for the cumulative graph ...
-
+ re-write ever more of bootchartd from shell into C ...
+ make the --dump thing return a success status,
+ poll the remote process (with ptrace ? - hmm ...)
@@ -30,11 +30,11 @@
+ hmm.
+ have a 20 second delay before starting to poll it ?
- + extend logs to to note when processes truly exit to improve
- duration rendering.
-
** Features:
+Add some tabs to the interactive renderer to show the raw files:
+kernel dmesg, header, and perhaps the full (processed) cmdline2 log
+
Use the same stats that Intel's tracer uses of I/O / scheduling foo ...
+ use them to avoid taskstats polling if there has been
no scheduled timeslice (?)
@@ -73,3 +73,9 @@
Notes:
+ SUSE initrd debugging; use "'shell=1'" - is dead good ...
+
+
+
+#18a303 - Tango / Green
+
+#2da31b - to same saturation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/bootchartd new/bootchart2-0.12.4/bootchartd
--- old/bootchart2-0.12.1/bootchartd 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/bootchartd 2010-10-24 16:26:26.000000000 +0200
@@ -80,9 +80,6 @@
# The processes we have to wait for
local exit_proc="kdm_greet xterm konsole gnome-terminal metacity mutter compiz ldm icewm-session"
- # Don't exit until *all* the mandatory procs are running. Only used if exit_proc
- # is unset.
- local mandatory_procs="nautilus mutter"
# Wait for /proc first - without it we have issues
while [ ! -e /proc/cmdline ]; do
@@ -105,7 +102,7 @@
stop
return
fi
- $USLEEP 200000
+ $USLEEP 1000000
done;
}
@@ -151,7 +148,7 @@
echo "Starting bootchart logging"
# Are we running in the initrd ?
- if [ ! -e /dev/random ]; then
+ if [ -x /init -o -x /linuxrc ]; then
IN_INITRD="yes"
start &
else # running inside the main system
@@ -200,7 +197,7 @@
stop
;;
*)
- echo $"Usage: $0 {init|start|stop}"
+ echo "Usage: $0 {init|start|stop}"
;;
esac
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/collector/collector.c new/bootchart2-0.12.4/collector/collector.c
--- old/bootchart2-0.12.1/collector/collector.c 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/collector/collector.c 2010-10-24 16:26:26.000000000 +0200
@@ -454,7 +454,7 @@
rep_len = recv(sd, &ans, sizeof(ans), 0);
if (ans.n.nlmsg_type == NLMSG_ERROR ||
(rep_len < 0) || !NLMSG_OK((&ans.n), rep_len))
- return 0;
+ return -1;
na = (struct nlattr *) GENLMSG_DATA(&ans);
na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len));
@@ -481,7 +481,7 @@
netlink_taskstats_id = get_family_id (netlink_socket);
- return 1;
+ return netlink_taskstats_id > 0;
error:
if (netlink_socket >= 0)
close (netlink_socket);
@@ -778,13 +778,13 @@
Arguments args;
int i, use_taskstat;
int in_initrd, clean_environment = 1;
- int stat_fd, disk_fd, uptime_fd, pid, ret = 1;
+ int stat_fd, disk_fd, uptime_fd, meminfo_fd, pid, ret = 1;
PidScanner *scanner = NULL;
unsigned long reltime = 0;
- BufferFile *stat_file, *disk_file, *per_pid_file;
+ BufferFile *stat_file, *disk_file, *per_pid_file, *meminfo_file;
PidEventClosure pid_ev_cl;
- int *fds[] = { &stat_fd, &disk_fd, &uptime_fd, NULL };
- const char *fd_names[] = { "/stat", "/diskstats", "/uptime", NULL };
+ int *fds[] = { &stat_fd, &disk_fd, &uptime_fd, &meminfo_fd, NULL };
+ const char *fd_names[] = { "/stat", "/diskstats", "/uptime", "/meminfo", NULL };
StackMap map = STACK_MAP_INIT; /* make me findable */
arguments_set_defaults (&args);
@@ -859,10 +859,11 @@
per_pid_file = buffer_file_new (&map, "taskstats.log");
else
per_pid_file = buffer_file_new (&map, "proc_ps.log");
+ meminfo_file = buffer_file_new (&map, "proc_meminfo.log");
pid_ev_cl.cmdline_file = buffer_file_new (&map, "cmdline2.log");
pid_ev_cl.paternity_file = buffer_file_new (&map, "paternity.log");
- if (!stat_file || !disk_file || !per_pid_file ||
+ if (!stat_file || !disk_file || !per_pid_file || !meminfo_file ||
!pid_ev_cl.cmdline_file || !pid_ev_cl.paternity_file) {
fprintf (stderr, "Error allocating output buffers\n");
return 1;
@@ -904,6 +905,7 @@
buffer_file_dump_frame_with_timestamp (stat_file, stat_fd, uptime, uptimelen);
buffer_file_dump_frame_with_timestamp (disk_file, disk_fd, uptime, uptimelen);
+ buffer_file_dump_frame_with_timestamp (meminfo_file, meminfo_fd, uptime, uptimelen);
/* output data for each pid */
buffer_file_append (per_pid_file, uptime, uptimelen);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/collector/dump.c new/bootchart2-0.12.4/collector/dump.c
--- old/bootchart2-0.12.1/collector/dump.c 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/collector/dump.c 2010-10-24 16:26:26.000000000 +0200
@@ -38,7 +38,7 @@
snprintf (buffer, 1024, "/proc/%d/maps", s->pid);
maps = fopen (buffer, "r");
- while (!result && fgets (buffer, 4096, maps)) {
+ while (!result && fgets (buffer, 1024, maps)) {
char *p, *copy;
size_t start, end, toread, read_bytes;
@@ -120,10 +120,10 @@
* wait a while hoping it exits (so we can
* cleanup after it).
*/
-static void
-close_wait_pid (DumpState *s, int avoid_kill)
+static int
+close_pid (DumpState *s, int avoid_kill)
{
- int i;
+ int pid;
if (!avoid_kill && ptrace (PTRACE_KILL, s->pid, 0, 0))
fprintf (stderr, "failed to ptrace_kill pid %d: %s\n",
@@ -133,17 +133,28 @@
ptrace (PTRACE_DETACH, s->pid, 0, 0);
close (s->mem);
+ pid = s->pid;
+ free (s);
+
+ return pid;
+}
+
+static void
+close_wait_pid (DumpState *s, int avoid_kill)
+{
+ int i, pid;
+ pid = close_pid (s, avoid_kill);
+ /* 's' invalid */
+
/* wait at most second max */
for (i = 0; i < 100; i++) {
char buffer[1024];
- sprintf (buffer, PROC_PATH "/%d/cmdline", s->pid);
+ sprintf (buffer, PROC_PATH "/%d/cmdline", pid);
if (access (buffer, R_OK))
break;
usleep (10 * 1000);
}
-
- free (s);
}
static void dump_buffers (DumpState *s)
@@ -181,7 +192,7 @@
int
buffers_extract_and_dump (const char *output_path, Arguments *remote_args)
{
- int pid, ret = 0;
+ int i, pid, ret = 0;
DumpState *state;
chdir (output_path);
@@ -193,16 +204,24 @@
}
fprintf (stderr, "Extracting profile data from pid %d\n", pid);
- if (!(state = open_pid (pid)))
- return 1;
-
- if (find_chunks (state)) {
- fprintf (stderr, "Couldn't find state structures on pid %d's stack\n", pid);
- ret = 1;
- } else
- dump_buffers (state);
-
- close_wait_pid (state, ret);
+ /* the kernel for reasons of it's own really likes to return
+ ESRCH - No such process from pread randomly, so retry a bit */
+ for (i = 0; i < 8; i++) {
+ if (!(state = open_pid (pid)))
+ return 1;
+
+ if (find_chunks (state)) {
+ ret = 1;
+ fprintf (stderr, "Couldn't find state structures on pid %d's stack%s\n",
+ pid, i < 7 ? ", retrying" : " aborting");
+ close_pid (state, 1);
+ } else {
+ ret = 0;
+ dump_buffers (state);
+ close_wait_pid (state, 0);
+ break;
+ }
+ }
return ret;
}
@@ -342,6 +361,17 @@
return 0;
}
+/* sane ASCII chars only please */
+static void
+rewrite_ascii (char *string)
+{
+ char *p;
+ for (p = string; *p; p++) {
+ if (!isgraph (*p) && !isblank (*p))
+ *p = '.';
+ }
+}
+
int
dump_header (const char *output_path)
{
@@ -361,8 +391,8 @@
{
time_t now;
- char host_buf[4096];
- char domain_buf[2048];
+ char host_buf[4096] = { '\0' };
+ char domain_buf[2048] = { '\0' };
char time_buf[128];
if (!gethostname (host_buf, 2047) &&
@@ -374,6 +404,8 @@
} else
strcpy (host_buf, "unknown");
+ rewrite_ascii (host_buf);
+
now = time (NULL);
ctime_r (&now, time_buf);
if (strrchr (time_buf, '\n'))
@@ -402,26 +434,43 @@
release[0] = '\0';
fprintf (header, "system.release = %s\n", release);
if (lsb)
- fclose (lsb);
+ pclose (lsb);
}
{
FILE *cpuinfo = fopen ("/proc/cpuinfo", "r");
+ FILE *cpuinfo_dump;
+ char fname[4096];
char line[4096];
- char cpu_model[4096] = "";
+ char cpu_model[4096] = {'\0'};
+ char cpu_model_alt[4096] = {'\0'};
+ char *cpu_m = cpu_model;
int cpus = 0;
+ sprintf (fname, "%s/proc_cpuinfo.log", output_path);
+ cpuinfo_dump = fopen(fname, "w");
+
+ /* Dump /proc/cpuinfo for easier debugging with unexpected formats */
while (cpuinfo && fgets (line, 4096, cpuinfo)) {
- if (!strncmp (line, "model name", 10) && strchr (line, ':')) {
+ if (!strncmp (line, "model name", 10) && strchr (line, ':'))
strcpy (cpu_model, strstr (line, ": ") + 2);
+ /* ARM platforms save cpu model on Processor field so try to get it */
+ if (!strncasecmp (line, "processor", 9)) {
cpus++;
+ strcpy (cpu_model_alt, strstr (line, ": ") + 2);
}
+ if (cpuinfo_dump)
+ fprintf(cpuinfo_dump, "%s", line);
}
if (cpuinfo)
fclose (cpuinfo);
- if (strrchr (cpu_model, '\n'))
- *strrchr (cpu_model, '\n') = '\0';
- fprintf (header, "system.cpu = %s %d\n", cpu_model, cpus);
+ if (cpuinfo_dump)
+ fclose(cpuinfo_dump);
+ if (!cpu_model[0])
+ cpu_m = cpu_model_alt;
+ if (strrchr (cpu_m, '\n'))
+ *strrchr (cpu_m, '\n') = '\0';
+ fprintf (header, "system.cpu = %s %d\n", cpu_m, cpus);
fprintf (header, "system.cpu.num = %d\n", cpus);
}
{
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/collector/tasks-netlink.c new/bootchart2-0.12.4/collector/tasks-netlink.c
--- old/bootchart2-0.12.1/collector/tasks-netlink.c 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/collector/tasks-netlink.c 2010-10-24 16:26:26.000000000 +0200
@@ -38,6 +38,7 @@
#include
#include
#include "linux/cn_proc.h"
+#include
#define SEND_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \
sizeof(enum proc_cn_mcast_op)))
@@ -355,6 +356,23 @@
}
}
+static size_t
+netlink_recvfrom (NetLinkPidScanner *nls, char *buffer)
+{
+ socklen_t from_nla_len;
+ struct sockaddr_nl from_nla;
+
+ ZERO_ARRAY (buffer);
+
+ ZERO (from_nla);
+ from_nla.nl_family = AF_NETLINK;
+ from_nla.nl_groups = CN_IDX_PROC;
+ from_nla.nl_pid = 1;
+
+ return recvfrom (nls->socket, buffer, BUFF_SIZE, 0,
+ (struct sockaddr*)&from_nla, &from_nla_len);
+}
+
static void *
netlink_listen_thread (void *user_data)
{
@@ -362,27 +380,14 @@
struct cn_msg *cn_hdr;
- struct sockaddr_nl kern_nla, from_nla;
- socklen_t from_nla_len;
char buff[BUFF_SIZE];
size_t recv_len = 0;
- ZERO (kern_nla);
- kern_nla.nl_family = AF_NETLINK;
- kern_nla.nl_groups = CN_IDX_PROC;
- kern_nla.nl_pid = 1;
-
for (;;) {
struct nlmsghdr *nlh = (struct nlmsghdr*)buff;
- ZERO_ARRAY (buff);
- from_nla_len = sizeof (from_nla);
-
- memcpy (&from_nla, &kern_nla, sizeof (from_nla));
-
/* block here mostly waiting for news ... */
- recv_len = recvfrom (nls->socket, buff, BUFF_SIZE, 0,
- (struct sockaddr*)&from_nla, &from_nla_len);
+ recv_len = netlink_recvfrom (nls, buff);
if (recv_len < 1)
continue;
@@ -439,7 +444,7 @@
*/
nls->socket = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (nls->socket == -1) {
- fprintf (stderr, "netlink socket error");
+ fprintf (stderr, "netlink socket error\n");
return NULL;
}
my_nla.nl_family = AF_NETLINK;
@@ -447,7 +452,7 @@
my_nla.nl_pid = getpid();
if (bind (nls->socket, (struct sockaddr *)&my_nla, sizeof(my_nla))) {
- fprintf (stderr, "binding nls->socket error");
+ fprintf (stderr, "binding nls->socket error\n");
goto close_and_exit;
}
nl_hdr = (struct nlmsghdr *)buff;
@@ -468,7 +473,39 @@
if (send (nls->socket, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) {
fprintf(stderr, "failed to send proc connector mcast ctl op!\n");
goto close_and_exit;
- }
+ } else {
+ size_t recv_len;
+ struct nlmsghdr *nlh = (struct nlmsghdr*)buff;
+ struct pollfd pr = { 0, };
+
+ pr.fd = nls->socket;
+ pr.events = POLLIN;
+ if (poll (&pr, 1, 50 /* ms */) || !(pr.revents & POLLIN)) {
+ fprintf (stderr, "No PROC_EVENTs present\n");
+ goto close_and_exit;
+ }
+
+ recv_len = netlink_recvfrom (nls, buff);
+ if (recv_len < 1 || !NLMSG_OK (nlh, recv_len) ||
+ nlh->nlmsg_type != NLMSG_DONE) {
+ fprintf (stderr, "Failed to connect to PROC_EVENT via netlink\n");
+ goto close_and_exit;
+ } else {
+ struct proc_event *ev;
+ struct cn_msg *cn_hdr;
+
+ cn_hdr = NLMSG_DATA (nlh);
+ ev = (struct proc_event*)cn_hdr->data;
+
+ if (ev->what != PROC_EVENT_NONE || ev->event_data.ack.err) {
+ fprintf (stderr, "error: expecting an EVENT_NONE in response "
+ "to PROC_EVENT connect (err 0x%x)\n",
+ ev->event_data.ack.err);
+ goto close_and_exit;
+ }
+ /* we made it ... */
+ }
+ }
pthread_mutex_init (&nls->lock, NULL);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/pybootchartgui/draw.py new/bootchart2-0.12.4/pybootchartgui/draw.py
--- old/bootchart2-0.12.1/pybootchartgui/draw.py 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/pybootchartgui/draw.py 2010-10-24 16:26:26.000000000 +0200
@@ -57,6 +57,14 @@
DISK_TPUT_COLOR = (0.20, 0.71, 0.20, 1.0)
# CPU load chart color.
FILE_OPEN_COLOR = (0.20, 0.71, 0.71, 1.0)
+# Mem cached color
+MEM_CACHED_COLOR = CPU_COLOR
+# Mem used color
+MEM_USED_COLOR = IO_COLOR
+# Buffers color
+MEM_BUFFERS_COLOR = (0.4, 0.4, 0.4, 0.3)
+# Swap color
+MEM_SWAP_COLOR = DISK_TPUT_COLOR
# Process border color.
PROC_BORDER_COLOR = (0.71, 0.71, 0.71, 1.0)
@@ -95,6 +103,10 @@
# Process description date format.
DESC_TIME_FORMAT = "mm:ss.SSS"
+# Cumulative coloring bits
+HSV_MAX_MOD = 31
+HSV_STEP = 7
+
# Process states
STATE_UNDEFINED = 0
STATE_RUNNING = 1
@@ -193,42 +205,56 @@
ctx.set_line_cap(cairo.LINE_CAP_BUTT)
ctx.set_dash([])
-def draw_chart(ctx, color, fill, chart_bounds, data, proc_tree):
+def draw_chart(ctx, color, fill, chart_bounds, data, proc_tree, data_range):
ctx.set_line_width(0.5)
x_shift = proc_tree.start_time
def transform_point_coords(point, x_base, y_base, \
xscale, yscale, x_trans, y_trans):
x = (point[0] - x_base) * xscale + x_trans
- y = (point[1] - y_base) * -yscale + y_trans + bar_h
+ y = (point[1] - y_base) * -yscale + y_trans + chart_bounds[3]
return x, y
- xscale = float (chart_bounds[2]) / max (x for (x, y) in data)
- yscale = float (chart_bounds[3]) / max (y for (x, y) in data)
-
- first = transform_point_coords (data[0], x_shift, 0, xscale, yscale, \
+ max_x = max (x for (x, y) in data)
+ max_y = max (y for (x, y) in data)
+ # avoid divide by zero
+ if max_y == 0:
+ max_y = 1.0
+ xscale = float (chart_bounds[2]) / max_x
+ # If data_range is given, scale the chart so that the value range in
+ # data_range matches the chart bounds exactly.
+ # Otherwise, scale so that the actual data matches the chart bounds.
+ if data_range:
+ yscale = float(chart_bounds[3]) / (data_range[1] - data_range[0])
+ ybase = data_range[0]
+ else:
+ yscale = float(chart_bounds[3]) / max_y
+ ybase = 0
+
+ first = transform_point_coords (data[0], x_shift, ybase, xscale, yscale, \
chart_bounds[0], chart_bounds[1])
- last = transform_point_coords (data[-1], x_shift, 0, xscale, yscale, \
+ last = transform_point_coords (data[-1], x_shift, ybase, xscale, yscale, \
chart_bounds[0], chart_bounds[1])
ctx.set_source_rgba(*color)
ctx.move_to(*first)
for point in data:
- x, y = transform_point_coords (point, x_shift, 0, xscale, yscale, \
+ x, y = transform_point_coords (point, x_shift, ybase, xscale, yscale, \
chart_bounds[0], chart_bounds[1])
ctx.line_to(x, y)
if fill:
ctx.stroke_preserve()
- ctx.line_to(last[0], chart_bounds[1]+bar_h)
- ctx.line_to(first[0], chart_bounds[1]+bar_h)
+ ctx.line_to(last[0], chart_bounds[1]+chart_bounds[3])
+ ctx.line_to(first[0], chart_bounds[1]+chart_bounds[3])
ctx.line_to(first[0], first[1])
ctx.fill()
else:
ctx.stroke()
ctx.set_line_width(1.0)
-header_h = 280
bar_h = 55
+meminfo_bar_h = 2 * bar_h
+header_h = 110 + 2 * (30 + bar_h) + 1 * (30 + meminfo_bar_h)
# offsets
off_x, off_y = 10, 10
sec_w_base = 50 # the width of a second
@@ -238,8 +264,7 @@
CUML_HEIGHT = 1000
OPTIONS = None
-
-def extents(xscale, headers, cpu_stats, disk_stats, proc_tree, times, filename):
+def extents(xscale, headers, cpu_stats, disk_stats, mem_stats, proc_tree, times, filename):
w = int (proc_tree.duration * sec_w_base * xscale / 100) + 2*off_x
h = proc_h * proc_tree.num_proc + header_h + 2 * off_y
if proc_tree.taskstats and WITH_CUMULATIVE_CHART:
@@ -256,9 +281,9 @@
# Render the chart.
#
def render(ctx, options, xscale, headers, cpu_stats, \
- disk_stats, proc_tree, times, filename):
+ disk_stats, mem_stats, proc_tree, times, filename):
(w, h) = extents (xscale, headers, cpu_stats, \
- disk_stats, proc_tree, times, filename)
+ disk_stats, mem_stats, proc_tree, times, filename)
global OPTIONS
OPTIONS = options
@@ -291,11 +316,11 @@
draw_annotations (ctx, proc_tree, times, chart_rect)
draw_chart (ctx, IO_COLOR, True, chart_rect, \
[(sample.time, sample.user + sample.sys + sample.io) for sample in cpu_stats], \
- proc_tree)
+ proc_tree, None)
# render CPU load
draw_chart (ctx, CPU_COLOR, True, chart_rect, \
[(sample.time, sample.user + sample.sys) for sample in cpu_stats], \
- proc_tree)
+ proc_tree, None)
curr_y = curr_y + 30 + bar_h
@@ -310,14 +335,14 @@
draw_annotations (ctx, proc_tree, times, chart_rect)
draw_chart (ctx, IO_COLOR, True, chart_rect, \
[(sample.time, sample.util) for sample in disk_stats], \
- proc_tree)
+ proc_tree, None)
# render disk throughput
max_sample = max (disk_stats, key = lambda s: s.tput)
if clip_visible (clip, chart_rect):
draw_chart (ctx, DISK_TPUT_COLOR, False, chart_rect, \
[(sample.time, sample.tput) for sample in disk_stats], \
- proc_tree)
+ proc_tree, None)
pos_x = off_x + ((max_sample.time - proc_tree.start_time) * w / proc_tree.duration)
@@ -328,12 +353,40 @@
label = "%dMB/s" % round ((max_sample.tput) / 1024.0)
draw_text (ctx, label, DISK_TPUT_COLOR, pos_x + shift_x, curr_y + shift_y)
+ curr_y = curr_y + 30 + bar_h
+
+ # render mem usage
+ chart_rect = (off_x, curr_y+30, w, meminfo_bar_h)
+ if mem_stats and clip_visible (clip, chart_rect):
+ mem_scale = max(sample.records['MemTotal'] - sample.records['MemFree'] for sample in mem_stats)
+ draw_legend_box(ctx, "Mem cached (scale: %u MiB)" % (float(mem_scale) / 1024), MEM_CACHED_COLOR, off_x, curr_y+20, leg_s)
+ draw_legend_box(ctx, "Used", MEM_USED_COLOR, off_x + 240, curr_y+20, leg_s)
+ draw_legend_box(ctx, "Buffers", MEM_BUFFERS_COLOR, off_x + 360, curr_y+20, leg_s)
+ draw_legend_line(ctx, "Swap (scale: %u MiB)" % max([(sample.records['SwapTotal'] - sample.records['SwapFree'])/1024 for sample in mem_stats]), \
+ MEM_SWAP_COLOR, off_x + 480, curr_y+20, leg_s)
+ draw_box_ticks(ctx, chart_rect, sec_w)
+ draw_annotations(ctx, proc_tree, times, chart_rect)
+ draw_chart(ctx, MEM_BUFFERS_COLOR, True, chart_rect, \
+ [(sample.time, sample.records['MemTotal'] - sample.records['MemFree']) for sample in mem_stats], \
+ proc_tree, [0, mem_scale])
+ draw_chart(ctx, MEM_USED_COLOR, True, chart_rect, \
+ [(sample.time, sample.records['MemTotal'] - sample.records['MemFree'] - sample.records['Buffers']) for sample in mem_stats], \
+ proc_tree, [0, mem_scale])
+ draw_chart(ctx, MEM_CACHED_COLOR, True, chart_rect, \
+ [(sample.time, sample.records['Cached']) for sample in mem_stats], \
+ proc_tree, [0, mem_scale])
+ draw_chart(ctx, MEM_SWAP_COLOR, False, chart_rect, \
+ [(sample.time, float(sample.records['SwapTotal'] - sample.records['SwapFree'])) for sample in mem_stats], \
+ proc_tree, None)
+
+ curr_y = curr_y + meminfo_bar_h
+
# draw process boxes
proc_height = h
if proc_tree.taskstats and WITH_CUMULATIVE_CHART:
proc_height -= CUML_HEIGHT
- draw_process_bar_chart(ctx, clip, proc_tree, times, curr_y + bar_h, w, proc_height, sec_w)
+ draw_process_bar_chart(ctx, clip, proc_tree, times, curr_y, w, proc_height, sec_w)
curr_y = proc_height
ctx.set_font_size(SIG_FONT_SIZE)
@@ -487,38 +540,58 @@
def elide_bootchart(proc):
return proc.cmd == 'bootchartd' or proc.cmd == 'bootchart-colle'
-def accumulate_time(times, proc):
-
- if elide_bootchart(proc):
- return 0.0
-
- time_so_far = 0.0
- for sample in proc.samples:
- time_so_far += sample.cpu_sample.user + sample.cpu_sample.sys
- if not sample.time in times:
- times[sample.time] = 1
-
-
- for c in proc.child_list:
- time_so_far += accumulate_time (times, c)
-
- return time_so_far
-
-
-def make_color():
- h = random.random()
- s = 0.5
- v = 1.0
- c = colorsys.hsv_to_rgb (h, s, v)
- return (c[0], c[1], c[2], 1.0)
+class CumlSample:
+ def __init__(self, proc):
+ self.cmd = proc.cmd
+ self.samples = []
+ self.merge_samples (proc)
+ self.color = None
+
+ def merge_samples(self, proc):
+ self.samples.extend (proc.samples)
+ self.samples.sort (key = lambda p: p.time)
+
+ def next(self):
+ global palette_idx
+ palette_idx += HSV_STEP
+ return palette_idx
+
+ def get_color(self):
+ if self.color is None:
+ i = self.next() % HSV_MAX_MOD
+ h = 0.0
+ if i is not 0:
+ h = (1.0 * i) / HSV_MAX_MOD
+ s = 0.5
+ v = 1.0
+ c = colorsys.hsv_to_rgb (h, s, v)
+ self.color = (c[0], c[1], c[2], 1.0)
+ return self.color
def draw_cuml_graph(ctx, proc_tree, chart_bounds, duration, sec_w):
- time_hash = {}
+ global palette_idx
+ palette_idx = 0
+ time_hash = {}
total_time = 0.0
- for root in proc_tree.process_tree:
- total_time += accumulate_time (time_hash, root)
+ m_proc_list = {}
+
+ for proc in proc_tree.process_list:
+ if elide_bootchart(proc):
+ continue
+
+ for sample in proc.samples:
+ total_time += sample.cpu_sample.user + sample.cpu_sample.sys
+ if not sample.time in time_hash:
+ time_hash[sample.time] = 1
+
+ # merge pids with the same cmd
+ if not proc.cmd in m_proc_list:
+ m_proc_list[proc.cmd] = CumlSample (proc)
+ continue
+ s = m_proc_list[proc.cmd]
+ s.merge_samples (proc)
# all the sample times
times = time_hash.keys()
@@ -530,9 +603,7 @@
pix_per_ns = chart_bounds[3] / total_time
# print "total time: %g pix-per-ns %g" % (total_time, pix_per_ns)
- # FIXME: we really need to aggregate by process name
# FIXME: we have duplicates in the process list too [!] - why !?
- # FIXME: rendering a legend would be nice too ...
# Render bottom up, left to right
below = {}
@@ -546,18 +617,14 @@
legends = []
labels = []
- pid_to_color = {}
# render each pid in order
- for proc in proc_tree.process_list:
+ for cs in m_proc_list.values():
row = {}
cuml = 0.0
- if elide_bootchart(proc):
- continue
-
-# print "pid : %s -> %g samples %d" % (proc.cmd, cuml, len (proc.samples))
- for sample in proc.samples:
+# print "pid : %s -> %g samples %d" % (proc.cmd, cuml, len (cs.samples))
+ for sample in cs.samples:
cuml += sample.cpu_sample.user + sample.cpu_sample.sys
row[sample.time] = cuml
process_total_time = cuml
@@ -570,14 +637,7 @@
y = last_below = below[last_time]
last_cuml = cuml = 0.0
- # make and store color
- color = make_color ()
- pid_to_color[proc.pid] = color
- ctx.set_source_rgba(*color)
-
-# if proc.cmd == 'modprobe':
-# ctx.set_source_rgba(0,0,0,1)
-
+ ctx.set_source_rgba(*cs.get_color())
for time in times:
render_seg = False
@@ -620,7 +680,7 @@
# render legend if it will fit
if cuml > 8:
- label = proc.cmd
+ label = cs.cmd
extnts = ctx.text_extents(label)
label_w = extnts[2]
label_h = extnts[3]
@@ -628,10 +688,10 @@
labels.append((label,
chart_bounds[0] + chart_bounds[2] - label_w - off_x * 2,
y + (cuml + label_h) / 2))
- if proc in legends:
+ if cs in legends:
print "ARGH - duplicate process in list !"
- legends.append ((proc, process_total_time))
+ legends.append ((cs, process_total_time))
below = row
@@ -662,12 +722,12 @@
legends.sort(lambda a, b: cmp (b[1], a[1]))
ctx.set_font_size(TEXT_FONT_SIZE)
for t in legends:
- proc = t[0]
+ cs = t[0]
time = t[1]
x = chart_bounds[0] + off_x + int (i/LEGENDS_PER_COL) * label_width
y = chart_bounds[1] + font_height * ((i % LEGENDS_PER_COL) + 2)
- str = "%s - %.0f(ms) (%2.2f%%)" % (proc.cmd, time/1000000, (time/total_time) * 100.0)
- draw_legend_box(ctx, str, pid_to_color [proc.pid], x, y, leg_s)
+ str = "%s - %.0f(ms) (%2.2f%%)" % (cs.cmd, time/1000000, (time/total_time) * 100.0)
+ draw_legend_box(ctx, str, cs.color, x, y, leg_s)
i = i + 1
if i >= LEGENDS_TOTAL:
break
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/pybootchartgui/gui.py new/bootchart2-0.12.4/pybootchartgui/gui.py
--- old/bootchart2-0.12.1/pybootchartgui/gui.py 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/pybootchartgui/gui.py 2010-10-24 16:26:26.000000000 +0200
@@ -158,13 +158,14 @@
return False
def on_area_scroll_event(self, area, event):
- if event.direction == gtk.gdk.SCROLL_UP:
- self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT)
- return True
- if event.direction == gtk.gdk.SCROLL_DOWN:
- self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT)
- return True
- return False
+ if event.state & gtk.gdk.CONTROL_MASK:
+ if event.direction == gtk.gdk.SCROLL_UP:
+ self.zoom_image(self.zoom_ratio * self.ZOOM_INCREMENT)
+ return True
+ if event.direction == gtk.gdk.SCROLL_DOWN:
+ self.zoom_image(self.zoom_ratio / self.ZOOM_INCREMENT)
+ return True
+ return False
def on_area_motion_notify(self, area, event):
state = event.state
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/pybootchartgui/main.py new/bootchart2-0.12.4/pybootchartgui/main.py
--- old/bootchart2-0.12.1/pybootchartgui/main.py 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/pybootchartgui/main.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,161 +0,0 @@
-# This file is part of pybootchartgui.
-
-# pybootchartgui 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.
-
-# pybootchartgui 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 pybootchartgui. If not, see http://www.gnu.org/licenses/.
-
-import sys
-import os
-import optparse
-
-import parsing
-import gui
-import batch
-
-def _mk_options_parser():
- """Make an options parser."""
- usage = "%prog [options] PATH, ..., PATH"
- version = "%prog v0.0.0"
- parser = optparse.OptionParser(usage, version=version)
- parser.add_option("-i", "--interactive", action="store_true", dest="interactive", default=False,
- help="start in active mode")
- parser.add_option("-f", "--format", dest="format", default = None,
- help="image format (...); default format ...")
- parser.add_option("-o", "--output", dest="output", metavar="PATH", default="bootchart.png",
- help="output path (file or directory) where charts are stored")
- parser.add_option("-n", "--no-prune", action="store_false", dest="prune", default=True,
- help="do not prune the process tree")
- parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False,
- help="suppress informational messages")
- parser.add_option("-t", "--boot-time", action="store_true", dest="boottime", default=False,
- help="only display the boot time of the boot in text format (stdout)")
- parser.add_option("--very-quiet", action="store_true", dest="veryquiet", default=False,
- help="suppress all messages except errors")
- parser.add_option("--verbose", action="store_true", dest="verbose", default=False,
- help="print all messages")
- parser.add_option("--profile", action="store_true", dest="profile", default=False,
- help="profile rendering of chart (only useful when in batch mode indicated by -f)")
- parser.add_option("--show-pid", action="store_true", dest="show_pid", default=False,
- help="show process ids in the bootchart as 'processname [pid]'")
- parser.add_option("--show-all", action="store_true", dest="show_all", default=False,
- help="show all process information in the bootchart as '/process/path/exe [pid] [args]'")
- parser.add_option("--crop-after", dest="crop_after", metavar="PROCESS", default=None,
- help="crop chart when idle after PROCESS is started")
- parser.add_option("--annotate", action="append", dest="annotate", metavar="PROCESS", default=None,
- help="annotate position where PROCESS is started; can be specified multiple times. " +
- "To create a single annotation when any one of a set of processes is started, use commas to separate the names")
- parser.add_option("--annotate-file", dest="annotate_file", metavar="FILENAME", default=None,
- help="filename to write annotation points to")
- return parser
-
-class Writer:
- def __init__(self, write, options):
- self.write = write
- self.options = options
-
- def error(self, msg):
- self.write(msg)
-
- def warn(self, msg):
- if not self.options.quiet:
- self.write(msg)
-
- def info(self, msg):
- if self.options.verbose:
- self.write(msg)
-
- def status(self, msg):
- if not self.options.quiet:
- self.write(msg)
-
-def _mk_writer(options):
- def write(s):
- print s
- return Writer(write, options)
-
-def _get_filename(paths, options):
- """Construct a usable filename for outputs based on the paths and options given on the commandline."""
- dname = ""
- fname = "bootchart"
- if options.output and not(os.path.isdir(options.output)):
- return options.output
- if options.output:
- dname = options.output
- if len (paths) == 1:
- path = paths[0]
- if os.path.isdir(path):
- fname = os.path.split(path)[-1]
- elif os.path.splitext(path)[1] in [".tar", ".tgz", ".tar.gz"]:
- fname = os.path.splitext(path)[0]
- return os.path.join (dname, fname + "." + options.format)
-
-def main(argv=None):
- try:
- if argv is None:
- argv = sys.argv[1:]
-
- parser = _mk_options_parser()
- options, args = parser.parse_args(argv)
- writer = _mk_writer(options)
-
- if len(args) == 0:
- print "No path given, trying /var/log/bootchart.tgz"
- args = [ "/var/log/bootchart.tgz" ]
-
-
- res = parsing.parse(writer, args, options.prune,
- options.crop_after, options.annotate)
-
- if options.interactive or options.output == None:
- gui.show(res, options)
- elif options.boottime:
- import math
- proc_tree = res[3]
- if proc_tree.idle:
- duration = proc_tree.idle
- else:
- duration = proc_tree.duration
- dur = duration / 100.0
- print '%02d:%05.2f' % (math.floor(dur/60), dur - 60 * math.floor(dur/60))
- else:
- if options.annotate_file:
- f = open (options.annotate_file, "w")
- try:
- for time in res[4]:
- if time is not None:
- # output as ms
- print >> f, time * 10
- else:
- print >> f
- finally:
- f.close()
- filename = _get_filename(args, options)
- def render():
- batch.render(writer, res, options, filename)
- if options.profile:
- import cProfile
- import pstats
- profile = '%s.prof' % os.path.splitext(filename)[0]
- cProfile.runctx('render()', globals(), locals(), profile)
- p = pstats.Stats(profile)
- p.strip_dirs().sort_stats('time').print_stats(20)
- else:
- render()
-
- return 0
- except parsing.ParseError, ex:
- print("Parse error: %s" % ex)
- return 2
-
-
-if __name__ == '__main__':
- sys.exit(main())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/pybootchartgui/main.py.in new/bootchart2-0.12.4/pybootchartgui/main.py.in
--- old/bootchart2-0.12.1/pybootchartgui/main.py.in 1970-01-01 01:00:00.000000000 +0100
+++ new/bootchart2-0.12.4/pybootchartgui/main.py.in 2010-10-24 16:26:26.000000000 +0200
@@ -0,0 +1,164 @@
+#
+# ***********************************************************************
+# Warning: This file is auto-generated from main.py.in - edit it there.
+# ***********************************************************************
+#
+# pybootchartgui 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.
+
+# pybootchartgui 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 pybootchartgui. If not, see http://www.gnu.org/licenses/.
+
+import sys
+import os
+import optparse
+
+import parsing
+import gui
+import batch
+
+def _mk_options_parser():
+ """Make an options parser."""
+ usage = "%prog [options] PATH, ..., PATH"
+ version = "%prog v@VER@"
+ parser = optparse.OptionParser(usage, version=version)
+ parser.add_option("-i", "--interactive", action="store_true", dest="interactive", default=False,
+ help="start in active mode")
+ parser.add_option("-f", "--format", dest="format", default = None,
+ help="image format (...); default format ...")
+ parser.add_option("-o", "--output", dest="output", metavar="PATH", default="bootchart.png",
+ help="output path (file or directory) where charts are stored")
+ parser.add_option("-n", "--no-prune", action="store_false", dest="prune", default=True,
+ help="do not prune the process tree")
+ parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False,
+ help="suppress informational messages")
+ parser.add_option("-t", "--boot-time", action="store_true", dest="boottime", default=False,
+ help="only display the boot time of the boot in text format (stdout)")
+ parser.add_option("--very-quiet", action="store_true", dest="veryquiet", default=False,
+ help="suppress all messages except errors")
+ parser.add_option("--verbose", action="store_true", dest="verbose", default=False,
+ help="print all messages")
+ parser.add_option("--profile", action="store_true", dest="profile", default=False,
+ help="profile rendering of chart (only useful when in batch mode indicated by -f)")
+ parser.add_option("--show-pid", action="store_true", dest="show_pid", default=False,
+ help="show process ids in the bootchart as 'processname [pid]'")
+ parser.add_option("--show-all", action="store_true", dest="show_all", default=False,
+ help="show all process information in the bootchart as '/process/path/exe [pid] [args]'")
+ parser.add_option("--crop-after", dest="crop_after", metavar="PROCESS", default=None,
+ help="crop chart when idle after PROCESS is started")
+ parser.add_option("--annotate", action="append", dest="annotate", metavar="PROCESS", default=None,
+ help="annotate position where PROCESS is started; can be specified multiple times. " +
+ "To create a single annotation when any one of a set of processes is started, use commas to separate the names")
+ parser.add_option("--annotate-file", dest="annotate_file", metavar="FILENAME", default=None,
+ help="filename to write annotation points to")
+ return parser
+
+class Writer:
+ def __init__(self, write, options):
+ self.write = write
+ self.options = options
+
+ def error(self, msg):
+ self.write(msg)
+
+ def warn(self, msg):
+ if not self.options.quiet:
+ self.write(msg)
+
+ def info(self, msg):
+ if self.options.verbose:
+ self.write(msg)
+
+ def status(self, msg):
+ if not self.options.quiet:
+ self.write(msg)
+
+def _mk_writer(options):
+ def write(s):
+ print s
+ return Writer(write, options)
+
+def _get_filename(paths, options):
+ """Construct a usable filename for outputs based on the paths and options given on the commandline."""
+ dname = ""
+ fname = "bootchart"
+ if options.output and not(os.path.isdir(options.output)):
+ return options.output
+ if options.output:
+ dname = options.output
+ if len (paths) == 1:
+ path = paths[0]
+ if os.path.isdir(path):
+ fname = os.path.split(path)[-1]
+ elif os.path.splitext(path)[1] in [".tar", ".tgz", ".tar.gz"]:
+ fname = os.path.splitext(path)[0]
+ return os.path.join (dname, fname + "." + options.format)
+
+def main(argv=None):
+ try:
+ if argv is None:
+ argv = sys.argv[1:]
+
+ parser = _mk_options_parser()
+ options, args = parser.parse_args(argv)
+ writer = _mk_writer(options)
+
+ if len(args) == 0:
+ print "No path given, trying /var/log/bootchart.tgz"
+ args = [ "/var/log/bootchart.tgz" ]
+
+
+ res = parsing.parse(writer, args, options.prune,
+ options.crop_after, options.annotate)
+
+ if options.interactive or options.output == None:
+ gui.show(res, options)
+ elif options.boottime:
+ import math
+ proc_tree = res[3]
+ if proc_tree.idle:
+ duration = proc_tree.idle
+ else:
+ duration = proc_tree.duration
+ dur = duration / 100.0
+ print '%02d:%05.2f' % (math.floor(dur/60), dur - 60 * math.floor(dur/60))
+ else:
+ if options.annotate_file:
+ f = open (options.annotate_file, "w")
+ try:
+ for time in res[4]:
+ if time is not None:
+ # output as ms
+ print >> f, time * 10
+ else:
+ print >> f
+ finally:
+ f.close()
+ filename = _get_filename(args, options)
+ def render():
+ batch.render(writer, res, options, filename)
+ if options.profile:
+ import cProfile
+ import pstats
+ profile = '%s.prof' % os.path.splitext(filename)[0]
+ cProfile.runctx('render()', globals(), locals(), profile)
+ p = pstats.Stats(profile)
+ p.strip_dirs().sort_stats('time').print_stats(20)
+ else:
+ render()
+
+ return 0
+ except parsing.ParseError, ex:
+ print("Parse error: %s" % ex)
+ return 2
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/pybootchartgui/parsing.py new/bootchart2-0.12.4/pybootchartgui/parsing.py
--- old/bootchart2-0.12.1/pybootchartgui/parsing.py 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/pybootchartgui/parsing.py 2010-10-24 16:26:26.000000000 +0200
@@ -212,21 +212,17 @@
def _parse_proc_disk_stat_log(file, numCpu):
"""
- Parse file for disk stats, but only look at the whole disks, eg. sda,
+ Parse file for disk stats, but only look at the whole device, eg. sda,
not sda1, sda2 etc. The format of relevant lines should be:
{major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq}
"""
- disk_regex_re = re.compile ('^[hsv]d.$')
-
+ disk_regex_re = re.compile ('^([hsv]d.|mtdblock\d|mmcblk\d)$')
+
# this gets called an awful lot.
def is_relevant_line(linetokens):
if len(linetokens) != 14:
return False
disk = linetokens[2]
- if len (disk) != 3:
- return False
- if disk == 'sda':
- return True
return disk_regex_re.match(disk)
disk_stat_samples = []
@@ -253,6 +249,28 @@
return disk_stats
+def _parse_proc_meminfo_log(file):
+ """
+ Parse file for global memory statistics.
+ The format of relevant lines should be: ^key: value( unit)?
+ """
+
+ mem_stats = []
+
+ for time, lines in _parse_timed_blocks(file):
+ sample = MemSample(time)
+
+ for line in lines:
+ match = re.match('([^ \t:]+):\s*(\d+).*', line)
+ if match:
+ sample.add_value(match.group(1), int(match.group(2)))
+ else:
+ raise ParseError("Invalid meminfo line \"%s\"" % match.groups(0))
+
+ mem_stats.append(sample)
+
+ return mem_stats
+
# if we boot the kernel with: initcall_debug printk.time=1 we can
# get all manner of interesting data from the dmesg output
# We turn this into a pseudo-process tree: each event is
@@ -273,6 +291,8 @@
inc = 1.0 / 1000000
kernel = Process(writer, idx, "k-boot", 0, 0.1)
processMap['k-boot'] = kernel
+ base_ts = False
+ max_ts = 0
for line in file.read().split('\n'):
t = timestamp_re.match (line)
if t is None:
@@ -280,6 +300,19 @@
continue
time_ms = float (t.group(1)) * 1000
+ # looks like we may have a huge diff after the clock
+ # has been set up. This could lead to huge graph:
+ # so huge we will be killed by the OOM.
+ # So instead of using the plain timestamp we will
+ # use a delta to first one and skip the first one
+ # for convenience
+ if max_ts == 0 and not base_ts and time_ms > 1000:
+ base_ts = time_ms
+ continue
+ max_ts = max(time_ms, max_ts)
+ if base_ts:
+# print "fscked clock: used %f instead of %f" % (time_ms - base_ts, time_ms)
+ time_ms -= base_ts
m = split_re.match (t.group(2))
if m is None:
@@ -379,14 +412,14 @@
if headers is None:
return 1
if headers.get("system.cpu.num"):
- return int (headers.get("system.cpu.num"))
+ return max (int (headers.get("system.cpu.num")), 1)
cpu_model = headers.get("system.cpu")
if cpu_model is None:
return 1
mat = re.match(".*\\((\\d+)\\)", cpu_model)
if mat is None:
return 1
- return int(mat.group(1))
+ return max (int(mat.group(1)), 1)
class ParserState:
def __init__(self):
@@ -399,6 +432,7 @@
self.kernel = None
self.filename = None
self.parent_map = None
+ self.mem_stats = None
def valid(self):
return self.headers != None and self.disk_stats != None and \
@@ -465,6 +499,8 @@
state.taskstats = True
elif name == "proc_stat.log":
state.cpu_stats = _parse_proc_stat_log(file)
+ elif name == "proc_meminfo.log":
+ state.mem_stats = _parse_proc_meminfo_log(file)
elif name == "dmesg":
state.kernel = _parse_dmesg(writer, file)
elif name == "cmdline2.log":
@@ -616,4 +652,4 @@
monitored_app = state.headers.get("profile.process")
proc_tree = ProcessTree(writer, state.kernel, state.ps_stats, monitored_app, prune, idle, state.taskstats)
- return (state.headers, state.cpu_stats, state.disk_stats, proc_tree, times, state.filename)
+ return (state.headers, state.cpu_stats, state.disk_stats, state.mem_stats, proc_tree, times, state.filename)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/pybootchartgui/process_tree.py new/bootchart2-0.12.4/pybootchartgui/process_tree.py
--- old/bootchart2-0.12.1/pybootchartgui/process_tree.py 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/pybootchartgui/process_tree.py 2010-10-24 16:26:26.000000000 +0200
@@ -211,7 +211,7 @@
if is_app_tree:
for child in p.child_list:
- self.__merge_processes(p, child)
+ self.merge_processes(p, child)
num_removed += 1
p.child_list = []
else:
@@ -228,7 +228,7 @@
if processes in processes and len(p.child_list) > 0:
subtreemap = self.getProcessMap(p.child_list)
for child in subtreemap.values():
- self.__merge_processes(p, child)
+ self.merge_processes(p, child)
num_removed += len(subtreemap)
p.child_list = []
p.cmd += " (+)"
@@ -251,7 +251,7 @@
idx -= 1
num_removed += 1
p.child_list.extend(nextp.child_list)
- self.__merge_processes(p, nextp)
+ self.merge_processes(p, nextp)
num_removed += self.merge_siblings(p.child_list)
idx += 1
if len(process_subtree) > 0:
@@ -271,14 +271,14 @@
if len(p.child_list) == 1 and p.child_list[0].cmd == p.cmd:
child = p.child_list[0]
p.child_list = list(child.child_list)
- self.__merge_processes(p, child)
+ self.merge_processes(p, child)
num_removed += 1
continue
num_removed += self.merge_runs(p.child_list)
idx += 1
return num_removed
- def __merge_processes(self, p1, p2):
+ def merge_processes(self, p1, p2):
"""Merges two process' samples."""
p1.samples.extend(p2.samples)
p1.samples.sort( key = lambda p: p.time )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/pybootchartgui/samples.py new/bootchart2-0.12.4/pybootchartgui/samples.py
--- old/bootchart2-0.12.1/pybootchartgui/samples.py 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/pybootchartgui/samples.py 2010-10-24 16:26:26.000000000 +0200
@@ -32,7 +32,15 @@
def __str__(self):
return str(self.time) + "\t" + str(self.user) + "\t" + \
str(self.sys) + "\t" + str(self.io) + "\t" + str (self.swap)
-
+
+class MemSample:
+ def __init__(self, time):
+ self.time = time
+ self.records = {}
+
+ def add_value(self, name, value):
+ self.records[name] = value
+
class ProcessSample:
def __init__(self, time, state, cpu_sample):
self.time = time
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/pybootchartgui/tests/parser_test.py new/bootchart2-0.12.4/pybootchartgui/tests/parser_test.py
--- old/bootchart2-0.12.1/pybootchartgui/tests/parser_test.py 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/pybootchartgui/tests/parser_test.py 2010-10-24 16:26:26.000000000 +0200
@@ -89,8 +89,8 @@
self.assert_(floatEq(float(tokens[3]), sample.io))
def testParseLogDir(self):
- res = parsing.parse(writer, [self.rootdir], False)
- self.assertEqual(4, len(res))
+ res = parsing.parse(writer, [self.rootdir], False, None, None)
+ self.assertEqual(6, len(res))
if __name__ == '__main__':
unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bootchart2-0.12.1/setup.py new/bootchart2-0.12.4/setup.py
--- old/bootchart2-0.12.1/setup.py 2010-07-15 12:02:18.000000000 +0200
+++ new/bootchart2-0.12.4/setup.py 2010-10-24 16:26:26.000000000 +0200
@@ -1,7 +1,19 @@
from distutils.core import setup
+from os import environ
+
+if 'PKG_VER' in environ:
+ VERSION = environ['PKG_VER']
+else:
+ VERSION = ''
+
setup(name = 'pybootchartgui',
+ version = VERSION,
description = 'Python bootchart graph utility',
+ url = 'http://github.com/mmeeks/bootchart/',
+
+ maintainer = 'Michael Meeks',
+ maintainer_email = 'michael.meeks@novell.com',
packages = ['pybootchartgui'],
package_dir = {'pybootchartgui': 'pybootchartgui'},
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Remember to have fun...
--
To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org
For additional commands, e-mail: opensuse-commit+help@opensuse.org