Ok, seems to work so far.
--- 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; }