tests/posix: Drop bonnie++, recipe exists and not api test

This commit is contained in:
Alexander von Gluck IV
2015-02-02 18:24:01 +00:00
parent 3b8ca9ebdf
commit 1c646c0bcd
51 changed files with 0 additions and 10676 deletions
-1
View File
@@ -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
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 &gt;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 &copy; <A HREF="MAILTO:[email protected]">Tim Bray
([email protected])</A>, 1990.<BR>
* Copyright &copy; <A HREF="MAILTO:[email protected]">Russell Coker
([email protected])</A> 1999.<P>
I have updated the program, added support for &gt;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;
}