init
This commit is contained in:
269
wcmtools/diskchecker/diskchecker.pl
Executable file
269
wcmtools/diskchecker/diskchecker.pl
Executable file
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Brad's el-ghetto do-our-storage-stacks-lie?-script
|
||||
#
|
||||
|
||||
sub usage {
|
||||
die <<'END';
|
||||
Usage: diskchecker.pl -s <server[:port]> verify <file>
|
||||
diskchecker.pl -s <server[:port]> create <file> <size_in_MB>
|
||||
diskchecker.pl -l [port]
|
||||
END
|
||||
}
|
||||
|
||||
use strict;
|
||||
use IO::Socket::INET;
|
||||
use IO::Handle;
|
||||
use Getopt::Long;
|
||||
use Socket qw(IPPROTO_TCP TCP_NODELAY);
|
||||
|
||||
my $server;
|
||||
my $listen;
|
||||
usage() unless GetOptions('server=s' => \$server,
|
||||
'listen:5400' => \$listen);
|
||||
usage() unless $server || $listen;
|
||||
usage() if $server && $listen;
|
||||
|
||||
# LISTEN MODE:
|
||||
listen_mode($listen) if $listen;
|
||||
|
||||
# CLIENT MODE:
|
||||
my $LEN = 16 * 1024; # 16kB (same as InnoDB page)
|
||||
my $mode = shift;
|
||||
usage() unless $mode =~ /^verify|create$/;
|
||||
|
||||
my $file = shift or usage();
|
||||
my $size;
|
||||
if ($mode eq "create") {
|
||||
$size = shift or usage();
|
||||
}
|
||||
|
||||
$server .= ":5400" unless $server =~ /:/;
|
||||
|
||||
my $sock = IO::Socket::INET->new(PeerAddr => $server)
|
||||
or die "Couldn't connect to host:port of '$server'\n";
|
||||
|
||||
setsockopt($sock, IPPROTO_TCP, TCP_NODELAY, pack("l", 1)) or die;
|
||||
|
||||
create() if $mode eq "create";
|
||||
verify() if $mode eq "verify";
|
||||
exit 0;
|
||||
|
||||
sub verify {
|
||||
sendmsg($sock, "read");
|
||||
|
||||
my $error_ct = 0;
|
||||
my %error_ct;
|
||||
|
||||
my $size = -s $file;
|
||||
my $max_pages = int($size / $LEN);
|
||||
|
||||
my $percent;
|
||||
my $last_dump = 0;
|
||||
my $show_percent = sub {
|
||||
printf " verifying: %.02f%%\n", $percent;
|
||||
};
|
||||
|
||||
open (F, $file) or die "Couldn't open file $file for read\n";
|
||||
|
||||
while (<$sock>) {
|
||||
chomp;
|
||||
my ($page, $good, $val, $ago) = split(/\t/, $_);
|
||||
$percent = 100 * $page / ($max_pages || 1);
|
||||
|
||||
my $now = time;
|
||||
if ($last_dump != $now) {
|
||||
$last_dump = $now;
|
||||
$show_percent->();
|
||||
}
|
||||
|
||||
next unless $good;
|
||||
|
||||
my $offset = $page * $LEN;
|
||||
sysseek F, $offset, 0;
|
||||
my $buf;
|
||||
my $rv = sysread(F, $buf, $LEN);
|
||||
my $tobe = sprintf("%08x", $val) x ($LEN / 8);
|
||||
substr($tobe, $LEN-1, 1) = "\n";
|
||||
|
||||
unless ($buf eq $tobe) {
|
||||
$error_ct{$ago}++;
|
||||
$error_ct++;
|
||||
print " Error at page $page, $ago seconds before end.\n";
|
||||
}
|
||||
}
|
||||
$show_percent->();
|
||||
|
||||
print "Total errors: $error_ct\n";
|
||||
if ($error_ct) {
|
||||
print "Histogram of seconds before end:\n";
|
||||
foreach (sort { $a <=> $b } keys %error_ct) {
|
||||
printf " %4d %4d\n", $_, $error_ct{$_};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub create {
|
||||
open (F, ">$file") or die "Couldn't open file $file\n";
|
||||
|
||||
my $ioh = IO::Handle->new_from_fd(fileno(F), "w")
|
||||
or die;
|
||||
|
||||
my $pages = int( ($size * 1024 * 1024) / $LEN ); # 50 MiB of 16k pages (3200 pages)
|
||||
|
||||
my %page_hit;
|
||||
my $pages_hit = 0;
|
||||
my $uniq_pages_hit = 0;
|
||||
my $start = time();
|
||||
my $last_dump = $start;
|
||||
|
||||
while (1) {
|
||||
my $rand = int rand 2000000;
|
||||
my $buf = sprintf("%08x", $rand) x ($LEN / 8);
|
||||
substr($buf, $LEN-1, 1) = "\n";
|
||||
|
||||
my $pagenum = int rand $pages;
|
||||
my $offset = $pagenum * $LEN;
|
||||
|
||||
sendmsg($sock, "pre\t$pagenum\t$rand");
|
||||
|
||||
# now wait for acknowledgement
|
||||
my $ok = readmsg($sock);
|
||||
die "didn't get 'ok' from server ($pagenum $rand), msg=[$ok] = $!" unless $ok eq "ok";
|
||||
|
||||
sysseek F,$offset,0;
|
||||
my $wv = syswrite(F, $buf, $LEN);
|
||||
die "return value wasn't $LEN\n" unless $wv == $LEN;
|
||||
$ioh->sync or die "couldn't do IO::Handle::sync"; # does fsync
|
||||
|
||||
sendmsg($sock, "post\t$pagenum\t$rand");
|
||||
|
||||
$pages_hit++;
|
||||
unless ($page_hit{$pagenum}++) {
|
||||
$uniq_pages_hit++;
|
||||
}
|
||||
|
||||
my $now = time;
|
||||
if ($now != $last_dump) {
|
||||
$last_dump = $now;
|
||||
my $runtime = $now - $start;
|
||||
printf(" diskchecker: running %d sec, %.02f%% coverage of %d MB (%d writes; %d/s)\n",
|
||||
$runtime,
|
||||
(100 * $uniq_pages_hit / $pages),
|
||||
$size,
|
||||
$pages_hit,
|
||||
$pages_hit / $runtime,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
sub readmsg {
|
||||
my $sock = shift;
|
||||
my $len;
|
||||
my $rv = sysread($sock, $len, 1);
|
||||
return undef unless $rv == 1;
|
||||
my $msg;
|
||||
$rv = sysread($sock, $msg, ord($len));
|
||||
return $msg;
|
||||
}
|
||||
|
||||
sub sendmsg {
|
||||
my ($sock, $msg) = @_;
|
||||
my $rv = syswrite($sock, chr(length($msg)) . $msg);
|
||||
my $expect = length($msg) + 1;
|
||||
die "sendmsg failed rv=$rv, expect=$expect" unless $rv == $expect;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub listen_mode {
|
||||
my $port = shift;
|
||||
my $server = IO::Socket::INET->new(ReuseAddr => 1,
|
||||
Listen => 1,
|
||||
LocalPort => $port)
|
||||
or die "couldn't make server socket\n";
|
||||
|
||||
while (1) {
|
||||
print "[server] diskchecker.pl: waiting for connection...\n";
|
||||
my $sock = $server->accept()
|
||||
or die " die: no connection?";
|
||||
setsockopt($sock, IPPROTO_TCP, TCP_NODELAY, pack("l", 1)) or die;
|
||||
|
||||
fork and next;
|
||||
process_incoming_conn($sock);
|
||||
exit 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub process_incoming_conn {
|
||||
my $sock = shift;
|
||||
my $peername = getpeername($sock) or
|
||||
die "connection not there?\n";
|
||||
my ($port, $iaddr) = sockaddr_in($peername);
|
||||
my $ip = inet_ntoa($iaddr);
|
||||
|
||||
my $file = "/tmp/$ip.diskchecker";
|
||||
die "[$ip] $file is a symlink" if -l $file;
|
||||
|
||||
print "[$ip] New connection\n";
|
||||
|
||||
my $lines = 0;
|
||||
my %state;
|
||||
my $end;
|
||||
|
||||
while (1) {
|
||||
if ($lines) {
|
||||
last unless wait_for_readability(fileno($sock), 3);
|
||||
}
|
||||
my $line = readmsg($sock);
|
||||
last unless $line;
|
||||
|
||||
if ($line eq "read") {
|
||||
print "[$ip] Sending state info from ${ip}'s last create.\n";
|
||||
open (S, "$file") or die "Couldn't open $file for reading.";
|
||||
while (<S>) {
|
||||
print $sock $_;
|
||||
}
|
||||
close S;
|
||||
print "[$ip] Done.\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
$lines++;
|
||||
my $now = time;
|
||||
$end = $now;
|
||||
my ($state, $pagenum, $rand) = split(/\t/, $line);
|
||||
if ($state eq "pre") {
|
||||
$state{$pagenum} = [ 0, $rand+0, $now ];
|
||||
sendmsg($sock, "ok");
|
||||
} elsif ($state eq "post") {
|
||||
$state{$pagenum} = [ 1, $rand+0, $now ];
|
||||
}
|
||||
print "[$ip] $lines writes\n" if $lines % 1000 == 0;
|
||||
}
|
||||
|
||||
print "[$ip] Writing state file...\n";
|
||||
open (S, ">$file") or die "Couldn't open $file for writing.";
|
||||
foreach (sort { $a <=> $b } keys %state) {
|
||||
my $v = $state{$_};
|
||||
my $before_end = $end - $v->[2];
|
||||
print S "$_\t$v->[0]\t$v->[1]\t$before_end\n";
|
||||
}
|
||||
print "[$ip] Done.\n";
|
||||
}
|
||||
|
||||
sub wait_for_readability {
|
||||
my ($fileno, $timeout) = @_;
|
||||
return 0 unless $fileno && $timeout;
|
||||
|
||||
my $rin;
|
||||
vec($rin, $fileno, 1) = 1;
|
||||
my $nfound = select($rin, undef, undef, $timeout);
|
||||
|
||||
return 0 unless defined $nfound;
|
||||
return $nfound ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user