From 5aa245e3bcf884ca1886eb72bc916a1ecf05acea Mon Sep 17 00:00:00 2001 From: Sardem FF7 Date: Tue, 31 May 2011 02:03:13 +0200 Subject: [PATCH 3/3] systemd-analyze: Vala implementation Start implementing systemd-analyze features using Vala not to depend of Python (and dbus-python and pycairo) --- .gitignore | 1 + Makefile.am | 28 ++++- configure.ac | 15 ++ src/.gitignore | 1 + src/systemd-analyze | 229 ------------------------------- src/systemd-analyze.py | 229 +++++++++++++++++++++++++++++++ src/systemd-analyze.vala | 338 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 609 insertions(+), 232 deletions(-) delete mode 100755 src/systemd-analyze create mode 100755 src/systemd-analyze.py create mode 100644 src/systemd-analyze.vala diff --git a/.gitignore b/.gitignore index ae2d204..9360c5b 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ test-job-type systemd-logger systemctl libsystemd-interfaces.vapi +systemd-analyze systemadm .dirstamp *.1 diff --git a/Makefile.am b/Makefile.am index 9c032a1..f91f9bb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -123,13 +123,11 @@ rootbin_PROGRAMS = \ systemd-machine-id-setup bin_PROGRAMS = \ + systemd-analyze \ systemd-cgls \ systemd-stdio-bridge \ systemd-nspawn -dist_bin_SCRIPTS = \ - src/systemd-analyze - if HAVE_GTK bin_PROGRAMS += \ systemadm \ @@ -1143,6 +1141,30 @@ systemadm_LDADD = \ $(GTK_LIBS) \ $(GLIB_LIBS) +systemd_analyze_SOURCES = \ + src/systemd-analyze.vala + +systemd_analyze_CFLAGS = \ + $(AM_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(CAIRO_CFLAGS) \ + -Wno-unused-variable \ + -Wno-unused-function \ + -Wno-shadow \ + -Wno-format-nonliteral + +systemd_analyze_VALAFLAGS = \ + libsystemd-interfaces.vapi \ + --pkg=posix \ + --pkg=cairo \ + --pkg=gio-2.0 \ + -g + +systemd_analyze_LDADD = \ + libsystemd-interfaces.la \ + $(GLIB_LIBS) \ + $(CAIRO_LIBS) + systemd_gnome_ask_password_agent_SOURCES = \ src/gnome-ask-password-agent.vala diff --git a/configure.ac b/configure.ac index cf53229..09b41fe 100644 --- a/configure.ac +++ b/configure.ac @@ -255,6 +255,21 @@ AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) AM_CONDITIONAL(HAVE_GLIB, [test "$have_glib" = "yes"]) +PKG_CHECK_MODULES(CAIRO, [ cairo ], + [AC_DEFINE(HAVE_CAIRO, 1, [Define if cairo is available]) have_cairo=yes], have_cairo=no) +AC_SUBST(CAIRO_CFLAGS) +AC_SUBST(CAIRO_LIBS) +AM_CONDITIONAL(HAVE_CAIRO, [test "$have_cairo" = "yes"]) + +AC_ARG_ENABLE(analyze, AS_HELP_STRING([--disable-analyze], [disable systemd-analyze tools])) +if test "x$enable_analyze" != "xno"; then + if test "x$have_glib" = xno -o "x$have_cairo" = xno; then + if test "x$enable_analyze" = xyes; then + AC_MSG_ERROR([*** systemd-analyze requested but glib or cairo libraries not found]) + fi + fi +fi + have_gtk=no AC_ARG_ENABLE(gtk, AS_HELP_STRING([--disable-gtk], [disable GTK tools])) if test "x$enable_gtk" != "xno"; then diff --git a/src/.gitignore b/src/.gitignore index 69e6a6d..2ffc1f7 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -2,4 +2,5 @@ org.freedesktop.systemd1.policy gnome-ask-password-agent.c systemd-interfaces.c systemd-interfaces.h +systemd-analyze.c systemadm.c diff --git a/src/systemd-analyze b/src/systemd-analyze deleted file mode 100755 index ae7dcfb..0000000 --- a/src/systemd-analyze +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/python - -import dbus, sys - -def acquire_time_data(): - - manager = dbus.Interface(bus.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1'), 'org.freedesktop.systemd1.Manager') - units = manager.ListUnits() - - l = [] - - for i in units: - if i[5] != "": - continue - - properties = dbus.Interface(bus.get_object('org.freedesktop.systemd1', i[6]), 'org.freedesktop.DBus.Properties') - - ixt = int(properties.Get('org.freedesktop.systemd1.Unit', 'InactiveExitTimestampMonotonic')) - aet = int(properties.Get('org.freedesktop.systemd1.Unit', 'ActiveEnterTimestampMonotonic')) - axt = int(properties.Get('org.freedesktop.systemd1.Unit', 'ActiveExitTimestampMonotonic')) - iet = int(properties.Get('org.freedesktop.systemd1.Unit', 'InactiveEnterTimestampMonotonic')) - - l.append((str(i[0]), ixt, aet, axt, iet)) - - return l - -def acquire_start_time(): - properties = dbus.Interface(bus.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1'), 'org.freedesktop.DBus.Properties') - - initrd_time = int(properties.Get('org.freedesktop.systemd1.Manager', 'InitRDTimestampMonotonic')) - startup_time = int(properties.Get('org.freedesktop.systemd1.Manager', 'StartupTimestampMonotonic')) - finish_time = int(properties.Get('org.freedesktop.systemd1.Manager', 'FinishTimestampMonotonic')) - - assert initrd_time <= startup_time - assert startup_time <= finish_time - - return initrd_time, startup_time, finish_time - -def draw_box(context, j, k, l, m, r = 0, g = 0, b = 0): - context.save() - context.set_source_rgb(r, g, b) - context.rectangle(j, k, l, m) - context.fill() - context.restore() - -def draw_text(context, x, y, text, size = 12, r = 0, g = 0, b = 0, vcenter = 0.5, hcenter = 0.5): - context.save() - - context.set_source_rgb(r, g, b) - context.select_font_face("Sans", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) - context.set_font_size(size) - - if vcenter or hcenter: - x_bearing, y_bearing, width, height = context.text_extents(text)[:4] - - if hcenter: - x = x - width*hcenter - x_bearing - - if vcenter: - y = y - height*vcenter - y_bearing - - context.move_to(x, y) - context.show_text(text) - - context.restore() - -def help(): - sys.stdout.write("""systemd-analyze time -systemd-analyze blame -systemd-analyze plot - -Process systemd profiling information - - -h --help Show this help -""") - - -bus = dbus.SystemBus() - -if len(sys.argv) <= 1 or sys.argv[1] == 'time': - - initrd_time, start_time, finish_time = acquire_start_time() - - if initrd_time > 0: - print "Startup finished in %lums (kernel) + %lums (initrd) + %lums (userspace) = %lums" % ( \ - initrd_time/1000, \ - (start_time - initrd_time)/1000, \ - (finish_time - start_time)/1000, \ - finish_time/1000) - else: - print "Startup finished in %lums (kernel) + %lums (userspace) = %lums" % ( \ - start_time/1000, \ - (finish_time - start_time)/1000, \ - finish_time/1000) - - -elif sys.argv[1] == 'blame': - - data = acquire_time_data() - s = sorted(data, key = lambda i: i[2] - i[1], reverse = True) - - for name, ixt, aet, axt, iet in s: - - if ixt <= 0 or aet <= 0: - continue - - if aet <= ixt: - continue - - sys.stdout.write("%6lums %s\n" % ((aet - ixt) / 1000, name)) - -elif sys.argv[1] == 'plot': - import cairo, os - - initrd_time, start_time, finish_time = acquire_start_time() - data = acquire_time_data() - s = sorted(data, key = lambda i: i[1]) - - count = 0 - - for name, ixt, aet, axt, iet in s: - - if (ixt >= start_time and ixt <= finish_time) or \ - (aet >= start_time and aet <= finish_time) or \ - (axt >= start_time and axt <= finish_time): - count += 1 - - border = 100 - bar_height = 20 - bar_space = bar_height * 0.1 - - # 1000px = 10s, 1px = 10ms - width = (finish_time - start_time)/10000 + border*2 - height = count * (bar_height + bar_space) + border * 2 - - if width < 1000: - width = 1000 - - surface = cairo.SVGSurface(sys.stdout, width, height) - context = cairo.Context(surface) - - draw_box(context, 0, 0, width, height, 1, 1, 1) - - context.translate(border + 0.5, border + 0.5) - - context.save() - context.set_line_width(1) - context.set_source_rgb(0.7, 0.7, 0.7) - - for x in range(0, (finish_time - start_time)/10000, 100): - context.move_to(x, 0) - context.line_to(x, height-border*2) - - context.move_to(0, 0) - context.line_to(width-border*2, 0) - - context.move_to(0, height-border*2) - context.line_to(width-border*2, height-border*2) - - context.stroke() - context.restore() - - banner = "Running on %s (%s %s) %s" % (os.uname()[1], os.uname()[2], os.uname()[3], os.uname()[4]) - draw_text(context, 0, -15, banner, hcenter = 0, vcenter = 1) - - for x in range(0, (finish_time - start_time)/10000, 100): - draw_text(context, x, -5, "%lus" % (x/100), vcenter = 0, hcenter = 0) - - y = 0 - - for name, ixt, aet, axt, iet in s: - - drawn = False - left = -1 - - if ixt >= start_time and ixt <= finish_time: - - # Activating - a = ixt - start_time - b = min(filter(lambda x: x >= ixt, (aet, axt, iet, finish_time))) - ixt - - draw_box(context, a/10000, y, b/10000, bar_height, 1, 0, 0) - drawn = True - - if left < 0: - left = a - - if aet >= start_time and aet <= finish_time: - - # Active - a = aet - start_time - b = min(filter(lambda x: x >= aet, (axt, iet, finish_time))) - aet - - draw_box(context, a/10000, y, b/10000, bar_height, .8, .6, .6) - drawn = True - - if left < 0: - left = a - - if axt >= start_time and axt <= finish_time: - - # Deactivating - a = axt - start_time - b = min(filter(lambda x: x >= axt, (iet, finish_time))) - axt - - draw_box(context, a/10000, y, b/10000, bar_height, .6, .4, .4) - drawn = True - - if left < 0: - left = a - - if drawn: - x = left/10000 - - if x < width/2-border: - draw_text(context, x + 10, y + bar_height/2, name, hcenter = 0) - else: - draw_text(context, x - 10, y + bar_height/2, name, hcenter = 1) - - y += bar_height + bar_space - - draw_text(context, 0, height-border*2, "Legend: Red = Activating; Pink = Active; Dark Pink = Deactivating", hcenter = 0, vcenter = -1) - - surface.finish() -elif sys.argv[1] in ("help", "--help", "-h"): - help() -else: - sys.stderr.write("Unknown verb '%s'.\n" % sys.argv[1]) - sys.exit(1) diff --git a/src/systemd-analyze.py b/src/systemd-analyze.py new file mode 100755 index 0000000..ae7dcfb --- /dev/null +++ b/src/systemd-analyze.py @@ -0,0 +1,229 @@ +#!/usr/bin/python + +import dbus, sys + +def acquire_time_data(): + + manager = dbus.Interface(bus.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1'), 'org.freedesktop.systemd1.Manager') + units = manager.ListUnits() + + l = [] + + for i in units: + if i[5] != "": + continue + + properties = dbus.Interface(bus.get_object('org.freedesktop.systemd1', i[6]), 'org.freedesktop.DBus.Properties') + + ixt = int(properties.Get('org.freedesktop.systemd1.Unit', 'InactiveExitTimestampMonotonic')) + aet = int(properties.Get('org.freedesktop.systemd1.Unit', 'ActiveEnterTimestampMonotonic')) + axt = int(properties.Get('org.freedesktop.systemd1.Unit', 'ActiveExitTimestampMonotonic')) + iet = int(properties.Get('org.freedesktop.systemd1.Unit', 'InactiveEnterTimestampMonotonic')) + + l.append((str(i[0]), ixt, aet, axt, iet)) + + return l + +def acquire_start_time(): + properties = dbus.Interface(bus.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1'), 'org.freedesktop.DBus.Properties') + + initrd_time = int(properties.Get('org.freedesktop.systemd1.Manager', 'InitRDTimestampMonotonic')) + startup_time = int(properties.Get('org.freedesktop.systemd1.Manager', 'StartupTimestampMonotonic')) + finish_time = int(properties.Get('org.freedesktop.systemd1.Manager', 'FinishTimestampMonotonic')) + + assert initrd_time <= startup_time + assert startup_time <= finish_time + + return initrd_time, startup_time, finish_time + +def draw_box(context, j, k, l, m, r = 0, g = 0, b = 0): + context.save() + context.set_source_rgb(r, g, b) + context.rectangle(j, k, l, m) + context.fill() + context.restore() + +def draw_text(context, x, y, text, size = 12, r = 0, g = 0, b = 0, vcenter = 0.5, hcenter = 0.5): + context.save() + + context.set_source_rgb(r, g, b) + context.select_font_face("Sans", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) + context.set_font_size(size) + + if vcenter or hcenter: + x_bearing, y_bearing, width, height = context.text_extents(text)[:4] + + if hcenter: + x = x - width*hcenter - x_bearing + + if vcenter: + y = y - height*vcenter - y_bearing + + context.move_to(x, y) + context.show_text(text) + + context.restore() + +def help(): + sys.stdout.write("""systemd-analyze time +systemd-analyze blame +systemd-analyze plot + +Process systemd profiling information + + -h --help Show this help +""") + + +bus = dbus.SystemBus() + +if len(sys.argv) <= 1 or sys.argv[1] == 'time': + + initrd_time, start_time, finish_time = acquire_start_time() + + if initrd_time > 0: + print "Startup finished in %lums (kernel) + %lums (initrd) + %lums (userspace) = %lums" % ( \ + initrd_time/1000, \ + (start_time - initrd_time)/1000, \ + (finish_time - start_time)/1000, \ + finish_time/1000) + else: + print "Startup finished in %lums (kernel) + %lums (userspace) = %lums" % ( \ + start_time/1000, \ + (finish_time - start_time)/1000, \ + finish_time/1000) + + +elif sys.argv[1] == 'blame': + + data = acquire_time_data() + s = sorted(data, key = lambda i: i[2] - i[1], reverse = True) + + for name, ixt, aet, axt, iet in s: + + if ixt <= 0 or aet <= 0: + continue + + if aet <= ixt: + continue + + sys.stdout.write("%6lums %s\n" % ((aet - ixt) / 1000, name)) + +elif sys.argv[1] == 'plot': + import cairo, os + + initrd_time, start_time, finish_time = acquire_start_time() + data = acquire_time_data() + s = sorted(data, key = lambda i: i[1]) + + count = 0 + + for name, ixt, aet, axt, iet in s: + + if (ixt >= start_time and ixt <= finish_time) or \ + (aet >= start_time and aet <= finish_time) or \ + (axt >= start_time and axt <= finish_time): + count += 1 + + border = 100 + bar_height = 20 + bar_space = bar_height * 0.1 + + # 1000px = 10s, 1px = 10ms + width = (finish_time - start_time)/10000 + border*2 + height = count * (bar_height + bar_space) + border * 2 + + if width < 1000: + width = 1000 + + surface = cairo.SVGSurface(sys.stdout, width, height) + context = cairo.Context(surface) + + draw_box(context, 0, 0, width, height, 1, 1, 1) + + context.translate(border + 0.5, border + 0.5) + + context.save() + context.set_line_width(1) + context.set_source_rgb(0.7, 0.7, 0.7) + + for x in range(0, (finish_time - start_time)/10000, 100): + context.move_to(x, 0) + context.line_to(x, height-border*2) + + context.move_to(0, 0) + context.line_to(width-border*2, 0) + + context.move_to(0, height-border*2) + context.line_to(width-border*2, height-border*2) + + context.stroke() + context.restore() + + banner = "Running on %s (%s %s) %s" % (os.uname()[1], os.uname()[2], os.uname()[3], os.uname()[4]) + draw_text(context, 0, -15, banner, hcenter = 0, vcenter = 1) + + for x in range(0, (finish_time - start_time)/10000, 100): + draw_text(context, x, -5, "%lus" % (x/100), vcenter = 0, hcenter = 0) + + y = 0 + + for name, ixt, aet, axt, iet in s: + + drawn = False + left = -1 + + if ixt >= start_time and ixt <= finish_time: + + # Activating + a = ixt - start_time + b = min(filter(lambda x: x >= ixt, (aet, axt, iet, finish_time))) - ixt + + draw_box(context, a/10000, y, b/10000, bar_height, 1, 0, 0) + drawn = True + + if left < 0: + left = a + + if aet >= start_time and aet <= finish_time: + + # Active + a = aet - start_time + b = min(filter(lambda x: x >= aet, (axt, iet, finish_time))) - aet + + draw_box(context, a/10000, y, b/10000, bar_height, .8, .6, .6) + drawn = True + + if left < 0: + left = a + + if axt >= start_time and axt <= finish_time: + + # Deactivating + a = axt - start_time + b = min(filter(lambda x: x >= axt, (iet, finish_time))) - axt + + draw_box(context, a/10000, y, b/10000, bar_height, .6, .4, .4) + drawn = True + + if left < 0: + left = a + + if drawn: + x = left/10000 + + if x < width/2-border: + draw_text(context, x + 10, y + bar_height/2, name, hcenter = 0) + else: + draw_text(context, x - 10, y + bar_height/2, name, hcenter = 1) + + y += bar_height + bar_space + + draw_text(context, 0, height-border*2, "Legend: Red = Activating; Pink = Active; Dark Pink = Deactivating", hcenter = 0, vcenter = -1) + + surface.finish() +elif sys.argv[1] in ("help", "--help", "-h"): + help() +else: + sys.stderr.write("Unknown verb '%s'.\n" % sys.argv[1]) + sys.exit(1) diff --git a/src/systemd-analyze.vala b/src/systemd-analyze.vala new file mode 100644 index 0000000..d0229e4 --- /dev/null +++ b/src/systemd-analyze.vala @@ -0,0 +1,338 @@ +namespace systemd { + static bool user = false; + struct unit_time { + string name; + uint64 ixt; + uint64 aet; + uint64 axt; + uint64 iet; + } + + int8 unit_time_sort_blame(unit_time? a, unit_time? b) { + if ( a == null ) + return 1; + if ( b == null ) + return -1; + var a_diff = a.aet - a.ixt; + var b_diff = b.aet - b.ixt; + if ( a_diff < b_diff ) + return 1; + if ( a_diff > b_diff ) + return -1; + return 0; + } + + int8 unit_time_sort_plot(unit_time? a, unit_time? b) { + if ( a == null ) + return -1; + if ( b == null ) + return 1; + if ( a.ixt < b.ixt ) + return -1; + if ( a.ixt > b.ixt ) + return 1; + return 0; + } + + int8 simple_sort(uint64 a, uint64 b) { + if ( a < b ) + return -1; + if ( a > b ) + return 1; + return 0; + } + + GLib.List acquire_time_data(GLib.CompareFunc sort_func) { + Manager manager; + try { + manager = Bus.get_proxy_sync( + user ? BusType.SESSION : BusType.SYSTEM, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1"); + } catch (IOError e) { + error(e.message); + } + + Manager.UnitInfo[] units; + try { + units = manager.list_units(); + } catch (IOError e) { + error(e.message); + } + var list = new GLib.List(); + + foreach (var i in units) { + Properties p; + try { + p = Bus.get_proxy_sync( + user ? BusType.SESSION : BusType.SYSTEM, + "org.freedesktop.systemd1", + i.unit_path); + } catch (IOError e) { + error(e.message); + } + + var u_t = unit_time(); + u_t.name = i.id; + try { + u_t.ixt = (uint64)(p.get("org.freedesktop.systemd1.Unit", "InactiveExitTimestampMonotonic")); + u_t.aet = (uint64)(p.get("org.freedesktop.systemd1.Unit", "ActiveEnterTimestampMonotonic")); + u_t.axt = (uint64)(p.get("org.freedesktop.systemd1.Unit", "ActiveExitTimestampMonotonic")); + u_t.iet = (uint64)(p.get("org.freedesktop.systemd1.Unit", "InactiveEnterTimestampMonotonic")); + } catch (IOError e) { + error(e.message); + } + list.insert_sorted(u_t, sort_func); + } + + return list; + } + + void acquire_start_time(out uint64 initrd_time, out uint64 startup_time, out uint64 finish_time) { + Properties p; + try { + p = Bus.get_proxy_sync( + user ? BusType.SESSION : BusType.SYSTEM, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1"); + } catch (IOError e) { + error(e.message); + } + + try { + initrd_time = (uint64)(p.get("org.freedesktop.systemd1.Manager", "InitRDTimestampMonotonic")); + startup_time = (uint64)(p.get("org.freedesktop.systemd1.Manager", "StartupTimestampMonotonic")); + finish_time = (uint64)(p.get("org.freedesktop.systemd1.Manager", "FinishTimestampMonotonic")); + } catch (IOError e) { + error(e.message); + } + + assert(initrd_time <= startup_time); + assert(startup_time <= finish_time); + } + + void draw_box(Cairo.Context context, double j, double k, double l, double m, double r = 0, double g = 0, double b = 0) { + context.save(); + context.set_source_rgb(r, g, b); + context.rectangle(j, k, l, m); + context.fill(); + context.restore(); + } + + void draw_text(Cairo.Context context, double x, double y, string text, double hcenter = 0.5, double vcenter = 0.5, double r = 0, double g = 0, double b = 0, double size = 12) { + context.save(); + + context.set_source_rgb(r, g, b); + context.select_font_face("Sans", Cairo.FontSlant.NORMAL, Cairo.FontWeight.NORMAL); + context.set_font_size(size); + + if ( vcenter != 0.0 || hcenter != 0.0 ) { + Cairo.TextExtents extents; + context.text_extents(text, out extents); + + if ( hcenter != 0.0 ) { + x = x - extents.width*hcenter - extents.x_bearing; + } + + if ( vcenter != 0.0 ) { + y = y - extents.height*vcenter - extents.y_bearing; + } + } + + context.move_to(x, y); + context.show_text(text); + + context.restore(); + } + + void help(string arg) { + GLib.print("""%s: Process systemd profiling information +Usage: + %s [ | -h | --help ] + + time Print the standard time information (default) + blame Print the Top10 time consumers + plot Create a SVG file of the boot processs + help As --help + -h, --help + Show this help +""", arg, arg); + } + + + int main(string[] args) { + if ( ( args.length <= 1 ) || ( args[1] == "time" ) ) { + uint64 initrd_time, start_time, finish_time; + acquire_start_time(out initrd_time, out start_time, out finish_time); + + if ( initrd_time > 0 ) { + GLib.print("Startup finished in %lums (kernel) + %lums (initrd) + %lums (userspace) = %lums\n", + (ulong)initrd_time/1000, + (ulong)(start_time - initrd_time)/1000, + (ulong)(finish_time - start_time)/1000, + (ulong)finish_time/1000); + } else { + GLib.print("Startup finished in %lums (kernel) + %lums (userspace) = %lums\n", + (ulong)start_time/1000, + (ulong)(finish_time - start_time)/1000, + (ulong)finish_time/1000); + } + } else if ( args[1] == "blame" ) { + var data = acquire_time_data((GLib.CompareFunc)unit_time_sort_blame); + foreach ( var u_t in data ) { + if ( ( u_t.ixt <= 0 ) || ( u_t.aet <= 0 ) ) + continue; + + if ( u_t.aet <= u_t.ixt ) + continue; + + GLib.print("%6lums %s\n", (ulong)(u_t.aet - u_t.ixt) / 1000, u_t.name); + } + } else if ( args[1] == "plot" ) { + + uint64 initrd_time, start_time, finish_time; + acquire_start_time(out initrd_time, out start_time, out finish_time); + var data = acquire_time_data((GLib.CompareFunc)unit_time_sort_plot); + + var count = 0; + + foreach ( var u_t in data ) { + if ( + ( u_t.ixt >= start_time && u_t.ixt <= finish_time ) + || + ( u_t.aet >= start_time && u_t.aet <= finish_time ) + || + ( u_t.axt >= start_time && u_t.axt <= finish_time ) + ) { + ++count; + } + } + + var border = 50; + var bar_height = 20; + var bar_space = bar_height * 0.1; + + // 1000px = 10s, 1px = 10ms + var width = (finish_time - start_time)/10000 + border * 2; + var height = count * (bar_height + bar_space) + border * 2; + + if ( width < 1000 ) + width = 1000; + + var surface = new Cairo.SvgSurface("/dev/stdout", width, height); + var context = new Cairo.Context(surface); + + + draw_box(context, 0, 0, width, height, 1, 1, 1); + + context.translate(border + 0.5, border + 0.5); + + context.save(); + context.set_line_width(1); + context.set_source_rgb(0.7, 0.7, 0.7); + + for ( double x = 0 ; x <= (finish_time - start_time)/10000 ; x+=100 ) { + context.move_to(x, 0); + context.line_to(x, height-border*2); + } + context.move_to(0, 0); + context.line_to(width-border*2, 0); + + context.move_to(0, height-border*2); + context.line_to(width-border*2, height-border*2); + + context.stroke(); + context.restore(); + + + for ( double x = 0 ; x <= (finish_time - start_time)/10000 ; x+=100 ) { + draw_text(context, x, -5, "%lus".printf((ulong)x/100), 0, 0); + } + + + double y = 0; + foreach ( var u_t in data ) { + var drawn = false; + uint64 left = 0; + + GLib.List list = null; + list.insert_sorted(finish_time, (GLib.CompareFunc)simple_sort); + list.insert_sorted(u_t.iet, (GLib.CompareFunc)simple_sort); + list.insert_sorted(u_t.axt, (GLib.CompareFunc)simple_sort); + list.insert_sorted(u_t.aet, (GLib.CompareFunc)simple_sort); + if ( u_t.ixt >= start_time && u_t.ixt <= finish_time ) { + // Activating + uint64 a = u_t.ixt - start_time; + uint64 c = 0; + foreach ( var i in list ) { + c = i; + if ( c >= u_t.ixt) + break; + } + uint64 b = c - u_t.ixt; + + draw_box(context, a/10000, y, b/10000, bar_height, 1, 0, 0); + + if ( ! drawn ) + left = a; + drawn = true; + } + + if ( u_t.aet >= start_time && u_t.aet <= finish_time ) { + // Active + uint64 a = u_t.aet - start_time; + uint64 c = 0; + foreach ( var i in list ) { + c = i; + if ( c >= u_t.aet) + break; + } + uint64 b = c - u_t.aet; + + draw_box(context, a/10000, y, b/10000, bar_height, 0.8, 0.6, 0.6); + + if ( ! drawn ) + left = a; + drawn = true; + } + + if ( u_t.axt >= start_time && u_t.axt <= finish_time ) { + // Deactivating + uint64 a = u_t.axt - start_time; + uint64 c = 0; + foreach ( var i in list ) { + c = i; + if ( c >= u_t.axt) + break; + } + uint64 b = c - u_t.axt; + + draw_box(context, a/10000, y, b/10000, bar_height, 0.6, 0.4, 0.4); + + if ( ! drawn ) + left = a; + drawn = true; + } + + if ( drawn ) { + var x = left/10000; + + x += ( x < width/2-border ) ? 10 : -10; + + draw_text(context, x, y + bar_height/2, u_t.name, 0); + + y += bar_height + bar_space; + } + } + draw_text(context, 0, height-border*2, "Legend: Red = Activating; Pink = Active; Dark Pink = Deactivating", 0, -1); + + surface.finish(); + + } else if ( ( args[1] == "-h") || ( args[1] == "--help") || ( args[1] == "help" ) ) + help(args[0]); + else + GLib.error("Unknown verb '%s'.\n", args[1]); + + return 0; + } +} -- 1.7.5.3.401.gfb674.dirty