tests/posix: Drop bonnie++, recipe exists and not api test
This commit is contained in:
@@ -77,6 +77,5 @@ SEARCH on [ FGristFiles
|
||||
syslog.cpp
|
||||
] = [ FDirName $(HAIKU_TOP) src system libroot posix ] ;
|
||||
|
||||
SubInclude HAIKU_TOP src tests system libroot posix bonnie++-1.03d ;
|
||||
SubInclude HAIKU_TOP src tests system libroot posix math ;
|
||||
SubInclude HAIKU_TOP src tests system libroot posix string ;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
SubDir HAIKU_TOP src tests system libroot posix bonnie++-1.03d ;
|
||||
|
||||
SimpleTest bonnie++ :
|
||||
bon_io.cpp
|
||||
bon_file.cpp
|
||||
bon_time.cpp
|
||||
semaphore.cpp
|
||||
forkit.cpp
|
||||
bon_suid.cpp
|
||||
|
||||
bonnie++.cpp
|
||||
:
|
||||
[ TargetLibsupc++ ]
|
||||
;
|
||||
|
||||
SimpleTest zcav :
|
||||
bon_suid.cpp
|
||||
zcav.cpp
|
||||
:
|
||||
[ TargetLibsupc++ ]
|
||||
;
|
||||
@@ -1,57 +0,0 @@
|
||||
EXES=bonnie++ zcav
|
||||
|
||||
all: $(EXES)
|
||||
|
||||
SCRIPTS=bon_csv2html bon_csv2txt
|
||||
|
||||
prefix=/home/rjc/debian/bonnie++-1.03d/debian/bonnie++/usr
|
||||
eprefix=${prefix}
|
||||
#MORE_WARNINGS=-Weffc++ -Wcast-align
|
||||
WFLAGS=-Wall -W -Wshadow -Wpointer-arith -Wwrite-strings -pedantic -ffor-scope $(MORE_WARNINGS)
|
||||
CFLAGS=-O2 -DNDEBUG $(WFLAGS) $(MORECFLAGS)
|
||||
CXX=g++ $(CFLAGS)
|
||||
|
||||
INSTALL=/usr/bin/install -c
|
||||
INSTALL_PROGRAM=${INSTALL}
|
||||
|
||||
BONSRC=bon_io.cpp bon_file.cpp bon_time.cpp semaphore.cpp forkit.cpp \
|
||||
bon_suid.cpp
|
||||
BONOBJS=$(BONSRC:.cpp=.o)
|
||||
|
||||
MAN1=bon_csv2html.1 bon_csv2txt.1
|
||||
MAN8=bonnie++.8 zcav.8
|
||||
|
||||
ZCAVSRC=bon_suid.cpp
|
||||
ZCAVOBJS=$(ZCAVSRC:.cpp=.o)
|
||||
|
||||
ALLOBJS=$(BONOBJS) $(ZCAVOBJS)
|
||||
|
||||
bonnie++: bonnie++.cpp $(BONOBJS)
|
||||
$(CXX) bonnie++.cpp -o bonnie++ $(BONOBJS) $(LFLAGS)
|
||||
|
||||
zcav: zcav.cpp $(ZCAVOBJS)
|
||||
$(CXX) zcav.cpp -o zcav $(ZCAVOBJS) $(LFLAGS)
|
||||
|
||||
install-bin: $(EXES)
|
||||
mkdir -p $(eprefix)/bin $(eprefix)/sbin
|
||||
${INSTALL} -s $(EXES) $(eprefix)/sbin
|
||||
${INSTALL} $(SCRIPTS) $(eprefix)/bin
|
||||
|
||||
install: install-bin
|
||||
mkdir -p /home/rjc/debian/bonnie++-1.03d/debian/bonnie++/usr/share/man/man1 /home/rjc/debian/bonnie++-1.03d/debian/bonnie++/usr/share/man/man8
|
||||
${INSTALL} -m 644 $(MAN1) /home/rjc/debian/bonnie++-1.03d/debian/bonnie++/usr/share/man/man1
|
||||
${INSTALL} -m 644 $(MAN8) /home/rjc/debian/bonnie++-1.03d/debian/bonnie++/usr/share/man/man8
|
||||
|
||||
%.o: %.cpp %.h bonnie.h port.h
|
||||
$(CXX) -c $<
|
||||
|
||||
bon_suid.o: bon_suid.cpp bonnie.h port.h
|
||||
$(CXX) -c $<
|
||||
|
||||
clean:
|
||||
rm -f $(EXES) $(ALLOBJS) build-stamp install-stamp
|
||||
rm -rf debian/tmp core debian/*.debhelper
|
||||
rm -f debian/{substvars,files} config.log
|
||||
|
||||
realclean: clean
|
||||
rm -f config.* Makefile bonnie.h port.h bonnie++.spec bon_csv2html bon_csv2txt sun/pkginfo
|
||||
@@ -1,57 +0,0 @@
|
||||
EXES=@exes@
|
||||
|
||||
all: $(EXES)
|
||||
|
||||
SCRIPTS=bon_csv2html bon_csv2txt
|
||||
|
||||
prefix=@prefix@
|
||||
eprefix=@exec_prefix@
|
||||
#MORE_WARNINGS=-Weffc++ -Wcast-align
|
||||
WFLAGS=-Wall -W -Wshadow -Wpointer-arith -Wwrite-strings -pedantic -ffor-scope $(MORE_WARNINGS)
|
||||
CFLAGS=-O2 @debug@ -DNDEBUG $(WFLAGS) $(MORECFLAGS)
|
||||
CXX=@CXX@ $(CFLAGS)
|
||||
|
||||
INSTALL=@INSTALL@
|
||||
INSTALL_PROGRAM=@INSTALL_PROGRAM@
|
||||
|
||||
BONSRC=bon_io.cpp bon_file.cpp bon_time.cpp semaphore.cpp forkit.cpp \
|
||||
bon_suid.cpp
|
||||
BONOBJS=$(BONSRC:.cpp=.o)
|
||||
|
||||
MAN1=bon_csv2html.1 bon_csv2txt.1
|
||||
MAN8=@man8@
|
||||
|
||||
ZCAVSRC=bon_suid.cpp
|
||||
ZCAVOBJS=$(ZCAVSRC:.cpp=.o)
|
||||
|
||||
ALLOBJS=$(BONOBJS) $(ZCAVOBJS)
|
||||
|
||||
bonnie++: bonnie++.cpp $(BONOBJS)
|
||||
$(CXX) bonnie++.cpp -o bonnie++ $(BONOBJS) $(LFLAGS)
|
||||
|
||||
zcav: zcav.cpp $(ZCAVOBJS)
|
||||
$(CXX) zcav.cpp -o zcav $(ZCAVOBJS) $(LFLAGS)
|
||||
|
||||
install-bin: $(EXES)
|
||||
mkdir -p $(eprefix)/bin $(eprefix)/sbin
|
||||
@INSTALL_PROGRAM@ @stripping@ $(EXES) $(eprefix)/sbin
|
||||
@INSTALL_SCRIPT@ $(SCRIPTS) $(eprefix)/bin
|
||||
|
||||
install: install-bin
|
||||
mkdir -p @mandir@/man1 @mandir@/man8
|
||||
@INSTALL_DATA@ $(MAN1) @mandir@/man1
|
||||
@INSTALL_DATA@ $(MAN8) @mandir@/man8
|
||||
|
||||
%.o: %.cpp %.h bonnie.h port.h
|
||||
$(CXX) -c $<
|
||||
|
||||
bon_suid.o: bon_suid.cpp bonnie.h port.h
|
||||
$(CXX) -c $<
|
||||
|
||||
clean:
|
||||
rm -f $(EXES) $(ALLOBJS) build-stamp install-stamp
|
||||
rm -rf debian/tmp core debian/*.debhelper
|
||||
rm -f debian/{substvars,files} config.log
|
||||
|
||||
realclean: clean
|
||||
rm -f config.* Makefile bonnie.h port.h bonnie++.spec bon_csv2html bon_csv2txt sun/pkginfo
|
||||
@@ -1,101 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
{
|
||||
header();
|
||||
my $line;
|
||||
while($line = <STDIN>)
|
||||
{
|
||||
while($line =~ /^name,/)
|
||||
{
|
||||
$line = <STDIN>;
|
||||
}
|
||||
process($line);
|
||||
}
|
||||
footer();
|
||||
}
|
||||
|
||||
sub header
|
||||
{
|
||||
print <<END;
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<HTML>
|
||||
<HEAD><TITLE>Bonnie++ V1.03d Benchmark results</TITLE>
|
||||
<STYLE type="text/css">
|
||||
TD.header {text-align: center; backgroundcolor: "#CCFFFF" }
|
||||
TD.rowheader {text-align: center; backgroundcolor: "#CCCFFF" }
|
||||
TD.size {text-align: center; backgroundcolor: "#CCCFFF" }
|
||||
TD.ksec {text-align: center; fontstyle: italic }
|
||||
</STYLE>
|
||||
<BODY>
|
||||
<TABLE ALIGN=center BORDER=3 CELLPADDING=2 CELLSPACING=1>
|
||||
<TR><TD COLSPAN=2 class="header"></TD>
|
||||
<TD COLSPAN=6 class="header"><FONT SIZE=+2><B>Sequential Output</B></FONT></TD>
|
||||
<TD COLSPAN=4 class="header"><FONT SIZE=+2><B>Sequential Input</B></FONT></TD>
|
||||
<TD COLSPAN=2 ROWSPAN=2 class="header"><FONT SIZE=+2><B>Random<BR>Seeks</B></FONT></TD>
|
||||
<TD COLSPAN=1 class="header"></TD>
|
||||
<TD COLSPAN=6 class="header"><FONT SIZE=+2><B>Sequential Create</B></FONT></TD>
|
||||
<TD COLSPAN=6 class="header"><FONT SIZE=+2><B>Random Create</B></FONT></TD>
|
||||
</tr>
|
||||
END
|
||||
print "<TR><TD></TD>";
|
||||
print "<TD>Size:Chunk Size</TD>";
|
||||
heading("Per Char"); heading("Block"); heading("Rewrite");
|
||||
heading("Per Char"); heading("Block");
|
||||
print "<TD>Num Files</TD>";
|
||||
heading("Create"); heading("Read"); heading("Delete");
|
||||
heading("Create"); heading("Read"); heading("Delete");
|
||||
print "</TR>";
|
||||
|
||||
print "<TR><TD COLSPAN=2></TD>";
|
||||
my $i;
|
||||
|
||||
for($i = 0; $i < 5; $i++)
|
||||
{
|
||||
print '<TD class="ksec"><FONT SIZE=-2>K/sec</FONT></TD>'
|
||||
. '<TD class="ksec"><FONT SIZE=-2>% CPU</FONT></TD>';
|
||||
}
|
||||
print '<TD class="ksec"><FONT SIZE=-2>/ sec</FONT></TD>'
|
||||
. '<TD class="ksec"><FONT SIZE=-2>% CPU</FONT></TD>';
|
||||
print "<TD></TD>";
|
||||
for($i = 0; $i < 6; $i++)
|
||||
{
|
||||
print '<TD class="ksec"><FONT SIZE=-2>/ sec</FONT></TD>'
|
||||
. '<TD class="ksec"><FONT SIZE=-2>% CPU</FONT></TD>';
|
||||
}
|
||||
print "</TR>\n";
|
||||
}
|
||||
|
||||
sub heading
|
||||
{
|
||||
my($item) = @_;
|
||||
print "<TD COLSPAN=2>" . $item . "</TD>";
|
||||
}
|
||||
|
||||
sub footer
|
||||
{
|
||||
print <<END;
|
||||
</TABLE>
|
||||
</BODY>
|
||||
</HTML>
|
||||
END
|
||||
}
|
||||
|
||||
sub process
|
||||
{
|
||||
my($line) = @_;
|
||||
|
||||
chop($line);
|
||||
my $name = $line;
|
||||
$name =~ s/,.*$//;
|
||||
$line =~ s/$name,//;
|
||||
my $size = $line;
|
||||
$size =~ s/,.*$//;
|
||||
$line =~ s/$size,//;
|
||||
print '<TR><TD class="rowheader"><FONT SIZE=+1><B>'
|
||||
. $name . "</B></FONT></TD>";
|
||||
print '<TD class="size">' . $size . "</TD>";
|
||||
|
||||
|
||||
$line =~ s/,/<\/TD><TD>/g;
|
||||
print "<TD>" . $line . "</TD></TR>\n";
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
.TH bon_csv2html 1
|
||||
.SH "NAME"
|
||||
.BR bon_csv2html
|
||||
program to convert CSV format Bonnie++ data to a HTML form
|
||||
using tables suitable for display on a web page. NB Lynx can't display this
|
||||
properly, and due to the size it probably requires 1024x768 monitor to display
|
||||
properly.
|
||||
.P
|
||||
.BR bon_csv2txt
|
||||
program to convert CSV format Bonnie++ data to plain\-text format
|
||||
suitable for pasting into an email or reading on a Braille display.
|
||||
|
||||
.SH "DESCRIPTION"
|
||||
They take CSV format (comma\-delimited spreadsheet files AKA Comma Seperated
|
||||
Values in MS land) data on standard input and produce HTML or plain text on
|
||||
standard output respectively.
|
||||
|
||||
.SH "AUTHOR"
|
||||
These programs were written by Russell Coker <[email protected]>. May be
|
||||
freely used and distributed without restriction.
|
||||
|
||||
.SH "AVAILABILITY"
|
||||
The source is available from http://www.coker.com.au/bonnie++ .
|
||||
.P
|
||||
See http://etbe.coker.com.au/category/benchmark for further information.
|
||||
@@ -1,101 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
{
|
||||
header();
|
||||
my $line;
|
||||
while($line = <STDIN>)
|
||||
{
|
||||
while($line =~ /^name,/)
|
||||
{
|
||||
$line = <STDIN>;
|
||||
}
|
||||
process($line);
|
||||
}
|
||||
footer();
|
||||
}
|
||||
|
||||
sub header
|
||||
{
|
||||
print <<END;
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<HTML>
|
||||
<HEAD><TITLE>Bonnie++ V@version@ Benchmark results</TITLE>
|
||||
<STYLE type="text/css">
|
||||
TD.header {text-align: center; backgroundcolor: "#CCFFFF" }
|
||||
TD.rowheader {text-align: center; backgroundcolor: "#CCCFFF" }
|
||||
TD.size {text-align: center; backgroundcolor: "#CCCFFF" }
|
||||
TD.ksec {text-align: center; fontstyle: italic }
|
||||
</STYLE>
|
||||
<BODY>
|
||||
<TABLE ALIGN=center BORDER=3 CELLPADDING=2 CELLSPACING=1>
|
||||
<TR><TD COLSPAN=2 class="header"></TD>
|
||||
<TD COLSPAN=6 class="header"><FONT SIZE=+2><B>Sequential Output</B></FONT></TD>
|
||||
<TD COLSPAN=4 class="header"><FONT SIZE=+2><B>Sequential Input</B></FONT></TD>
|
||||
<TD COLSPAN=2 ROWSPAN=2 class="header"><FONT SIZE=+2><B>Random<BR>Seeks</B></FONT></TD>
|
||||
<TD COLSPAN=1 class="header"></TD>
|
||||
<TD COLSPAN=6 class="header"><FONT SIZE=+2><B>Sequential Create</B></FONT></TD>
|
||||
<TD COLSPAN=6 class="header"><FONT SIZE=+2><B>Random Create</B></FONT></TD>
|
||||
</tr>
|
||||
END
|
||||
print "<TR><TD></TD>";
|
||||
print "<TD>Size:Chunk Size</TD>";
|
||||
heading("Per Char"); heading("Block"); heading("Rewrite");
|
||||
heading("Per Char"); heading("Block");
|
||||
print "<TD>Num Files</TD>";
|
||||
heading("Create"); heading("Read"); heading("Delete");
|
||||
heading("Create"); heading("Read"); heading("Delete");
|
||||
print "</TR>";
|
||||
|
||||
print "<TR><TD COLSPAN=2></TD>";
|
||||
my $i;
|
||||
|
||||
for($i = 0; $i < 5; $i++)
|
||||
{
|
||||
print '<TD class="ksec"><FONT SIZE=-2>K/sec</FONT></TD>'
|
||||
. '<TD class="ksec"><FONT SIZE=-2>% CPU</FONT></TD>';
|
||||
}
|
||||
print '<TD class="ksec"><FONT SIZE=-2>/ sec</FONT></TD>'
|
||||
. '<TD class="ksec"><FONT SIZE=-2>% CPU</FONT></TD>';
|
||||
print "<TD></TD>";
|
||||
for($i = 0; $i < 6; $i++)
|
||||
{
|
||||
print '<TD class="ksec"><FONT SIZE=-2>/ sec</FONT></TD>'
|
||||
. '<TD class="ksec"><FONT SIZE=-2>% CPU</FONT></TD>';
|
||||
}
|
||||
print "</TR>\n";
|
||||
}
|
||||
|
||||
sub heading
|
||||
{
|
||||
my($item) = @_;
|
||||
print "<TD COLSPAN=2>" . $item . "</TD>";
|
||||
}
|
||||
|
||||
sub footer
|
||||
{
|
||||
print <<END;
|
||||
</TABLE>
|
||||
</BODY>
|
||||
</HTML>
|
||||
END
|
||||
}
|
||||
|
||||
sub process
|
||||
{
|
||||
my($line) = @_;
|
||||
|
||||
chop($line);
|
||||
my $name = $line;
|
||||
$name =~ s/,.*$//;
|
||||
$line =~ s/$name,//;
|
||||
my $size = $line;
|
||||
$size =~ s/,.*$//;
|
||||
$line =~ s/$size,//;
|
||||
print '<TR><TD class="rowheader"><FONT SIZE=+1><B>'
|
||||
. $name . "</B></FONT></TD>";
|
||||
print '<TD class="size">' . $size . "</TD>";
|
||||
|
||||
|
||||
$line =~ s/,/<\/TD><TD>/g;
|
||||
print "<TD>" . $line . "</TD></TR>\n";
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
{
|
||||
my $line;
|
||||
$iocount = 0;
|
||||
$filecount = 0;
|
||||
while($line = <STDIN>)
|
||||
{
|
||||
while($line =~ /^name,/)
|
||||
{
|
||||
$line = <STDIN>;
|
||||
}
|
||||
process($line);
|
||||
}
|
||||
printIOData();
|
||||
|
||||
printFileData();
|
||||
}
|
||||
|
||||
sub IOHeader
|
||||
{
|
||||
my $version = '1.03d';
|
||||
print "Version $version ------Sequential Output------ --Sequential Input- --Random-\n";
|
||||
print " -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--\n";
|
||||
print "Machine Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP /sec %CP\n";
|
||||
}
|
||||
|
||||
sub FileHeader
|
||||
{
|
||||
print " ------Sequential Create------ --------Random Create--------\n";
|
||||
print " -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--\n";
|
||||
print "files:max:min /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP\n";
|
||||
}
|
||||
|
||||
sub process
|
||||
{
|
||||
my($line) = @_;
|
||||
|
||||
chop($line);
|
||||
my $name = $line;
|
||||
$name =~ s/,.*$//;
|
||||
$line =~ s/$name,//;
|
||||
|
||||
# $ioline contains the file-IO data, IE we strip off the data on file creation
|
||||
my $ioline = $line;
|
||||
$ioline =~ s/,[:0-9\-\+.\/]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+$//;
|
||||
$ioline =~ s/,,,,,,,,,,,,,$//;
|
||||
if(join('', split(/,/,$ioline)) != "")
|
||||
{
|
||||
$io[$iocount] = $name . "," . $ioline;
|
||||
$iocount++;
|
||||
}
|
||||
# $fileline contains the file creation data - everything but $ioline
|
||||
my $fileline = substr($line, length($ioline));
|
||||
if(join('', split(/,/,$fileline)) != "")
|
||||
{
|
||||
$file[$filecount] = $name . $fileline;
|
||||
$filecount++;
|
||||
}
|
||||
}
|
||||
|
||||
sub printFileData
|
||||
{
|
||||
if($filecount < 1){ return; }
|
||||
FileHeader();
|
||||
for($i = 0; $i < $filecount; $i++)
|
||||
{
|
||||
@arr = split(/,/,$file[$i]);
|
||||
my $size = $arr[1];
|
||||
$size =~ s/:0:0:/\//;
|
||||
my $desc = $arr[0] . " ";
|
||||
$desc = substr($desc, 0, 18 - length($size) );
|
||||
$desc .= " " . $size;
|
||||
printf(STDOUT "%19s %5s %3s %5s %3s %5s %3s %5s %3s %5s %3s %5s %3s\n"
|
||||
, $desc, $arr[2], $arr[3], $arr[4], $arr[5], $arr[6], $arr[7]
|
||||
, $arr[8], $arr[9], $arr[10], $arr[11], $arr[12], $arr[13]
|
||||
, $arr[14], $arr[15]);
|
||||
}
|
||||
}
|
||||
|
||||
sub printIOData
|
||||
{
|
||||
if($iocount < 1){ return; }
|
||||
IOHeader();
|
||||
for($i = 0; $i < $iocount; $i++)
|
||||
{
|
||||
@arr = split(/,/,$io[$i]);
|
||||
my $size = $arr[1];
|
||||
my $desc = $arr[0] . " ";
|
||||
$desc = substr($desc, 0, 18 - length($size) );
|
||||
$desc .= " " . $size;
|
||||
printf(STDOUT "%19s %5s %3s %5s %3s %5s %3s %5s %3s %5s %3s %5s %3s\n"
|
||||
, $desc, $arr[2], $arr[3], $arr[4], $arr[5], $arr[6], $arr[7]
|
||||
, $arr[8], $arr[9], $arr[10], $arr[11], $arr[12], $arr[13]
|
||||
, $arr[14], $arr[15]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.so man1/bon_csv2html.1
|
||||
@@ -1,98 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
{
|
||||
my $line;
|
||||
$iocount = 0;
|
||||
$filecount = 0;
|
||||
while($line = <STDIN>)
|
||||
{
|
||||
while($line =~ /^name,/)
|
||||
{
|
||||
$line = <STDIN>;
|
||||
}
|
||||
process($line);
|
||||
}
|
||||
printIOData();
|
||||
|
||||
printFileData();
|
||||
}
|
||||
|
||||
sub IOHeader
|
||||
{
|
||||
my $version = '@version@';
|
||||
print "Version $version ------Sequential Output------ --Sequential Input- --Random-\n";
|
||||
print " -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--\n";
|
||||
print "Machine Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP /sec %CP\n";
|
||||
}
|
||||
|
||||
sub FileHeader
|
||||
{
|
||||
print " ------Sequential Create------ --------Random Create--------\n";
|
||||
print " -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--\n";
|
||||
print "files:max:min /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP\n";
|
||||
}
|
||||
|
||||
sub process
|
||||
{
|
||||
my($line) = @_;
|
||||
|
||||
chop($line);
|
||||
my $name = $line;
|
||||
$name =~ s/,.*$//;
|
||||
$line =~ s/$name,//;
|
||||
|
||||
# $ioline contains the file-IO data, IE we strip off the data on file creation
|
||||
my $ioline = $line;
|
||||
$ioline =~ s/,[:0-9\-\+.\/]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+,[0-9\+.]+$//;
|
||||
$ioline =~ s/,,,,,,,,,,,,,$//;
|
||||
if(join('', split(/,/,$ioline)) != "")
|
||||
{
|
||||
$io[$iocount] = $name . "," . $ioline;
|
||||
$iocount++;
|
||||
}
|
||||
# $fileline contains the file creation data - everything but $ioline
|
||||
my $fileline = substr($line, length($ioline));
|
||||
if(join('', split(/,/,$fileline)) != "")
|
||||
{
|
||||
$file[$filecount] = $name . $fileline;
|
||||
$filecount++;
|
||||
}
|
||||
}
|
||||
|
||||
sub printFileData
|
||||
{
|
||||
if($filecount < 1){ return; }
|
||||
FileHeader();
|
||||
for($i = 0; $i < $filecount; $i++)
|
||||
{
|
||||
@arr = split(/,/,$file[$i]);
|
||||
my $size = $arr[1];
|
||||
$size =~ s/:0:0:/\//;
|
||||
my $desc = $arr[0] . " ";
|
||||
$desc = substr($desc, 0, 18 - length($size) );
|
||||
$desc .= " " . $size;
|
||||
printf(STDOUT "%19s %5s %3s %5s %3s %5s %3s %5s %3s %5s %3s %5s %3s\n"
|
||||
, $desc, $arr[2], $arr[3], $arr[4], $arr[5], $arr[6], $arr[7]
|
||||
, $arr[8], $arr[9], $arr[10], $arr[11], $arr[12], $arr[13]
|
||||
, $arr[14], $arr[15]);
|
||||
}
|
||||
}
|
||||
|
||||
sub printIOData
|
||||
{
|
||||
if($iocount < 1){ return; }
|
||||
IOHeader();
|
||||
for($i = 0; $i < $iocount; $i++)
|
||||
{
|
||||
@arr = split(/,/,$io[$i]);
|
||||
my $size = $arr[1];
|
||||
my $desc = $arr[0] . " ";
|
||||
$desc = substr($desc, 0, 18 - length($size) );
|
||||
$desc .= " " . $size;
|
||||
printf(STDOUT "%19s %5s %3s %5s %3s %5s %3s %5s %3s %5s %3s %5s %3s\n"
|
||||
, $desc, $arr[2], $arr[3], $arr[4], $arr[5], $arr[6], $arr[7]
|
||||
, $arr[8], $arr[9], $arr[10], $arr[11], $arr[12], $arr[13]
|
||||
, $arr[14], $arr[15]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,682 +0,0 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef OS2
|
||||
#define INCL_DOSFILEMGR
|
||||
#include <os2.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "bon_file.h"
|
||||
#include "bon_time.h"
|
||||
|
||||
CPCCHAR rand_chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
|
||||
COpenTest::COpenTest(int chunk_size, bool use_sync, bool *doExit)
|
||||
: m_chunk_size(chunk_size)
|
||||
, m_number(0)
|
||||
, m_number_directories(1)
|
||||
, m_max(0)
|
||||
, m_min(0)
|
||||
, m_size_range(0)
|
||||
, m_dirname(NULL)
|
||||
, m_file_name_buf(NULL)
|
||||
, m_file_names(NULL)
|
||||
, m_sync(use_sync)
|
||||
, m_directoryHandles(NULL)
|
||||
, m_dirIndex(NULL)
|
||||
, m_buf(new char[m_chunk_size])
|
||||
, m_exit(doExit)
|
||||
, m_sync_dir(true)
|
||||
{
|
||||
}
|
||||
|
||||
void COpenTest::random_sort()
|
||||
{
|
||||
for(int i = 0; i < m_number; i++)
|
||||
{
|
||||
char *tmp = m_file_names[i];
|
||||
int newind = rand() % m_number;
|
||||
m_file_names[i] = m_file_names[newind];
|
||||
m_file_names[newind] = tmp;
|
||||
if(m_dirIndex)
|
||||
{
|
||||
int tmpInd = m_dirIndex[i];
|
||||
m_dirIndex[i] = m_dirIndex[newind];
|
||||
m_dirIndex[newind] = tmpInd;
|
||||
}
|
||||
if(*m_exit) return;
|
||||
}
|
||||
}
|
||||
|
||||
COpenTest::~COpenTest()
|
||||
{
|
||||
int i;
|
||||
if(m_dirname)
|
||||
{
|
||||
fprintf(stderr, "Cleaning up test directory after error.\n");
|
||||
if(m_file_names)
|
||||
{
|
||||
for(i = 0; i < m_number; i++)
|
||||
unlink(m_file_names[i]);
|
||||
}
|
||||
if(m_number_directories > 1)
|
||||
{
|
||||
char buf[6];
|
||||
for(i = 0; i < m_number_directories; i++)
|
||||
{
|
||||
sprintf(buf, "%05d", i);
|
||||
if(rmdir(buf))
|
||||
io_error("rmdir");
|
||||
}
|
||||
}
|
||||
if(chdir("..") || rmdir(m_dirname))
|
||||
io_error("rmdir");
|
||||
delete m_dirname;
|
||||
}
|
||||
if(m_directoryHandles)
|
||||
{
|
||||
for(i = 0; i < m_number_directories; i++)
|
||||
close(m_directoryHandles[i]);
|
||||
delete m_directoryHandles;
|
||||
}
|
||||
delete m_file_name_buf;
|
||||
delete m_file_names;
|
||||
delete m_dirIndex;
|
||||
delete m_buf;
|
||||
}
|
||||
|
||||
void COpenTest::make_names(bool do_random)
|
||||
{
|
||||
delete m_file_name_buf;
|
||||
delete m_file_names;
|
||||
int names_per_directory = m_number / m_number_directories;
|
||||
int names_in_dir = 0;
|
||||
int directory_num = 0;
|
||||
if(!m_dirIndex && m_sync)
|
||||
m_dirIndex = new int[m_number];
|
||||
if(m_number_directories == 1)
|
||||
{
|
||||
m_file_name_buf = new char[(MaxNameLen + 1) * m_number];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_file_name_buf = new char[(MaxNameLen + 1 + 6) * m_number];
|
||||
}
|
||||
m_file_names = new PCHAR[m_number];
|
||||
PCHAR buf = m_file_name_buf;
|
||||
for(int i = 0; i < m_number; i++)
|
||||
{
|
||||
if(*m_exit)
|
||||
{
|
||||
delete m_file_names;
|
||||
m_file_names = NULL;
|
||||
return;
|
||||
}
|
||||
char rand_buf[RandExtraLen + 1];
|
||||
int len = rand() % (RandExtraLen + 1);
|
||||
int j;
|
||||
for(j = 0; j < len; j++)
|
||||
{
|
||||
rand_buf[j] = rand_chars[rand() % strlen(rand_chars)];
|
||||
}
|
||||
rand_buf[j] = '\0';
|
||||
m_file_names[i] = buf;
|
||||
if(m_number_directories != 1)
|
||||
{
|
||||
sprintf(buf, "%05d/", directory_num);
|
||||
buf += strlen(buf);
|
||||
}
|
||||
if(m_sync)
|
||||
m_dirIndex[i] = directory_num;
|
||||
names_in_dir++;
|
||||
if(names_in_dir > names_per_directory)
|
||||
{
|
||||
names_in_dir = 0;
|
||||
directory_num++;
|
||||
}
|
||||
if(do_random)
|
||||
{
|
||||
sprintf(buf, "%s%07d", rand_buf, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(buf, "%07d%s", i, rand_buf);
|
||||
}
|
||||
buf += strlen(buf) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int COpenTest::create_a_file(const char *filename, char *buf, int size, int dir)
|
||||
{
|
||||
FILE_TYPE fd = 0;
|
||||
#ifdef OS2
|
||||
ULONG action = 0;
|
||||
ULONG rc = DosOpen(filename, &fd, &action, 0, FILE_NORMAL
|
||||
, OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS
|
||||
, OPEN_FLAGS_SEQUENTIAL | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE
|
||||
, NULL);
|
||||
#else
|
||||
fd = creat(filename, S_IRUSR | S_IWUSR);
|
||||
#endif
|
||||
if(fd == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't create file %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
if(m_max)
|
||||
{
|
||||
for(int i = 0; i < size; i += m_chunk_size)
|
||||
{
|
||||
int to_write = size - i;
|
||||
if(to_write > m_chunk_size) to_write = m_chunk_size;
|
||||
#ifdef OS2
|
||||
ULONG actual = 0;
|
||||
if(DosWrite(fd, PVOID(buf), to_write, &actual))
|
||||
{
|
||||
fprintf(stderr, "Can't write data.\n");
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
if(to_write != write(fd, static_cast<void *>(buf), to_write))
|
||||
{
|
||||
fprintf(stderr, "Can't write data.\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if(m_sync)
|
||||
{
|
||||
if(fsync(fd))
|
||||
{
|
||||
fprintf(stderr, "Can't sync file.\n");
|
||||
return -1;
|
||||
}
|
||||
if(m_sync_dir && fsync(m_directoryHandles[dir]))
|
||||
{
|
||||
fprintf(stderr, "Can't sync directory, turning off dir-sync.\n");
|
||||
m_sync_dir = false;
|
||||
}
|
||||
}
|
||||
file_close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int COpenTest::create_a_link(const char *original, const char *filename, int dir)
|
||||
{
|
||||
#ifdef OS2
|
||||
fprintf(stderr, "Not supported on OS/2\n");
|
||||
return -1;
|
||||
#else
|
||||
if(m_max == -1)
|
||||
{
|
||||
if(link(original, filename))
|
||||
{
|
||||
fprintf(stderr, "Can't create link %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
if(m_sync)
|
||||
{
|
||||
if(fsync(m_directoryHandles[dir]))
|
||||
{
|
||||
fprintf(stderr, "Can't sync file.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(symlink(original, filename))
|
||||
{
|
||||
fprintf(stderr, "Can't create symlink %s\n", filename);
|
||||
return -1;
|
||||
}
|
||||
if(m_sync)
|
||||
{
|
||||
if(fsync(m_directoryHandles[dir]))
|
||||
{
|
||||
fprintf(stderr, "Can't sync file.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int COpenTest::create(CPCCHAR dirname, BonTimer &timer, int num, int max_size
|
||||
, int min_size, int num_directories, bool do_random)
|
||||
{
|
||||
if(num_directories >= 100000)
|
||||
{
|
||||
fprintf(stderr, "Can't have more than 99,999 directories.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_number = num * DirectoryUnit;
|
||||
m_number_directories = num_directories;
|
||||
make_names(do_random);
|
||||
m_max = max_size;
|
||||
m_min = min_size;
|
||||
m_size_range = m_max - m_min;
|
||||
m_dirname = new char[strlen(dirname) + 1];
|
||||
strcpy(m_dirname, dirname);
|
||||
|
||||
if(make_directory(dirname))
|
||||
{
|
||||
fprintf(stderr, "Can't make directory %s\n", dirname);
|
||||
return -1;
|
||||
}
|
||||
if(chdir(dirname))
|
||||
{
|
||||
fprintf(stderr, "Can't change to directory %s\n", dirname);
|
||||
return -1;
|
||||
}
|
||||
int i;
|
||||
if(m_sync)
|
||||
m_directoryHandles = new FILE_TYPE[num_directories];
|
||||
if(num_directories > 1)
|
||||
{
|
||||
for(i = 0; i < num_directories; i++)
|
||||
{
|
||||
sprintf(m_buf, "%05d", i);
|
||||
if(make_directory(m_buf))
|
||||
{
|
||||
fprintf(stderr, "Can't make directory %s\n", m_buf);
|
||||
return -1;
|
||||
}
|
||||
if(m_sync)
|
||||
{
|
||||
m_directoryHandles[i] = open(m_buf, O_RDONLY);
|
||||
if(m_directoryHandles[i] == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't get directory handle.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(m_sync)
|
||||
{
|
||||
m_directoryHandles[0] = open(".", O_RDONLY);
|
||||
if(m_directoryHandles[0] == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't get directory handle.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
timer.timestamp();
|
||||
for(i = 0; i < m_number; i++)
|
||||
{
|
||||
if(*m_exit)
|
||||
{
|
||||
return EXIT_CTRL_C;
|
||||
}
|
||||
// m_max < 0 means link or sym-link
|
||||
if(m_max < 0)
|
||||
{
|
||||
if(i == 0)
|
||||
{
|
||||
if(create_a_file(m_file_names[0], m_buf, 0, m_dirIndex ? m_dirIndex[0] : 0))
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create_a_link() looks at m_max to see what to do
|
||||
if(create_a_link(m_file_names[0], m_file_names[i], m_dirIndex ? m_dirIndex[i] : 0))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int size;
|
||||
if(m_size_range)
|
||||
size = m_min + (rand() % (m_size_range + 1));
|
||||
else
|
||||
size = m_max;
|
||||
if(create_a_file(m_file_names[i], m_buf, size, m_dirIndex ? m_dirIndex[i] : 0))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
timer.get_delta_t(do_random ? CreateRand : CreateSeq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int COpenTest::delete_random(BonTimer &timer)
|
||||
{
|
||||
random_sort();
|
||||
timer.timestamp();
|
||||
int i;
|
||||
for(i = 0; i < m_number; i++)
|
||||
{
|
||||
if(unlink(m_file_names[i]))
|
||||
{
|
||||
fprintf(stderr, "Can't delete file %s\n", m_file_names[i]);
|
||||
return -1;
|
||||
}
|
||||
if(m_sync && m_sync_dir)
|
||||
{
|
||||
if(fsync(m_directoryHandles[m_dirIndex[i]]))
|
||||
{
|
||||
fprintf(stderr, "Can't sync directory, turning off dir-sync.\n");
|
||||
m_sync_dir = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(m_number_directories > 1)
|
||||
{
|
||||
char buf[6];
|
||||
for(i = 0; i < m_number_directories; i++)
|
||||
{
|
||||
sprintf(buf, "%05d", i);
|
||||
if(m_sync)
|
||||
{
|
||||
close(m_directoryHandles[i]);
|
||||
}
|
||||
if(rmdir(buf))
|
||||
{
|
||||
io_error("rmdir");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_sync)
|
||||
{
|
||||
close(m_directoryHandles[0]);
|
||||
}
|
||||
}
|
||||
if(chdir("..") || rmdir(m_dirname))
|
||||
{
|
||||
io_error("rmdir");
|
||||
return -1;
|
||||
}
|
||||
delete m_dirname;
|
||||
m_dirname = NULL;
|
||||
timer.get_delta_t(DelRand);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int COpenTest::delete_sequential(BonTimer &timer)
|
||||
{
|
||||
timer.timestamp();
|
||||
int count = 0;
|
||||
for(int i = 0; i < m_number_directories; i++)
|
||||
{
|
||||
char buf[6];
|
||||
if(m_number_directories != 1)
|
||||
{
|
||||
sprintf(buf, "%05d", i);
|
||||
if(chdir(buf))
|
||||
{
|
||||
fprintf(stderr, "Can't change to directory %s\n", buf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#ifdef OS2
|
||||
HDIR d = 0;
|
||||
ULONG entries = 1;
|
||||
FILEFINDBUF3 findBuf;
|
||||
ULONG rc = DosFindFirst("*", &d
|
||||
, FILE_ARCHIVED | FILE_SYSTEM | FILE_HIDDEN | FILE_READONLY
|
||||
, &findBuf, sizeof(findBuf), &entries, FIL_STANDARD);
|
||||
if(rc || !entries)
|
||||
{
|
||||
fprintf(stderr, "Can't open directory.\n");
|
||||
return -1;
|
||||
}
|
||||
do
|
||||
{
|
||||
if(unlink(findBuf.achName))
|
||||
{
|
||||
fprintf(stderr, "Can't delete file %s\n", findBuf.achName);
|
||||
DosFindClose(d);
|
||||
return -1;
|
||||
}
|
||||
count++;
|
||||
rc = DosFindNext(d, &findBuf, sizeof(findBuf), &entries);
|
||||
} while(!rc && entries == 1);
|
||||
DosFindClose(d);
|
||||
#else
|
||||
DIR *d = opendir(".");
|
||||
if(!d)
|
||||
{
|
||||
fprintf(stderr, "Can't open directory.\n");
|
||||
return -1;
|
||||
}
|
||||
dirent *file_ent;
|
||||
|
||||
while((file_ent = readdir(d)) != NULL)
|
||||
{
|
||||
if(file_ent->d_name[0] != '.')
|
||||
{
|
||||
if(unlink(file_ent->d_name))
|
||||
{
|
||||
fprintf(stderr, "Can't delete file %s\n", file_ent->d_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if(m_sync && m_sync_dir)
|
||||
{
|
||||
if(fsync(m_directoryHandles[i]))
|
||||
{
|
||||
fprintf(stderr, "Can't sync directory, turning off dir-sync.\n");
|
||||
m_sync_dir = false;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
#endif
|
||||
if(m_sync)
|
||||
{
|
||||
close(m_directoryHandles[i]);
|
||||
}
|
||||
if(m_number_directories != 1)
|
||||
{
|
||||
if(chdir("..") || rmdir(buf))
|
||||
{
|
||||
io_error("rmdir");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(chdir("..") || rmdir(m_dirname))
|
||||
{
|
||||
io_error("rmdir");
|
||||
return -1;
|
||||
}
|
||||
delete m_dirname;
|
||||
m_dirname = NULL;
|
||||
if(count != m_number)
|
||||
{
|
||||
fprintf(stderr, "Expected %d files but only got %d\n", m_number, count);
|
||||
return -1;
|
||||
}
|
||||
timer.get_delta_t(DelSeq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int COpenTest::stat_file(CPCCHAR file)
|
||||
{
|
||||
struct stat st;
|
||||
if(stat(file, &st))
|
||||
{
|
||||
fprintf(stderr, "Can't stat file %s\n", file);
|
||||
return -1;
|
||||
}
|
||||
if(st.st_size)
|
||||
{
|
||||
FILE_TYPE fd = 0;
|
||||
#ifdef OS2
|
||||
ULONG action = 0;
|
||||
ULONG rc = DosOpen(file, &fd, &action, 0, FILE_NORMAL
|
||||
, OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS
|
||||
, OPEN_FLAGS_SEQUENTIAL | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE
|
||||
, NULL);
|
||||
if(rc)
|
||||
fd = -1;
|
||||
#else
|
||||
fd = open(file, O_RDONLY);
|
||||
#endif
|
||||
if(fd == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't open file %s\n", file);
|
||||
return -1;
|
||||
}
|
||||
for(int i = 0; i < st.st_size; i += m_chunk_size)
|
||||
{
|
||||
int to_read = st.st_size - i;
|
||||
if(to_read > m_chunk_size) to_read = m_chunk_size;
|
||||
#ifdef OS2
|
||||
ULONG actual = 0;
|
||||
rc = DosRead(fd, PVOID(m_buf), to_read, &actual);
|
||||
if(to_read != actual || rc)
|
||||
#else
|
||||
if(to_read != read(fd, static_cast<void *>(m_buf), to_read))
|
||||
#endif
|
||||
{
|
||||
fprintf(stderr, "Can't read data.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
file_close(fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int COpenTest::stat_random(BonTimer &timer)
|
||||
{
|
||||
random_sort();
|
||||
timer.timestamp();
|
||||
|
||||
int i;
|
||||
for(i = 0; i < m_number; i++)
|
||||
{
|
||||
if(-1 == stat_file(m_file_names[i]))
|
||||
return -1;
|
||||
}
|
||||
timer.get_delta_t(StatRand);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int COpenTest::stat_sequential(BonTimer &timer)
|
||||
{
|
||||
timer.timestamp();
|
||||
int count = 0;
|
||||
for(int i = 0; i < m_number_directories; i++)
|
||||
{
|
||||
char buf[6];
|
||||
if(m_number_directories != 1)
|
||||
{
|
||||
sprintf(buf, "%05d", i);
|
||||
if(chdir(buf))
|
||||
{
|
||||
fprintf(stderr, "Can't change to directory %s\n", buf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#ifdef OS2
|
||||
HDIR d = 0;
|
||||
ULONG entries = 1;
|
||||
FILEFINDBUF3 findBuf;
|
||||
ULONG rc = DosFindFirst("*", &d
|
||||
, FILE_ARCHIVED | FILE_SYSTEM | FILE_HIDDEN | FILE_READONLY
|
||||
, &findBuf, sizeof(findBuf), &entries, FIL_STANDARD);
|
||||
if(rc || !entries)
|
||||
{
|
||||
fprintf(stderr, "Can't open directory.\n");
|
||||
if(m_number_directories != 1)
|
||||
{
|
||||
if(chdir(".."))
|
||||
fprintf(stderr, "Can't chdir().\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
do
|
||||
{
|
||||
if(*m_exit)
|
||||
{
|
||||
return EXIT_CTRL_C;
|
||||
}
|
||||
if(findBuf.achName[0] != '.') // our files do not start with a dot
|
||||
{
|
||||
if(-1 == stat_file(findBuf.achName))
|
||||
{
|
||||
if(m_number_directories != 1)
|
||||
{
|
||||
if(chdir(".."))
|
||||
fprintf(stderr, "Can't chdir().\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
rc = DosFindNext(d, &findBuf, sizeof(findBuf), &entries);
|
||||
} while(!rc && entries == 1);
|
||||
DosFindClose(d);
|
||||
#else
|
||||
DIR *d = opendir(".");
|
||||
if(!d)
|
||||
{
|
||||
fprintf(stderr, "Can't open directory.\n");
|
||||
if(m_number_directories != 1)
|
||||
{
|
||||
if(chdir(".."))
|
||||
fprintf(stderr, "Can't chdir().\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
dirent *file_ent;
|
||||
while((file_ent = readdir(d)) != NULL)
|
||||
{
|
||||
if(*m_exit)
|
||||
{
|
||||
if(m_number_directories != 1 && chdir(".."))
|
||||
{
|
||||
fprintf(stderr, "Can't change to directory ..\n");
|
||||
return -1;
|
||||
}
|
||||
return EXIT_CTRL_C;
|
||||
}
|
||||
if(file_ent->d_name[0] != '.') // our files do not start with a dot
|
||||
{
|
||||
if(-1 == stat_file(file_ent->d_name))
|
||||
{
|
||||
if(m_number_directories != 1)
|
||||
{
|
||||
if(chdir(".."))
|
||||
fprintf(stderr, "Can't chdir().\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
#endif
|
||||
if(m_number_directories != 1 && chdir(".."))
|
||||
{
|
||||
fprintf(stderr, "Can't change to directory ..\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(count != m_number)
|
||||
{
|
||||
fprintf(stderr, "Expected %d files but only got %d\n", m_number, count);
|
||||
return -1;
|
||||
}
|
||||
timer.get_delta_t(StatSeq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#ifndef BON_IO
|
||||
#define BON_IO
|
||||
|
||||
#include "bonnie.h"
|
||||
class BonTimer;
|
||||
|
||||
typedef unsigned long MASK_TYPE;
|
||||
|
||||
class COpenTest
|
||||
{
|
||||
public:
|
||||
COpenTest(int chunk_size, bool use_sync, bool *doExit);
|
||||
~COpenTest();
|
||||
|
||||
int create(CPCCHAR dirname, BonTimer &timer, int num, int max_size
|
||||
, int min_size, int num_directories, bool do_random);
|
||||
int delete_random(BonTimer &timer);
|
||||
int delete_sequential(BonTimer &timer);
|
||||
int stat_random(BonTimer &timer);
|
||||
int stat_sequential(BonTimer &timer);
|
||||
|
||||
private:
|
||||
void make_names(bool do_random);
|
||||
int stat_file(CPCCHAR file);
|
||||
int create_a_file(const char *filename, char *buf, int size, int dir);
|
||||
int create_a_link(const char *original, const char *filename, int dir);
|
||||
|
||||
const int m_chunk_size;
|
||||
int m_number; // the total number of files to create
|
||||
int m_number_directories; // the number of directories to store files in
|
||||
int m_max; // maximum file size (negative for links)
|
||||
int m_min; // minimum file size
|
||||
int m_size_range; // m_max - m_min
|
||||
char *m_dirname; // name of the master directory
|
||||
char *m_file_name_buf; // buffer to store all file names
|
||||
char **m_file_names; // pointer to entries in m_file_name_buf
|
||||
bool m_sync; // do we sync after every significant operation?
|
||||
FILE_TYPE *m_directoryHandles; // handles to the directories for m_sync
|
||||
int *m_dirIndex; // which directory we are in
|
||||
char *m_buf;
|
||||
bool *m_exit;
|
||||
bool m_sync_dir;
|
||||
|
||||
void random_sort();
|
||||
|
||||
COpenTest(const COpenTest &t);
|
||||
COpenTest & operator =(const COpenTest &t);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,503 +0,0 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef OS2
|
||||
#define INCL_DOSPROCESS
|
||||
#define INCL_DOSFILEMGR
|
||||
#include <os2.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bonnie.h"
|
||||
#include "bon_io.h"
|
||||
#include "semaphore.h"
|
||||
#include "bon_time.h"
|
||||
|
||||
#include "forkit.h"
|
||||
|
||||
CFileOp::~CFileOp()
|
||||
{
|
||||
close();
|
||||
if(m_name)
|
||||
{
|
||||
int len = strlen(m_name);
|
||||
for(int i = 0; i < m_num_files; i++)
|
||||
{
|
||||
sprintf(&m_name[len], ".%03d", i);
|
||||
unlink(m_name);
|
||||
}
|
||||
delete m_name;
|
||||
}
|
||||
delete m_buf;
|
||||
}
|
||||
|
||||
void seeker(Fork *f, PVOID param, int)
|
||||
{
|
||||
struct report_s seeker_report;
|
||||
CFileOp *file = (CFileOp *)param;
|
||||
int num_chunks = file->chunks();
|
||||
if(file->reopen(false))
|
||||
exit(1);
|
||||
char ticket;
|
||||
int rc;
|
||||
int lseek_count = 0;
|
||||
|
||||
rc = f->Read(&ticket, 1, 0);
|
||||
|
||||
file->getTimer().timestamp();
|
||||
seeker_report.StartTime = file->getTimer().get_cur_time();
|
||||
|
||||
if(rc == 1 && ticket) do
|
||||
{
|
||||
bool update;
|
||||
if( (lseek_count++ % UpdateSeek) == 0)
|
||||
update = true;
|
||||
else
|
||||
update = false;
|
||||
if(file->doseek(rand() % num_chunks, update) )
|
||||
exit(1);
|
||||
}
|
||||
while((rc = f->Read(&ticket, 1, 0)) == 1 && ticket);
|
||||
|
||||
if(rc != 1)
|
||||
{
|
||||
fprintf(stderr, "Can't read ticket.\n");
|
||||
exit(1);
|
||||
}
|
||||
file->close();
|
||||
file->getTimer().get_delta_report(seeker_report);
|
||||
if(f->Write(&seeker_report, sizeof(seeker_report)) != sizeof(seeker_report))
|
||||
{
|
||||
fprintf(stderr, "Can't write report.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int CFileOp::seek_test(bool quiet, Semaphore &s)
|
||||
{
|
||||
char seek_tickets[SeekProcCount + Seeks];
|
||||
int next;
|
||||
for (next = 0; next < Seeks; next++)
|
||||
seek_tickets[next] = 1;
|
||||
for ( ; next < (Seeks + SeekProcCount); next++)
|
||||
seek_tickets[next] = 0;
|
||||
Fork f;
|
||||
f.go(seeker, this, SeekProcCount);
|
||||
|
||||
sleep(5);
|
||||
if(s.decrement_and_wait(Lseek))
|
||||
return 1;
|
||||
if(!quiet) fprintf(stderr, "start 'em...");
|
||||
if(f.Write(seek_tickets, sizeof(seek_tickets)) != int(sizeof(seek_tickets)) )
|
||||
{
|
||||
fprintf(stderr, "Can't write tickets.\n");
|
||||
return 1;
|
||||
}
|
||||
for (next = 0; next < SeekProcCount; next++)
|
||||
{ /* for each child */
|
||||
struct report_s seeker_report;
|
||||
|
||||
int rc;
|
||||
if((rc = f.Read(&seeker_report, sizeof(seeker_report), 0))
|
||||
!= sizeof(seeker_report))
|
||||
{
|
||||
fprintf(stderr, "Can't read from pipe, expected %d, got %d.\n"
|
||||
, int(sizeof(seeker_report)), rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* each child writes back its CPU, start & end times. The elapsed time
|
||||
* to do all the seeks is the time the first child started until the
|
||||
* time the last child stopped
|
||||
*/
|
||||
m_timer.add_delta_report(seeker_report, Lseek);
|
||||
#ifdef OS2
|
||||
TID status = 0;
|
||||
if(DosWaitThread(&status, DCWW_WAIT))
|
||||
#else
|
||||
int status = 0;
|
||||
if(wait(&status) == -1)
|
||||
#endif
|
||||
return io_error("wait");
|
||||
if(!quiet) fprintf(stderr, "done...");
|
||||
} /* for each child */
|
||||
if(!quiet) fprintf(stderr, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFileOp::seek(int offset, int whence)
|
||||
{
|
||||
switch(whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
m_file_ind = offset / m_chunks_per_file;
|
||||
m_cur_pos = offset % m_chunks_per_file;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
m_cur_pos += offset;
|
||||
while(m_cur_pos < 0)
|
||||
{
|
||||
m_file_ind--;
|
||||
m_cur_pos += m_chunks_per_file;
|
||||
}
|
||||
while(m_cur_pos >= m_chunks_per_file)
|
||||
{
|
||||
m_file_ind++;
|
||||
m_cur_pos -= m_chunks_per_file;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unsupported seek option\n");
|
||||
return -1;
|
||||
}
|
||||
if(m_file_ind == m_num_files)
|
||||
{
|
||||
m_file_ind--;
|
||||
m_cur_pos += m_chunks_per_file;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_file_ind < 0 || m_file_ind >= m_num_files
|
||||
|| (m_file_ind == m_num_files - 1 && m_cur_pos >= m_last_file_chunks))
|
||||
{
|
||||
fprintf(stderr, "Bad seek offset\n");
|
||||
return -1;
|
||||
}
|
||||
off_t rc;
|
||||
if(m_fd)
|
||||
{
|
||||
#ifdef OS2
|
||||
unsigned long actual;
|
||||
rc = DosSetFilePtr(m_fd[m_file_ind], m_cur_pos << m_chunk_bits, FILE_BEGIN, &actual);
|
||||
if(rc != 0) rc = -1;
|
||||
#else
|
||||
rc = lseek(m_fd[m_file_ind], m_cur_pos << m_chunk_bits, SEEK_SET);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = fseek(m_stream[m_file_ind], m_cur_pos << m_chunk_bits, SEEK_SET);
|
||||
}
|
||||
|
||||
if(rc == off_t(-1))
|
||||
fprintf(stderr, "Error in lseek to %d\n", (m_cur_pos << m_chunk_bits));
|
||||
else
|
||||
rc = 0;
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFileOp::read_block(PVOID buf)
|
||||
{
|
||||
int total = 0;
|
||||
bool printed_error = false;
|
||||
while(total != m_chunk_size)
|
||||
{
|
||||
#ifdef OS2
|
||||
unsigned long actual;
|
||||
int rc = DosRead(m_fd[m_file_ind], buf, m_chunk_size - total
|
||||
, &actual);
|
||||
if(rc)
|
||||
rc = -1;
|
||||
else
|
||||
rc = actual;
|
||||
#else
|
||||
int rc = ::read(m_fd[m_file_ind], buf, m_chunk_size - total);
|
||||
#endif
|
||||
m_cur_pos++;
|
||||
if(m_cur_pos >= m_chunks_per_file)
|
||||
{
|
||||
if(seek(0, SEEK_CUR) == -1)
|
||||
{
|
||||
fprintf(stderr, "Error in seek(0)\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(rc == -1)
|
||||
{
|
||||
io_error("re-write read"); // exits program
|
||||
}
|
||||
else if(rc != m_chunk_size && !printed_error)
|
||||
{
|
||||
fprintf(stderr, "Can't read a full block, only got %d bytes.\n", rc);
|
||||
printed_error = true;
|
||||
}
|
||||
total += rc;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
int CFileOp::read_block_getc(char *buf)
|
||||
{
|
||||
int next;
|
||||
for(int i = 0; i < m_chunk_size; i++)
|
||||
{
|
||||
if ((next = getc(m_stream[m_file_ind])) == EOF)
|
||||
{
|
||||
fprintf(stderr, "Can't getc(3)\n");
|
||||
return -1;
|
||||
}
|
||||
/* just to fool optimizers */
|
||||
buf[next]++;
|
||||
}
|
||||
|
||||
m_cur_pos++;
|
||||
if(m_cur_pos >= m_chunks_per_file)
|
||||
{
|
||||
if(seek(0, SEEK_CUR) == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFileOp::write_block(PVOID buf)
|
||||
{
|
||||
#ifdef OS2
|
||||
unsigned long actual;
|
||||
int rc = DosWrite(m_fd[m_file_ind], buf, m_chunk_size, &actual);
|
||||
if(rc)
|
||||
rc = -1;
|
||||
else
|
||||
rc = 0;
|
||||
if(actual != m_chunk_size)
|
||||
rc = -1;
|
||||
#else
|
||||
int rc = ::write(m_fd[m_file_ind], buf, m_chunk_size);
|
||||
if(rc != m_chunk_size)
|
||||
{
|
||||
fprintf(stderr, "Can't write block.\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
m_cur_pos++;
|
||||
if(m_cur_pos >= m_chunks_per_file)
|
||||
{
|
||||
if(seek(0, SEEK_CUR) == -1)
|
||||
return -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int CFileOp::write_block_putc()
|
||||
{
|
||||
for(int i = 0; i < m_chunk_size; i++)
|
||||
{
|
||||
if (putc(i & 0x7f, m_stream[m_file_ind]) == EOF)
|
||||
{
|
||||
fprintf(stderr, "Can't putc() - disk full?\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
m_cur_pos++;
|
||||
if(m_cur_pos >= m_chunks_per_file)
|
||||
{
|
||||
if(seek(0, SEEK_CUR) == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFileOp::open(CPCCHAR base_name, bool create, bool use_fopen)
|
||||
{
|
||||
m_name = new char[strlen(base_name) + 8];
|
||||
strcpy(m_name, base_name);
|
||||
return reopen(create, use_fopen);
|
||||
}
|
||||
|
||||
CFileOp::CFileOp(BonTimer &timer, int file_size, int chunk_bits, bool use_sync)
|
||||
: m_timer(timer)
|
||||
, m_stream(NULL)
|
||||
, m_fd(NULL)
|
||||
, m_isopen(false)
|
||||
, m_name(NULL)
|
||||
, m_sync(use_sync)
|
||||
, m_chunk_bits(chunk_bits)
|
||||
, m_chunk_size(1 << m_chunk_bits)
|
||||
, m_chunks_per_file(Unit / m_chunk_size * IOFileSize)
|
||||
, m_total_chunks(Unit / m_chunk_size * file_size)
|
||||
, m_last_file_chunks(m_total_chunks % m_chunks_per_file)
|
||||
, m_cur_pos(0)
|
||||
, m_file_ind(0)
|
||||
, m_file_size(file_size)
|
||||
, m_num_files(m_file_size / IOFileSize + 1)
|
||||
, m_buf(new char[m_chunk_size])
|
||||
{
|
||||
if(m_last_file_chunks == 0)
|
||||
{
|
||||
m_last_file_chunks = m_chunks_per_file;
|
||||
m_num_files--;
|
||||
}
|
||||
}
|
||||
|
||||
typedef FILE * PFILE;
|
||||
|
||||
int CFileOp::reopen(bool create, bool use_fopen)
|
||||
{
|
||||
int i;
|
||||
m_cur_pos = 0;
|
||||
m_file_ind = 0;
|
||||
|
||||
if(m_isopen) close();
|
||||
|
||||
m_isopen = true;
|
||||
if(use_fopen)
|
||||
{
|
||||
m_stream = new PFILE[m_num_files];
|
||||
for(i = 0; i < m_num_files; i++)
|
||||
m_stream[i] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef OS2
|
||||
m_fd = new HFILE[m_num_files];
|
||||
#else
|
||||
m_fd = new int[m_num_files];
|
||||
#endif
|
||||
for(i = 0; i < m_num_files; i++)
|
||||
m_fd[i] = -1;
|
||||
}
|
||||
int len = strlen(m_name);
|
||||
for(i = 0; i < m_num_files; i++)
|
||||
{
|
||||
sprintf(&m_name[len], ".%03d", i);
|
||||
if(m_open(m_name, i, create))
|
||||
return 1;
|
||||
}
|
||||
m_name[len] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CFileOp::m_open(CPCCHAR base_name, int ind, bool create)
|
||||
{
|
||||
#ifdef OS2
|
||||
ULONG createFlag;
|
||||
#else
|
||||
int flags;
|
||||
#endif
|
||||
const char *fopen_mode;
|
||||
if(create)
|
||||
{ /* create from scratch */
|
||||
#ifndef OS2
|
||||
unlink(base_name);
|
||||
#endif
|
||||
fopen_mode = "w+";
|
||||
#ifdef OS2
|
||||
createFlag = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS;
|
||||
#else
|
||||
flags = O_RDWR | O_CREAT | O_EXCL;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
fopen_mode = "r+";
|
||||
#ifdef OS2
|
||||
createFlag = OPEN_ACTION_OPEN_IF_EXISTS;
|
||||
#else
|
||||
flags = O_RDWR;
|
||||
#endif
|
||||
}
|
||||
if(m_fd)
|
||||
{
|
||||
#ifdef OS2
|
||||
ULONG action = 0;
|
||||
ULONG rc = DosOpen(base_name, &m_fd[ind], &action, 0, FILE_NORMAL, createFlag
|
||||
, OPEN_FLAGS_SEQUENTIAL | OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE
|
||||
, NULL);
|
||||
if(rc)
|
||||
m_fd[ind] = -1;
|
||||
#else
|
||||
m_fd[ind] = ::open(base_name, flags, S_IRUSR | S_IWUSR);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stream[ind] = fopen(base_name, fopen_mode);
|
||||
}
|
||||
|
||||
if( (m_fd && m_fd[ind] == -1) || (m_stream && m_stream[ind] == NULL) )
|
||||
{
|
||||
fprintf(stderr, "Can't open file %s\n", base_name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CFileOp::close()
|
||||
{
|
||||
if(!m_isopen)
|
||||
return;
|
||||
for(int i = 0; i < m_num_files; i++)
|
||||
{
|
||||
if(m_sync)
|
||||
fflush(NULL);
|
||||
if(m_stream && m_stream[i]) fclose(m_stream[i]);
|
||||
if(m_fd && m_fd[i] != -1)
|
||||
{
|
||||
if(m_sync)
|
||||
{
|
||||
if(fsync(m_fd[i]))
|
||||
fprintf(stderr, "Can't sync files.\n");
|
||||
}
|
||||
file_close(m_fd[i]);
|
||||
}
|
||||
}
|
||||
m_isopen = false;
|
||||
delete m_fd;
|
||||
delete m_stream;
|
||||
m_fd = NULL;
|
||||
m_stream = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Do a typical-of-something random I/O. Any serious application that
|
||||
* has a random I/O bottleneck is going to be smart enough to operate
|
||||
* in a page mode, and not stupidly pull individual words out at
|
||||
* odd offsets. To keep the cache from getting too clever, some
|
||||
* pages must be updated. However an application that updated each of
|
||||
* many random pages that it looked at is hard to imagine.
|
||||
* However, it would be wrong to put the update percentage in as a
|
||||
* parameter - the effect is too nonlinear. Need a profile
|
||||
* of what Oracle or Ingres or some such actually does.
|
||||
* Be warned - there is a *sharp* elbow in this curve - on a 1-MiB file,
|
||||
* most substantial unix systems show >2000 random I/Os per second -
|
||||
* obviously they've cached the whole thing and are just doing buffer
|
||||
* copies.
|
||||
*/
|
||||
int
|
||||
CFileOp::doseek(long where, bool update)
|
||||
{
|
||||
if (seek(where, SEEK_SET) == -1)
|
||||
return io_error("lseek in doseek");
|
||||
if (read_block(PVOID(m_buf)) == -1)
|
||||
return io_error("read in doseek");
|
||||
|
||||
/* every so often, update a block */
|
||||
if (update)
|
||||
{ /* update this block */
|
||||
|
||||
/* touch a byte */
|
||||
m_buf[int(rand()) % m_chunk_size]--;
|
||||
if(seek(where, SEEK_SET) == -1)
|
||||
return io_error("lseek in doseek update");
|
||||
if (write_block(PVOID(m_buf)) == -1)
|
||||
return io_error("write in doseek");
|
||||
if(m_sync)
|
||||
{
|
||||
if(fsync(m_fd[m_file_ind]))
|
||||
{
|
||||
fprintf(stderr, "Can't sync file.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} /* update this block */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#ifndef BON_FILE
|
||||
#define BON_FILE
|
||||
|
||||
#include "bonnie.h"
|
||||
class Semaphore;
|
||||
class BonTimer;
|
||||
|
||||
class CFileOp
|
||||
{
|
||||
public:
|
||||
CFileOp(BonTimer &timer, int file_size, int chunk_bits, bool use_sync = false);
|
||||
int open(CPCCHAR base_name, bool create, bool use_fopen = false);
|
||||
~CFileOp();
|
||||
int write_block_putc();
|
||||
int write_block(PVOID buf);
|
||||
int read_block_getc(char *buf);
|
||||
int read_block(PVOID buf);
|
||||
int seek(int offset, int whence);
|
||||
int doseek(long where, bool update);
|
||||
int seek_test(bool quiet, Semaphore &s);
|
||||
void close();
|
||||
// reopen a file, bools for whether the file should be unlink()'d and
|
||||
// creat()'d and for whether fopen should be used
|
||||
int reopen(bool create, bool use_fopen = false);
|
||||
BonTimer &getTimer() { return m_timer; }
|
||||
int chunks() const { return m_total_chunks; }
|
||||
private:
|
||||
int m_open(CPCCHAR base_name, int ind, bool create);
|
||||
|
||||
BonTimer &m_timer;
|
||||
FILE **m_stream;
|
||||
FILE_TYPE *m_fd;
|
||||
bool m_isopen;
|
||||
char *m_name;
|
||||
bool m_sync;
|
||||
const int m_chunk_bits, m_chunk_size;
|
||||
int m_chunks_per_file, m_total_chunks;
|
||||
int m_last_file_chunks;
|
||||
int m_cur_pos;
|
||||
int m_file_ind;
|
||||
int m_file_size;
|
||||
int m_num_files;
|
||||
CFileOp(const CFileOp &f);
|
||||
CFileOp & operator =(const CFileOp &f);
|
||||
char *m_buf;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,83 +0,0 @@
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include "bonnie.h"
|
||||
|
||||
int bon_setugid(CPCCHAR userName, CPCCHAR groupName, bool quiet)
|
||||
{
|
||||
int id = 0;
|
||||
uid_t userId = 0;
|
||||
gid_t groupId = 0;
|
||||
bool setGroup = false;
|
||||
struct passwd *pw;
|
||||
struct group *gr;
|
||||
if(userName)
|
||||
{
|
||||
if(sscanf(userName, "%d", &id) == 1)
|
||||
{
|
||||
userId = uid_t(id);
|
||||
pw = getpwuid(userId);
|
||||
if(pw)
|
||||
{
|
||||
groupId = pw->pw_gid;
|
||||
setGroup = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
gr = getgrnam("nogroup");
|
||||
if(gr)
|
||||
groupId = gr->gr_gid;
|
||||
setGroup = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pw = getpwnam(userName);
|
||||
if(!pw)
|
||||
{
|
||||
fprintf(stderr, "Can't find user %s\n", userName);
|
||||
return 1;
|
||||
}
|
||||
userId = pw->pw_uid;
|
||||
groupId = pw->pw_gid;
|
||||
setGroup = true;
|
||||
}
|
||||
}
|
||||
if(groupName)
|
||||
{
|
||||
if(sscanf(groupName, "%d", &id) == 1)
|
||||
{
|
||||
groupId = gid_t(id);
|
||||
setGroup = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
gr = getgrnam(groupName);
|
||||
if(!gr)
|
||||
{
|
||||
fprintf(stderr, "Can't find group %s\n", groupName);
|
||||
return 1;
|
||||
}
|
||||
groupId = gr->gr_gid;
|
||||
setGroup = true;
|
||||
}
|
||||
}
|
||||
if(setGroup)
|
||||
{
|
||||
if(setgid(groupId))
|
||||
{
|
||||
fprintf(stderr, "Can't set gid to %d.\n", int(groupId));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(setuid(userId))
|
||||
{
|
||||
fprintf(stderr, "Can't set uid to %d.\n", int(userId));
|
||||
return 1;
|
||||
}
|
||||
if(!quiet)
|
||||
fprintf(stderr, "Using uid:%d, gid:%d.\n", int(getuid()), int(getgid()));
|
||||
return 0;
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <sys/time.h>
|
||||
#ifdef OS2
|
||||
#define INCL_DOSFILEMGR
|
||||
#define INCL_BASE
|
||||
#include <os2.h>
|
||||
#include "os2-perfutil.h"
|
||||
/*
|
||||
Convert 8-byte (low, high) time value to double
|
||||
*/
|
||||
#define LL2F(high, low) (4294967296.0*(high)+(low))
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#include "bon_time.h"
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef NON_UNIX
|
||||
#include "conf.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ALGORITHM
|
||||
#include <algorithm>
|
||||
#else
|
||||
|
||||
#ifdef HAVE_ALGO
|
||||
#include <algo>
|
||||
#else
|
||||
#ifdef HAVE_ALGO_H
|
||||
#include <algo.h>
|
||||
#else
|
||||
#define min(XX,YY) ((XX) < (YY) ? (XX) : (YY))
|
||||
#define max(XX,YY) ((XX) > (YY) ? (XX) : (YY))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define TIMEVAL_TO_DOUBLE(XX) (double((XX).tv_sec) + double((XX).tv_usec) / 1000000.0)
|
||||
|
||||
void BonTimer::timestamp()
|
||||
{
|
||||
m_last_timestamp = get_cur_time();
|
||||
m_last_cpustamp = get_cpu_use();
|
||||
}
|
||||
|
||||
double
|
||||
BonTimer::time_so_far()
|
||||
{
|
||||
return get_cur_time() - m_last_timestamp;
|
||||
}
|
||||
|
||||
double
|
||||
BonTimer::cpu_so_far()
|
||||
{
|
||||
return get_cpu_use() - m_last_cpustamp;
|
||||
}
|
||||
|
||||
double
|
||||
BonTimer::get_cur_time()
|
||||
{
|
||||
#ifdef OS2
|
||||
ULONG count = 0;
|
||||
ULONG rc = DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &count, sizeof(count));
|
||||
if(rc)
|
||||
return 0.0;
|
||||
return double(count)/1000.0;
|
||||
#else
|
||||
struct timeval tp;
|
||||
|
||||
if (gettimeofday(&tp, static_cast<struct timezone *>(NULL)) == -1)
|
||||
io_error("gettimeofday", true);
|
||||
return TIMEVAL_TO_DOUBLE(tp);
|
||||
#endif
|
||||
}
|
||||
|
||||
double
|
||||
BonTimer::get_cpu_use()
|
||||
{
|
||||
#ifdef OS2
|
||||
double busy_val, busy_val_prev;
|
||||
double intr_val, intr_val_prev;
|
||||
CPUUTIL CPUUtil;
|
||||
|
||||
ULONG rc = DosPerfSysCall(CMD_KI_RDCNT,(ULONG) &CPUUtil,0,0);
|
||||
if(rc)
|
||||
io_error("times", true);
|
||||
return LL2F(CPUUtil.ulBusyHigh, CPUUtil.ulBusyLow)
|
||||
+ LL2F(CPUUtil.ulIntrHigh, CPUUtil.ulIntrLow);
|
||||
#else
|
||||
struct rusage res_usage;
|
||||
|
||||
getrusage(RUSAGE_SELF, &res_usage);
|
||||
return TIMEVAL_TO_DOUBLE(res_usage.ru_utime) + TIMEVAL_TO_DOUBLE(res_usage.ru_stime);
|
||||
#endif
|
||||
}
|
||||
|
||||
void BonTimer::get_delta_t(tests_t test)
|
||||
{
|
||||
m_delta[test].Elapsed = time_so_far();
|
||||
m_delta[test].CPU = cpu_so_far();
|
||||
timestamp();
|
||||
}
|
||||
|
||||
void BonTimer::get_delta_report(report_s &rep)
|
||||
{
|
||||
rep.EndTime = get_cur_time();
|
||||
rep.CPU = cpu_so_far();
|
||||
timestamp();
|
||||
}
|
||||
|
||||
void BonTimer::add_delta_report(report_s &rep, tests_t test)
|
||||
{
|
||||
if(m_delta[test].CPU == 0.0)
|
||||
{
|
||||
m_delta[test].FirstStart = rep.StartTime;
|
||||
m_delta[test].LastStop = rep.EndTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_delta[test].FirstStart = min(m_delta[test].FirstStart, rep.StartTime);
|
||||
m_delta[test].LastStop = max(m_delta[test].LastStop, rep.EndTime);
|
||||
}
|
||||
m_delta[test].CPU += rep.CPU;
|
||||
m_delta[test].Elapsed = m_delta[test].LastStop - m_delta[test].FirstStart;
|
||||
}
|
||||
|
||||
BonTimer::BonTimer()
|
||||
: m_type(txt)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void
|
||||
BonTimer::Initialize()
|
||||
{
|
||||
for(int i = 0; i < TestCount; i++)
|
||||
{
|
||||
m_delta[i].CPU = 0.0;
|
||||
m_delta[i].Elapsed = 0.0;
|
||||
}
|
||||
timestamp();
|
||||
}
|
||||
|
||||
int BonTimer::print_cpu_stat(tests_t test)
|
||||
{
|
||||
if(m_delta[test].Elapsed == 0.0)
|
||||
{
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, " ");
|
||||
else
|
||||
fprintf(m_fp, ",");
|
||||
return 0;
|
||||
}
|
||||
if(m_delta[test].Elapsed < MinTime)
|
||||
{
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, " +++");
|
||||
else
|
||||
fprintf(m_fp, ",+++");
|
||||
return 0;
|
||||
}
|
||||
int cpu = int(m_delta[test].CPU / m_delta[test].Elapsed * 100.0);
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, " %3d", cpu);
|
||||
else
|
||||
fprintf(m_fp, ",%d", cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BonTimer::print_stat(tests_t test)
|
||||
{
|
||||
if(m_delta[test].Elapsed == 0.0)
|
||||
{
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, " ");
|
||||
else
|
||||
fprintf(m_fp, ",");
|
||||
}
|
||||
else if(m_delta[test].Elapsed < MinTime)
|
||||
{
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, " +++++");
|
||||
else
|
||||
fprintf(m_fp, ",+++++");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(test == Lseek)
|
||||
{
|
||||
double seek_stat = double(Seeks) / m_delta[test].Elapsed;
|
||||
if(m_type == txt)
|
||||
{
|
||||
if(seek_stat >= 1000.0)
|
||||
fprintf(m_fp, " %5.0f", seek_stat);
|
||||
else
|
||||
fprintf(m_fp, " %5.1f", seek_stat);
|
||||
}
|
||||
else
|
||||
fprintf(m_fp, ",%.1f", seek_stat);
|
||||
}
|
||||
else
|
||||
{
|
||||
int res = int(double(m_file_size) / (m_delta[test].Elapsed / 1024.0));
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, " %5d", res);
|
||||
else
|
||||
fprintf(m_fp, ",%d", res);
|
||||
}
|
||||
}
|
||||
return print_cpu_stat(test);
|
||||
}
|
||||
|
||||
int BonTimer::print_file_stat(tests_t test)
|
||||
{
|
||||
if(m_delta[test].Elapsed == 0.0)
|
||||
{
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, " ");
|
||||
else
|
||||
fprintf(m_fp, ",");
|
||||
}
|
||||
else if(m_delta[test].Elapsed < MinTime)
|
||||
{
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, " +++++");
|
||||
else
|
||||
fprintf(m_fp, ",+++++");
|
||||
}
|
||||
else
|
||||
{
|
||||
int res = int(double(m_directory_size) * double(DirectoryUnit)
|
||||
/ m_delta[test].Elapsed);
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, " %5d", res);
|
||||
else
|
||||
fprintf(m_fp, ",%d", res);
|
||||
}
|
||||
|
||||
return print_cpu_stat(test);
|
||||
}
|
||||
|
||||
void
|
||||
BonTimer::PrintHeader(FILE *fp)
|
||||
{
|
||||
fprintf(fp, "name");
|
||||
fprintf(fp, ",file_size,putc,putc_cpu,put_block,put_block_cpu,rewrite,rewrite_cpu,getc,getc_cpu,get_block,get_block_cpu,seeks,seeks_cpu");
|
||||
fprintf(fp, ",num_files,seq_create,seq_create_cpu,seq_stat,seq_stat_cpu,seq_del,seq_del_cpu,ran_create,ran_create_cpu,ran_stat,ran_stat_cpu,ran_del,ran_del_cpu");
|
||||
fprintf(fp, "\n");
|
||||
fflush(NULL);
|
||||
}
|
||||
|
||||
int
|
||||
BonTimer::DoReport(CPCCHAR machine, int file_size, int directory_size
|
||||
, int max_size, int min_size, int num_directories
|
||||
, int chunk_size, FILE *fp)
|
||||
{
|
||||
m_fp = fp;
|
||||
m_file_size = file_size;
|
||||
m_directory_size = directory_size;
|
||||
m_chunk_size = chunk_size;
|
||||
const int txt_machine_size = 20;
|
||||
if(m_file_size)
|
||||
{
|
||||
if(m_type == txt)
|
||||
{
|
||||
fprintf(m_fp, "Version %5s ", BON_VERSION);
|
||||
fprintf(m_fp,
|
||||
"------Sequential Output------ --Sequential Input- --Random-\n");
|
||||
fprintf(m_fp, " ");
|
||||
fprintf(m_fp,
|
||||
"-Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--\n");
|
||||
if(m_chunk_size == DefaultChunkSize)
|
||||
fprintf(m_fp, "Machine Size ");
|
||||
else
|
||||
fprintf(m_fp, "Machine Size:chnk ");
|
||||
fprintf(m_fp, "K/sec %%CP K/sec %%CP K/sec %%CP K/sec %%CP K/sec ");
|
||||
fprintf(m_fp, "%%CP /sec %%CP\n");
|
||||
}
|
||||
char size_buf[1024];
|
||||
if(m_file_size % 1024 == 0)
|
||||
sprintf(size_buf, "%dG", m_file_size / 1024);
|
||||
else
|
||||
sprintf(size_buf, "%dM", m_file_size);
|
||||
if(m_chunk_size != DefaultChunkSize)
|
||||
{
|
||||
char *tmp = size_buf + strlen(size_buf);
|
||||
if(m_chunk_size >= 1024)
|
||||
sprintf(tmp, ":%dk", m_chunk_size / 1024);
|
||||
else
|
||||
sprintf(tmp, ":%d", m_chunk_size);
|
||||
}
|
||||
char buf[4096];
|
||||
if(m_type == txt)
|
||||
{
|
||||
// copy machine name to buf
|
||||
//
|
||||
snprintf(buf
|
||||
#ifndef NO_SNPRINTF
|
||||
, txt_machine_size - 1
|
||||
#endif
|
||||
, "%s ", machine);
|
||||
buf[txt_machine_size - 1] = '\0';
|
||||
// set cur to point to a byte past where we end the machine name
|
||||
// size of the buf - size of the new data - 1 for the space - 1 for the
|
||||
// terminating zero on the string
|
||||
char *cur = &buf[txt_machine_size - strlen(size_buf) - 2];
|
||||
*cur = ' '; // make cur a space
|
||||
cur++; // increment to where we store the size
|
||||
strcpy(cur, size_buf); // copy the size in
|
||||
fputs(buf, m_fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%s,%s", machine, size_buf);
|
||||
}
|
||||
print_stat(Putc);
|
||||
print_stat(FastWrite);
|
||||
print_stat(ReWrite);
|
||||
print_stat(Getc);
|
||||
print_stat(FastRead);
|
||||
print_stat(Lseek);
|
||||
if(m_type == txt)
|
||||
fprintf(m_fp, "\n");
|
||||
}
|
||||
else if(m_type == csv)
|
||||
{
|
||||
fprintf(m_fp, "%s,,,,,,,,,,,,,", machine);
|
||||
}
|
||||
|
||||
if(m_directory_size)
|
||||
{
|
||||
char buf[128];
|
||||
char *tmp;
|
||||
sprintf(buf, "%d", m_directory_size);
|
||||
if(max_size == -1)
|
||||
{
|
||||
strcat(buf, ":link");
|
||||
}
|
||||
else if(max_size == -2)
|
||||
{
|
||||
strcat(buf, ":symlink");
|
||||
}
|
||||
else if(max_size)
|
||||
{
|
||||
tmp = &buf[strlen(buf)];
|
||||
sprintf(tmp, ":%d:%d", max_size, min_size);
|
||||
}
|
||||
if(num_directories > 1)
|
||||
{
|
||||
tmp = &buf[strlen(buf)];
|
||||
sprintf(tmp, "/%d", num_directories);
|
||||
}
|
||||
if(m_type == txt)
|
||||
{
|
||||
if(m_file_size)
|
||||
fprintf(m_fp, " ");
|
||||
else
|
||||
fprintf(m_fp, "Version %5s ", BON_VERSION);
|
||||
fprintf(m_fp,
|
||||
"------Sequential Create------ --------Random Create--------\n");
|
||||
if(m_file_size)
|
||||
fprintf(m_fp, " ");
|
||||
else
|
||||
fprintf(m_fp, "%-19.19s ", machine);
|
||||
fprintf(m_fp,
|
||||
"-Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--\n");
|
||||
if(min_size)
|
||||
{
|
||||
fprintf(m_fp, "files:max:min ");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(max_size)
|
||||
fprintf(m_fp, "files:max ");
|
||||
else
|
||||
fprintf(m_fp, " files ");
|
||||
}
|
||||
fprintf(m_fp, " /sec %%CP /sec %%CP /sec %%CP /sec %%CP /sec ");
|
||||
fprintf(m_fp, "%%CP /sec %%CP\n");
|
||||
fprintf(m_fp, "%19s", buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(m_fp, ",%s", buf);
|
||||
}
|
||||
print_file_stat(CreateSeq);
|
||||
print_file_stat(StatSeq);
|
||||
print_file_stat(DelSeq);
|
||||
print_file_stat(CreateRand);
|
||||
print_file_stat(StatRand);
|
||||
print_file_stat(DelRand);
|
||||
fprintf(m_fp, "\n");
|
||||
}
|
||||
else if(m_type == csv)
|
||||
{
|
||||
fprintf(m_fp, ",,,,,,,,,,,,,\n");
|
||||
}
|
||||
fflush(stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
#ifndef BON_TIME_H
|
||||
#define BON_TIME_H
|
||||
|
||||
#include "bonnie.h"
|
||||
|
||||
struct report_s
|
||||
{
|
||||
double CPU;
|
||||
double StartTime;
|
||||
double EndTime;
|
||||
};
|
||||
|
||||
struct delta_s
|
||||
{
|
||||
double CPU;
|
||||
double Elapsed;
|
||||
double FirstStart;
|
||||
double LastStop;
|
||||
};
|
||||
|
||||
class BonTimer
|
||||
{
|
||||
public:
|
||||
enum RepType { csv, txt };
|
||||
|
||||
BonTimer();
|
||||
|
||||
void timestamp();
|
||||
void get_delta_t(tests_t test);
|
||||
void get_delta_report(report_s &rep);
|
||||
void add_delta_report(report_s &rep, tests_t test);
|
||||
int DoReport(CPCCHAR machine, int size, int directory_size
|
||||
, int max_size, int min_size, int num_directories
|
||||
, int chunk_size, FILE *fp);
|
||||
void SetType(RepType type) { m_type = type; }
|
||||
double cpu_so_far();
|
||||
double time_so_far();
|
||||
void PrintHeader(FILE *fp);
|
||||
void Initialize();
|
||||
static double get_cur_time();
|
||||
static double get_cpu_use();
|
||||
|
||||
private:
|
||||
int print_cpu_stat(tests_t test);
|
||||
int print_stat(tests_t test);
|
||||
int print_file_stat(tests_t test);
|
||||
|
||||
delta_s m_delta[TestCount];
|
||||
double m_last_cpustamp;
|
||||
double m_last_timestamp;
|
||||
RepType m_type;
|
||||
int m_file_size;
|
||||
int m_directory_size;
|
||||
int m_chunk_size;
|
||||
FILE *m_fp;
|
||||
|
||||
BonTimer(const BonTimer&);
|
||||
BonTimer &operator=(const BonTimer&);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,214 +0,0 @@
|
||||
.TH bonnie++ 8
|
||||
.SH "NAME"
|
||||
bonnie++ \- program to test hard drive performance.
|
||||
|
||||
.SH "SYNOPSIS"
|
||||
.B bonnie++
|
||||
.I [\-d dir] [\-s size(MiB)[:chunk\-size(b)]]
|
||||
.I [\-n number\-to\-stat(*1024)[:max\-size[:min\-size][:num\-directories]]]
|
||||
.I [\-m machine\-name] [\-r ram\-size\-in\-MiB] [\-x number\-of\-tests]
|
||||
.I [\-u uid\-to\-use:gid\-to\-use] [\-g gid\-to\-use]
|
||||
.I [\-q] [\-f] [\-b] [\-p processes | \-y]
|
||||
|
||||
.SH "DESCRIPTION"
|
||||
This manual page documents briefly the
|
||||
.BR bonnie++ ,
|
||||
program.
|
||||
.P
|
||||
Bonnie++ is a program to test hard drives and file systems for performance or
|
||||
the lack therof. There are a many different types of file system operations
|
||||
which different applications use to different degrees. Bonnie++ tests some of
|
||||
them and for each test gives a result of the amount of work done per second
|
||||
and the percentage of CPU time this took. For performance results higher
|
||||
numbers are better, for CPU usage lower are better (NB a configuration scoring
|
||||
a performance result of 2000 and a CPU result of 90% is better in terms of CPU
|
||||
use than a configuration delivering performance of 1000 and CPU usage of 60%).
|
||||
.P
|
||||
There are two sections to the program's operations. The first is to test the
|
||||
IO throughput in a fashion that is designed to simulate some types of database
|
||||
applications. The second is to test creation, reading, and deleting many small
|
||||
files in a fashion similar to the usage patterns of programs such as Squid or
|
||||
INN.
|
||||
.P
|
||||
All the details of the tests performed by Bonnie++ are contained in the file
|
||||
.BR /usr/share/doc/bonnie++/readme.html
|
||||
|
||||
.SH "OPTIONS"
|
||||
For Bonnie++ every option is of the form of a hyphen followed by a letter and
|
||||
then the next parameter contains the value.
|
||||
.TP
|
||||
.B \-d
|
||||
the directory to use for the tests.
|
||||
.TP
|
||||
.B \-s
|
||||
the size of the file(s) for IO performance measures in megabytes. If the size
|
||||
is greater than 1G then multiple files will be used to store the data, and
|
||||
each file will be up to 1G in size. This parameter may include the chunk size
|
||||
seperated from the size by a colon. The chunk\-size is measured in bytes and
|
||||
must be a power of two from 256 to 1048576, the default is 8192. NB You can
|
||||
specify the size in giga\-bytes or the chunk\-size in kilo\-bytes if you add
|
||||
\f3g\f1 or \f3k\f1 to the end of the number respectively.
|
||||
|
||||
If the specified size is 0 then this test will be skipped.
|
||||
.TP
|
||||
.B \-n
|
||||
the number of files for the file creation test. This is measured in multiples
|
||||
of 1024 files. This is because no\-one will want to test less than 1024 files,
|
||||
and we need the extra space on braille displays.
|
||||
|
||||
If the specified number is 0 then this test will be skipped.
|
||||
|
||||
The default for this test is to test with 0 byte files. To use files of other
|
||||
sizes you can specify
|
||||
.B number:max:min:num\-directories
|
||||
where
|
||||
.B max
|
||||
is the maximum size and
|
||||
.B min
|
||||
is the minimum size (both default to 0 if not specified). If minimum and
|
||||
maximum sizes are specified then every file will have a random size from the
|
||||
range
|
||||
.B min..max
|
||||
inclusive. If you specify a number of directories then the files will be
|
||||
evenly distributed amoung that many sub\-directories.
|
||||
|
||||
If
|
||||
.B max
|
||||
is
|
||||
.B \-1
|
||||
then hard links will be created instead of files.
|
||||
If
|
||||
.B max
|
||||
is
|
||||
.B \-2
|
||||
then soft links will be created instead of files.
|
||||
|
||||
|
||||
.TP
|
||||
.B \-m
|
||||
name of the machine \- for display purposes only.
|
||||
|
||||
.TP
|
||||
.B \-r
|
||||
RAM size in megabytes. If you specify this the other parameters will be
|
||||
checked to ensure they make sense for a machine of that much RAM. You should
|
||||
not need to do this in general use as it should be able to discover the RAM
|
||||
size. NB If you specify a size of 0 then all checks will be disabled...
|
||||
|
||||
.TP
|
||||
.B \-x
|
||||
number of test runs. This is useful if you want to perform more than one test.
|
||||
It will dump output continuously in CSV format until either the number of
|
||||
tests have been completed, or it is killed.
|
||||
|
||||
.TP
|
||||
.B \-u
|
||||
user\-id to use. When running as root specify the UID to use for the tests. It
|
||||
is not recommended to use root (since the occasion when a Bonnie++ bug wiped
|
||||
out someone's system), so if you really want to run as root then use
|
||||
.B \-u root.
|
||||
Also if you want to specify the group to run as then use the
|
||||
.B user:group
|
||||
format. If you specify a user by name but no group then the primary group of
|
||||
that user will be chosen. If you specify a user by number and no group then
|
||||
the group will be
|
||||
.B nogroup.
|
||||
|
||||
.TP
|
||||
.B \-g
|
||||
group\-id to use. Same as using
|
||||
.B :group
|
||||
for the
|
||||
.B \-u
|
||||
parameter, just a different way to specify it for compatibility with other
|
||||
programs.
|
||||
|
||||
.TP
|
||||
.B \-q
|
||||
quiet mode. If specified then some of the extra informational messages will be
|
||||
suppressed.
|
||||
|
||||
.TP
|
||||
.B \-f
|
||||
fast mode, skips per\-char IO tests.
|
||||
|
||||
.TP
|
||||
.B \-b
|
||||
no write buffering. fsync() after every write.
|
||||
|
||||
.TP
|
||||
.B \-p
|
||||
number of processes to serve semaphores for. This is used to create the
|
||||
semaphores for synchronising multiple Bonnie++ processes. All the processes
|
||||
which are told to use the semaphore with
|
||||
.B \-y
|
||||
will start each test at the same time. Use the value \-1 to delete the
|
||||
semaphore.
|
||||
|
||||
.TP
|
||||
.B \-y
|
||||
wait for semaphore before each test.
|
||||
|
||||
.P
|
||||
|
||||
.SH "MULTIPLE PROCESSES"
|
||||
Run the following commands to run three copies of Bonnie++ simultaneously:
|
||||
.P
|
||||
bonnie++ -p3
|
||||
.P
|
||||
bonnie++ -y > out1 &
|
||||
.P
|
||||
bonnie++ -y > out2 &
|
||||
.P
|
||||
bonnie++ -y > out3 &
|
||||
|
||||
.SH "OUTPUT"
|
||||
The primary output is plain\-text in 80 columns which is designed to fit well
|
||||
when pasted into email and which will work well with Braille displays.
|
||||
.P
|
||||
The second type of output is CSV (Comma Seperated Values). This can easily be
|
||||
imported into any spread\-sheet or database program. Also I have included
|
||||
the programs
|
||||
.B bon_csv2html
|
||||
and
|
||||
.B bon_csv2txt
|
||||
to convert CSV data to HTML and plain\-ascii respectively.
|
||||
.P
|
||||
For every test two numbers are reported, the amount of work done (higher
|
||||
numbers are better) and the percentage of CPU time taken to perform the work
|
||||
(lower numbers are better). If a test completes in less than 500ms then
|
||||
the output will be displayed as "++++". This is because such a test result
|
||||
can't be calculated accurately due to rounding errors and I would rather
|
||||
display no result than a wrong result.
|
||||
|
||||
.SH "AUTHOR"
|
||||
This program, its manual page, and the Debian package were written by
|
||||
Russell Coker <[email protected]>, parts of the program are based on the
|
||||
work of Tim Bray <[email protected]>.
|
||||
.P
|
||||
The documentation, the Perl scripts, and all the code for testing the creation
|
||||
of thousands of files was written by Russell Coker, but the entire package is
|
||||
under joint copyright with Tim Bray.
|
||||
|
||||
.SH "SIGNALS"
|
||||
Handles SIGINT and does a cleanup (which may take some time), a second SIGINT
|
||||
or a SIGQUIT will cause it to immediately die.
|
||||
.P
|
||||
SIGXCPU and SIGXFSZ act like SIGINT.
|
||||
.P
|
||||
Ignores SIGHUP.
|
||||
|
||||
|
||||
.SH "BUGS"
|
||||
The random file sizes will add up to different values for different test runs.
|
||||
I plan to add some code that checks the sum and ensures that the sum of the
|
||||
values will be the same on seperate runs.
|
||||
|
||||
.SH "AVAILABILITY"
|
||||
The source is available from http://www.coker.com.au/bonnie++ .
|
||||
.P
|
||||
See http://etbe.coker.com.au/category/benchmark for further information.
|
||||
|
||||
.SH "SEE ALSO"
|
||||
.BR bon_csv2html (1),
|
||||
.BR bon_csv2txt (1)
|
||||
@@ -1,689 +0,0 @@
|
||||
|
||||
/*
|
||||
* COPYRIGHT NOTICE:
|
||||
* Copyright (c) Tim Bray, 1990.
|
||||
* Copyright (c) Russell Coker, 1999. I have updated the program, added
|
||||
* support for >2G on 32bit machines, and tests for file creation.
|
||||
* Licensed under the GPL version 2.0.
|
||||
* DISCLAIMER:
|
||||
* This program is provided AS IS with no warranty of any kind, and
|
||||
* The author makes no representation with respect to the adequacy of this
|
||||
* program for any particular purpose or with respect to its adequacy to
|
||||
* produce any particular result, and
|
||||
* The author shall not be liable for loss or damage arising out of
|
||||
* the use of this program regardless of how sustained, and
|
||||
* In no event shall the author be liable for special, direct, indirect
|
||||
* or consequential damage, loss, costs or fees or expenses of any
|
||||
* nature or kind.
|
||||
*/
|
||||
|
||||
#ifdef OS2
|
||||
#define INCL_DOSFILEMGR
|
||||
#define INCL_DOSMISC
|
||||
#define INCL_DOSQUEUES
|
||||
#define INCL_DOSPROCESS
|
||||
#include <os2.h>
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include "bonnie.h"
|
||||
#include "bon_io.h"
|
||||
#include "bon_file.h"
|
||||
#include "bon_time.h"
|
||||
#include "semaphore.h"
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef AIX_MEM_SIZE
|
||||
#include <cf.h>
|
||||
#include <sys/cfgodm.h>
|
||||
#include <sys/cfgdb.h>
|
||||
#endif
|
||||
|
||||
void usage();
|
||||
|
||||
class CGlobalItems
|
||||
{
|
||||
public:
|
||||
bool quiet;
|
||||
bool fast;
|
||||
bool sync_bonnie;
|
||||
BonTimer timer;
|
||||
int ram;
|
||||
Semaphore sem;
|
||||
char *name;
|
||||
bool bufSync;
|
||||
int chunk_bits;
|
||||
int chunk_size() const { return m_chunk_size; }
|
||||
bool *doExit;
|
||||
void set_chunk_size(int size)
|
||||
{ delete m_buf; m_buf = new char[size]; m_chunk_size = size; }
|
||||
|
||||
char *buf() { return m_buf; }
|
||||
|
||||
CGlobalItems(bool *exitFlag);
|
||||
~CGlobalItems() { delete name; delete m_buf; }
|
||||
|
||||
void decrement_and_wait(int nr_sem);
|
||||
|
||||
void SetName(CPCCHAR path)
|
||||
{
|
||||
delete name;
|
||||
name = new char[strlen(path) + 15];
|
||||
#ifdef OS2
|
||||
ULONG myPid = 0;
|
||||
DosQuerySysInfo(QSV_FOREGROUND_PROCESS, QSV_FOREGROUND_PROCESS
|
||||
, &myPid, sizeof(myPid));
|
||||
#else
|
||||
pid_t myPid = getpid();
|
||||
#endif
|
||||
sprintf(name, "%s/Bonnie.%d", path, int(myPid));
|
||||
}
|
||||
private:
|
||||
int m_chunk_size;
|
||||
char *m_buf;
|
||||
|
||||
|
||||
CGlobalItems(const CGlobalItems &f);
|
||||
CGlobalItems & operator =(const CGlobalItems &f);
|
||||
};
|
||||
|
||||
CGlobalItems::CGlobalItems(bool *exitFlag)
|
||||
: quiet(false)
|
||||
, fast(false)
|
||||
, sync_bonnie(false)
|
||||
, timer()
|
||||
, ram(0)
|
||||
, sem(SemKey, TestCount)
|
||||
, name(NULL)
|
||||
, bufSync(false)
|
||||
, chunk_bits(DefaultChunkBits)
|
||||
, doExit(exitFlag)
|
||||
, m_chunk_size(DefaultChunkSize)
|
||||
, m_buf(new char[m_chunk_size])
|
||||
{
|
||||
SetName(".");
|
||||
}
|
||||
|
||||
void CGlobalItems::decrement_and_wait(int nr_sem)
|
||||
{
|
||||
if(sem.decrement_and_wait(nr_sem))
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int TestDirOps(int directory_size, int max_size, int min_size
|
||||
, int num_directories, CGlobalItems &globals);
|
||||
int TestFileOps(int file_size, CGlobalItems &globals);
|
||||
|
||||
static bool exitNow;
|
||||
static bool already_printed_error;
|
||||
|
||||
#undef USE_SA_SIGACTION
|
||||
#ifdef USE_SA_SIGACTION
|
||||
#define SIGNAL_NUMBER siginf->si_signo
|
||||
#else
|
||||
#define SIGNAL_NUMBER sig
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void ctrl_c_handler(int sig
|
||||
#ifdef USE_SA_SIGACTION
|
||||
, siginfo_t *siginf, void *unused
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if(SIGNAL_NUMBER == SIGXCPU)
|
||||
fprintf(stderr, "Exceeded CPU usage.\n");
|
||||
else if(SIGNAL_NUMBER == SIGXFSZ)
|
||||
fprintf(stderr, "exceeded file storage limits.\n");
|
||||
exitNow = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int file_size = DefaultFileSize;
|
||||
int directory_size = DefaultDirectorySize;
|
||||
int directory_max_size = DefaultDirectoryMaxSize;
|
||||
int directory_min_size = DefaultDirectoryMinSize;
|
||||
int num_bonnie_procs = 0;
|
||||
int num_directories = 1;
|
||||
int count = -1;
|
||||
const char * machine = NULL;
|
||||
char *userName = NULL, *groupName = NULL;
|
||||
CGlobalItems globals(&exitNow);
|
||||
bool setSize = false;
|
||||
|
||||
exitNow = false;
|
||||
already_printed_error = false;
|
||||
|
||||
struct sigaction sa;
|
||||
#ifdef USE_SA_SIGACTION
|
||||
sa.sa_sigaction = &ctrl_c_handler;
|
||||
sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
|
||||
#else
|
||||
sa.sa_handler = ctrl_c_handler;
|
||||
sa.sa_flags = SA_RESETHAND;
|
||||
#endif
|
||||
if(sigaction(SIGINT, &sa, NULL)
|
||||
|| sigaction(SIGXCPU, &sa, NULL)
|
||||
|| sigaction(SIGXFSZ, &sa, NULL))
|
||||
{
|
||||
printf("Can't handle SIGINT.\n");
|
||||
return 1;
|
||||
}
|
||||
#ifdef USE_SA_SIGACTION
|
||||
sa.sa_sigaction = NULL;
|
||||
#endif
|
||||
sa.sa_handler = SIG_IGN;
|
||||
if(sigaction(SIGHUP, &sa, NULL))
|
||||
{
|
||||
printf("Can't handle SIGHUP.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef _SC_PHYS_PAGES
|
||||
int page_size = sysconf(_SC_PAGESIZE);
|
||||
int num_pages = sysconf(_SC_PHYS_PAGES);
|
||||
if(page_size != -1 && num_pages != -1)
|
||||
{
|
||||
globals.ram = page_size/1024 * (num_pages/1024);
|
||||
}
|
||||
#else
|
||||
|
||||
#ifdef AIX_MEM_SIZE
|
||||
struct CuAt *odm_obj;
|
||||
int how_many;
|
||||
|
||||
odm_set_path("/etc/objrepos");
|
||||
odm_obj = getattr("sys0", "realmem", 0, &how_many);
|
||||
globals.ram = atoi(odm_obj->value) / 1024;
|
||||
odm_terminate();
|
||||
printf("Memory = %d MiB\n", globals.ram);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
int int_c;
|
||||
while(-1 != (int_c = getopt(argc, argv, "bd:fg:m:n:p:qr:s:u:x:y")) )
|
||||
{
|
||||
switch(char(int_c))
|
||||
{
|
||||
case '?':
|
||||
case ':':
|
||||
usage();
|
||||
break;
|
||||
case 'b':
|
||||
globals.bufSync = true;
|
||||
break;
|
||||
case 'd':
|
||||
if(chdir(optarg))
|
||||
{
|
||||
fprintf(stderr, "Can't change to directory \"%s\".\n", optarg);
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
globals.fast = true;
|
||||
break;
|
||||
case 'm':
|
||||
machine = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
sscanf(optarg, "%d:%d:%d:%d", &directory_size
|
||||
, &directory_max_size, &directory_min_size
|
||||
, &num_directories);
|
||||
break;
|
||||
case 'p':
|
||||
num_bonnie_procs = atoi(optarg);
|
||||
/* Set semaphore to # of bonnie++ procs
|
||||
to synchronize */
|
||||
break;
|
||||
case 'q':
|
||||
globals.quiet = true;
|
||||
break;
|
||||
case 'r':
|
||||
globals.ram = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
{
|
||||
char *sbuf = strdup(optarg);
|
||||
char *size = strtok(sbuf, ":");
|
||||
file_size = atoi(size);
|
||||
char c = size[strlen(size) - 1];
|
||||
if(c == 'g' || c == 'G')
|
||||
file_size *= 1024;
|
||||
size = strtok(NULL, "");
|
||||
if(size)
|
||||
{
|
||||
int tmp = atoi(size);
|
||||
c = size[strlen(size) - 1];
|
||||
if(c == 'k' || c == 'K')
|
||||
tmp *= 1024;
|
||||
globals.set_chunk_size(tmp);
|
||||
}
|
||||
setSize = true;
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
if(groupName)
|
||||
usage();
|
||||
groupName = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
{
|
||||
if(userName)
|
||||
usage();
|
||||
userName = strdup(optarg);
|
||||
int i;
|
||||
for(i = 0; userName[i] && userName[i] != ':'; i++)
|
||||
{}
|
||||
if(userName[i] == ':')
|
||||
{
|
||||
if(groupName)
|
||||
usage();
|
||||
userName[i] = '\0';
|
||||
groupName = &userName[i + 1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
count = atoi(optarg);
|
||||
break;
|
||||
case 'y':
|
||||
/* tell procs to synchronize via previous
|
||||
defined semaphore */
|
||||
globals.sync_bonnie = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(optind < argc)
|
||||
usage();
|
||||
|
||||
if(globals.ram && !setSize)
|
||||
{
|
||||
if(file_size < (globals.ram * 2))
|
||||
file_size = globals.ram * 2;
|
||||
// round up to the nearest gig
|
||||
if(file_size % 1024 > 512)
|
||||
file_size = file_size + 1024 - (file_size % 1024);
|
||||
}
|
||||
|
||||
if(machine == NULL)
|
||||
{
|
||||
struct utsname utsBuf;
|
||||
if(uname(&utsBuf) != -1)
|
||||
machine = utsBuf.nodename;
|
||||
}
|
||||
|
||||
if(userName || groupName)
|
||||
{
|
||||
if(bon_setugid(userName, groupName, globals.quiet))
|
||||
return 1;
|
||||
if(userName)
|
||||
free(userName);
|
||||
}
|
||||
else if(geteuid() == 0)
|
||||
{
|
||||
fprintf(stderr, "You must use the \"-u\" switch when running as root.\n");
|
||||
usage();
|
||||
}
|
||||
|
||||
if(num_bonnie_procs && globals.sync_bonnie)
|
||||
usage();
|
||||
|
||||
if(num_bonnie_procs)
|
||||
{
|
||||
if(num_bonnie_procs == -1)
|
||||
{
|
||||
return globals.sem.clear_sem();
|
||||
}
|
||||
else
|
||||
{
|
||||
return globals.sem.create(num_bonnie_procs);
|
||||
}
|
||||
}
|
||||
|
||||
if(globals.sync_bonnie)
|
||||
{
|
||||
if(globals.sem.get_semid())
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(file_size < 0 || directory_size < 0 || (!file_size && !directory_size) )
|
||||
usage();
|
||||
if(globals.chunk_size() < 256 || globals.chunk_size() > Unit)
|
||||
usage();
|
||||
int i;
|
||||
globals.chunk_bits = 0;
|
||||
for(i = globals.chunk_size(); i > 1; i = i >> 1, globals.chunk_bits++)
|
||||
{}
|
||||
if(1 << globals.chunk_bits != globals.chunk_size())
|
||||
usage();
|
||||
|
||||
if( (directory_max_size != -1 && directory_max_size != -2)
|
||||
&& (directory_max_size < directory_min_size || directory_max_size < 0
|
||||
|| directory_min_size < 0) )
|
||||
usage();
|
||||
/* If the storage size is too big for the maximum number of files (1000G) */
|
||||
if(file_size > IOFileSize * MaxIOFiles)
|
||||
usage();
|
||||
/* If the file size is so large and the chunk size is so small that we have
|
||||
* more than 2G of chunks */
|
||||
if(globals.chunk_bits < 20 && file_size > (1 << (31 - 20 + globals.chunk_bits)) )
|
||||
usage();
|
||||
// if doing more than one test run then we print a header before the
|
||||
// csv format output.
|
||||
if(count > 1)
|
||||
{
|
||||
globals.timer.SetType(BonTimer::csv);
|
||||
globals.timer.PrintHeader(stdout);
|
||||
}
|
||||
#ifdef OS2
|
||||
ULONG myPid = 0;
|
||||
DosQuerySysInfo(QSV_FOREGROUND_PROCESS, QSV_FOREGROUND_PROCESS
|
||||
, &myPid, sizeof(myPid));
|
||||
#else
|
||||
pid_t myPid = getpid();
|
||||
#endif
|
||||
srand(myPid ^ time(NULL));
|
||||
for(; count > 0 || count == -1; count--)
|
||||
{
|
||||
globals.timer.Initialize();
|
||||
int rc;
|
||||
rc = TestFileOps(file_size, globals);
|
||||
if(rc) return rc;
|
||||
rc = TestDirOps(directory_size, directory_max_size, directory_min_size
|
||||
, num_directories, globals);
|
||||
if(rc) return rc;
|
||||
// if we are only doing one test run then print a plain-text version of
|
||||
// the results before printing a csv version.
|
||||
if(count == -1)
|
||||
{
|
||||
globals.timer.SetType(BonTimer::txt);
|
||||
rc = globals.timer.DoReport(machine, file_size, directory_size
|
||||
, directory_max_size, directory_min_size
|
||||
, num_directories, globals.chunk_size()
|
||||
, globals.quiet ? stderr : stdout);
|
||||
}
|
||||
// print a csv version in every case
|
||||
globals.timer.SetType(BonTimer::csv);
|
||||
rc = globals.timer.DoReport(machine, file_size, directory_size
|
||||
, directory_max_size, directory_min_size
|
||||
, num_directories, globals.chunk_size(), stdout);
|
||||
if(rc) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
TestFileOps(int file_size, CGlobalItems &globals)
|
||||
{
|
||||
if(file_size)
|
||||
{
|
||||
CFileOp file(globals.timer, file_size, globals.chunk_bits, globals.bufSync);
|
||||
int num_chunks;
|
||||
int words;
|
||||
char *buf = globals.buf();
|
||||
int bufindex;
|
||||
int i;
|
||||
|
||||
if(globals.ram && file_size < globals.ram * 2)
|
||||
{
|
||||
fprintf(stderr
|
||||
, "File size should be double RAM for good results, RAM is %dM.\n"
|
||||
, globals.ram);
|
||||
return 1;
|
||||
}
|
||||
// default is we have 1M / 8K * 200 chunks = 25600
|
||||
num_chunks = Unit / globals.chunk_size() * file_size;
|
||||
|
||||
int rc;
|
||||
rc = file.open(globals.name, true, true);
|
||||
if(rc)
|
||||
return rc;
|
||||
if(exitNow)
|
||||
return EXIT_CTRL_C;
|
||||
globals.timer.timestamp();
|
||||
|
||||
if(!globals.fast)
|
||||
{
|
||||
globals.decrement_and_wait(Putc);
|
||||
// Fill up a file, writing it a char at a time with the stdio putc() call
|
||||
if(!globals.quiet) fprintf(stderr, "Writing with putc()...");
|
||||
for(words = 0; words < num_chunks; words++)
|
||||
{
|
||||
if(file.write_block_putc() == -1)
|
||||
return 1;
|
||||
if(exitNow)
|
||||
return EXIT_CTRL_C;
|
||||
}
|
||||
fflush(NULL);
|
||||
/*
|
||||
* note that we always close the file before measuring time, in an
|
||||
* effort to force as much of the I/O out as we can
|
||||
*/
|
||||
file.close();
|
||||
globals.timer.get_delta_t(Putc);
|
||||
if(!globals.quiet) fprintf(stderr, "done\n");
|
||||
}
|
||||
/* Write the whole file from scratch, again, with block I/O */
|
||||
if(file.reopen(true))
|
||||
return 1;
|
||||
globals.decrement_and_wait(FastWrite);
|
||||
if(!globals.quiet) fprintf(stderr, "Writing intelligently...");
|
||||
memset(buf, 0, globals.chunk_size());
|
||||
globals.timer.timestamp();
|
||||
bufindex = 0;
|
||||
// for the number of chunks of file data
|
||||
for(i = 0; i < num_chunks; i++)
|
||||
{
|
||||
if(exitNow)
|
||||
return EXIT_CTRL_C;
|
||||
// for each chunk in the Unit
|
||||
buf[bufindex]++;
|
||||
bufindex = (bufindex + 1) % globals.chunk_size();
|
||||
if(file.write_block(PVOID(buf)) == -1)
|
||||
return io_error("write(2)");
|
||||
}
|
||||
file.close();
|
||||
globals.timer.get_delta_t(FastWrite);
|
||||
if(!globals.quiet) fprintf(stderr, "done\n");
|
||||
|
||||
|
||||
/* Now read & rewrite it using block I/O. Dirty one word in each block */
|
||||
if(file.reopen(false))
|
||||
return 1;
|
||||
if (file.seek(0, SEEK_SET) == -1)
|
||||
{
|
||||
if(!globals.quiet) fprintf(stderr, "error in lseek(2) before rewrite\n");
|
||||
return 1;
|
||||
}
|
||||
globals.decrement_and_wait(ReWrite);
|
||||
if(!globals.quiet) fprintf(stderr, "Rewriting...");
|
||||
globals.timer.timestamp();
|
||||
bufindex = 0;
|
||||
for(words = 0; words < num_chunks; words++)
|
||||
{ // for each chunk in the file
|
||||
if (file.read_block(PVOID(buf)) == -1)
|
||||
return 1;
|
||||
bufindex = bufindex % globals.chunk_size();
|
||||
buf[bufindex]++;
|
||||
bufindex++;
|
||||
if (file.seek(-1, SEEK_CUR) == -1)
|
||||
return 1;
|
||||
if (file.write_block(PVOID(buf)) == -1)
|
||||
return io_error("re write(2)");
|
||||
if(exitNow)
|
||||
return EXIT_CTRL_C;
|
||||
}
|
||||
file.close();
|
||||
globals.timer.get_delta_t(ReWrite);
|
||||
if(!globals.quiet) fprintf(stderr, "done\n");
|
||||
|
||||
|
||||
if(!globals.fast)
|
||||
{
|
||||
// read them all back with getc()
|
||||
if(file.reopen(false, true))
|
||||
return 1;
|
||||
globals.decrement_and_wait(Getc);
|
||||
if(!globals.quiet) fprintf(stderr, "Reading with getc()...");
|
||||
globals.timer.timestamp();
|
||||
|
||||
for(words = 0; words < num_chunks; words++)
|
||||
{
|
||||
if(file.read_block_getc(buf) == -1)
|
||||
return 1;
|
||||
if(exitNow)
|
||||
return EXIT_CTRL_C;
|
||||
}
|
||||
|
||||
file.close();
|
||||
globals.timer.get_delta_t(Getc);
|
||||
if(!globals.quiet) fprintf(stderr, "done\n");
|
||||
}
|
||||
|
||||
/* Now suck it in, Chunk at a time, as fast as we can */
|
||||
if(file.reopen(false))
|
||||
return 1;
|
||||
if (file.seek(0, SEEK_SET) == -1)
|
||||
return io_error("lseek before read");
|
||||
globals.decrement_and_wait(FastRead);
|
||||
if(!globals.quiet) fprintf(stderr, "Reading intelligently...");
|
||||
globals.timer.timestamp();
|
||||
for(i = 0; i < num_chunks; i++)
|
||||
{ /* per block */
|
||||
if ((words = file.read_block(PVOID(buf))) == -1)
|
||||
return io_error("read(2)");
|
||||
if(exitNow)
|
||||
return EXIT_CTRL_C;
|
||||
} /* per block */
|
||||
file.close();
|
||||
globals.timer.get_delta_t(FastRead);
|
||||
if(!globals.quiet) fprintf(stderr, "done\n");
|
||||
|
||||
globals.timer.timestamp();
|
||||
if(file.seek_test(globals.quiet, globals.sem))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Now test random seeks; first, set up for communicating with children.
|
||||
* The object of the game is to do "Seeks" lseek() calls as quickly
|
||||
* as possible. So we'll farm them out among SeekProcCount processes.
|
||||
* We'll control them by writing 1-byte tickets down a pipe which
|
||||
* the children all read. We write "Seeks" bytes with val 1, whichever
|
||||
* child happens to get them does it and the right number of seeks get
|
||||
* done.
|
||||
* The idea is that since the write() of the tickets is probably
|
||||
* atomic, the parent process likely won't get scheduled while the
|
||||
* children are seeking away. If you draw a picture of the likely
|
||||
* timelines for three children, it seems likely that the seeks will
|
||||
* overlap very nicely with the process scheduling with the effect
|
||||
* that there will *always* be a seek() outstanding on the file.
|
||||
* Question: should the file be opened *before* the fork, so that
|
||||
* all the children are lseeking on the same underlying file object?
|
||||
*/
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
TestDirOps(int directory_size, int max_size, int min_size
|
||||
, int num_directories, CGlobalItems &globals)
|
||||
{
|
||||
COpenTest open_test(globals.chunk_size(), globals.bufSync, globals.doExit);
|
||||
if(!directory_size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// if directory_size (in K) * data per file*2 > (ram << 10) (IE memory /1024)
|
||||
// then the storage of file names will take more than half RAM and there
|
||||
// won't be enough RAM to have Bonnie++ paged in and to have a reasonable
|
||||
// meta-data cache.
|
||||
if(globals.ram && directory_size * MaxDataPerFile * 2 > (globals.ram << 10))
|
||||
{
|
||||
fprintf(stderr
|
||||
, "When testing %dK of files in %d MiB of RAM the system is likely to\n"
|
||||
"start paging Bonnie++ data and the test will give suspect\n"
|
||||
"results, use less files or install more RAM for this test.\n"
|
||||
, directory_size, globals.ram);
|
||||
return 1;
|
||||
}
|
||||
// Can't use more than 1G of RAM
|
||||
if(directory_size * MaxDataPerFile > (1 << 20))
|
||||
{
|
||||
fprintf(stderr, "Not enough ram to test with %dK files.\n"
|
||||
, directory_size);
|
||||
return 1;
|
||||
}
|
||||
globals.decrement_and_wait(CreateSeq);
|
||||
if(!globals.quiet) fprintf(stderr, "Create files in sequential order...");
|
||||
if(open_test.create(globals.name, globals.timer, directory_size
|
||||
, max_size, min_size, num_directories, false))
|
||||
return 1;
|
||||
globals.decrement_and_wait(StatSeq);
|
||||
if(!globals.quiet) fprintf(stderr, "done.\nStat files in sequential order...");
|
||||
if(open_test.stat_sequential(globals.timer))
|
||||
return 1;
|
||||
globals.decrement_and_wait(DelSeq);
|
||||
if(!globals.quiet) fprintf(stderr, "done.\nDelete files in sequential order...");
|
||||
if(open_test.delete_sequential(globals.timer))
|
||||
return 1;
|
||||
if(!globals.quiet) fprintf(stderr, "done.\n");
|
||||
|
||||
globals.decrement_and_wait(CreateRand);
|
||||
if(!globals.quiet) fprintf(stderr, "Create files in random order...");
|
||||
if(open_test.create(globals.name, globals.timer, directory_size
|
||||
, max_size, min_size, num_directories, true))
|
||||
return 1;
|
||||
globals.decrement_and_wait(StatRand);
|
||||
if(!globals.quiet) fprintf(stderr, "done.\nStat files in random order...");
|
||||
if(open_test.stat_random(globals.timer))
|
||||
return 1;
|
||||
globals.decrement_and_wait(DelRand);
|
||||
if(!globals.quiet) fprintf(stderr, "done.\nDelete files in random order...");
|
||||
if(open_test.delete_random(globals.timer))
|
||||
return 1;
|
||||
if(!globals.quiet) fprintf(stderr, "done.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
usage()
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: bonnie++ [-d scratch-dir] [-s size(MiB)[:chunk-size(b)]]\n"
|
||||
" [-n number-to-stat[:max-size[:min-size][:num-directories]]]\n"
|
||||
" [-m machine-name]\n"
|
||||
" [-r ram-size-in-MiB]\n"
|
||||
" [-x number-of-tests] [-u uid-to-use:gid-to-use] [-g gid-to-use]\n"
|
||||
" [-q] [-f] [-b] [-p processes | -y]\n"
|
||||
"\nVersion: " BON_VERSION "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
io_error(CPCCHAR message, bool do_exit)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
if(!already_printed_error && !do_exit)
|
||||
{
|
||||
sprintf(buf, "Bonnie: drastic I/O error (%s)", message);
|
||||
perror(buf);
|
||||
already_printed_error = 1;
|
||||
}
|
||||
if(do_exit)
|
||||
exit(1);
|
||||
return(1);
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
Summary: A program for benchmarking hard drives and filesystems
|
||||
Name: bonnie++
|
||||
Version: 1.03d
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Utilities/Benchmarking
|
||||
URL: http://www.coker.com.au/bonnie++
|
||||
Source: http://www.coker.com.au/bonnie++/bonnie++-%{version}.tgz
|
||||
BuildRoot: /tmp/%{name}-buildroot
|
||||
Prefixes: %{_prefix} %{_datadir}
|
||||
Requires: glibc >= 2.1
|
||||
Requires: perl >= 5.0
|
||||
BuildRequires: glibc-devel >= 2.1
|
||||
|
||||
%description
|
||||
Bonnie++ is a benchmark suite that is aimed at performing a number of simple
|
||||
tests of hard drive and file system performance.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
./configure --prefix=$RPM_BUILD_ROOT%{_prefix} --mandir=$RPM_BUILD_ROOT%{_mandir}
|
||||
make
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make install
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc changelog.txt readme.html
|
||||
|
||||
%{_prefix}/sbin/bonnie++
|
||||
%{_prefix}/sbin/zcav
|
||||
%{_prefix}/bin/bon_csv2html
|
||||
%{_prefix}/bin/bon_csv2txt
|
||||
%{_mandir}/man1/*
|
||||
%{_mandir}/man8/*
|
||||
|
||||
%changelog
|
||||
* Wed Sep 06 2000 Rob Latham <[email protected]>
|
||||
- first packaging
|
||||
@@ -1,46 +0,0 @@
|
||||
Summary: A program for benchmarking hard drives and filesystems
|
||||
Name: bonnie++
|
||||
Version: @version@
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Utilities/Benchmarking
|
||||
URL: http://www.coker.com.au/bonnie++
|
||||
Source: http://www.coker.com.au/bonnie++/bonnie++-%{version}.tgz
|
||||
BuildRoot: /tmp/%{name}-buildroot
|
||||
Prefixes: %{_prefix} %{_datadir}
|
||||
Requires: glibc >= 2.1
|
||||
Requires: perl >= 5.0
|
||||
BuildRequires: glibc-devel >= 2.1
|
||||
|
||||
%description
|
||||
Bonnie++ is a benchmark suite that is aimed at performing a number of simple
|
||||
tests of hard drive and file system performance.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
./configure --prefix=$RPM_BUILD_ROOT%{_prefix} --mandir=$RPM_BUILD_ROOT%{_mandir}
|
||||
make
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make install
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc changelog.txt readme.html
|
||||
|
||||
%{_prefix}/sbin/bonnie++
|
||||
%{_prefix}/sbin/zcav
|
||||
%{_prefix}/bin/bon_csv2html
|
||||
%{_prefix}/bin/bon_csv2txt
|
||||
%{_mandir}/man1/*
|
||||
%{_mandir}/man8/*
|
||||
|
||||
%changelog
|
||||
* Wed Sep 06 2000 Rob Latham <[email protected]>
|
||||
- first packaging
|
||||
@@ -1 +0,0 @@
|
||||
.so man8/bonnie++.8
|
||||
@@ -1,64 +0,0 @@
|
||||
#ifndef BONNIE
|
||||
#define BONNIE
|
||||
|
||||
#define BON_VERSION "1.03d"
|
||||
|
||||
#include "conf.h"
|
||||
#include "port.h"
|
||||
|
||||
#define SemKey 4711
|
||||
#define NumSems TestCount
|
||||
// million files (7) + up to 12 random extra chars
|
||||
#define RandExtraLen (12)
|
||||
#define MaxNameLen (7 + RandExtraLen)
|
||||
// data includes index to which directory (6 bytes) and terminating '\0' for
|
||||
// the name and pointer to file name
|
||||
#define MaxDataPerFile (MaxNameLen + 6 + 1 + 4)
|
||||
#define MinTime (0.5)
|
||||
#define Seeks (8192)
|
||||
#define UpdateSeek (10)
|
||||
#define SeekProcCount (3)
|
||||
#define DefaultChunkBits (13)
|
||||
#define DefaultChunkSize (1 << DefaultChunkBits)
|
||||
#define UnitBits (20)
|
||||
#define Unit (1 << UnitBits)
|
||||
#define CreateNameLen 6
|
||||
#define DefaultFileSize 300
|
||||
#define DirectoryUnit 1024
|
||||
#define DefaultDirectorySize 16
|
||||
#define DefaultDirectoryMaxSize 0
|
||||
#define DefaultDirectoryMinSize 0
|
||||
// 1024M per file for IO.
|
||||
#define IOFileSize 1024
|
||||
// 3 digits
|
||||
#define MaxIOFiles 1000
|
||||
|
||||
typedef const char * PCCHAR;
|
||||
typedef char * PCHAR;
|
||||
typedef PCHAR const CPCHAR;
|
||||
typedef PCCHAR const CPCCHAR;
|
||||
typedef void * PVOID;
|
||||
typedef PVOID const CPVOID;
|
||||
typedef const CPVOID CPCVOID;
|
||||
|
||||
enum tests_t
|
||||
{
|
||||
Putc = 0,
|
||||
FastWrite,
|
||||
ReWrite,
|
||||
Getc,
|
||||
FastRead,
|
||||
Lseek,
|
||||
CreateSeq,
|
||||
StatSeq,
|
||||
DelSeq,
|
||||
CreateRand,
|
||||
StatRand,
|
||||
DelRand,
|
||||
TestCount
|
||||
};
|
||||
|
||||
int io_error(CPCCHAR message, bool do_exit = false);
|
||||
int bon_setugid(CPCCHAR user, CPCCHAR group, bool quiet);
|
||||
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
#ifndef BONNIE
|
||||
#define BONNIE
|
||||
|
||||
#define BON_VERSION "@version@"
|
||||
|
||||
#include "conf.h"
|
||||
#include "port.h"
|
||||
|
||||
#define SemKey 4711
|
||||
#define NumSems TestCount
|
||||
// million files (7) + up to 12 random extra chars
|
||||
#define RandExtraLen (12)
|
||||
#define MaxNameLen (7 + RandExtraLen)
|
||||
// data includes index to which directory (6 bytes) and terminating '\0' for
|
||||
// the name and pointer to file name
|
||||
#define MaxDataPerFile (MaxNameLen + 6 + 1 + 4)
|
||||
#define MinTime (0.5)
|
||||
#define Seeks (8192)
|
||||
#define UpdateSeek (10)
|
||||
#define SeekProcCount (3)
|
||||
#define DefaultChunkBits (13)
|
||||
#define DefaultChunkSize (1 << DefaultChunkBits)
|
||||
#define UnitBits (20)
|
||||
#define Unit (1 << UnitBits)
|
||||
#define CreateNameLen 6
|
||||
#define DefaultFileSize 300
|
||||
#define DirectoryUnit 1024
|
||||
#define DefaultDirectorySize 16
|
||||
#define DefaultDirectoryMaxSize 0
|
||||
#define DefaultDirectoryMinSize 0
|
||||
// 1024M per file for IO.
|
||||
#define IOFileSize 1024
|
||||
// 3 digits
|
||||
#define MaxIOFiles 1000
|
||||
|
||||
typedef const char * PCCHAR;
|
||||
typedef char * PCHAR;
|
||||
typedef PCHAR const CPCHAR;
|
||||
typedef PCCHAR const CPCCHAR;
|
||||
typedef void * PVOID;
|
||||
typedef PVOID const CPVOID;
|
||||
typedef const CPVOID CPCVOID;
|
||||
|
||||
enum tests_t
|
||||
{
|
||||
Putc = 0,
|
||||
FastWrite,
|
||||
ReWrite,
|
||||
Getc,
|
||||
FastRead,
|
||||
Lseek,
|
||||
CreateSeq,
|
||||
StatSeq,
|
||||
DelSeq,
|
||||
CreateRand,
|
||||
StatRand,
|
||||
DelRand,
|
||||
TestCount
|
||||
};
|
||||
|
||||
int io_error(CPCCHAR message, bool do_exit = false);
|
||||
int bon_setugid(CPCCHAR user, CPCCHAR group, bool quiet);
|
||||
|
||||
#endif
|
||||
@@ -1,10 +0,0 @@
|
||||
/* conf.h. Generated from conf.h.in by configure. */
|
||||
#ifndef CONF_H
|
||||
#define CONF_H
|
||||
|
||||
#define HAVE_ALGO_H 1
|
||||
/* #undef HAVE_ALGO */
|
||||
#define HAVE_ALGORITHM 1
|
||||
#define HAVE_VECTOR 1
|
||||
|
||||
#endif
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef CONF_H
|
||||
#define CONF_H
|
||||
|
||||
#undef HAVE_ALGO_H
|
||||
#undef HAVE_ALGO
|
||||
#undef HAVE_ALGORITHM
|
||||
#undef HAVE_VECTOR
|
||||
|
||||
#endif
|
||||
-4897
File diff suppressed because it is too large
Load Diff
@@ -1,78 +0,0 @@
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(bonnie++.cpp)
|
||||
AC_CONFIG_HEADER(conf.h)
|
||||
AC_SUBST(version)
|
||||
version="1.03d"
|
||||
|
||||
AC_SUBST(stripping)
|
||||
AC_ARG_ENABLE(stripping,
|
||||
[ --disable-stripping disables stripping of installed binaries],
|
||||
STRIPPING=$strippingval, STRIPPING=no)
|
||||
if [[ ! "$STRIPPING" = "no" ]]; then
|
||||
stripping=""
|
||||
else
|
||||
stripping="-s"
|
||||
fi
|
||||
echo $DEB_BUILD_OPTIONS | grep -q nostrip
|
||||
if [[ "$?" = "0" ]]; then
|
||||
stripping=""
|
||||
fi
|
||||
|
||||
AC_SUBST(debug)
|
||||
AC_ARG_ENABLE(debug,
|
||||
[ --with-debug enables debug code generation for binaries],
|
||||
debug=-g, debug="")
|
||||
echo $DEB_BUILD_OPTIONS | grep -q debug
|
||||
if [[ "$?" = "0" ]]; then
|
||||
debug=-g
|
||||
fi
|
||||
|
||||
dnl Checks for programs.
|
||||
AC_LANG_CPLUSPLUS
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CXXCPP
|
||||
AC_PROG_INSTALL
|
||||
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
AC_TYPE_SIZE_T
|
||||
|
||||
AC_SUBST(semun)
|
||||
AC_TRY_COMPILE(#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/sem.h>
|
||||
, union semun sem , semun="yes")
|
||||
if [[ -n "$semun" ]]; then
|
||||
semun="#define SEMUN_IN_SEM_H"
|
||||
fi
|
||||
AC_SUBST(sa_sigaction)
|
||||
AC_TRY_COMPILE([#include <signal.h>]
|
||||
, [struct sigaction sa; sa.sa_sigaction = 0]
|
||||
, sa_sigaction=y)
|
||||
if [[ "$sa_sigaction" = "y" ]]; then
|
||||
sa_sigaction="#define USE_SA_SIGACTION"
|
||||
fi
|
||||
AC_SUBST(bool)
|
||||
AC_TRY_COMPILE([], [bool var;] , , bool="typedef bool char;")
|
||||
AC_SUBST(true_false)
|
||||
AC_TRY_COMPILE(, [char c = true; char d = false;
|
||||
] , true_false="0", true_false="1")
|
||||
AC_SUBST(snprintf)
|
||||
AC_TRY_LINK([#include <stdio.h>], char buf[[10]]; snprintf(buf, sizeof(buf), "abc");,,snprintf="no");
|
||||
if [[ -n "$snprintf" ]]; then
|
||||
snprintf="#define NO_SNPRINTF"
|
||||
fi
|
||||
|
||||
AC_SUBST(exes)
|
||||
AC_SUBST(man8)
|
||||
AC_CHECK_HEADERS(vector.h vector, vec_exes="zcav" vec_man8="zcav.8", echo "STL not installed so no ZCAV!")
|
||||
exes="bonnie++ $vec_exes"
|
||||
man8="bonnie++.8 $vec_man8"
|
||||
AC_CHECK_HEADERS(algorithm algo.h algo)
|
||||
|
||||
AC_SUBST(aix_mem_size)
|
||||
if [[ -f /etc/objrepos ]]; then
|
||||
aix_mem_size="#define AIX_MEM_SIZE 1"
|
||||
fi
|
||||
|
||||
AC_OUTPUT(Makefile bonnie.h port.h bonnie++.spec bon_csv2html bon_csv2txt sun/pkginfo)
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright:
|
||||
|
||||
* COPYRIGHT NOTICE:
|
||||
* Copyright (c) Tim Bray <[email protected]>, 1990.
|
||||
* Copyright (c) Russell Coker <[email protected]>, 1999.
|
||||
|
||||
|
||||
Licensed under the GPL version 2 license.
|
||||
|
||||
* DISCLAIMER:
|
||||
* This program is provided AS IS with no warranty of any kind, and
|
||||
* The author makes no representation with respect to the adequacy of this
|
||||
* program for any particular purpose or with respect to its adequacy to
|
||||
* produce any particular result, and
|
||||
* The author shall not be liable for loss or damage arising out of
|
||||
* the use of this program regardless of how sustained, and
|
||||
* In no event shall the author be liable for special, direct, indirect
|
||||
* or consequential damage, loss, costs or fees or expenses of any
|
||||
* nature or kind.
|
||||
@@ -1,10 +0,0 @@
|
||||
Tim Bray <[email protected]> Author of the original Bonnie program.
|
||||
|
||||
Christian Kagerhuber <[email protected]> Contributed some patches
|
||||
including the semaphore code. Also found the srand() bug in the seek code!
|
||||
|
||||
Brian A. May <[email protected]> Sponsored the Debianization of this
|
||||
and other programs I am writing.
|
||||
|
||||
Brad Knowles <[email protected]> Did heaps of testing, especially on BSD, also
|
||||
contributed lots of little patches to the source.
|
||||
@@ -1,534 +0,0 @@
|
||||
bonnie++ (1.03d) unstable; urgency=low
|
||||
|
||||
* Made it compile with GCC 4.3.
|
||||
* Added the URL to the man pages.
|
||||
* Made zcav display the total time and average speed for a loop and also the
|
||||
time that each loop completed.
|
||||
* Made the zcav default block size 256M to cope with larger and faster disks.
|
||||
* Made the zcav output units be in MiB/s and GiB.
|
||||
* Improved the Bonnie++ man page in regard to the -y and -p options.
|
||||
* Use DH_COMPAT=4 and dh_installman.
|
||||
|
||||
-- Russell Coker <[email protected]> Wed, 23 Jul 2008 10:54:24 +1000
|
||||
|
||||
bonnie++ (1.03c) unstable; urgency=low
|
||||
|
||||
* Fixed some failures to check the return code of chdir().
|
||||
|
||||
-- Russell Coker <[email protected]> Sun, 27 Jan 2008 15:31:00 +1100
|
||||
|
||||
bonnie++ (1.03b.1) unstable; urgency=low
|
||||
|
||||
* Fixed typos in man page.
|
||||
Closes: #133714
|
||||
Closes: #449596
|
||||
* Documented why UID==0 is bad.
|
||||
Closes: #182023
|
||||
|
||||
-- Russell Coker <[email protected]> Wed, 5 Dec 2007 09:09:00 +1100
|
||||
|
||||
bonnie++ (1.03b) unstable; urgency=low
|
||||
|
||||
* Added support for testing write speed to zcav.
|
||||
|
||||
* Documented the default chunk size in bonnie++.8.
|
||||
|
||||
* Improved error handling for chunk size.
|
||||
|
||||
-- Russell Coker <[email protected]> Mon, 3 Dec 2007 10:13:00 +1100
|
||||
|
||||
bonnie++ (1.03a) unstable; urgency=low
|
||||
|
||||
* Fixed the bugs in GCC 3.2 compilation and compiled with GCC 3.2. Results
|
||||
will be slightly lower now, but it's still a fair test of compiler/OS...
|
||||
|
||||
-- Russell Coker <[email protected]> Fri, 10 Jan 2002 15:20:00 +0100
|
||||
|
||||
bonnie++ (1.03) unstable; urgency=low
|
||||
|
||||
* Fixed a bug in error handling during a stat test with multiple directories.
|
||||
|
||||
-- Russell Coker <[email protected]> Fri, 6 Dec 2002 12:52:00 +0100
|
||||
|
||||
bonnie++ (1.02d) unstable; urgency=low
|
||||
|
||||
* Fixed a bug where it would SEGV if you specified more than 999 directories
|
||||
(now it handles 99,999 and displays an error message if you ask for more).
|
||||
Closes: #171507
|
||||
|
||||
* Made it build on Irix.
|
||||
|
||||
* Fixed <vector> and <algo> checks to work with GCC 3.2.
|
||||
|
||||
-- Russell Coker <[email protected]> Tue, 3 Dec 2002 15:12:00 +0100
|
||||
|
||||
bonnie++ (1.02c) unstable; urgency=low
|
||||
|
||||
* Improved the RPM packaging.
|
||||
|
||||
* Added support for detecting memory size on AIX.
|
||||
|
||||
* I changed minimum time for a test to be considered valid to half a second
|
||||
but forgot to change the man page, changed it now.
|
||||
|
||||
* Fixed an error in calculating seeks, it used to add an extra 5 seconds to
|
||||
the test time before calculation and therefore under-reported the results.
|
||||
|
||||
-- Russell Coker <[email protected]> Mon, 22 Apr 2002 00:33:00 +0200
|
||||
|
||||
bonnie++ (1.02b) unstable; urgency=low
|
||||
|
||||
* Made the signal handler extern "C".
|
||||
|
||||
* Fixed a spelling error in the description.
|
||||
Closes: #124462
|
||||
|
||||
-- Russell Coker <[email protected]> Wed, 19 Dec 2001 07:55:00 +0100
|
||||
|
||||
bonnie++ (1.02a) unstable; urgency=low
|
||||
|
||||
* Removed optind=0, which caused problems on BSD.
|
||||
|
||||
-- Russell Coker <[email protected]> Tue, 6 Nov 2001 17:23:00 +0100
|
||||
|
||||
bonnie++ (1.02-1) unstable; urgency=low
|
||||
|
||||
* Updated to latest debian standards version and follow the latest Debian
|
||||
policy on stripping and debug generation.
|
||||
|
||||
-- Russell Coker <[email protected]> Sat, 6 Oct 2001 19:06:39 +0200
|
||||
|
||||
bonnie++ (1.02) unstable; urgency=low
|
||||
|
||||
* Fixed the cause of a minor warning on AIX.
|
||||
|
||||
-- Russell Coker <[email protected]> Wed, 19 Sep 2001 17:05:01 +0200
|
||||
|
||||
bonnie++ (1.01d) unstable; urgency=low
|
||||
|
||||
* Fixed a bug where bonnie++ would SEGV if ^C was pressed at the start of the
|
||||
file creation tests.
|
||||
|
||||
* Clarified the -r option to bonnie++ in the man page.
|
||||
|
||||
* Minor fix to command line checking for chunk size. Now works for 1M chunks.
|
||||
|
||||
-- Russell Coker <[email protected]> Thu, 26 Jul 2001 21:46:18 +0200
|
||||
|
||||
bonnie++ (1.01c) unstable; urgency=low
|
||||
|
||||
* Made the -d option to bonnie++ work with relative paths. Now it does
|
||||
chdir() to the specifed directory and proceeds as usual.
|
||||
|
||||
-- Russell Coker <[email protected]> Fri, 25 May 2001 14:36:17 +0100
|
||||
|
||||
bonnie++ (1.01b-1) unstable; urgency=low
|
||||
|
||||
* Compiled with GCC 2.95 for Debian.
|
||||
Closes:#94349
|
||||
|
||||
-- Russell Coker <[email protected]> Mon, 23 Apr 2001 9:20:00 +0100
|
||||
|
||||
bonnie++ (1.01b) unstable; urgency=low
|
||||
|
||||
* Made it remove the ./configure cache files before Debian package build.
|
||||
This fixes the problems with incorrect data being cached for ports.
|
||||
Closes:#94289
|
||||
|
||||
* Fixed the memory checks for file creation tests, thanks to
|
||||
Andreas Dilger <[email protected]> .
|
||||
|
||||
-- Russell Coker <[email protected]> Wed, 18 Apr 2001 12:30:18 +0100
|
||||
|
||||
bonnie++ (1.01a) unstable; urgency=low
|
||||
|
||||
* Fixed bon_csv2txt so that it can handle a '/' in the CSV file denoting
|
||||
multiple directories for the file creation tests.
|
||||
|
||||
* Stopped it giving a floating point exception (on correctly performing
|
||||
machines such as Alphas) when a test is skipped.
|
||||
|
||||
* Fixed a warning about the variable name "basename" with the latest gcc.
|
||||
|
||||
-- Russell Coker <[email protected]> Mon, 16 Apr 2001 19:10:34 +0100
|
||||
|
||||
bonnie++ (1.01) unstable; urgency=low
|
||||
|
||||
* Made it conflict/provide/replace the bonnie package. Bonnie++ does
|
||||
everything that bonnie does and fixes some bugs including doing srand()
|
||||
after fork(), and having sensible defaults and detection of RAM installed
|
||||
to avoid meaningless test results.
|
||||
There is no need to have bonnie and bonnie++ installed.
|
||||
|
||||
* Added an autoconf check for snprintf() for IRIX.
|
||||
|
||||
-- Russell Coker <[email protected]> Tue, 13 Mar 2001 12:42:43 +0100
|
||||
|
||||
bonnie++ (1.00h) unstable; urgency=low
|
||||
|
||||
* Changed readme.html to make it more up to date.
|
||||
|
||||
* Now use exec_prefix for binary installation.
|
||||
|
||||
-- Russell Coker <[email protected]> Mon, 19 Feb 2001 15:23:17 +0100
|
||||
|
||||
bonnie++ (1.00g) unstable; urgency=low
|
||||
|
||||
* Fixed a bug in zcav that prevented it working with parameters other than
|
||||
the file name.
|
||||
|
||||
* Updated to standards version 3.2.1.
|
||||
|
||||
* Closes:#70331
|
||||
Fixed this a couple of versions ago.
|
||||
|
||||
* Fixed these bugs ages ago but being a sponsored package had confused the
|
||||
bug tracker:
|
||||
Closes:#53545
|
||||
Closes:#53546
|
||||
Closes:#61925
|
||||
Closes:#64995
|
||||
|
||||
* Back-ported some extra error handling from 1.90c.
|
||||
|
||||
* Made quiet mode not tell you which UID it uses.
|
||||
|
||||
-- Russell Coker <[email protected]> Mon, 1 Jan 2001 14:29:32 +1100
|
||||
|
||||
bonnie++ (1.00f) unstable; urgency=low
|
||||
|
||||
* Fixed the bugs in timing of seeks.
|
||||
|
||||
* Changed the number of seeks from 8000 to 8192.
|
||||
|
||||
* Now the minimum time for a test that will be considered valid is 500ms (for
|
||||
bonnie++ and zcav), it was previously 1000ms but I have been convinced that
|
||||
system clocks are accurate enough for this.
|
||||
|
||||
* Changed the default number of files created for file creation tests from
|
||||
30K to 16K, this change makes the test time bearable on obsolete file
|
||||
systems like UFS but relies on the above change to work on ReiserFS on
|
||||
Pentium4 class CPUs.
|
||||
|
||||
* Changed the default file size to 300M for IO tests, this reflects the
|
||||
increase in RAM sizes over the last year.
|
||||
|
||||
* Added some more compile warnings and fixed some more trivial bugs.
|
||||
|
||||
* Made the loops: line in zcav output always be prefixed by a '#' for a
|
||||
gnuplot comment.
|
||||
|
||||
* Made zcav correctly display the position in megabytes instead of block-size
|
||||
units.
|
||||
|
||||
-- Russell Coker <[email protected]> Mon, 27 Nov 2000 09:45:30 +0200
|
||||
|
||||
bonnie++ (1.00e) unstable; urgency=low
|
||||
|
||||
* Now exit on sync errors.
|
||||
|
||||
* When directory can't be synced it will display one warning and not try to
|
||||
sync again.
|
||||
|
||||
* Stopped it crashing when there is only one directory to be synced.
|
||||
|
||||
* Made the version number managed by autoconf so I don't release it with
|
||||
inconsistant version numbers again!
|
||||
|
||||
-- Russell Coker <[email protected]> Thu, 9 Nov 2000 03:26:15 +0200
|
||||
|
||||
bonnie++ (1.00d) unstable; urgency=low
|
||||
|
||||
* Use SA_RESETHAND instead of SA_ONESHOT for the benefit of Solaris.
|
||||
|
||||
* Added a specific rule for bon_suid.cpp as it doesn't have a header file.
|
||||
|
||||
* Added --prefix support to the ./configure , also made the default prefix
|
||||
be /usr/local instead of /usr .
|
||||
|
||||
* Changed the autoconf support for checking for C++ slightly. It should
|
||||
now work better for some strange setups and work the same for everyone
|
||||
else (I hope).
|
||||
|
||||
* Made the autoconf tests for semaphore headers work correctly, now it
|
||||
should compile on *BSD.
|
||||
|
||||
* Added --disable-stripping option for ./configure if you don't want binaries
|
||||
stripped.
|
||||
|
||||
* Added autoconf checking for bool, now it should compile on AIX using ICC
|
||||
without any special options!
|
||||
|
||||
* Reverted zcav to the 1.00a version and then added the code for -u, -g, and
|
||||
the fix for large numbers of data points. The multi-threaded zcav code
|
||||
will go into 1.90 (the pre-2.00 tree).
|
||||
Bonnie++ versions < 1.90 will never again have threading code.
|
||||
|
||||
* Made bon_csv2txt use every available character for the description.
|
||||
|
||||
* Made it install man pages by default.
|
||||
|
||||
* Added sun package support - "make -C sun"!
|
||||
|
||||
-- Russell Coker <[email protected]> Thu, 28 Sep 2000 16:22:15 +0200
|
||||
|
||||
bonnie++ (1.00c) unstable; urgency=low
|
||||
|
||||
* Closes:#53545
|
||||
Fixed in 0.99e, should have been closed in Debian BTS.
|
||||
|
||||
* Closes:#53546
|
||||
Fixed in 0.99e, should have been closed in Debian BTS.
|
||||
|
||||
* Closes:#61925
|
||||
Fixed in 1.00a.
|
||||
|
||||
* Closes:#64995
|
||||
It is in /usr/sbin because it can only sensibly be run by the administrator
|
||||
of the machine, otherwise it probably will give bad results and may impact
|
||||
the performance of the machine in question. Also it can now change
|
||||
UID/GID. The new way of dealing with the running as root issue is in the
|
||||
next change.
|
||||
|
||||
* Made zcav and bonnie++ take -u and -g options to set the UID and GID to run
|
||||
as. For bonnie++ it is now mandatory to use the -u option when running as
|
||||
root.
|
||||
|
||||
* Made bonnie++ not change it's command-line.
|
||||
|
||||
* Documented the K and G suffixes for sizes on the command-line.
|
||||
|
||||
* Now the CPU time field also displays as "+++" if the elapsed time is < 1s.
|
||||
|
||||
* Fixed the machine-name broken-ness from 1.00b, and made the machine name
|
||||
default to the nodename as reported by utsname() (also the output of
|
||||
`hostname` or `uname -n`).
|
||||
|
||||
* Now uses sysconf() to check the ram size, you can use -r to over-ride it.
|
||||
Also the default file size for IO tests will be twice the reported RAM
|
||||
size or 200M (whichever is larger).
|
||||
|
||||
* Now Bonnie++ handles ^C from the keyboard, the XCPU and XFSZ (excessive CPU
|
||||
and excessive file size) flags and aborts the program cleanly removing all
|
||||
files when it receives them. Also ignores SIGHUP.
|
||||
|
||||
* Added AC_PROG_CC to configure.in so that it can compile on systems with
|
||||
strange C compiler setups.
|
||||
|
||||
-- Russell Coker <[email protected]> Fri, 08 Sep 2000 08:22:47 +0200
|
||||
|
||||
bonnie++ (1.00b) unstable; urgency=low
|
||||
|
||||
* Added more warnings to the compile and removed assertions. Made some
|
||||
trivial changes to the code (like changing variable names) to stop the
|
||||
warnings.
|
||||
|
||||
* Fixed the memory management problem on bonnie++, these made it not work on
|
||||
IA64 (and stopped it working correctly on most platforms). Thanks to
|
||||
Electric Fence by Bruce Perens for the discovery of this.
|
||||
The worst part of it was introduced in testing this version, so it only
|
||||
hit me and my alpha-testers.
|
||||
|
||||
* Fixed zcav for large numbers of data points.
|
||||
|
||||
* Made zcav multi-threaded to test multiple hard drives at once. Changed the
|
||||
way it works totally.
|
||||
|
||||
* Removed some dependencies on extensions to the C++ standard which are not
|
||||
supported in all compilers, also removed some trivial header file issues.
|
||||
These were found in testing on Tru64Unix.
|
||||
|
||||
* Fixed a bug in bonnie++, it would go into an infinite loop when the file
|
||||
creation tests had a non-zero size.
|
||||
|
||||
* Made bonnie++ work for block-reads that return partial blocks, now it will
|
||||
print an error and do another read for the rest.
|
||||
|
||||
* Made Bonnie++ accept machine names up to 4095 bytes and not crash if the
|
||||
name is longer. Previously the limit was 20 bytes and it crashed when you
|
||||
exceeded it.
|
||||
|
||||
* This version is fairly experimental but I'm releasing it now because I need
|
||||
wider testing of the new features.
|
||||
|
||||
-- Russell Coker <[email protected]> Fri, 25 Aug 2000 12:15:06 +0200
|
||||
|
||||
bonnie++ (1.00a) unstable; urgency=low
|
||||
|
||||
* Added a 30 second startup delay when run as root. A user lost some data
|
||||
because of running it as root, if they had run it as a regular account
|
||||
they would be OK. I don't want this to happen again.
|
||||
|
||||
* Zcav now displays an error if it can't read a single block.
|
||||
|
||||
* Added some basic autoconf support which I will use to increase portability
|
||||
in future versions.
|
||||
|
||||
* Now provides zcav.
|
||||
|
||||
* Should compile with all old versions of gcc.
|
||||
|
||||
* Fixed a warning on Alpha.
|
||||
|
||||
-- Russell Coker <[email protected]> Mon, 24 Apr 2000 23:34:02 +0100
|
||||
|
||||
bonnie++ (1.00) unstable; urgency=low
|
||||
|
||||
* Now include ZCAV in the same package. ZCAV package should disappear.
|
||||
|
||||
* License is now GPL. Tim Bray agrees to the GPL for his parts, the license
|
||||
conflict was stopping me from putting ZCAV into the archive.
|
||||
|
||||
* ZCAV reads through a hard drive sequentially and reports the IO speeds for
|
||||
different zones of the drive.
|
||||
|
||||
* Fixed a few minor issues with the documentation, and put the test programs
|
||||
in /usr/sbin as they aren't generally run by regular users. Also use man
|
||||
section 8.
|
||||
|
||||
-- Russell Coker <[email protected]> Sat, 01 Mar 2000 12:01:00 +0100
|
||||
|
||||
bonnie++ (0.99j) unstable; urgency=low
|
||||
|
||||
* 0.99h core dumped when you didn't specify "-b" for file creation tests,
|
||||
fixed.
|
||||
|
||||
-- Russell Coker <[email protected]> Sun, 05 Mar 2000 11:16:42 +0100
|
||||
|
||||
bonnie++ (0.99h) unstable; urgency=low
|
||||
|
||||
* Fixed a variety of bugs in the semaphore code which were introduced in
|
||||
0.99g.
|
||||
|
||||
* Fixed formatting of output.
|
||||
|
||||
* Added "-b" option to sync all writes.
|
||||
|
||||
* Changed the semaphore code to make it more easily hackable for BSD users,
|
||||
it won't compile as-is on BSD at the moment...
|
||||
|
||||
-- Russell Coker <[email protected]> Sun, 05 Mar 2000 11:16:42 +0100
|
||||
|
||||
bonnie++ (0.99g) unstable; urgency=low
|
||||
|
||||
* Now use getopt() for checking command-line options.
|
||||
|
||||
* Added new versions of fork and semaphore code, initially developed for
|
||||
postal.
|
||||
|
||||
* Fixed the message that's displayed when bad command-line parameters are
|
||||
entered.
|
||||
|
||||
* Version 1.[0-8]0 will use fork(). Version 1.90 and above will use POSIX
|
||||
threads and include the concurrant bonnie++ functionality I've been
|
||||
promising for so long.
|
||||
|
||||
-- Russell Coker <[email protected]> Wed, 23 Feb 2000 22:16:23 +0100
|
||||
|
||||
bonnie++ (0.99f) unstable; urgency=low
|
||||
|
||||
* Added "-f" parameter to skip per-char tests and semaphore code to
|
||||
synchronise multiple instances of Bonnie++. Thanks to
|
||||
Christian Kagerhuber <[email protected]> for the patch!
|
||||
|
||||
* Added srand() after the fork so each child gets different random numbers.
|
||||
|
||||
-- Russell Coker <[email protected]> Wed, 12 Jan 2000 16:45:28 +1100
|
||||
|
||||
bonnie++ (0.99e) unstable; urgency=low
|
||||
|
||||
* Fixed the operation of "-x" parameter (used to just cause crashes).
|
||||
|
||||
* Made it cleanly exit under some error conditions where it used to crash.
|
||||
|
||||
* Improved the bonnie++ man page.
|
||||
|
||||
* Fixed some checking of command-line parameters.
|
||||
|
||||
* Merged code from the OS/2 port, needs lots of testing...
|
||||
|
||||
-- Russell Coker <[email protected]> Wed, 12 Jan 2000 16:45:28 +1100
|
||||
|
||||
bonnie++ (0.99d) unstable; urgency=low
|
||||
|
||||
* Added some more functionality. Tests hard and soft link creation.
|
||||
|
||||
* Fixed CSV output of <100 seeks per second.
|
||||
|
||||
-- Russell Coker <[email protected]> Sun, 21 Nov 1999 22:37:42 +0200
|
||||
|
||||
bonnie++ (0.99c) unstable; urgency=low
|
||||
|
||||
* Fix some bugs with big IO (fseek related) and include man pages.
|
||||
|
||||
* Made it always print the CSV data.
|
||||
|
||||
-- Russell Coker <[email protected]> Sun, 21 Nov 1999 22:37:42 +0200
|
||||
|
||||
bonnie++ (0.99b) unstable; urgency=low
|
||||
|
||||
* Initial Release as a Debian package.
|
||||
|
||||
|
||||
0.99
|
||||
Files are created mode 0600 not 0777.
|
||||
|
||||
Fixed some bugs in 0.98 where the results from several tests were totally
|
||||
wrong.
|
||||
|
||||
Now the random file code will take less CPU time when there are extremely
|
||||
large numbers of files.
|
||||
|
||||
Changed the format of all the output files slightly. Notable change is that
|
||||
the percentages of CPU time are now rounded off to the nearest percent. This
|
||||
is because it's not that accurate anyway (results that are provably more than
|
||||
1% wrong are not uncommon), and because I needed the extra 1 character per
|
||||
field. Also now it handles CPU time >100% properly. This is for SMP systems
|
||||
where more than 1 CPU is being used. Concurrant Bonnie++ will return many
|
||||
results significantly greater than 100% on OSs that work well with SMP.
|
||||
|
||||
Added a csv2txt.pl program. The main aim of this is to display data well
|
||||
for 80 column braille displays for the blind.
|
||||
|
||||
Added "-q" option for quiet mode (less output).
|
||||
|
||||
Now the "-n" option works on a multiple of 1024. So "-n 10" means create
|
||||
10240 files. This change is to allow the output to display in the same
|
||||
format and save space in display (who would want to test as a lower resolution
|
||||
than per 1024 files anyway).
|
||||
|
||||
The -n option is now of the form "num[:max[:min]]" where max is the maximum
|
||||
size (default 0) and min is the minimum size (default 0). To simulate Squid
|
||||
use a max of 15000 and a min of 300. To simulate INN use a maximum of 4096
|
||||
and a minimum of 512.
|
||||
|
||||
1.0 will be out soon!
|
||||
|
||||
0.98
|
||||
Fixed a bug where the data size couldn't be an exact multiple of the size of
|
||||
each file (1 gig). Fixed a number of other minor bugs related to that and
|
||||
added more error checking as well.
|
||||
Changed the code to support up to 1000 files for the IO test, if each is a
|
||||
gig then you can test a tera-byte of data. Changing the code to have more
|
||||
than 1000 files wouldn't be that difficult to do.
|
||||
|
||||
Use the new C++ type conversions.
|
||||
|
||||
0.97
|
||||
I have stopped using cout/cerr and never plan to use them again. They caused
|
||||
me significant pain when trying to get it going on an ancient SGI system.
|
||||
|
||||
Also changed the code structure a bit to make it cleaner. One advantage of
|
||||
this is that there is now a "-x" option to tell bonnie++ to run the same test
|
||||
a number of times (it's interesting to see the variance in the results).
|
||||
|
||||
Now use fflush() after writing each set of results. This means that killing
|
||||
the program unexpectedly won't result in results being lost. Also fixes a
|
||||
strange bug related to printf() on Linux which I am still looking into.
|
||||
|
||||
|
||||
-- Russell Coker <[email protected]> Wed, 13 Oct 1999 22:15:53 +0200
|
||||
|
||||
Local variables:
|
||||
mode: debian-changelog
|
||||
End:
|
||||
@@ -1,22 +0,0 @@
|
||||
Source: bonnie++
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Maintainer: Russell Coker <[email protected]>
|
||||
Standards-Version: 3.5.6
|
||||
Build-Depends: debhelper
|
||||
|
||||
Package: bonnie++
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}
|
||||
Conflicts: zcav, bonnie
|
||||
Replaces: zcav, bonnie
|
||||
Provides: zcav, bonnie
|
||||
Description: Hard drive bottleneck testing benchmark suite.
|
||||
It is called Bonnie++ because it was based on the Bonnie program. This
|
||||
program also tests performance with creating large numbers of files.
|
||||
Now includes zcav raw-read test program. A modern hard drive will have more
|
||||
sectors in the outer tracks because they are longer. The hard drive will
|
||||
have a number (often more than 8) of zones where each zone has the same
|
||||
number of sectors (due to the need for an integral number of sectors per
|
||||
track). This program allows you to determine the levels of performance
|
||||
provided by different zones and store them in a convenient format for gnuplot.
|
||||
@@ -1,12 +0,0 @@
|
||||
This package was debianized by Russell Coker <[email protected]> on
|
||||
Wed, 13 Oct 1999 22:15:53 +0200.
|
||||
|
||||
It was downloaded from http://www.coker.com.au/bonnie++/
|
||||
|
||||
Upstream Author(s): Russell Coker <[email protected]>
|
||||
|
||||
Copyright:
|
||||
|
||||
GPL 2.0
|
||||
|
||||
See /usr/share/common-licenses/GPL-2
|
||||
@@ -1,2 +0,0 @@
|
||||
usr/bin
|
||||
usr/share/doc
|
||||
@@ -1,2 +0,0 @@
|
||||
readme.html
|
||||
credits.txt
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
# Sample debian/rules that uses debhelper.
|
||||
# GNU copyright 1997 to 1999 by Joey Hess.
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
# This is the debhelper compatability version to use.
|
||||
export DH_COMPAT=4
|
||||
|
||||
build: build-stamp
|
||||
build-stamp:
|
||||
dh_testdir
|
||||
|
||||
|
||||
# Add here commands to compile the package.
|
||||
./configure --prefix=`pwd`/debian/bonnie++/usr --mandir=`pwd`/debian/bonnie++/usr/share/man
|
||||
$(MAKE)
|
||||
|
||||
touch build-stamp
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -f build-stamp
|
||||
|
||||
# Add here commands to clean up after the build process.
|
||||
-$(MAKE) clean
|
||||
rm -f config.*
|
||||
|
||||
dh_clean
|
||||
|
||||
install: build
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_clean -k
|
||||
dh_installdirs
|
||||
|
||||
# Add here commands to install the package into debian/bonnie++.
|
||||
$(MAKE) install-bin
|
||||
ln -s bonnie++ debian/bonnie++/usr/sbin/bonnie
|
||||
|
||||
touch install-stamp
|
||||
|
||||
# Build architecture-independent files here.
|
||||
binary-indep: build install
|
||||
# We have nothing to do by default.
|
||||
|
||||
# Build architecture-dependent files here.
|
||||
binary-arch: build install
|
||||
# dh_testversion
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
# dh_installdebconf
|
||||
dh_installdocs
|
||||
# dh_installexamples
|
||||
# dh_installmenu
|
||||
# dh_installemacsen
|
||||
# dh_installpam
|
||||
# dh_installinit
|
||||
# dh_installcron
|
||||
dh_installman *.1 *.8
|
||||
# dh_installinfo
|
||||
# dh_undocumented
|
||||
dh_installchangelogs
|
||||
dh_strip
|
||||
dh_compress
|
||||
dh_fixperms
|
||||
# You may want to make some executables suid here.
|
||||
# dh_suidregister
|
||||
# dh_makeshlibs
|
||||
dh_installdeb
|
||||
# dh_perl
|
||||
dh_shlibdeps
|
||||
dh_gencontrol
|
||||
dh_md5sums
|
||||
dh_builddeb
|
||||
|
||||
binary: binary-indep binary-arch
|
||||
.PHONY: build clean binary-indep binary-arch binary install
|
||||
@@ -1,187 +0,0 @@
|
||||
|
||||
#ifdef OS2
|
||||
#define INCL_DOSPROCESS
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include "forkit.h"
|
||||
|
||||
|
||||
Fork::Fork()
|
||||
: m_read(-1)
|
||||
, m_write(-1)
|
||||
, m_numThreads(0)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef OS2
|
||||
// for the benefit of this function and the new Fork class it may create
|
||||
// the Fork class must do nothing of note in it's constructor or it's
|
||||
// go() member function.
|
||||
VOID APIENTRY thread_func(ULONG param)
|
||||
{
|
||||
THREAD_DATA *td = (THREAD_DATA *)param;
|
||||
td->f = new Fork;
|
||||
td->f->startit(td);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Fork::startit(THREAD_DATA *td)
|
||||
{
|
||||
m_read = td->child_read;
|
||||
m_write = td->child_write;
|
||||
td->func(this, td->param, td->threadNum);
|
||||
delete td->f; // delete ourself if td->f is not NULL
|
||||
delete td;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void Fork::go(FUNCTION func, PVOID param, int num)
|
||||
{
|
||||
m_numThreads = num;
|
||||
FILE_TYPE control[2];
|
||||
FILE_TYPE feedback[2];
|
||||
if (pipe(feedback) || pipe(control))
|
||||
{
|
||||
fprintf(stderr, "Can't open pipes.\n");
|
||||
exit(1);
|
||||
}
|
||||
#ifndef OS2
|
||||
m_readPoll.events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
|
||||
m_writePoll.events = POLLOUT | POLLERR | POLLHUP | POLLNVAL;
|
||||
#endif
|
||||
|
||||
THREAD_DATA *td = new THREAD_DATA;
|
||||
td->child_read = control[0];
|
||||
td->child_write = feedback[1];
|
||||
td->f = NULL;
|
||||
td->param = param;
|
||||
td->func = func;
|
||||
for(int i = 0; i < num; i++)
|
||||
{
|
||||
#ifdef OS2
|
||||
THREAD_DATA *tmp = new THREAD_DATA;
|
||||
memcpy(tmp, td, sizeof(THREAD_DATA));
|
||||
#endif
|
||||
td->threadNum = i;
|
||||
#ifdef OS2
|
||||
// yes I know I am casting a pointer to an unsigned long
|
||||
// it's the way you're supposed to do things in OS/2
|
||||
TID id = 0;
|
||||
if(DosCreateThread(&id, thread_func, ULONG(td), CREATE_READY, 32*1024))
|
||||
{
|
||||
fprintf(stderr, "Can't create a thread.\n");
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
int p = fork();
|
||||
if(p == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't fork.\n");
|
||||
exit(1);
|
||||
}
|
||||
if(p == 0) // child
|
||||
{
|
||||
m_readPoll.fd = td->child_read;
|
||||
m_writePoll.fd = td->child_write;
|
||||
file_close(control[1]);
|
||||
file_close(feedback[0]);
|
||||
srand(getpid() ^ time(NULL));
|
||||
startit(td);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// now we're in the parent thread/process
|
||||
m_write = control[1];
|
||||
m_read = feedback[0];
|
||||
#ifndef OS2
|
||||
m_readPoll.fd = m_read;
|
||||
m_writePoll.fd = m_write;
|
||||
file_close(control[0]);
|
||||
file_close(feedback[1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
int Fork::wait()
|
||||
{
|
||||
#ifdef OS2
|
||||
TID status = 0;
|
||||
if(DosWaitThread(&status, DCWW_WAIT))
|
||||
{
|
||||
fprintf(stderr, "Can't wait for thread.\n");
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
int status = 0;
|
||||
if(::wait(&status) == -1)
|
||||
{
|
||||
fprintf(stderr, "Can't wait for thread.\n");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Fork::Read(PVOID buf, int size, int timeout)
|
||||
{
|
||||
#ifndef OS2
|
||||
if(timeout)
|
||||
{
|
||||
int rc = poll(&m_readPoll, 1, timeout * 1000);
|
||||
if(rc < 0)
|
||||
{
|
||||
fprintf(stderr, "Can't poll.\n");
|
||||
return -1;
|
||||
}
|
||||
if(!rc)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef OS2
|
||||
unsigned long actual;
|
||||
int rc = DosRead(m_read, buf, size, &actual);
|
||||
if(rc || actual != size)
|
||||
#else
|
||||
if(size != read(m_read, buf, size) )
|
||||
#endif
|
||||
{
|
||||
fprintf(stderr, "Can't read data from IPC pipe.\n");
|
||||
return -1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int Fork::Write(PVOID buf, int size, int timeout)
|
||||
{
|
||||
#ifndef OS2
|
||||
if(timeout)
|
||||
{
|
||||
int rc = poll(&m_writePoll, 1, timeout * 1000);
|
||||
if(rc < 0)
|
||||
{
|
||||
fprintf(stderr, "Can't poll for write.\n");
|
||||
return -1;
|
||||
}
|
||||
if(!rc)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef OS2
|
||||
unsigned long actual;
|
||||
int rc = DosWrite(m_write, buf, size, &actual);
|
||||
if(rc || actual != size)
|
||||
#else
|
||||
if(size != write(m_write, buf, size))
|
||||
#endif
|
||||
{
|
||||
fprintf(stderr, "Can't write data to IPC pipe.\n");
|
||||
return -1;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef FORKIT_H
|
||||
#define FORKIT_H
|
||||
|
||||
#ifndef OS2
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#include "port.h"
|
||||
|
||||
class Fork;
|
||||
|
||||
typedef void *PVOID;
|
||||
|
||||
typedef void(* FUNCTION)(Fork *, PVOID, int);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FILE_TYPE child_read, child_write;
|
||||
Fork *f;
|
||||
PVOID param;
|
||||
FUNCTION func;
|
||||
int threadNum;
|
||||
} THREAD_DATA;
|
||||
|
||||
class Fork
|
||||
{
|
||||
public:
|
||||
Fork();
|
||||
void go(FUNCTION func, PVOID param, int num);
|
||||
|
||||
|
||||
int Read(PVOID buf, int size, int timeout = 60);
|
||||
int Write(PVOID buf, int size, int timeout = 60);
|
||||
|
||||
void startit(THREAD_DATA *td); // free td when finished
|
||||
|
||||
int wait();
|
||||
|
||||
int getNumThreads() const { return m_numThreads; }
|
||||
|
||||
private:
|
||||
#ifndef OS2
|
||||
pollfd m_readPoll;
|
||||
pollfd m_writePoll;
|
||||
#endif
|
||||
FILE_TYPE m_read;
|
||||
FILE_TYPE m_write;
|
||||
int m_numThreads;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
##
|
||||
## install -- Install a program, script or datafile
|
||||
## Copyright (c) 1997-2000 Ralf S. Engelschall <[email protected]>
|
||||
## Originally written for shtool
|
||||
##
|
||||
## This file is part of shtool and 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 of the License, or (at your option) any later version.
|
||||
##
|
||||
## This file 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 this program; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
## USA, or contact Ralf S. Engelschall <[email protected]>.
|
||||
##
|
||||
|
||||
str_tool="install"
|
||||
str_usage="[-v] [-t] [-c] [-C] [-s] [-m<mode>] [-o<owner>] [-g<group>] [-e<ext>] <file> [<file> ...] <path>"
|
||||
arg_spec="2+"
|
||||
opt_spec="v.t.c.C.s.m:o:g:e:"
|
||||
opt_v=no
|
||||
opt_t=no
|
||||
opt_c=no
|
||||
opt_C=no
|
||||
opt_s=no
|
||||
opt_m=""
|
||||
opt_o=""
|
||||
opt_g=""
|
||||
opt_e=""
|
||||
|
||||
. ./sh.common
|
||||
|
||||
# determine source(s) and destination
|
||||
argc=$#
|
||||
srcs=""
|
||||
while [ $# -gt 1 ]; do
|
||||
srcs="$srcs $1"
|
||||
shift
|
||||
done
|
||||
dstpath="$1"
|
||||
|
||||
# type check for destination
|
||||
dstisdir=0
|
||||
if [ -d $dstpath ]; then
|
||||
dstpath=`echo "$dstpath" | sed -e 's:/$::'`
|
||||
dstisdir=1
|
||||
fi
|
||||
|
||||
# consistency check for destination
|
||||
if [ $argc -gt 2 -a $dstisdir = 0 ]; then
|
||||
echo "$msgprefix:Error: multiple sources require destination to be directory" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# iterate over all source(s)
|
||||
for src in $srcs; do
|
||||
dst=$dstpath
|
||||
|
||||
# If destination is a directory, append the input filename
|
||||
if [ $dstisdir = 1 ]; then
|
||||
dstfile=`echo "$src" | sed -e 's;.*/\([^/]*\)$;\1;'`
|
||||
dst="$dst/$dstfile"
|
||||
fi
|
||||
|
||||
# Add a possible extension to src and dst
|
||||
if [ ".$opt_e" != . ]; then
|
||||
src="$src$opt_e"
|
||||
dst="$dst$opt_e"
|
||||
fi
|
||||
|
||||
# Check for correct arguments
|
||||
if [ ".$src" = ".$dst" ]; then
|
||||
echo "$msgprefix:Warning: source and destination are the same - skipped" 1>&2
|
||||
continue
|
||||
fi
|
||||
if [ -d "$src" ]; then
|
||||
echo "$msgprefix:Warning: source \`$src' is a directory - skipped" 1>&2
|
||||
continue
|
||||
fi
|
||||
|
||||
# Make a temp file name in the destination directory
|
||||
dsttmp=`echo $dst |\
|
||||
sed -e 's;[^/]*$;;' -e 's;\(.\)/$;\1;' -e 's;^$;.;' \
|
||||
-e "s;\$;/#INST@$$#;"`
|
||||
|
||||
# Verbosity
|
||||
if [ ".$opt_v" = .yes ]; then
|
||||
echo "$src -> $dst" 1>&2
|
||||
fi
|
||||
|
||||
# Copy or move the file name to the temp name
|
||||
# (because we might be not allowed to change the source)
|
||||
if [ ".$opt_C" = .yes ]; then
|
||||
opt_c=yes
|
||||
fi
|
||||
if [ ".$opt_c" = .yes ]; then
|
||||
if [ ".$opt_t" = .yes ]; then
|
||||
echo "cp $src $dsttmp" 1>&2
|
||||
fi
|
||||
cp $src $dsttmp || exit $?
|
||||
else
|
||||
if [ ".$opt_t" = .yes ]; then
|
||||
echo "mv $src $dsttmp" 1>&2
|
||||
fi
|
||||
mv $src $dsttmp || exit $?
|
||||
fi
|
||||
|
||||
# Adjust the target file
|
||||
# (we do chmod last to preserve setuid bits)
|
||||
if [ ".$opt_s" = .yes ]; then
|
||||
if [ ".$opt_t" = .yes ]; then
|
||||
echo "strip $dsttmp" 1>&2
|
||||
fi
|
||||
strip $dsttmp || exit $?
|
||||
fi
|
||||
if [ ".$opt_o" != . ]; then
|
||||
if [ ".$opt_t" = .yes ]; then
|
||||
echo "chown $opt_o $dsttmp" 1>&2
|
||||
fi
|
||||
chown $opt_o $dsttmp || exit $?
|
||||
fi
|
||||
if [ ".$opt_g" != . ]; then
|
||||
if [ ".$opt_t" = .yes ]; then
|
||||
echo "chgrp $opt_g $dsttmp" 1>&2
|
||||
fi
|
||||
chgrp $opt_g $dsttmp || exit $?
|
||||
fi
|
||||
if [ ".$opt_m" != . ]; then
|
||||
if [ ".$opt_t" = .yes ]; then
|
||||
echo "chmod $opt_m $dsttmp" 1>&2
|
||||
fi
|
||||
chmod $opt_m $dsttmp || exit $?
|
||||
fi
|
||||
|
||||
# Determine whether to do a quick install
|
||||
# (has to be done _after_ the strip was already done)
|
||||
quick=no
|
||||
if [ ".$opt_C" = .yes ]; then
|
||||
if [ -r $dst ]; then
|
||||
if cmp -s $src $dst; then
|
||||
quick=yes
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Finally install the file to the real destination
|
||||
if [ $quick = yes ]; then
|
||||
if [ ".$opt_t" = .yes ]; then
|
||||
echo "rm -f $dsttmp" 1>&2
|
||||
fi
|
||||
rm -f $dsttmp
|
||||
else
|
||||
if [ ".$opt_t" = .yes ]; then
|
||||
echo "rm -f $dst && mv $dsttmp $dst" 1>&2
|
||||
fi
|
||||
rm -f $dst && mv $dsttmp $dst
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#ifndef PORT_H
|
||||
#define PORT_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined (WIN32) || defined (OS2)
|
||||
#define NON_UNIX
|
||||
#endif
|
||||
|
||||
|
||||
#define USE_SA_SIGACTION
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
#define false 0
|
||||
#define true 1
|
||||
#endif
|
||||
|
||||
#ifdef OS2
|
||||
#define NO_SNPRINTF
|
||||
typedef enum
|
||||
{
|
||||
false = 0,
|
||||
true = 1
|
||||
} bool;
|
||||
|
||||
#define INCL_DOSQUEUES
|
||||
#include <os2.h>
|
||||
|
||||
#define rmdir(XX) { DosDeleteDir(XX); }
|
||||
#define chdir(XX) DosSetCurrentDir(XX)
|
||||
#define file_close(XX) { DosClose(XX); }
|
||||
#define make_directory(XX) DosCreateDir(XX, NULL)
|
||||
typedef HFILE FILE_TYPE;
|
||||
#define pipe(XX) DosCreatePipe(&XX[0], &XX[1], 8 * 1024)
|
||||
#define sleep(XX) DosSleep((XX) * 1000)
|
||||
#define exit(XX) DosExit(EXIT_THREAD, XX)
|
||||
#else
|
||||
#define file_close(XX) { ::close(XX); }
|
||||
#define make_directory(XX) mkdir(XX, S_IRWXU)
|
||||
typedef int FILE_TYPE;
|
||||
#endif
|
||||
typedef FILE_TYPE *PFILE_TYPE;
|
||||
//typedef FILE *PFILE;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef NO_SNPRINTF
|
||||
#define snprintf sprintf
|
||||
#endif
|
||||
|
||||
#define EXIT_CTRL_C 5
|
||||
@@ -1,53 +0,0 @@
|
||||
#ifndef PORT_H
|
||||
#define PORT_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined (WIN32) || defined (OS2)
|
||||
#define NON_UNIX
|
||||
#endif
|
||||
|
||||
@semun@
|
||||
@sa_sigaction@
|
||||
@bool@
|
||||
@snprintf@
|
||||
@aix_mem_size@
|
||||
#if @true_false@
|
||||
#define false 0
|
||||
#define true 1
|
||||
#endif
|
||||
|
||||
#ifdef OS2
|
||||
#define NO_SNPRINTF
|
||||
typedef enum
|
||||
{
|
||||
false = 0,
|
||||
true = 1
|
||||
} bool;
|
||||
|
||||
#define INCL_DOSQUEUES
|
||||
#include <os2.h>
|
||||
|
||||
#define rmdir(XX) { DosDeleteDir(XX); }
|
||||
#define chdir(XX) DosSetCurrentDir(XX)
|
||||
#define file_close(XX) { DosClose(XX); }
|
||||
#define make_directory(XX) DosCreateDir(XX, NULL)
|
||||
typedef HFILE FILE_TYPE;
|
||||
#define pipe(XX) DosCreatePipe(&XX[0], &XX[1], 8 * 1024)
|
||||
#define sleep(XX) DosSleep((XX) * 1000)
|
||||
#define exit(XX) DosExit(EXIT_THREAD, XX)
|
||||
#else
|
||||
#define file_close(XX) { ::close(XX); }
|
||||
#define make_directory(XX) mkdir(XX, S_IRWXU)
|
||||
typedef int FILE_TYPE;
|
||||
#endif
|
||||
typedef FILE_TYPE *PFILE_TYPE;
|
||||
//typedef FILE *PFILE;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef NO_SNPRINTF
|
||||
#define snprintf sprintf
|
||||
#endif
|
||||
|
||||
#define EXIT_CTRL_C 5
|
||||
@@ -1,178 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<HTML>
|
||||
<HEAD><TITLE>Bonnie++ Documentation</TITLE></HEAD>
|
||||
<BODY>
|
||||
|
||||
<UL><LI><B>Introduction</B><BR>
|
||||
This benchmark is named <B>Bonnie++</B>, it is based on the <B>Bonnie</B>
|
||||
benchmark written by <A HREF="MAILTO:[email protected]">Tim Bray</A>. I was
|
||||
originally hoping to work with Tim on developing the
|
||||
next version of Bonnie, but we could not agree on the issue of whether C++
|
||||
should be used in the program. Tim has graciously given me permission to use
|
||||
the name "bonnie++" for my program which is based around his benchmark.<BR>
|
||||
Bonnie++ adds the facility to test more than 2G of storage on a 32bit
|
||||
machine, and tests for file creat(), stat(), unlink() operations.<BR>
|
||||
Also it will output in CSV spread-sheet format to standard output. If you use
|
||||
the "-q" option for quiet mode then the human-readable version will go to
|
||||
stderr so redirecting stdout to a file will get only the csv in the file.
|
||||
The program bon_csv2html takes csv format data on stdin and writes a HTML
|
||||
file on standard output which has a nice display of all the data. The program
|
||||
bon_csv2txt takes csv format data on stdin and writes a formatted plain text
|
||||
version on stdout, this was originally written to work with 80 column braille
|
||||
displays, but can also work well in email.<BR></LI>
|
||||
<LI><B>A note on blocking writes</B><BR>
|
||||
I have recently added a <B>-b</B> option to cause a fsync() after every
|
||||
write (and a fsync() of the directory after file create or delete). This is
|
||||
what you probably want to do if testing performance of mail or database
|
||||
servers as they like to sync everything. The default is to allow write-back
|
||||
caching in the OS which is what you want if testing performance for copying
|
||||
files, compiling, etc.<BR></LI>
|
||||
<LI><B>Waiting for semaphores</B><BR>
|
||||
There is often a need to test multiple types of IO at the same time. This is
|
||||
most important for testing RAID arrays where you will almost never see full
|
||||
performance with only one process active. Bonnie++ 2.0 will address this
|
||||
issue, but there is also a need for more flexibility than the ability to
|
||||
create multiple files in the same directory and fork processes to access
|
||||
them (which is what version 2.0 will do). There is also need to perform tests
|
||||
such as determining whether access to an NFS server will load the system and
|
||||
slow down access to a local hard drive. <A HREF="[email protected]">
|
||||
Christian Kagerhuber</A> contributed the initial code to do semaphores so that
|
||||
several copies of Bonnie++ can be run in a synchronised fashion. This means
|
||||
you can have 8 copies of Bonnie++ doing per-char reads to test out your 8CPU
|
||||
system!</LI>
|
||||
<LI><B>Summary of tests</B><BR>
|
||||
The first 6 tests are from the original Bonnie: Specifically, these are the
|
||||
types of filesystem activity that have been observed to be bottlenecks in
|
||||
I/O-intensive applications, in particular the text database work done in
|
||||
connection with the New Oxford English Dictionary Project at the University
|
||||
of Waterloo.<BR>
|
||||
It initially performs a series of tests on a file (or files) of known size.
|
||||
By default, that size is 200 MiB (but that's not enough - see below). For
|
||||
each test, Bonnie reports the number of Kilo-bytes processed per elapsed
|
||||
second, and the % CPU usage (sum of user and system). If a size >1G is
|
||||
specified then we will use a number of files of size 1G or less. This way
|
||||
we can use a 32bit program to test machines with 8G of RAM! NB I have not
|
||||
yet tested more than 2100M of file storage. If you test with larger storage
|
||||
then this please send me the results.<BR>
|
||||
The next 6 tests involve file create/stat/unlink to simulate some operations
|
||||
that are common bottlenecks on large Squid and INN servers, and machines with
|
||||
tens of thousands of mail files in /var/spool/mail.<BR>
|
||||
In each case, an attempt is made to keep optimizers from noticing it's
|
||||
all bogus. The idea is to make sure that these are real transfers to/from
|
||||
user space to the physical disk.<P></LI>
|
||||
<LI><B>Test Details</B><BR>
|
||||
<UL><LI>The file IO tests are:
|
||||
<OL>
|
||||
<LI><B>Sequential Output</B>
|
||||
<OL>
|
||||
<LI>Per-Character. The file is written using the putc() stdio macro.
|
||||
The loop that does the writing should be small enough to fit into any
|
||||
reasonable I-cache. The CPU overhead here is that required to do the
|
||||
stdio code plus the OS file space allocation.</LI>
|
||||
|
||||
<LI>Block. The file is created using write(2). The CPU overhead
|
||||
should be just the OS file space allocation.</LI>
|
||||
|
||||
<LI>Rewrite. Each BUFSIZ of the file is read with read(2), dirtied, and
|
||||
rewritten with write(2), requiring an lseek(2). Since no space
|
||||
allocation is done, and the I/O is well-localized, this should test the
|
||||
effectiveness of the filesystem cache and the speed of data transfer.</LI>
|
||||
</OL>
|
||||
</LI>
|
||||
|
||||
<LI><B>Sequential Input</B>
|
||||
<OL>
|
||||
<LI>Per-Character. The file is read using the getc() stdio macro. Once
|
||||
again, the inner loop is small. This should exercise only stdio and
|
||||
sequential input.</LI>
|
||||
|
||||
<LI>Block. The file is read using read(2). This should be a very pure
|
||||
test of sequential input performance.</LI>
|
||||
</OL>
|
||||
</LI>
|
||||
|
||||
<LI><B>Random Seeks</B><BR>
|
||||
|
||||
This test runs SeekProcCount processes (default 3) in parallel, doing a total of
|
||||
8000 lseek()s to locations in the file specified by random() in bsd systems,
|
||||
drand48() on sysV systems. In each case, the block is read with read(2).
|
||||
In 10% of cases, it is dirtied and written back with write(2).<BR>
|
||||
|
||||
The idea behind the SeekProcCount processes is to make sure there's always
|
||||
a seek queued up.<BR>
|
||||
|
||||
AXIOM: For any unix filesystem, the effective number of lseek(2) calls
|
||||
per second declines asymptotically to near 30, once the effect of
|
||||
caching is defeated.<BR>
|
||||
One thing to note about this is that the number of disks in a RAID set
|
||||
increases the number of seeks. For read using RAID-1 (mirroring) will
|
||||
double the number of seeks. For write using RAID-0 will multiply the number
|
||||
of writes by the number of disks in the RAID-0 set (provided that enough
|
||||
seek processes exist).<BR>
|
||||
|
||||
The size of the file has a strong nonlinear effect on the results of
|
||||
this test. Many Unix systems that have the memory available will make
|
||||
aggressive efforts to cache the whole thing, and report random I/O rates
|
||||
in the thousands per second, which is ridiculous. As an extreme
|
||||
example, an IBM RISC 6000 with 64 MiB of memory reported 3,722 per second
|
||||
on a 50 MiB file. Some have argued that bypassing the cache is artificial
|
||||
since the cache is just doing what it's designed to. True, but in any
|
||||
application that requires rapid random access to file(s) significantly
|
||||
larger than main memory which is running on a system which is doing
|
||||
significant other work, the caches will inevitably max out. There is
|
||||
a hard limit hiding behind the cache which has been observed by the
|
||||
author to be of significant import in many situations - what we are trying
|
||||
to do here is measure that number.</LI>
|
||||
</OL>
|
||||
</LI>
|
||||
|
||||
<LI>
|
||||
The file creation tests use file names with 7 digits numbers and a random
|
||||
number (from 0 to 12) of random alpha-numeric characters.
|
||||
For the sequential tests the random characters in the file name follow the
|
||||
number. For the random tests the random characters are first.<BR>
|
||||
The sequential tests involve creating the files in numeric order, then
|
||||
stat()ing them in readdir() order (IE the order they are stored in the
|
||||
directory which is very likely to be the same order as which they were
|
||||
created), and deleting them in the same order.<BR>
|
||||
For the random tests we create the files in an order that will appear
|
||||
random to the file system (the last 7 characters are in numeric order on
|
||||
the files). Then we stat() random files (NB this will return very good
|
||||
results on file systems with sorted directories because not every file
|
||||
will be stat()ed and the cache will be more effective). After that we
|
||||
delete all the files in random order.<BR>
|
||||
If a maximum size greater than 0 is specified then when each file is created
|
||||
it will have a random amount of data written to it. Then when the file is
|
||||
stat()ed it's data will be read.
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI><B>COPYRIGHT NOTICE</B><BR>
|
||||
* Copyright © <A HREF="MAILTO:[email protected]">Tim Bray
|
||||
([email protected])</A>, 1990.<BR>
|
||||
* Copyright © <A HREF="MAILTO:[email protected]">Russell Coker
|
||||
([email protected])</A> 1999.<P>
|
||||
I have updated the program, added support for >2G on 32bit machines, and
|
||||
tests for file creation.<BR>
|
||||
Licensed under the GPL version 2.0.
|
||||
</LI><LI>
|
||||
<B>DISCLAIMER</B><BR>
|
||||
This program is provided AS IS with no warranty of any kind, and<BR>
|
||||
The author makes no representation with respect to the adequacy of this
|
||||
program for any particular purpose or with respect to its adequacy to
|
||||
produce any particular result, and<BR>
|
||||
The authors shall not be liable for loss or damage arising out of
|
||||
the use of this program regardless of how sustained, and
|
||||
In no event shall the author be liable for special, direct, indirect
|
||||
or consequential damage, loss, costs or fees or expenses of any
|
||||
nature or kind.<P>
|
||||
|
||||
NB The results of running this program on live server machines can include
|
||||
extremely bad performance of server processes, and excessive consumption of
|
||||
disk space and/or Inodes which may cause the machine to cease performing it's
|
||||
designated tasks. Also the benchmark results are likely to be bad.<P>
|
||||
Do not run this program on live production machines.
|
||||
</LI>
|
||||
</UL>
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -1,127 +0,0 @@
|
||||
#include "port.h"
|
||||
#include "semaphore.h"
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
Semaphore::Semaphore(int semKey, int numSems, int val)
|
||||
: m_semid(0)
|
||||
, m_semflg(IPC_CREAT | 0666)
|
||||
, m_semopen(false)
|
||||
, m_semKey(semKey)
|
||||
, m_numSems(numSems)
|
||||
{
|
||||
m_arg.val = (0);
|
||||
if(val)
|
||||
{
|
||||
if(create(val))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int Semaphore::clear_sem()
|
||||
{
|
||||
int semid;
|
||||
// have num-sems set to 1 so that we remove regardless of how many
|
||||
// semaphores were present.
|
||||
if((semid = semget(m_semKey, 1, 0666)) == -1)
|
||||
{
|
||||
perror("Can't get semaphore ID");
|
||||
return 1;
|
||||
}
|
||||
if(semctl(semid, 0, IPC_RMID, m_arg) == -1)
|
||||
{
|
||||
perror("Can't get remove semaphore");
|
||||
return 1;
|
||||
}
|
||||
printf("Semaphore removed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Semaphore::create(int count)
|
||||
{
|
||||
if((m_semid = semget(m_semKey, m_numSems, m_semflg)) == -1)
|
||||
{
|
||||
perror("Can't get semaphore");
|
||||
return 1;
|
||||
}
|
||||
m_arg.val = count;
|
||||
int i;
|
||||
for(i = 0; i < m_numSems; ++i)
|
||||
{
|
||||
if(semctl(m_semid, i, SETVAL, m_arg) == -1)
|
||||
{
|
||||
perror("Can't set semaphore value");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
m_semopen = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Semaphore::get_semid()
|
||||
{
|
||||
int semflg = 0666;
|
||||
if ((m_semid = semget(m_semKey, m_numSems, semflg)) == -1)
|
||||
{
|
||||
perror("Can't get semaphore ID");
|
||||
return 1;
|
||||
}
|
||||
m_semopen = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Semaphore::decrement_and_wait(int nr_sem)
|
||||
{
|
||||
if(!m_semopen)
|
||||
return 0;
|
||||
struct sembuf sops;
|
||||
sops.sem_num = nr_sem;
|
||||
sops.sem_op = -1;
|
||||
sops.sem_flg = IPC_NOWAIT;
|
||||
if(semop(m_semid, &sops, 1) == -1)
|
||||
{
|
||||
perror("semop: semop failed.\n");
|
||||
return 1;
|
||||
}
|
||||
sops.sem_num = nr_sem;
|
||||
sops.sem_op = 0;
|
||||
sops.sem_flg = SEM_UNDO;
|
||||
if(semop(m_semid, &sops, 1) == -1)
|
||||
{
|
||||
perror("semop: semop failed.\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Semaphore::get_mutex()
|
||||
{
|
||||
if(!m_semopen)
|
||||
return 0;
|
||||
struct sembuf sops;
|
||||
sops.sem_num = 0;
|
||||
sops.sem_op = -1;
|
||||
sops.sem_flg = SEM_UNDO;
|
||||
if(semop(m_semid, &sops, 1) == -1)
|
||||
{
|
||||
perror("semop: semop failed.\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Semaphore::put_mutex()
|
||||
{
|
||||
if(!m_semopen)
|
||||
return 0;
|
||||
struct sembuf sops;
|
||||
sops.sem_num = 0;
|
||||
sops.sem_op = 1;
|
||||
sops.sem_flg = IPC_NOWAIT;
|
||||
if(semop(m_semid, &sops, 1) == -1)
|
||||
{
|
||||
perror("semop: semop failed.\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#ifndef SEMAPHORE_H
|
||||
#define SEMAPHORE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/sem.h>
|
||||
|
||||
#ifndef SEMUN_IN_SEM_H
|
||||
union semun
|
||||
{
|
||||
int val; /* value for SETVAL */
|
||||
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
|
||||
unsigned short int *array; /* array for GETALL, SETALL */
|
||||
struct seminfo *__buf; /* buffer for IPC_INFO */
|
||||
};
|
||||
#endif
|
||||
|
||||
class Semaphore
|
||||
{
|
||||
public:
|
||||
|
||||
// numSems is the number of semaphores to be in the set
|
||||
// semKey is the ID number for the semaphore set
|
||||
// val is the initial value for the semaphores, no values will be assigned
|
||||
// if the default (0) is specified.
|
||||
Semaphore(int semKey, int numSems = 1, int val = 0);
|
||||
|
||||
// clear the semaphores and return an error code.
|
||||
int clear_sem();
|
||||
|
||||
// create the semaphores
|
||||
// count is the initial value assigned to each semaphore
|
||||
int create(int count);
|
||||
|
||||
// get the handle to a semaphore set previously created
|
||||
int get_semid();
|
||||
|
||||
int decrement_and_wait(int nr_sem);
|
||||
int get_mutex();
|
||||
int put_mutex();
|
||||
|
||||
private:
|
||||
union semun m_arg;
|
||||
int m_semid;
|
||||
int m_semflg;
|
||||
bool m_semopen;
|
||||
int m_semKey;
|
||||
int m_numSems;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
##
|
||||
## This file is part of shtool and 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 of the License, or (at your option) any later version.
|
||||
##
|
||||
## This file 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 this program; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
## USA, or contact Ralf S. Engelschall <[email protected]>.
|
||||
##
|
||||
|
||||
##
|
||||
## COMMON UTILITY CODE
|
||||
##
|
||||
|
||||
# determine name of tool
|
||||
if [ ".$tool" != . ]; then
|
||||
# used inside shtool script
|
||||
toolcmd="$0 $tool"
|
||||
toolcmdhelp="shtool $tool"
|
||||
msgprefix="shtool:$tool"
|
||||
else
|
||||
# used as standalone script
|
||||
toolcmd="$0"
|
||||
toolcmdhelp="sh $0"
|
||||
msgprefix="$str_tool"
|
||||
fi
|
||||
|
||||
# parse argument specification string
|
||||
eval `echo $arg_spec |\
|
||||
sed -e 's/^\([0-9]*\)\([+=]\)/arg_NUMS=\1; arg_MODE=\2/'`
|
||||
|
||||
# parse option specification string
|
||||
eval `echo h.$opt_spec |\
|
||||
sed -e 's/\([a-zA-Z0-9]\)\([.:+]\)/opt_MODE_\1=\2;/g'`
|
||||
|
||||
# interate over argument line
|
||||
opt_PREV=''
|
||||
while [ $# -gt 0 ]; do
|
||||
# special option stops processing
|
||||
if [ ".$1" = ".--" ]; then
|
||||
shift
|
||||
break
|
||||
fi
|
||||
|
||||
# determine option and argument
|
||||
opt_ARG_OK=no
|
||||
if [ ".$opt_PREV" != . ]; then
|
||||
# merge previous seen option with argument
|
||||
opt_OPT="$opt_PREV"
|
||||
opt_ARG="$1"
|
||||
opt_ARG_OK=yes
|
||||
opt_PREV=''
|
||||
else
|
||||
# split argument into option and argument
|
||||
case "$1" in
|
||||
-[a-zA-Z0-9]*)
|
||||
eval `echo "x$1" |\
|
||||
sed -e 's/^x-\([a-zA-Z0-9]\)/opt_OPT="\1";/' \
|
||||
-e 's/";\(.*\)$/"; opt_ARG="\1"/'`
|
||||
;;
|
||||
-[a-zA-Z0-9])
|
||||
opt_OPT=`echo "x$1" | cut -c3-`
|
||||
opt_ARG=''
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# eat up option
|
||||
shift
|
||||
|
||||
# determine whether option needs an argument
|
||||
eval "opt_MODE=\$opt_MODE_${opt_OPT}"
|
||||
if [ ".$opt_ARG" = . -a ".$opt_ARG_OK" != .yes ]; then
|
||||
if [ ".$opt_MODE" = ".:" -o ".$opt_MODE" = ".+" ]; then
|
||||
opt_PREV="$opt_OPT"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# process option
|
||||
case $opt_MODE in
|
||||
'.' )
|
||||
# boolean option
|
||||
eval "opt_${opt_OPT}=yes"
|
||||
;;
|
||||
':' )
|
||||
# option with argument (multiple occurances override)
|
||||
eval "opt_${opt_OPT}=\"\$opt_ARG\""
|
||||
;;
|
||||
'+' )
|
||||
# option with argument (multiple occurances append)
|
||||
eval "opt_${opt_OPT}=\"\$opt_${opt_OPT} \$opt_ARG\""
|
||||
;;
|
||||
* )
|
||||
echo "$msgprefix:Error: unknown option: \`-$opt_OPT'" 1>&2
|
||||
echo "$msgprefix:Hint: run \`$toolcmdhelp -h' or \`man shtool' for details" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ ".$opt_PREV" != . ]; then
|
||||
echo "$msgprefix:Error: missing argument to option \`-$opt_PREV'" 1>&2
|
||||
echo "$msgprefix:Hint: run \`$toolcmdhelp -h' or \`man shtool' for details" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# process help option
|
||||
if [ ".$opt_h" = .yes ]; then
|
||||
echo "Usage: $toolcmdhelp $str_usage"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# complain about incorrect number of arguments
|
||||
case $arg_MODE in
|
||||
'=' )
|
||||
if [ $# -ne $arg_NUMS ]; then
|
||||
echo "$msgprefix:Error: invalid number of arguments (exactly $arg_NUMS expected)" 1>&2
|
||||
echo "$msgprefix:Hint: run \`$toolcmd -h' or \`man shtool' for details" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
'+' )
|
||||
if [ $# -lt $arg_NUMS ]; then
|
||||
echo "$msgprefix:Error: invalid number of arguments (at least $arg_NUMS expected)" 1>&2
|
||||
echo "$msgprefix:Hint: run \`$toolcmd -h' or \`man shtool' for details" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# establish a temporary file on request
|
||||
if [ ".$gen_tmpfile" = .yes ]; then
|
||||
if [ ".$TMPDIR" != . ]; then
|
||||
tmpdir="$TMPDIR"
|
||||
elif [ ".$TEMPDIR" != . ]; then
|
||||
tmpdir="$TEMPDIR"
|
||||
else
|
||||
tmpdir="/tmp"
|
||||
fi
|
||||
tmpfile="$tmpdir/.shtool.$$"
|
||||
rm -f $tmpfile >/dev/null 2>&1
|
||||
touch $tmpfile
|
||||
fi
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
all: pkg
|
||||
|
||||
INSTROOT=`pwd`/tmp
|
||||
PKGNAME=bonnie++
|
||||
|
||||
../Makefile:
|
||||
( cd .. ; ./configure --prefix=/usr --mandir=`pwd`/sun/tmp/usr/share/man )
|
||||
|
||||
pkg: ../Makefile
|
||||
make -C .. WFLAGS="-Wall -W -Wshadow -Wpointer-arith -Wwrite-strings"
|
||||
make -C .. prefix=${INSTROOT}/usr install
|
||||
pkgmk -o -r ${INSTROOT}
|
||||
pkgtrans -s spool ${PKGNAME}.pkg ${PKGNAME}
|
||||
@echo
|
||||
@echo "/var/spool/pkg/${PKGNAME}.pkg complete!"
|
||||
@echo
|
||||
@@ -1,9 +0,0 @@
|
||||
PKG=bonnie++
|
||||
ARCH=sparc
|
||||
VERSION=1.03d
|
||||
CATEGORY=application
|
||||
NAME=Bonnie++
|
||||
DESC=Hard drive benchmark suite
|
||||
VENDOR=Russell Coker
|
||||
[email protected]
|
||||
MAXINST=1
|
||||
@@ -1,9 +0,0 @@
|
||||
PKG=bonnie++
|
||||
ARCH=sparc
|
||||
VERSION=@version@
|
||||
CATEGORY=application
|
||||
NAME=Bonnie++
|
||||
DESC=Hard drive benchmark suite
|
||||
VENDOR=Russell Coker
|
||||
[email protected]
|
||||
MAXINST=1
|
||||
@@ -1,16 +0,0 @@
|
||||
i pkginfo
|
||||
d none /usr 0775 root sys
|
||||
d none /usr/bin 0775 root bin
|
||||
f none /usr/bin/bon_csv2html 0755 root root
|
||||
f none /usr/bin/bon_csv2txt 0755 root root
|
||||
d none /usr/sbin 0775 root bin
|
||||
f none /usr/sbin/bonnie++ 0755 root root
|
||||
f none /usr/sbin/zcav 0755 root root
|
||||
d none /usr/share 0755 root sys
|
||||
d none /usr/share/man 0755 bin bin
|
||||
d none /usr/share/man/man1 0755 bin bin
|
||||
f none /usr/share/man/man1/bon_csv2html.1 0644 root root
|
||||
f none /usr/share/man/man1/bon_csv2txt.1 0644 root root
|
||||
d none /usr/share/man/man8 0755 bin bin
|
||||
f none /usr/share/man/man8/bonnie++.8 0644 root root
|
||||
f none /usr/share/man/man8/zcav.8 0644 root root
|
||||
@@ -1,95 +0,0 @@
|
||||
.TH zcav 8
|
||||
.SH "NAME"
|
||||
zcav \- program to test raw hard drive throughput.
|
||||
|
||||
.SH "SYNOPSIS"
|
||||
.B zcav
|
||||
.I [\-b block\-size[:chunk\-size]] [\-c count] [\-n num_blocks] [\-w]
|
||||
.I [\-u uid\-to\-use:gid\-to\-use] [\-g gid\-to\-use]
|
||||
.I [\-f] file\-name
|
||||
|
||||
.SH "DESCRIPTION"
|
||||
This manual page documents briefly the
|
||||
.BR zcav ,
|
||||
program.
|
||||
.P
|
||||
Modern hard drives have a constant rotational speed but have varying numbers
|
||||
of sectors per track (outside tracks are longer and have more sectors). This
|
||||
is referred to as Zoned Constant Angular Velocity (or ZCAV). The outer tracks
|
||||
will have a higher data transfer rate due to having more sectors per track,
|
||||
these tracks generally have the lower track/sector numbers.
|
||||
.P
|
||||
This program tests the ZCAV performance of a hard drive, by reading the entire
|
||||
data on it a specified number of times. The file name given as the first
|
||||
parameter, it can be specified as
|
||||
.BR \- ,
|
||||
for standard input. This file will be opened as read\-only and in usual
|
||||
operation it will be
|
||||
.BR /dev/hdX
|
||||
or
|
||||
.BR /dev/ide/host0/busX/targetY/lun0/disc
|
||||
depending on whether you use devfs or not (NB operating systems other than
|
||||
Linux will have different device names).
|
||||
.P
|
||||
The output should be able to be easily graphed with
|
||||
.BR gnuplot
|
||||
which is what I use to view the results.
|
||||
|
||||
.SH "OPTIONS"
|
||||
|
||||
.TP
|
||||
.B \-b
|
||||
the size (in Meg) of the blocks to read/write (default 100M), optionally
|
||||
followed by a ':' and the chunk size for read/write operations (default 1M).
|
||||
Note that the chunk size must be less than or equal to the block size and
|
||||
must also be significantly less than the size of the RAM in the machine.
|
||||
Also note that for the write test there will be a fsync() after writing every
|
||||
chunk.
|
||||
|
||||
.TP
|
||||
.B \-c
|
||||
the number of times to read/write the entire disk.
|
||||
|
||||
.TP
|
||||
.B \-n
|
||||
the amount of data (in Meg) to read/write on each pass (default the entire device).
|
||||
|
||||
.TP
|
||||
.B \-f
|
||||
the file\-name for the input data. This isn't needed on well configured
|
||||
systems that have a recent Glibc where you can specify the file name without
|
||||
the \-f flag.
|
||||
|
||||
.TP
|
||||
.B \-u
|
||||
user\-id to use. When running as root specify the UID to run the tests as, it
|
||||
is not recommended to use root, so if you want to run as root use
|
||||
.B \-u root.
|
||||
Also if you want to specify the group to run as then use the
|
||||
.B user:group
|
||||
format. If you specify a user by name but no group then the primary group of
|
||||
that user will be chosen. If you specify a user by number and no group then
|
||||
the group will be
|
||||
.B nogroup.
|
||||
|
||||
.TP
|
||||
.B \-g
|
||||
group\-id to use. Same as using
|
||||
.B :group
|
||||
for the
|
||||
.B \-u
|
||||
parameter, just a different way to specify it for compatibility with other
|
||||
programs.
|
||||
|
||||
.TP
|
||||
.B \-w
|
||||
write zero blocks to the disk instead of reading from the disk - will destroy data!
|
||||
|
||||
.SH "AUTHOR"
|
||||
This program, it's manual page, and the Debian package were written by
|
||||
Russell Coker <[email protected]>.
|
||||
|
||||
.SH "AVAILABILITY"
|
||||
The source is available from http://www.coker.com.au/bonnie++ .
|
||||
.P
|
||||
See http://etbe.coker.com.au/category/benchmark for further information.
|
||||
@@ -1,317 +0,0 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include "bonnie.h"
|
||||
#ifdef HAVE_VECTOR
|
||||
#include <vector>
|
||||
#else
|
||||
#include <vector.h>
|
||||
#endif
|
||||
|
||||
// Read the specified number of megabytes of data from the fd and return the
|
||||
// amount of time elapsed in seconds.
|
||||
double access_data(int fd, int size, void *buf, int chunk_size, int do_write);
|
||||
|
||||
// Returns the mean of the values in the array. If the array contains
|
||||
// more than 2 items then discard the highest and lowest thirds of the
|
||||
// results before calculating the mean.
|
||||
double average(double *array, int count);
|
||||
void printavg(int position, double avg, int block_size);
|
||||
|
||||
const int MEG = 1024*1024;
|
||||
const int DEFAULT_CHUNK_SIZE = 1;
|
||||
typedef double *PDOUBLE;
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("Usage: zcav [-b block-size] [-c count] [-n number of megs to read]\n"
|
||||
" [-u uid-to-use:gid-to-use] [-g gid-to-use]\n"
|
||||
" [-f] file-name\n"
|
||||
"File name of \"-\" means standard input\n"
|
||||
"Count is the number of times to read the data (default 1).\n"
|
||||
"Version: " BON_VERSION "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
vector<double *> times;
|
||||
vector<int> count;
|
||||
int block_size = 256;
|
||||
|
||||
int max_loops = 1, pass_size = 0, chunk_size = DEFAULT_CHUNK_SIZE;
|
||||
int do_write = 0;
|
||||
char *file_name = NULL;
|
||||
|
||||
char *userName = NULL, *groupName = NULL;
|
||||
int c;
|
||||
while(-1 != (c = getopt(argc, argv, "-c:b:f:g:n:u:w")) )
|
||||
{
|
||||
switch(char(c))
|
||||
{
|
||||
case 'b':
|
||||
{
|
||||
int rc = sscanf(optarg, "%d:%d", &block_size, &chunk_size);
|
||||
if(rc == 1)
|
||||
chunk_size = DEFAULT_CHUNK_SIZE;
|
||||
else if(rc != 2)
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
max_loops = atoi(optarg);
|
||||
break;
|
||||
case 'g':
|
||||
if(groupName)
|
||||
usage();
|
||||
groupName = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
{
|
||||
if(userName)
|
||||
usage();
|
||||
userName = strdup(optarg);
|
||||
int i;
|
||||
for(i = 0; userName[i] && userName[i] != ':'; i++)
|
||||
{}
|
||||
if(userName[i] == ':')
|
||||
{
|
||||
if(groupName)
|
||||
usage();
|
||||
userName[i] = '\0';
|
||||
groupName = &userName[i + 1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
pass_size = atoi(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
do_write = 1;
|
||||
break;
|
||||
case 'f':
|
||||
case char(1):
|
||||
file_name = optarg;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
pass_size = pass_size / block_size;
|
||||
|
||||
if(userName || groupName)
|
||||
{
|
||||
if(bon_setugid(userName, groupName, false))
|
||||
return 1;
|
||||
if(userName)
|
||||
free(userName);
|
||||
}
|
||||
|
||||
if(max_loops < 1 || block_size < 1 || chunk_size < 1
|
||||
|| chunk_size > block_size)
|
||||
usage();
|
||||
if(!file_name)
|
||||
usage();
|
||||
printf("#loops: %d, version: %s\n", max_loops, BON_VERSION);
|
||||
|
||||
int i;
|
||||
void *buf = calloc(chunk_size * MEG, 1);
|
||||
int fd;
|
||||
if(strcmp(file_name, "-"))
|
||||
{
|
||||
if(do_write)
|
||||
fd = open(file_name, O_WRONLY);
|
||||
else
|
||||
fd = open(file_name, O_RDONLY);
|
||||
if(fd == -1)
|
||||
{
|
||||
printf("Can't open %s\n", file_name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fd = 0;
|
||||
}
|
||||
if(max_loops > 1)
|
||||
{
|
||||
struct stat stat_out, stat_err;
|
||||
if(fstat(1, &stat_out) || fstat(2, &stat_err))
|
||||
{
|
||||
printf("Can't stat stdout/stderr.\n");
|
||||
return 1;
|
||||
}
|
||||
for(int loops = 0; loops < max_loops; loops++)
|
||||
{
|
||||
if(lseek(fd, 0, SEEK_SET))
|
||||
{
|
||||
printf("Can't llseek().\n");
|
||||
return 1;
|
||||
}
|
||||
double total_read_time = 0.0;
|
||||
for(i = 0; (loops == 0 || times[0][i] != -1.0) && (!pass_size || i < pass_size); i++)
|
||||
{
|
||||
double read_time = access_data(fd, block_size, buf, chunk_size, do_write);
|
||||
total_read_time += read_time;
|
||||
if(loops == 0)
|
||||
{
|
||||
times.push_back(new double[max_loops]);
|
||||
count.push_back(0);
|
||||
}
|
||||
times[i][loops] = read_time;
|
||||
if(read_time < 0.0)
|
||||
{
|
||||
if(i == 0)
|
||||
{
|
||||
fprintf(stderr, "Data file/device too small.\n");
|
||||
return 1;
|
||||
}
|
||||
times[i][0] = -1.0;
|
||||
break;
|
||||
}
|
||||
count[i]++;
|
||||
}
|
||||
time_t now = time(NULL);
|
||||
struct tm *cur_time = localtime(&now);
|
||||
fprintf(stderr, "# Finished loop %d, %d:%02d:%02d\n", loops + 1
|
||||
, cur_time->tm_hour, cur_time->tm_min, cur_time->tm_sec);
|
||||
printf("# Read %d gigs in %d seconds, %d megabytes per second.\n"
|
||||
, i * block_size / 1024, int(total_read_time)
|
||||
, int(double(i * block_size) / total_read_time));
|
||||
if(stat_out.st_dev != stat_err.st_dev || stat_out.st_ino != stat_err.st_ino)
|
||||
{
|
||||
fprintf(stderr, "Read %d gigs in %d seconds, %d megabytes per second.\n"
|
||||
, i * block_size / 1024, int(total_read_time)
|
||||
, int(double(i * block_size) / total_read_time));
|
||||
}
|
||||
}
|
||||
printf("#\n#block offset (GiB), MiB/s, time\n");
|
||||
for(i = 0; count[i]; i++)
|
||||
{
|
||||
printavg(i, average(times[i], count[i]), block_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("#block offset (GiB), MiB/s, time\n");
|
||||
double total_read_time = 0.0;
|
||||
for(i = 0; !pass_size || i < pass_size; i++)
|
||||
{
|
||||
double read_time = access_data(fd, block_size, buf, chunk_size, do_write);
|
||||
if(read_time < 0.0)
|
||||
break;
|
||||
printavg(i, read_time, block_size);
|
||||
total_read_time += read_time;
|
||||
}
|
||||
if(i == 0)
|
||||
{
|
||||
fprintf(stderr, "File/device too small.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("# Read %d gigs in %d seconds, %d megabytes per second.\n"
|
||||
, i * block_size / 1024, int(total_read_time)
|
||||
, int(double(i * block_size) / total_read_time));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void printavg(int position, double avg, int block_size)
|
||||
{
|
||||
if(avg < MinTime)
|
||||
printf("#%.2f ++++ %.3f \n", float(position) * float(block_size) / 1024.0, avg);
|
||||
else
|
||||
printf("%.2f %.2f %.3f\n", float(position) * float(block_size) / 1024.0, float(double(block_size) / avg), avg);
|
||||
}
|
||||
|
||||
int compar(const void *a, const void *b)
|
||||
{
|
||||
double *c = (double *)(a);
|
||||
double *d = (double *)(b);
|
||||
if(*c < *d) return -1;
|
||||
if(*c > *d) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns the mean of the values in the array. If the array contains
|
||||
// more than 2 items then discard the highest and lowest thirds of the
|
||||
// results before calculating the mean.
|
||||
double average(double *array, int count)
|
||||
{
|
||||
qsort(array, count, sizeof(double), compar);
|
||||
int skip = count / 3;
|
||||
int arr_items = count - (skip * 2);
|
||||
double total = 0.0;
|
||||
for(int i = skip; i < (count - skip); i++)
|
||||
{
|
||||
total += array[i];
|
||||
}
|
||||
return total / arr_items;
|
||||
}
|
||||
|
||||
// just like read() or write() but will not return a partial result and the
|
||||
// size is expressed in MEG.
|
||||
ssize_t access_all(int fd, void *buf, size_t chunk_size, int do_write)
|
||||
{
|
||||
ssize_t total = 0;
|
||||
chunk_size *= MEG;
|
||||
while(total != static_cast<ssize_t>(chunk_size) )
|
||||
{
|
||||
ssize_t rc;
|
||||
// for both read and write just pass the base address of the buffer
|
||||
// as we don't care for the data, if we ever do checksums we have to
|
||||
// change this
|
||||
if(do_write)
|
||||
rc = write(fd, buf, chunk_size - total);
|
||||
else
|
||||
rc = read(fd, buf, chunk_size - total);
|
||||
if(rc == -1 || rc == 0)
|
||||
return -1;
|
||||
total += rc;
|
||||
}
|
||||
if(do_write && fsync(fd))
|
||||
return -1;
|
||||
return total / MEG;
|
||||
}
|
||||
|
||||
// Read the specified number of megabytes of data from the fd and return the
|
||||
// amount of time elapsed in seconds. If do_write == 1 then write data.
|
||||
double access_data(int fd, int size, void *buf, int chunk_size, int do_write)
|
||||
{
|
||||
struct timeval tp;
|
||||
|
||||
if (gettimeofday(&tp, static_cast<struct timezone *>(NULL)) == -1)
|
||||
{
|
||||
printf("Can't get time.\n");
|
||||
return -1.0;
|
||||
}
|
||||
double start = double(tp.tv_sec) +
|
||||
(double(tp.tv_usec) / 1000000.0);
|
||||
|
||||
for(int i = 0; i < size; i += chunk_size)
|
||||
{
|
||||
int access_size = chunk_size;
|
||||
if(i + chunk_size > size)
|
||||
access_size = size - i;
|
||||
int rc = access_all(fd, buf, access_size, do_write);
|
||||
if(rc != access_size)
|
||||
return -1.0;
|
||||
}
|
||||
if (gettimeofday(&tp, static_cast<struct timezone *>(NULL)) == -1)
|
||||
{
|
||||
printf("Can't get time.\n");
|
||||
return -1.0;
|
||||
}
|
||||
return (double(tp.tv_sec) + (double(tp.tv_usec) / 1000000.0))
|
||||
- start;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user