Ok, seems to work so far.
authorHeiko Schlittermann (JUMPER) <hs@schlittermann.de>
Tue, 06 Nov 2012 21:20:45 +0100
changeset 3 944224048b5a
parent 2 6fae69860129
child 4 80051b1cf0ff
Ok, seems to work so far.
.hgignore
pdd
plot-bs.example
plot.example
--- a/.hgignore	Sun Nov 04 20:38:55 2012 +0100
+++ b/.hgignore	Tue Nov 06 21:20:45 2012 +0100
@@ -0,0 +1,2 @@
+syntax:glob
+log-*.log
--- a/pdd	Sun Nov 04 20:38:55 2012 +0100
+++ b/pdd	Tue Nov 06 21:20:45 2012 +0100
@@ -1,79 +1,101 @@
 #!/usr/bin/perl
 
+# Copyright (C) 2012 Heiko Schlittermann <hs@schlittermann.de>
+# These scripts are 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, or (at your option) any
+# later version.
+
 use 5.010;
 use strict;
 use warnings;
 use threads;
 use Getopt::Long;
+use Pod::Usage;
 
 sub dd;
 sub timer;
 sub main;
 sub kbd;
+sub bg;
 
-our $o_interval = 10;
+my $o_interval  = 10;
+my $o_size_from = undef;
+my $o_watch     = undef;
+my $cmd         = "dd";
 
 MAIN: {
     $| = 1;
-    my $dd;
+    my $worker;
 
-    GetOptions("i|interval=i" => \$o_interval)
-	or die "Usage: $0 [-i|--interval n] <dd options>\n";
+    GetOptions(
+        "i|interval=i" => \$o_interval,
+        "h|help"       => sub { pod2usage(-exit => 0, -verbose => 1) },
+        "m|man"        => sub {
+            pod2usage(
+                -exit      => 0,
+                -verbose   => 2,
+                -noperldoc => system("perldoc -V 1>/dev/null 2>&1")
+            );
+        }
+    ) or pod2usage;
 
     $SIG{INT} = sub {
-        if (defined $dd) {
-            warn "Got $_[0], going to kill dd(pid:$dd)\n";
-            kill $_[0] => $dd;
+        if (defined $worker) {
+            warn "Got $_[0], going to kill worker(pid:$worker)\n";
+            kill $_[0] => $worker;
         }
     };
 
-    say "[dd @ARGV]";
-
-    my ($if) = map /^if=(.*)/ => @ARGV;
-    my $ifsize = -s (defined $if ? $if : \*STDIN);
+    say "[$cmd @ARGV]";
 
-    $dd = dd @ARGV;
-
-    # the timer runs in its own thread, there it will
-    # send a USR1 every now and then to the $dd process
+    my ($if) = map { /^if=(.*)/ } @ARGV;
+    my $ifsize = -s (defined $if ? $if : \*STDIN);
+    $worker = dd @ARGV;
 
-    threads->create(\&timer, $dd);
-#    threads->create(\&kbd, $dd);
+    # we use an own thread for the timer
+    # (alarm/SIGALRM would suffice too… but we need
+    # some practice using threads)
+    threads->create(\&timer, $o_interval, $worker);
+    threads->create(\&kbd, $worker);
 
-    printf "%8s %8s %8s %15s %3s\n", 
-	"time", "MB", "M/s", "speed", "done";
+    printf "%8s %8s %8s %15s %3s\n", "elapsed", "MB", "M/s", "speed", "done";
 
-    my ($speed, $time, $size) = (0, 0, 0);
-    my @output = ();
+    my ($speed, $elapsed, $size) = (0, 0, 0);
+    my @output = ();    # collect temporarly the output
+
     while (<DD>) {
-	push @output, $_;
+        push @output, $_;
 
         /^(?<size>\d+)
 	    \s+bytes.*copied,\s+
-	  (?<time>\d+\.\d+)\ss,\s
+	  (?<elapsed>\d+\.\d+)\ss,\s
 	  (?<speed>.*)/x
           or next;
 
-	@output = ();
-        $speed = $+{speed};
-        my $dt = $+{time} - $time;
+        @output = ();
+        $speed  = $+{speed};
+        my $dt = $+{elapsed} - $elapsed;
         my $ds = $+{size} - $size;
 
         printf "%8.3f %8.3f %8.3f %15s %3d\n",
-          $+{time}, $+{size} / 2**20, $dt ? $ds / 2**20 / $dt : "-0", 
-	  $+{speed},
-	  $ifsize? $+{size}/$ifsize*100 : 0;
+          $+{elapsed}, $+{size} / 2**20, $dt ? $ds / 2**20 / $dt : "-0",
+          $+{speed},
+          $ifsize ? $+{size} / $ifsize * 100 : 0;
 
-        $time = $+{time};
-        $size = $+{size};
+        $elapsed = $+{elapsed};
+        $size    = $+{size};
     }
 
+    # overkill for one worker thread, but this way we do not
+    # need to remember its thread id
     map { $_->kill("KILL") } threads->list;
-    map { $_->join } threads->list;
+    map { $_->detach } threads->list;
 
-    if (defined $dd) {
-	waitpid($dd, 0) == $dd or die "unexpected kid died\n";
-	die @output if $?;
+    # wait for the worker sup*process*, if any
+    if (defined $worker) {
+        waitpid($worker, 0) == $worker or die "unexpected kid died\n";
+        die @output, "\n" if $?;
     }
 
     say "[dd @ARGV] => $speed";
@@ -92,17 +114,21 @@
     return $pid;
 }
 
-sub timer {
-    my $pid = shift;
-    local $SIG{"KILL"} = sub { threads->exit };
-    my $i = 0;
+sub bg {
+    my $dd  = fork();
+    my $pid = fork;
+    die "$0: Can't fork\n" if not defined $pid;
+    exec shift() => @_ or die "Can't exec: $!\n" if not $pid;
+    return $pid;
+}
 
-    # just sleep 1 second to make the thread killer more 
-    # that what we would expect
-    while (1) {
-        sleep 1;
-        kill "USR1" => $pid
-          if ++$i % $o_interval == 0;
+sub timer {
+    my ($interval, $pid) = @_;
+    local $SIG{"KILL"} = sub { warn "got @_\n"; threads->exit };
+
+    for (;;) {
+        sleep $interval;
+        kill "USR1" => $pid;
     }
 }
 
@@ -111,7 +137,43 @@
     local $SIG{"KILL"} = sub { threads->exit };
     open(KBD, "/dev/tty") or die "Can't open /dev/tty: $!";
     while (<KBD>) {
-	kill "USR1" => $pid;
+        kill "USR1" => $pid;
     }
     close(KBD);
 }
+
+__END__
+
+=head1 NAME
+
+pdd - perl dd and performance tool
+
+=head1 SYNOPSIS
+
+B<pdd> [-i|--interval n] <dd options>
+
+=head1 DESCRIPTION
+
+B<pdd> is a simple wrapper around B<dd> to create a tabular log 
+suitable as input for B<gnuplot>
+
+=head1 OPTIONS
+
+=over 
+
+=item B<-i>|B<--interval> I<interval>
+
+The tick interval for measurement, unit is seconds. (default: 10)
+
+=item B<-h>|B<--help>
+
+This help/man page.
+
+=back
+
+=head1 AUTHOR
+
+Heiko Schlittermann <hs@schlittermann.de>
+
+=cut
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plot-bs.example	Tue Nov 06 21:20:45 2012 +0100
@@ -0,0 +1,21 @@
+#! /usr/bin/gnuplot
+set terminal qt noraise
+set grid
+set xlabel 'size (MB)'
+set ylabel 'speed (MB/s)'
+set logscale y
+set title 'dd oflag=direct if=/dev/zero of=/dev/sdb'
+plot 'log-dd-512M-bs=1k.log' using 2:3 title 'bs=1k' with points , \
+     'log-dd-512M-bs=2k.log' using 2:3 title 'bs=2k' with points , \
+     'log-dd-512M-bs=4k.log' using 2:3 title 'bs=4k' with points , \
+     'log-dd-512M-bs=8k.log' using 2:3 title 'bs=8k' with points , \
+     'log-dd-512M-bs=16k.log' using 2:3 title 'bs=16k' with points , \
+     'log-dd-512M-bs=32k.log' using 2:3 title 'bs=32k' with points , \
+     'log-dd-512M-bs=64k.log' using 2:3 title 'bs=64k' with points , \
+     'log-dd-512M-bs=128k.log' using 2:3 title 'bs=128k' with points , \
+     'log-dd-512M-bs=256k.log' using 2:3 title 'bs=256k' with points 
+
+# interactive
+pause -1
+# continous
+#while (1) { pause 3; replot; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plot.example	Tue Nov 06 21:20:45 2012 +0100
@@ -0,0 +1,24 @@
+#! /usr/bin/gnuplot
+
+# example script for gnuplot.
+# I'm sure, it could be more sophisticated…
+
+set terminal qt noraise
+set grid
+set xlabel 'time (s)'
+set ylabel 'speed (MB/s)'
+set logscale y
+set title 'dd if=bigFile …'
+plot 'log-big-dd-async.log' using 1:3 title 'dd of=/dev/sdb' with lines, \
+     'log-big-dd-sync.log'  using 1:3 title 'dd of=/dev/sdb oflag=sync' with lines, \
+     'log-big-vfat-default.log'  using 1:3 title 'dd of=<vfat default>' with lines, \
+     'log-big-ext2-default.log'  using 1:3 title 'dd of=<ext2 default>' with lines, \
+     'log-big-ext4-default.log'  using 1:3 title 'dd of=<ext4 default>' with lines, \
+     'log-big-btrfs-default.log'  using 1:3 title 'dd of=<btrfs default>' with lines
+
+
+# interaktiv
+pause -1
+
+# Verlauf anzeigen
+#while (1) { pause 3; replot; }