"Fossies" - the Fresh Open Source Software Archive

Member "bonnie++-1.04/bonnie++.cpp" (4 Sep 2017, 20641 Bytes) of package /linux/privat/bonnie++_1.04.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "bonnie++.cpp": 1.97_vs_1.97.3.

    1 
    2 /*
    3  * COPYRIGHT NOTICE:
    4  * Copyright (c) Tim Bray, 1990.
    5  * Copyright (c) Russell Coker, 1999.  I have updated the program, added
    6  * support for >2G on 32bit machines, and tests for file creation.
    7  * Licensed under the GPL version 2.0.
    8  * DISCLAIMER:
    9  * This program is provided AS IS with no warranty of any kind, and
   10  * The author makes no representation with respect to the adequacy of this
   11  *  program for any particular purpose or with respect to its adequacy to
   12  *  produce any particular result, and
   13  * The author shall not be liable for loss or damage arising out of
   14  *  the use of this program regardless of how sustained, and
   15  * In no event shall the author be liable for special, direct, indirect
   16  *  or consequential damage, loss, costs or fees or expenses of any
   17  *  nature or kind.
   18  */
   19 
   20 #ifdef OS2
   21 #define INCL_DOSFILEMGR
   22 #define INCL_DOSMISC
   23 #define INCL_DOSQUEUES
   24 #define INCL_DOSPROCESS
   25 #include <os2.h>
   26 #else
   27 #include <sys/wait.h>
   28 #include <unistd.h>
   29 #endif
   30 #include <sys/time.h>
   31 #include <time.h>
   32 #include <stdlib.h>
   33 #include "bonnie.h"
   34 #include "bon_io.h"
   35 #include "bon_file.h"
   36 #include "bon_time.h"
   37 #include "semaphore.h"
   38 #include <pwd.h>
   39 #include <grp.h>
   40 #include <ctype.h>
   41 #include <string.h>
   42 #include <sys/utsname.h>
   43 #include <signal.h>
   44 
   45 #ifdef AIX_MEM_SIZE
   46 #include <cf.h>
   47 #include <sys/cfgodm.h>
   48 #include <sys/cfgdb.h>
   49 #endif
   50 
   51 void usage();
   52 
   53 class CGlobalItems
   54 {
   55 public:
   56   bool quiet;
   57   bool fast;
   58   bool sync_bonnie;
   59 #ifdef O_DIRECT
   60   bool use_direct_io;
   61 #endif
   62   BonTimer timer;
   63   int ram;
   64   Semaphore sem;
   65   char *name;
   66   bool bufSync;
   67   int  chunk_bits;
   68   int chunk_size() const { return m_chunk_size; }
   69   bool *doExit;
   70   void set_chunk_size(int size)
   71     { delete m_buf; pa_new(size, m_buf, m_buf_pa); m_chunk_size = size; }
   72 
   73   // Return the page-aligned version of the local buffer
   74   char *buf() { return m_buf_pa; }
   75 
   76   CGlobalItems(bool *exitFlag);
   77   ~CGlobalItems() { delete name; delete m_buf; }
   78 
   79   void decrement_and_wait(int nr_sem);
   80 
   81   void SetName(CPCCHAR path)
   82   {
   83     delete name;
   84     name = new char[strlen(path) + 15];
   85 #ifdef OS2
   86     ULONG myPid = 0;
   87     DosQuerySysInfo(QSV_FOREGROUND_PROCESS, QSV_FOREGROUND_PROCESS
   88                   , &myPid, sizeof(myPid));
   89 #else
   90     pid_t myPid = getpid();
   91 #endif
   92     sprintf(name, "%s/Bonnie.%d", path, int(myPid));
   93   }
   94 private:
   95   int m_chunk_size;
   96   char *m_buf;     // Pointer to the entire buffer
   97   char *m_buf_pa;  // Pointer to the page-aligned version of the same buffer
   98 
   99   CGlobalItems(const CGlobalItems &f);
  100   CGlobalItems & operator =(const CGlobalItems &f);
  101 
  102   // Implement a page-aligned version of new.
  103   // 'p' is the pointer created
  104   // 'page_aligned_p' is the page-aligned pointer created
  105   void pa_new(unsigned int num_bytes, char *&p, char *&page_aligned_p)
  106   {
  107 #ifdef NON_UNIX
  108     SYSTEM_INFO system_info;
  109     GetSystemInfo(&system_info);
  110     long page_size = system_info.dwPageSize;
  111 #else
  112     int page_size = getpagesize();
  113 #endif
  114     p = ::new char [num_bytes + page_size];
  115 
  116     page_aligned_p = (char *)((((unsigned long)p + page_size - 1) / page_size) * page_size);
  117   }
  118 };
  119 
  120 CGlobalItems::CGlobalItems(bool *exitFlag)
  121  : quiet(false)
  122  , fast(false)
  123  , sync_bonnie(false)
  124 #ifdef O_DIRECT
  125  , use_direct_io(false)
  126 #endif
  127  , timer()
  128  , ram(0)
  129  , sem(SemKey, TestCount)
  130  , name(NULL)
  131  , bufSync(false)
  132  , chunk_bits(DefaultChunkBits)
  133  , doExit(exitFlag)
  134  , m_chunk_size(DefaultChunkSize)
  135  , m_buf(NULL)
  136  , m_buf_pa(NULL)
  137 {
  138   pa_new(m_chunk_size, m_buf, m_buf_pa);
  139   SetName(".");
  140 }
  141 
  142 void CGlobalItems::decrement_and_wait(int nr_sem)
  143 {
  144   if(sem.decrement_and_wait(nr_sem))
  145     exit(1);
  146 }
  147 
  148 int TestDirOps(int directory_size, int max_size, int min_size
  149              , int num_directories, CGlobalItems &globals);
  150 int TestFileOps(int file_size, CGlobalItems &globals);
  151 
  152 static bool exitNow;
  153 static bool already_printed_error;
  154 
  155 #ifdef USE_SA_SIGACTION
  156 #define SIGNAL_NUMBER siginf->si_signo
  157 #else
  158 #define SIGNAL_NUMBER sig
  159 #endif
  160 
  161 extern "C"
  162 {
  163   void ctrl_c_handler(int sig
  164 #ifdef USE_SA_SIGACTION
  165             , siginfo_t *siginf, void *unused
  166 #endif
  167              )
  168   {
  169     if(SIGNAL_NUMBER == SIGXCPU)
  170       fprintf(stderr, "Exceeded CPU usage.\n");
  171     else if(SIGNAL_NUMBER == SIGXFSZ)
  172       fprintf(stderr, "exceeded file storage limits.\n");
  173     exitNow = true;
  174   }
  175 }
  176 
  177 int main(int argc, char *argv[])
  178 {
  179   int    file_size = DefaultFileSize;
  180   int    directory_size = DefaultDirectorySize;
  181   int    directory_max_size = DefaultDirectoryMaxSize;
  182   int    directory_min_size = DefaultDirectoryMinSize;
  183   int    num_bonnie_procs = 0;
  184   int    num_directories = 1;
  185   int    count = -1;
  186   const char * machine = NULL;
  187   char *userName = NULL, *groupName = NULL;
  188   CGlobalItems globals(&exitNow);
  189   bool setSize = false;
  190 
  191   exitNow = false;
  192   already_printed_error = false;
  193 
  194   struct sigaction sa;
  195 #ifdef USE_SA_SIGACTION
  196   sa.sa_sigaction = &ctrl_c_handler;
  197   sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
  198 #else
  199   sa.sa_handler = ctrl_c_handler;
  200   sa.sa_flags = SA_RESETHAND;
  201 #endif
  202   if(sigaction(SIGINT, &sa, NULL)
  203    || sigaction(SIGXCPU, &sa, NULL)
  204    || sigaction(SIGXFSZ, &sa, NULL))
  205   {
  206     printf("Can't handle SIGINT.\n");
  207     return 1;
  208   }
  209 #ifdef USE_SA_SIGACTION
  210   sa.sa_sigaction = NULL;
  211 #endif
  212   sa.sa_handler = SIG_IGN;
  213   if(sigaction(SIGHUP, &sa, NULL))
  214   {
  215     printf("Can't handle SIGHUP.\n");
  216     return 1;
  217   }
  218 
  219 #ifdef _SC_PHYS_PAGES
  220   int page_size = sysconf(_SC_PAGESIZE);
  221   int num_pages = sysconf(_SC_PHYS_PAGES);
  222   if(page_size != -1 && num_pages != -1)
  223   {
  224     globals.ram = page_size/1024 * (num_pages/1024);
  225   }
  226 #else
  227 
  228 #ifdef AIX_MEM_SIZE
  229   struct CuAt *odm_obj;
  230   int how_many;
  231 
  232   odm_set_path("/etc/objrepos");
  233   odm_obj = getattr("sys0", "realmem", 0, &how_many);
  234   globals.ram = atoi(odm_obj->value) / 1024;
  235   odm_terminate();
  236   printf("Memory = %d MiB\n", globals.ram);
  237 #endif
  238 
  239 #endif
  240 
  241   int int_c;
  242   while(-1 != (int_c = getopt(argc, argv, "bd:fg:m:n:p:qr:s:u:x:y"
  243 #ifdef O_DIRECT
  244                              "D"
  245 #endif
  246                              )) )
  247   {
  248     switch(char(int_c))
  249     {
  250       case '?':
  251       case ':':
  252         usage();
  253       break;
  254       case 'b':
  255         globals.bufSync = true;
  256       break;
  257       case 'd':
  258         if(chdir(optarg))
  259         {
  260           fprintf(stderr, "Can't change to directory \"%s\".\n", optarg);
  261           usage();
  262         }
  263       break;
  264       case 'f':
  265         globals.fast = true;
  266       break;
  267       case 'm':
  268         machine = optarg;
  269       break;
  270       case 'n':
  271         sscanf(optarg, "%d:%d:%d:%d", &directory_size
  272                      , &directory_max_size, &directory_min_size
  273                      , &num_directories);
  274       break;
  275       case 'p':
  276         num_bonnie_procs = atoi(optarg);
  277                         /* Set semaphore to # of bonnie++ procs 
  278                            to synchronize */
  279       break;
  280       case 'q':
  281         globals.quiet = true;
  282       break;
  283       case 'r':
  284         globals.ram = atoi(optarg);
  285       break;
  286       case 's':
  287       {
  288         char *sbuf = strdup(optarg);
  289         char *size = strtok(sbuf, ":");
  290         file_size = atoi(size);
  291         char c = size[strlen(size) - 1];
  292         if(c == 'g' || c == 'G')
  293           file_size *= 1024;
  294         size = strtok(NULL, "");
  295         if(size)
  296         {
  297           int tmp = atoi(size);
  298           c = size[strlen(size) - 1];
  299           if(c == 'k' || c == 'K')
  300             tmp *= 1024;
  301           globals.set_chunk_size(tmp);
  302         }
  303         setSize = true;
  304       }
  305       break;
  306       case 'g':
  307         if(groupName)
  308           usage();
  309         groupName = optarg;
  310       break;
  311       case 'u':
  312       {
  313         if(userName)
  314           usage();
  315         userName = strdup(optarg);
  316         int i;
  317         for(i = 0; userName[i] && userName[i] != ':'; i++)
  318         {}
  319         if(userName[i] == ':')
  320         {
  321           if(groupName)
  322             usage();
  323           userName[i] = '\0';
  324           groupName = &userName[i + 1];
  325         }
  326       }
  327       break;
  328       case 'x':
  329         count = atoi(optarg);
  330       break;
  331       case 'y':
  332                         /* tell procs to synchronize via previous
  333                            defined semaphore */
  334         globals.sync_bonnie = true;
  335       break;
  336 #ifdef O_DIRECT
  337       case 'D':
  338                         /* open file descriptor with direct I/O */
  339         globals.use_direct_io = true;
  340       break;
  341 #endif
  342     }
  343   }
  344   if(optind < argc)
  345     usage();
  346 
  347   if(globals.ram && !setSize)
  348   {
  349     if(file_size < (globals.ram * 2))
  350       file_size = globals.ram * 2;
  351     // round up to the nearest gig
  352     if(file_size % 1024 > 512)
  353       file_size = file_size + 1024 - (file_size % 1024);
  354   }
  355 
  356   if(machine == NULL)
  357   {
  358     struct utsname utsBuf;
  359     if(uname(&utsBuf) != -1)
  360       machine = utsBuf.nodename;
  361   }
  362 
  363   if(userName || groupName)
  364   {
  365     if(bon_setugid(userName, groupName, globals.quiet))
  366       return 1;
  367     if(userName)
  368       free(userName);
  369   }
  370   else if(geteuid() == 0)
  371   {
  372     fprintf(stderr, "You must use the \"-u\" switch when running as root.\n");
  373     usage();
  374   }
  375 
  376   if(num_bonnie_procs && globals.sync_bonnie)
  377     usage();
  378 
  379   if(num_bonnie_procs)
  380   {
  381     if(num_bonnie_procs == -1)
  382     {
  383       return globals.sem.clear_sem();
  384     }
  385     else
  386     {
  387       return globals.sem.create(num_bonnie_procs);
  388     }
  389   }
  390 
  391   if(globals.sync_bonnie)
  392   {
  393     if(globals.sem.get_semid())
  394       return 1;
  395   }
  396 
  397   if(file_size < 0 || directory_size < 0 || (!file_size && !directory_size) )
  398     usage();
  399   if(directory_size > 262143)
  400   {
  401     fprintf(stderr, "Maximum directory size is 976\n");
  402     return 1;
  403   }
  404   if(globals.chunk_size() < 256 || globals.chunk_size() > Unit)
  405     usage();
  406   int i;
  407   globals.chunk_bits = 0;
  408   for(i = globals.chunk_size(); i > 1; i = i >> 1, globals.chunk_bits++)
  409   {}
  410   if(1 << globals.chunk_bits != globals.chunk_size())
  411     usage();
  412 
  413   if( (directory_max_size != -1 && directory_max_size != -2)
  414      && (directory_max_size < directory_min_size || directory_max_size < 0
  415      || directory_min_size < 0) )
  416     usage();
  417   /* If the storage size is too big for the maximum number of files (1000G) */
  418   if(file_size > IOFileSize * MaxIOFiles)
  419     usage();
  420   /* If the file size is so large and the chunk size is so small that we have
  421    * more than 2G of chunks */
  422   if(globals.chunk_bits < 20 && file_size > (1 << (31 - 20 + globals.chunk_bits)) )
  423     usage();
  424   // if doing more than one test run then we print a header before the
  425   // csv format output.
  426   if(count > 1)
  427   {
  428     globals.timer.SetType(BonTimer::csv);
  429     globals.timer.PrintHeader(stdout);
  430   }
  431 #ifdef OS2
  432     ULONG myPid = 0;
  433     DosQuerySysInfo(QSV_FOREGROUND_PROCESS, QSV_FOREGROUND_PROCESS
  434                   , &myPid, sizeof(myPid));
  435 #else
  436   pid_t myPid = getpid();
  437 #endif
  438   srand(myPid ^ time(NULL));
  439   for(; count > 0 || count == -1; count--)
  440   {
  441     globals.timer.Initialize();
  442     int rc;
  443     rc = TestFileOps(file_size, globals);
  444     if(rc) return rc;
  445     rc = TestDirOps(directory_size, directory_max_size, directory_min_size
  446                   , num_directories, globals);
  447     if(rc) return rc;
  448     // if we are only doing one test run then print a plain-text version of
  449     // the results before printing a csv version.
  450     if(count == -1)
  451     {
  452       globals.timer.SetType(BonTimer::txt);
  453       rc = globals.timer.DoReport(machine, file_size, directory_size
  454                                 , directory_max_size, directory_min_size
  455                                 , num_directories, globals.chunk_size()
  456                                 , globals.quiet ? stderr : stdout);
  457     }
  458     // print a csv version in every case
  459     globals.timer.SetType(BonTimer::csv);
  460     rc = globals.timer.DoReport(machine, file_size, directory_size
  461                               , directory_max_size, directory_min_size
  462                               , num_directories, globals.chunk_size(), stdout);
  463     if(rc) return rc;
  464   }
  465 }
  466 
  467 int
  468 TestFileOps(int file_size, CGlobalItems &globals)
  469 {
  470   if(file_size)
  471   {
  472     CFileOp file(globals.timer, file_size, globals.chunk_bits, globals.bufSync
  473 #ifdef O_DIRECT
  474                , globals.use_direct_io
  475 #endif
  476                 );
  477     int    num_chunks;
  478     int    words;
  479     char  *buf = globals.buf();
  480     int    bufindex;
  481     int    i;
  482 
  483     if(globals.ram && file_size < globals.ram * 2)
  484     {
  485       fprintf(stderr
  486             , "File size should be double RAM for good results, RAM is %dM.\n"
  487             , globals.ram);
  488       return 1;
  489     }
  490     // default is we have 1M / 8K * 200 chunks = 25600
  491     num_chunks = Unit / globals.chunk_size() * file_size;
  492 
  493     int rc;
  494     rc = file.open(globals.name, true, true);
  495     if(rc)
  496       return rc;
  497     if(exitNow)
  498       return EXIT_CTRL_C;
  499     globals.timer.timestamp();
  500 
  501     if(!globals.fast)
  502     {
  503       globals.decrement_and_wait(Putc);
  504       // Fill up a file, writing it a char at a time with the stdio putc() call
  505       if(!globals.quiet) fprintf(stderr, "Writing with putc()...");
  506       for(words = 0; words < num_chunks; words++)
  507       {
  508         if(file.write_block_putc() == -1)
  509           return 1;
  510         if(exitNow)
  511           return EXIT_CTRL_C;
  512       }
  513       fflush(NULL);
  514       /*
  515        * note that we always close the file before measuring time, in an
  516        *  effort to force as much of the I/O out as we can
  517        */
  518       file.close();
  519       globals.timer.get_delta_t(Putc);
  520       if(!globals.quiet) fprintf(stderr, "done\n");
  521     }
  522     /* Write the whole file from scratch, again, with block I/O */
  523     if(file.reopen(true))
  524       return 1;
  525     globals.decrement_and_wait(FastWrite);
  526     if(!globals.quiet) fprintf(stderr, "Writing intelligently...");
  527     memset(buf, 0, globals.chunk_size());
  528     globals.timer.timestamp();
  529     bufindex = 0;
  530     // for the number of chunks of file data
  531     for(i = 0; i < num_chunks; i++)
  532     {
  533       if(exitNow)
  534         return EXIT_CTRL_C;
  535       // for each chunk in the Unit
  536       buf[bufindex]++;
  537       bufindex = (bufindex + 1) % globals.chunk_size();
  538       if(file.write_block(PVOID(buf)) == -1)
  539         return io_error("write(2)");
  540     }
  541     file.close();
  542     globals.timer.get_delta_t(FastWrite);
  543     if(!globals.quiet) fprintf(stderr, "done\n");
  544 
  545 
  546     /* Now read & rewrite it using block I/O.  Dirty one word in each block */
  547     if(file.reopen(false))
  548       return 1;
  549     if (file.seek(0, SEEK_SET) == -1)
  550     {
  551       if(!globals.quiet) fprintf(stderr, "error in lseek(2) before rewrite\n");
  552       return 1;
  553     }
  554     globals.decrement_and_wait(ReWrite);
  555     if(!globals.quiet) fprintf(stderr, "Rewriting...");
  556     globals.timer.timestamp();
  557     bufindex = 0;
  558     for(words = 0; words < num_chunks; words++)
  559     { // for each chunk in the file
  560       if (file.read_block(PVOID(buf)) == -1)
  561         return 1;
  562       bufindex = bufindex % globals.chunk_size();
  563       buf[bufindex]++;
  564       bufindex++;
  565       if (file.seek(-1, SEEK_CUR) == -1)
  566         return 1;
  567       if (file.write_block(PVOID(buf)) == -1)
  568         return io_error("re write(2)");
  569       if(exitNow)
  570         return EXIT_CTRL_C;
  571     }
  572     file.close();
  573     globals.timer.get_delta_t(ReWrite);
  574     if(!globals.quiet) fprintf(stderr, "done\n");
  575 
  576 
  577     if(!globals.fast)
  578     {
  579       // read them all back with getc()
  580       if(file.reopen(false, true))
  581         return 1;
  582       globals.decrement_and_wait(Getc);
  583       if(!globals.quiet) fprintf(stderr, "Reading with getc()...");
  584       globals.timer.timestamp();
  585 
  586       for(words = 0; words < num_chunks; words++)
  587       {
  588         if(file.read_block_getc(buf) == -1)
  589           return 1;
  590         if(exitNow)
  591           return EXIT_CTRL_C;
  592       }
  593 
  594       file.close();
  595       globals.timer.get_delta_t(Getc);
  596       if(!globals.quiet) fprintf(stderr, "done\n");
  597     }
  598 
  599     /* Now suck it in, Chunk at a time, as fast as we can */
  600     if(file.reopen(false))
  601       return 1;
  602     if (file.seek(0, SEEK_SET) == -1)
  603       return io_error("lseek before read");
  604     globals.decrement_and_wait(FastRead);
  605     if(!globals.quiet) fprintf(stderr, "Reading intelligently...");
  606     globals.timer.timestamp();
  607     for(i = 0; i < num_chunks; i++)
  608     { /* per block */
  609       if ((words = file.read_block(PVOID(buf))) == -1)
  610         return io_error("read(2)");
  611       if(exitNow)
  612         return EXIT_CTRL_C;
  613     } /* per block */
  614     file.close();
  615     globals.timer.get_delta_t(FastRead);
  616     if(!globals.quiet) fprintf(stderr, "done\n");
  617 
  618     globals.timer.timestamp();
  619     if(file.seek_test(globals.quiet, globals.sem))
  620       return 1;
  621 
  622     /*
  623      * Now test random seeks; first, set up for communicating with children.
  624      * The object of the game is to do "Seeks" lseek() calls as quickly
  625      *  as possible.  So we'll farm them out among SeekProcCount processes.
  626      *  We'll control them by writing 1-byte tickets down a pipe which
  627      *  the children all read.  We write "Seeks" bytes with val 1, whichever
  628      *  child happens to get them does it and the right number of seeks get
  629      *  done.
  630      * The idea is that since the write() of the tickets is probably
  631      *  atomic, the parent process likely won't get scheduled while the
  632      *  children are seeking away.  If you draw a picture of the likely
  633      *  timelines for three children, it seems likely that the seeks will
  634      *  overlap very nicely with the process scheduling with the effect
  635      *  that there will *always* be a seek() outstanding on the file.
  636      * Question: should the file be opened *before* the fork, so that
  637      *  all the children are lseeking on the same underlying file object?
  638      */
  639   }
  640   return 0;
  641 }
  642 
  643 int
  644 TestDirOps(int directory_size, int max_size, int min_size
  645          , int num_directories, CGlobalItems &globals)
  646 {
  647   COpenTest open_test(globals.chunk_size(), globals.bufSync, globals.doExit);
  648   if(!directory_size)
  649   {
  650     return 0;
  651   }
  652   // if directory_size (in K) * data per file*2 > (ram << 10) (IE memory /1024)
  653   // then the storage of file names will take more than half RAM and there
  654   // won't be enough RAM to have Bonnie++ paged in and to have a reasonable
  655   // meta-data cache.
  656   if(globals.ram && directory_size * MaxDataPerFile * 2 > (globals.ram << 10))
  657   {
  658     fprintf(stderr
  659         , "When testing %dK of files in %d MiB of RAM the system is likely to\n"
  660            "start paging Bonnie++ data and the test will give suspect\n"
  661            "results, use less files or install more RAM for this test.\n"
  662           , directory_size, globals.ram);
  663     return 1;
  664   }
  665   // Can't use more than 1G of RAM
  666   if(directory_size * MaxDataPerFile > (1 << 20))
  667   {
  668     fprintf(stderr, "Not enough ram to test with %dK files.\n"
  669                   , directory_size);
  670     return 1;
  671   }
  672   globals.decrement_and_wait(CreateSeq);
  673   if(!globals.quiet) fprintf(stderr, "Create files in sequential order...");
  674   if(open_test.create(globals.name, globals.timer, directory_size
  675                     , max_size, min_size, num_directories, false))
  676     return 1;
  677   globals.decrement_and_wait(StatSeq);
  678   if(!globals.quiet) fprintf(stderr, "done.\nStat files in sequential order...");
  679   if(open_test.stat_sequential(globals.timer))
  680     return 1;
  681   globals.decrement_and_wait(DelSeq);
  682   if(!globals.quiet) fprintf(stderr, "done.\nDelete files in sequential order...");
  683   if(open_test.delete_sequential(globals.timer))
  684     return 1;
  685   if(!globals.quiet) fprintf(stderr, "done.\n");
  686 
  687   globals.decrement_and_wait(CreateRand);
  688   if(!globals.quiet) fprintf(stderr, "Create files in random order...");
  689   if(open_test.create(globals.name, globals.timer, directory_size
  690                     , max_size, min_size, num_directories, true))
  691     return 1;
  692   globals.decrement_and_wait(StatRand);
  693   if(!globals.quiet) fprintf(stderr, "done.\nStat files in random order...");
  694   if(open_test.stat_random(globals.timer))
  695     return 1;
  696   globals.decrement_and_wait(DelRand);
  697   if(!globals.quiet) fprintf(stderr, "done.\nDelete files in random order...");
  698   if(open_test.delete_random(globals.timer))
  699     return 1;
  700   if(!globals.quiet) fprintf(stderr, "done.\n");
  701   return 0;
  702 }
  703 
  704 void
  705 usage()
  706 {
  707   fprintf(stderr,
  708     "usage: bonnie++ [-d scratch-dir] [-s size(MiB)[:chunk-size(b)]]\n"
  709     "                [-n number-to-stat[:max-size[:min-size][:num-directories]]]\n"
  710     "                [-m machine-name]\n"
  711     "                [-r ram-size-in-MiB]\n"
  712     "                [-x number-of-tests] [-u uid-to-use:gid-to-use] [-g gid-to-use]\n"
  713     "                [-q] [-f] [-b] [-p processes | -y]\n"
  714 #ifdef O_DIRECT
  715     "                [-D]\n"
  716 #endif
  717     "\nVersion: " BON_VERSION "\n");
  718   exit(1);
  719 }
  720 
  721 int
  722 io_error(CPCCHAR message, bool do_exit)
  723 {
  724   char buf[1024];
  725 
  726   if(!already_printed_error && !do_exit)
  727   {
  728     sprintf(buf, "Bonnie: drastic I/O error (%s)", message);
  729     perror(buf);
  730     already_printed_error = 1;
  731   }
  732   if(do_exit)
  733     exit(1);
  734   return(1);
  735 }
  736