"Fossies" - the Fresh Open Source Software Archive

Member "bonnie++-2.00a/bonnie++.cpp" (16 Sep 2018, 23584 Bytes) of package /linux/privat/bonnie++-2.00a.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 #include "bonnie.h"
   21 
   22 #include <stdlib.h>
   23 
   24 #include "conf.h"
   25 #ifdef HAVE_ALGORITHM
   26 #include <algorithm>
   27 #else
   28 #ifdef HAVE_ALGO
   29 #include <algo>
   30 #else
   31 #include <algo.h>
   32 #endif
   33 #endif
   34 
   35 #include <sys/wait.h>
   36 #include <unistd.h>
   37 #include <sys/time.h>
   38 #include <pwd.h>
   39 #include <grp.h>
   40 #include <sys/utsname.h>
   41 #include "sync.h"
   42 
   43 #include <time.h>
   44 #include "bon_io.h"
   45 #include "bon_file.h"
   46 #include "bon_time.h"
   47 #include "rand.h"
   48 #include <ctype.h>
   49 #include <string.h>
   50 #include <signal.h>
   51 
   52 void usage();
   53 
   54 class CGlobalItems
   55 {
   56 public:
   57   bool quiet;
   58   int byte_io_size;
   59   bool sync_bonnie;
   60 #ifdef O_DIRECT
   61   bool use_direct_io;
   62 #endif
   63   BonTimer timer;
   64   int ram;
   65   Sync *syn;
   66   char *name;
   67   bool bufSync;
   68   int  io_chunk_bits;
   69   int  file_chunk_bits;
   70   int  file_seeks;
   71   int  file_seek_procs;
   72   int io_chunk_size() const { return m_io_chunk_size; }
   73   int file_chunk_size() const { return m_file_chunk_size; }
   74   bool *doExit;
   75   void set_io_chunk_size(int size)
   76     { delete m_buf; pa_new(size, m_buf, m_buf_pa); m_io_chunk_size = size; }
   77   void set_file_chunk_size(int size)
   78     { delete m_buf; m_buf = new char[max(size, m_io_chunk_size)]; m_file_chunk_size = size; }
   79 
   80   // Return the page-aligned version of the local buffer
   81   char *buf() { return m_buf_pa; }
   82 
   83   CGlobalItems(bool *exitFlag);
   84   ~CGlobalItems() { delete name; delete m_buf; delete syn; }
   85 
   86   void decrement_and_wait(int nr_sem);
   87 
   88   void SetName(CPCCHAR path)
   89   {
   90     delete name;
   91     name = new char[strlen(path) + 15];
   92     pid_t myPid = getpid();
   93     sprintf(name, "%s/Bonnie.%d", path, int(myPid));
   94   }
   95 
   96   void setSync(SYNC_TYPE type, int semKey = 0, int num_tests = 0)
   97   {
   98     syn = new Sync(type, semKey, num_tests);
   99   }
  100 
  101 private:
  102   int m_io_chunk_size;
  103   int m_file_chunk_size;
  104 
  105   char *m_buf;     // Pointer to the entire buffer
  106   char *m_buf_pa;  // Pointer to the page-aligned version of the same buffer
  107 
  108   // Implement a page-aligned version of new.
  109   // 'p' is the pointer created
  110   // 'page_aligned_p' is the page-aligned pointer created
  111   void pa_new(unsigned int num_bytes, char *&p, char *&page_aligned_p)
  112   {
  113     int page_size = getpagesize();
  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   CGlobalItems(const CGlobalItems &f);
  120   CGlobalItems & operator =(const CGlobalItems &f);
  121 };
  122 
  123 CGlobalItems::CGlobalItems(bool *exitFlag)
  124  : quiet(false)
  125  , byte_io_size(DefaultByteIO)
  126  , sync_bonnie(false)
  127 #ifdef O_DIRECT
  128  , use_direct_io(false)
  129 #endif
  130  , timer()
  131  , ram(0)
  132  , syn(NULL)
  133  , name(NULL)
  134  , bufSync(false)
  135  , io_chunk_bits(DefaultChunkBits)
  136  , file_chunk_bits(DefaultChunkBits)
  137  , file_seeks(DefaultSeeks)
  138  , file_seek_procs(DefaultSeekProcCount)
  139  , doExit(exitFlag)
  140  , m_io_chunk_size(DefaultChunkSize)
  141  , m_file_chunk_size(DefaultChunkSize)
  142  , m_buf(NULL)
  143  , m_buf_pa(NULL)
  144 {
  145   pa_new(max(m_io_chunk_size, m_file_chunk_size), m_buf, m_buf_pa);
  146   SetName(".");
  147 }
  148 
  149 void CGlobalItems::decrement_and_wait(int nr_sem)
  150 {
  151   if(syn->decrement_and_wait(nr_sem))
  152     exit(1);
  153 }
  154 
  155 int TestDirOps(int directory_size, int max_size, int min_size
  156              , int num_directories, CGlobalItems &globals);
  157 int TestFileOps(int file_size, CGlobalItems &globals);
  158 
  159 static bool exitNow;
  160 static bool already_printed_error;
  161 
  162 extern "C"
  163 {
  164   void ctrl_c_handler(int sig, siginfo_t *siginf, void *unused)
  165   {
  166     if(siginf->si_signo == SIGXCPU)
  167       fprintf(stderr, "Exceeded CPU usage.\n");
  168     else if(siginf->si_signo == SIGXFSZ)
  169       fprintf(stderr, "exceeded file storage limits.\n");
  170     exitNow = true;
  171   }
  172 }
  173 
  174 int main(int argc, char *argv[])
  175 {
  176   int    file_size = DefaultFileSize;
  177   int    directory_size = DefaultDirectorySize;
  178   int    directory_max_size = DefaultDirectoryMaxSize;
  179   int    directory_min_size = DefaultDirectoryMinSize;
  180   int    num_bonnie_procs = 0;
  181   int    num_directories = 1;
  182   int    test_count = -1;
  183   const char * machine = NULL;
  184   char *userName = NULL, *groupName = NULL;
  185   CGlobalItems globals(&exitNow);
  186   bool setSize = false;
  187 
  188   exitNow = false;
  189   already_printed_error = false;
  190 
  191   struct sigaction sa;
  192   sa.sa_sigaction = &ctrl_c_handler;
  193   sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
  194   if(sigaction(SIGINT, &sa, NULL)
  195    || sigaction(SIGXCPU, &sa, NULL)
  196    || sigaction(SIGXFSZ, &sa, NULL))
  197   {
  198     printf("Can't handle SIGINT.\n");
  199     return 1;
  200   }
  201   sa.sa_handler = SIG_IGN;
  202   if(sigaction(SIGHUP, &sa, NULL))
  203   {
  204     printf("Can't handle SIGHUP.\n");
  205     return 1;
  206   }
  207 
  208 #ifdef _SC_PHYS_PAGES
  209   int page_size = sysconf(_SC_PAGESIZE);
  210   int num_pages = sysconf(_SC_PHYS_PAGES);
  211   if(page_size != -1 && num_pages != -1)
  212   {
  213     globals.ram = page_size/1024 * (num_pages/1024);
  214   }
  215 #endif
  216 
  217   pid_t myPid = 0;
  218   myPid = getpid();
  219   globals.timer.random_source.seedNum(myPid ^ time(NULL));
  220   int concurrency = 1;
  221 
  222   int int_c;
  223   while(-1 != (int_c = getopt(argc, argv, "bc:d:f::g:l:m:n:p:qr:s:u:x:y:z:Z:"
  224 #ifdef O_DIRECT
  225                              "D"
  226 #endif
  227                             )) )
  228   {
  229     switch(char(int_c))
  230     {
  231       case '?':
  232       case ':':
  233         usage();
  234       break;
  235       case 'b':
  236         globals.bufSync = true;
  237       break;
  238       case 'c':
  239         concurrency = atoi(optarg);
  240       break;
  241       case 'd':
  242         if(chdir(optarg))
  243         {
  244           fprintf(stderr, "Can't change to directory \"%s\".\n", optarg);
  245           usage();
  246         }
  247       break;
  248       case 'f':
  249         if(optarg)
  250           globals.byte_io_size = atoi(optarg);
  251         else
  252           globals.byte_io_size = 0;
  253       break;
  254       case 'm':
  255         machine = optarg;
  256       break;
  257       case 'n':
  258       {
  259         char *sbuf = _strdup(optarg);
  260         char *size = strtok(sbuf, ":");
  261         directory_size = size_from_str(size, "m");
  262         size = strtok(NULL, ":");
  263         if(size)
  264         {
  265           directory_max_size = size_from_str(size, "kmg");
  266           size = strtok(NULL, ":");
  267           if(size)
  268           {
  269             directory_min_size = size_from_str(size, "kmg");
  270             size = strtok(NULL, ":");
  271             if(size)
  272             {
  273               num_directories = size_from_str(size, "k");
  274               size = strtok(NULL, "");
  275               if(size)
  276               {
  277                 int tmp = size_from_str(size, "kmg");
  278                 globals.set_file_chunk_size(tmp);
  279               }
  280             }
  281           }
  282         }
  283         free(sbuf);
  284       }
  285       break;
  286       case 'p':
  287         num_bonnie_procs = atoi(optarg);
  288                         /* Set semaphore to # of bonnie++ procs
  289                            to synchronize */
  290       break;
  291       case 'q':
  292         globals.quiet = true;
  293       break;
  294       case 'r':
  295         globals.ram = atoi(optarg);
  296       break;
  297       case 's':
  298       {
  299         char *sbuf = _strdup(optarg);
  300         char *size = strtok(sbuf, ":");
  301 #ifdef _LARGEFILE64_SOURCE
  302         file_size = size_from_str(size, "gt");
  303 #else
  304         file_size = size_from_str(size, "g");
  305 #endif
  306         char * chunk_size = strtok(NULL, ":");
  307         if(chunk_size)
  308         {
  309           char *seeks, *seek_procs;
  310           seeks = strtok(NULL, ":");
  311           globals.set_io_chunk_size(size_from_str(chunk_size, "k"));
  312           if(seeks)
  313           {
  314             seek_procs = strtok(NULL, "");
  315             globals.file_seeks = size_from_str(seeks, "k");
  316             if(seek_procs)
  317             {
  318               globals.file_seek_procs = size_from_str(seek_procs, NULL);
  319             }
  320           }
  321         }
  322         setSize = true;
  323         free(sbuf);
  324       }
  325       break;
  326       case 'g':
  327         if(groupName)
  328           usage();
  329         groupName = optarg;
  330       break;
  331       case 'u':
  332       {
  333         if(userName)
  334           usage();
  335         userName = _strdup(optarg);
  336         int i;
  337         for(i = 0; userName[i] && userName[i] != ':'; i++) {}
  338 
  339         if(userName[i] == ':')
  340         {
  341           if(groupName)
  342             usage();
  343           userName[i] = '\0';
  344           groupName = &userName[i + 1];
  345         }
  346       }
  347       break;
  348       case 'x':
  349         test_count = atoi(optarg);
  350       break;
  351       case 'y':
  352                         /* tell procs to synchronize via previous
  353                            defined semaphore */
  354         switch(tolower(optarg[0]))
  355         {
  356         case 's':
  357           globals.setSync(eSem, SemKey, TestCount);
  358         break;
  359         case 'p':
  360           globals.setSync(ePrompt);
  361         break;
  362         }
  363         globals.sync_bonnie = true;
  364       break;
  365       case 'z':
  366       {
  367         UINT tmp;
  368         if(sscanf(optarg, "%u", &tmp) == 1)
  369           globals.timer.random_source.seedNum(tmp);
  370       }
  371       break;
  372 #ifdef O_DIRECT
  373       case 'D':
  374         /* open file descriptor with direct I/O */
  375         globals.use_direct_io = true;
  376       break;
  377 #endif
  378       case 'Z':
  379       {
  380         if(globals.timer.random_source.seedFile(optarg))
  381           return eParam;
  382       }
  383       break;
  384     }
  385   }
  386   if(concurrency < 1 || concurrency > 200)
  387     usage();
  388   if(!globals.syn)
  389     globals.setSync(eNone);
  390   if(optind < argc)
  391     usage();
  392 
  393   if(globals.ram && !setSize)
  394   {
  395     if(file_size < (globals.ram * 2))
  396       file_size = globals.ram * 2;
  397     // round up to the nearest gig
  398     if(file_size % 1024 > 512)
  399       file_size = file_size + 1024 - (file_size % 1024);
  400   }
  401 #ifndef _LARGEFILE64_SOURCE
  402   if(file_size == 2048)
  403     file_size = 2047;
  404   if(file_size > 2048)
  405   {
  406     fprintf(stderr, "Large File Support not present, can't do %dM.\n", file_size);
  407     usage();
  408   }
  409 #endif
  410   globals.byte_io_size = min(file_size, globals.byte_io_size);
  411   globals.byte_io_size = max(0, globals.byte_io_size);
  412 
  413   if(machine == NULL)
  414   {
  415     struct utsname utsBuf;
  416     if(uname(&utsBuf) != -1)
  417       machine = utsBuf.nodename;
  418   }
  419 
  420   globals.timer.setMachineName(machine);
  421   globals.timer.setConcurrency(concurrency);
  422 
  423   if(userName || groupName)
  424   {
  425     if(bon_setugid(userName, groupName, globals.quiet))
  426       return 1;
  427     if(userName)
  428       free(userName);
  429   }
  430   else if(geteuid() == 0)
  431   {
  432     fprintf(stderr, "You must use the \"-u\" switch when running as root.\n");
  433     usage();
  434   }
  435 
  436   if(num_bonnie_procs && globals.sync_bonnie)
  437     usage();
  438 
  439   if(num_bonnie_procs)
  440   {
  441     globals.setSync(eSem, SemKey, TestCount);
  442     if(num_bonnie_procs == -1)
  443     {
  444       return globals.syn->clear_sem();
  445     }
  446     else
  447     {
  448       return globals.syn->create(num_bonnie_procs);
  449     }
  450   }
  451 
  452   if(globals.sync_bonnie)
  453   {
  454     if(globals.syn->get_semid())
  455       return 1;
  456   }
  457 
  458   if(file_size < 0 || directory_size < 0 || (!file_size && !directory_size) )
  459     usage();
  460   if(globals.io_chunk_size() < 256 || globals.io_chunk_size() > Unit)
  461     usage();
  462   if(globals.file_chunk_size() < 256 || globals.file_chunk_size() > Unit)
  463     usage();
  464   int i;
  465   globals.io_chunk_bits = 0;
  466   globals.file_chunk_bits = 0;
  467   for(i = globals.io_chunk_size(); i > 1; i = i >> 1, globals.io_chunk_bits++)
  468   {}
  469 
  470   if(1 << globals.io_chunk_bits != globals.io_chunk_size())
  471     usage();
  472   for(i = globals.file_chunk_size(); i > 1; i = i >> 1, globals.file_chunk_bits++)
  473   {}
  474 
  475   if(1 << globals.file_chunk_bits != globals.file_chunk_size())
  476     usage();
  477 
  478   if( (directory_max_size != -1 && directory_max_size != -2)
  479      && (directory_max_size < directory_min_size || directory_max_size < 0
  480      || directory_min_size < 0) )
  481     usage();
  482 #ifndef _LARGEFILE64_SOURCE
  483   if(file_size > (1 << (31 - 20 + globals.io_chunk_bits)) )
  484   {
  485     fprintf(stderr
  486    , "The small chunk size and large IO size make this test impossible in 32bit.\n");
  487     usage();
  488   }
  489 #endif
  490   if(file_size && globals.ram && (file_size * concurrency) < (globals.ram * 2) )
  491   {
  492     fprintf(stderr
  493           , "File size should be double RAM for good results, RAM is %dM.\n"
  494           , globals.ram);
  495     usage();
  496   }
  497 
  498   // if doing more than one test run then we print a header before the
  499   // csv format output.
  500   if(test_count > 1)
  501   {
  502     globals.timer.SetType(BonTimer::csv);
  503     globals.timer.PrintHeader(stdout);
  504   }
  505   for(; test_count > 0 || test_count == -1; test_count--)
  506   {
  507     globals.timer.Initialize();
  508     int rc;
  509     rc = TestFileOps(file_size, globals);
  510     if(rc) return rc;
  511     rc = TestDirOps(directory_size, directory_max_size, directory_min_size
  512                   , num_directories, globals);
  513     if(rc) return rc;
  514     // if we are only doing one test run then print a plain-text version of
  515     // the results before printing a csv version.
  516     if(test_count == -1)
  517     {
  518       globals.timer.SetType(BonTimer::txt);
  519       rc = globals.timer.DoReportIO(file_size, globals.byte_io_size
  520                     , globals.io_chunk_size(), globals.file_seeks, globals.file_seek_procs, globals.quiet ? stderr : stdout);
  521       rc |= globals.timer.DoReportFile(directory_size
  522                     , directory_max_size, directory_min_size, num_directories
  523                     , globals.file_chunk_size()
  524                     , globals.quiet ? stderr : stdout);
  525     }
  526     // print a csv version in every case
  527     globals.timer.SetType(BonTimer::csv);
  528     rc = globals.timer.DoReportIO(file_size, globals.byte_io_size
  529                    , globals.io_chunk_size(), globals.file_seeks, globals.file_seek_procs, stdout);
  530     rc |= globals.timer.DoReportFile(directory_size
  531                     , directory_max_size, directory_min_size, num_directories
  532                     , globals.file_chunk_size(), stdout);
  533     if(rc) return rc;
  534   }
  535   return eNoErr;
  536 }
  537 
  538 int
  539 TestFileOps(int file_size, CGlobalItems &globals)
  540 {
  541   if(file_size)
  542   {
  543     CFileOp file(globals.timer, file_size, globals.io_chunk_bits, globals.bufSync
  544 #ifdef O_DIRECT
  545                , globals.use_direct_io
  546 #endif
  547                );
  548     int    num_chunks;
  549     int    words;
  550     char  *buf = globals.buf();
  551     int    bufindex;
  552     int    i;
  553 
  554     // default is we have 1M / 8K * 300 chunks = 38400
  555     num_chunks = Unit / globals.io_chunk_size() * file_size;
  556     int char_io_chunks = Unit / globals.io_chunk_size() * globals.byte_io_size;
  557 
  558     int rc;
  559     rc = file.Open(globals.name, true);
  560     if(rc)
  561       return rc;
  562     if(exitNow)
  563       return eCtrl_C;
  564     Duration dur;
  565 
  566     globals.timer.start();
  567     if(char_io_chunks)
  568     {
  569       dur.reset();
  570       globals.decrement_and_wait(ByteWrite);
  571       // Fill up a file, writing it a char at a time
  572       if(!globals.quiet) fprintf(stderr, "Writing a byte at a time...");
  573       for(words = 0; words < char_io_chunks; words++)
  574       {
  575         dur.start();
  576         if(file.write_block_byte() == -1)
  577           return 1;
  578         dur.stop();
  579         if(exitNow)
  580           return eCtrl_C;
  581       }
  582       fflush(NULL);
  583       /*
  584        * note that we always close the file before measuring time, in an
  585        *  effort to force as much of the I/O out as we can
  586        */
  587       file.Close();
  588       globals.timer.stop_and_record(ByteWrite);
  589       globals.timer.add_latency(ByteWrite, dur.getMax());
  590       if(!globals.quiet) fprintf(stderr, "done\n");
  591     }
  592     /* Write the whole file from scratch, again, with block I/O */
  593     if(file.reopen(true))
  594       return 1;
  595     dur.reset();
  596     globals.decrement_and_wait(FastWrite);
  597     if(!globals.quiet) fprintf(stderr, "Writing intelligently...");
  598     memset(buf, 0, globals.io_chunk_size());
  599     globals.timer.start();
  600     bufindex = 0;
  601     // for the number of chunks of file data
  602     for(i = 0; i < num_chunks; i++)
  603     {
  604       if(exitNow)
  605         return eCtrl_C;
  606       // for each chunk in the Unit
  607       buf[bufindex]++;
  608       bufindex = (bufindex + 1) % globals.io_chunk_size();
  609       dur.start();
  610       if(file.write_block(PVOID(buf)) == -1)
  611       {
  612         fprintf(stderr, "Can't write block %d.\n", i);
  613         return 1;
  614       }
  615       dur.stop();
  616     }
  617     file.Close();
  618     globals.timer.stop_and_record(FastWrite);
  619     globals.timer.add_latency(FastWrite, dur.getMax());
  620     if(!globals.quiet) fprintf(stderr, "done\n");
  621 
  622 
  623     /* Now read & rewrite it using block I/O.  Dirty one word in each block */
  624     if(file.reopen(false))
  625       return 1;
  626     if (file.seek(0, SEEK_SET) == -1)
  627     {
  628       if(!globals.quiet) fprintf(stderr, "error in lseek(2) before rewrite\n");
  629       return 1;
  630     }
  631     dur.reset();
  632     globals.decrement_and_wait(ReWrite);
  633     if(!globals.quiet) fprintf(stderr, "Rewriting...");
  634     globals.timer.start();
  635     bufindex = 0;
  636     for(words = 0; words < num_chunks; words++)
  637     { // for each chunk in the file
  638       dur.start();
  639       if (file.read_block(PVOID(buf)) == -1)
  640         return 1;
  641       bufindex = bufindex % globals.io_chunk_size();
  642       buf[bufindex]++;
  643       bufindex++;
  644       if (file.seek(-1, SEEK_CUR) == -1)
  645         return 1;
  646       if (file.write_block(PVOID(buf)) == -1)
  647         return io_error("re write(2)");
  648       dur.stop();
  649       if(exitNow)
  650         return eCtrl_C;
  651     }
  652     file.Close();
  653     globals.timer.stop_and_record(ReWrite);
  654     globals.timer.add_latency(ReWrite, dur.getMax());
  655     if(!globals.quiet) fprintf(stderr, "done\n");
  656 
  657     if(char_io_chunks)
  658     {
  659       // read them all back a byte at a time
  660       if(file.reopen(false))
  661         return 1;
  662       dur.reset();
  663       globals.decrement_and_wait(ByteRead);
  664       if(!globals.quiet) fprintf(stderr, "Reading a byte at a time...");
  665       globals.timer.start();
  666 
  667       for(words = 0; words < char_io_chunks; words++)
  668       {
  669         dur.start();
  670         if(file.read_block_byte(buf) == -1)
  671           return 1;
  672         dur.stop();
  673         if(exitNow)
  674           return eCtrl_C;
  675       }
  676 
  677       file.Close();
  678       globals.timer.stop_and_record(ByteRead);
  679       globals.timer.add_latency(ByteRead, dur.getMax());
  680       if(!globals.quiet) fprintf(stderr, "done\n");
  681     }
  682 
  683     /* Now suck it in, Chunk at a time, as fast as we can */
  684     if(file.reopen(false))
  685       return 1;
  686     if (file.seek(0, SEEK_SET) == -1)
  687       return io_error("lseek before read");
  688     dur.reset();
  689     globals.decrement_and_wait(FastRead);
  690     if(!globals.quiet) fprintf(stderr, "Reading intelligently...");
  691     globals.timer.start();
  692     for(i = 0; i < num_chunks; i++)
  693     { /* per block */
  694       dur.start();
  695       if ((words = file.read_block(PVOID(buf))) == -1)
  696         return io_error("read(2)");
  697       dur.stop();
  698       if(exitNow)
  699         return eCtrl_C;
  700     } /* per block */
  701     file.Close();
  702     globals.timer.stop_and_record(FastRead);
  703     globals.timer.add_latency(FastRead, dur.getMax());
  704     if(!globals.quiet) fprintf(stderr, "done\n");
  705 
  706     globals.timer.start();
  707     if(file.seek_test(globals.timer.random_source, globals.quiet, globals.file_seeks, globals.file_seek_procs, *globals.syn))
  708       return 1;
  709 
  710     /*
  711      * Now test random seeks; first, set up for communicating with children.
  712      * The object of the game is to do "Seeks" lseek() calls as quickly
  713      *  as possible.  So we'll farm them out among SeekProcCount processes.
  714      *  We'll control them by writing 1-byte tickets down a pipe which
  715      *  the children all read.  We write "Seeks" bytes with val 1, whichever
  716      *  child happens to get them does it and the right number of seeks get
  717      *  done.
  718      * The idea is that since the write() of the tickets is probably
  719      *  atomic, the parent process likely won't get scheduled while the
  720      *  children are seeking away.  If you draw a picture of the likely
  721      *  timelines for three children, it seems likely that the seeks will
  722      *  overlap very nicely with the process scheduling with the effect
  723      *  that there will *always* be a seek() outstanding on the file.
  724      * Question: should the file be opened *before* the fork, so that
  725      *  all the children are lseeking on the same underlying file object?
  726      */
  727   }
  728   return eNoErr;
  729 }
  730 
  731 int
  732 TestDirOps(int directory_size, int max_size, int min_size
  733          , int num_directories, CGlobalItems &globals)
  734 {
  735   COpenTest open_test(globals.file_chunk_size(), globals.bufSync, globals.doExit);
  736   if(!directory_size)
  737   {
  738     return 0;
  739   }
  740   // if directory_size (in K) * data per file*2 > (ram << 10) (IE memory /1024)
  741   // then the storage of file names will take more than half RAM and there
  742   // won't be enough RAM to have Bonnie++ paged in and to have a reasonable
  743   // meta-data cache.
  744   if(globals.ram && directory_size * MaxDataPerFile * 2 > (unsigned int)((globals.ram << 10)))
  745   {
  746     fprintf(stderr
  747         , "When testing %dK of files in %d MiB of RAM the system is likely to\n"
  748            "start paging Bonnie++ data and the test will give suspect\n"
  749            "results, use less files or install more RAM for this test.\n"
  750           , directory_size, globals.ram);
  751     return eParam;
  752   }
  753   // Can't use more than 1G of RAM
  754   if(directory_size * MaxDataPerFile > (1 << 20))
  755   {
  756     fprintf(stderr, "Not enough ram to test with %dK files.\n"
  757                   , directory_size);
  758     return eParam;
  759   }
  760   globals.decrement_and_wait(CreateSeq);
  761   if(!globals.quiet) fprintf(stderr, "Create files in sequential order...");
  762   if(open_test.create(globals.name, globals.timer, directory_size
  763                     , max_size, min_size, num_directories, false))
  764     return 1;
  765   globals.decrement_and_wait(StatSeq);
  766   if(!globals.quiet) fprintf(stderr, "done.\nStat files in sequential order...");
  767   if(open_test.stat_sequential(globals.timer))
  768     return 1;
  769   globals.decrement_and_wait(DelSeq);
  770   if(!globals.quiet) fprintf(stderr, "done.\nDelete files in sequential order...");
  771   if(open_test.delete_sequential(globals.timer))
  772     return 1;
  773   if(!globals.quiet) fprintf(stderr, "done.\n");
  774 
  775   globals.decrement_and_wait(CreateRand);
  776   if(!globals.quiet) fprintf(stderr, "Create files in random order...");
  777   if(open_test.create(globals.name, globals.timer, directory_size
  778                     , max_size, min_size, num_directories, true))
  779     return 1;
  780   globals.decrement_and_wait(StatRand);
  781   if(!globals.quiet) fprintf(stderr, "done.\nStat files in random order...");
  782   if(open_test.stat_random(globals.timer))
  783     return 1;
  784   globals.decrement_and_wait(DelRand);
  785   if(!globals.quiet) fprintf(stderr, "done.\nDelete files in random order...");
  786   if(open_test.delete_random(globals.timer))
  787     return 1;
  788   if(!globals.quiet) fprintf(stderr, "done.\n");
  789   return eNoErr;
  790 }
  791 
  792 void
  793 usage()
  794 {
  795   fprintf(stderr, "usage:\n"
  796     "bonnie++ [-d scratch-dir] [-c concurrency] [-s size(MiB)[:chunk-size(b)]]\n"
  797     "      [-n number-to-stat[:max-size[:min-size][:num-directories[:chunk-size]]]]\n"
  798     "      [-m machine-name] [-r ram-size-in-MiB]\n"
  799     "      [-x number-of-tests] [-u uid-to-use:gid-to-use] [-g gid-to-use]\n"
  800     "      [-q] [-f] [-b] [-p processes | -y] [-z seed | -Z random-file]\n"
  801 #ifdef O_DIRECT
  802     "      [-D]\n"
  803 #endif
  804     "\nVersion: " BON_VERSION "\n");
  805   exit(eParam);
  806 }
  807 
  808 int
  809 io_error(CPCCHAR message, bool do_exit)
  810 {
  811   char buf[1024];
  812 
  813   if(!already_printed_error && !do_exit)
  814   {
  815     sprintf(buf, "Bonnie: drastic I/O error (%s)", message);
  816     perror(buf);
  817     already_printed_error = 1;
  818   }
  819   if(do_exit)
  820     exit(1);
  821   return(1);
  822 }
  823