Statistics
| Branch: | Revision:

root / synthbench / bonnie++ / .svn / text-base / mpibonnie++.cpp.svn-base @ 0:839f52ef7657

History | View | Annotate | Download (19.7 kB)

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
#include "mpi.h" 
46

    
47
#ifdef AIX_MEM_SIZE
48
#include <cf.h>
49
#include <sys/cfgodm.h>
50
#include <sys/cfgdb.h>
51
#endif
52

    
53
void usage();
54

    
55
class CGlobalItems
56
{
57
public:
58
  bool quiet;
59
  bool fast;
60
  bool sync_bonnie;
61
  BonTimer timer;
62
  int ram;
63
  Semaphore sem;
64
  char *name;
65
  bool bufSync;
66
  int rank;
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; m_buf = new char[size]; m_chunk_size = size; }
72

    
73
  char *buf() { return m_buf; }
74

    
75
  CGlobalItems(bool *exitFlag);
76
  ~CGlobalItems() { delete name; delete m_buf; }
77

    
78
  void decrement_and_wait(int nr_sem);
79

    
80
  void SetName(CPCCHAR path)
81
  {
82
    delete name;
83
    name = new char[strlen(path) + 15];
84
#ifdef OS2
85
    ULONG myPid = 0;
86
    DosQuerySysInfo(QSV_FOREGROUND_PROCESS, QSV_FOREGROUND_PROCESS
87
                  , &myPid, sizeof(myPid));
88
#else
89
    pid_t myPid = getpid();
90
#endif
91
//    sprintf(name, "%s/Bonnie.%d", path, int(myPid));
92
    
93
    MPI_Comm_rank(MPI_COMM_WORLD,&rank); 
94
    sprintf(name, "%s/Bonnie.%d", path, rank);
95

    
96
  }
97
private:
98
  int m_chunk_size;
99
  char *m_buf;
100

    
101

    
102
  CGlobalItems(const CGlobalItems &f);
103
  CGlobalItems & operator =(const CGlobalItems &f);
104
};
105

    
106
CGlobalItems::CGlobalItems(bool *exitFlag)
107
 : quiet(false)
108
 , fast(false)
109
 , sync_bonnie(false)
110
 , timer()
111
 , ram(0)
112
 , sem(SemKey, TestCount)
113
 , name(NULL)
114
 , bufSync(false)
115
 , chunk_bits(DefaultChunkBits)
116
 , doExit(exitFlag)
117
 , m_chunk_size(DefaultChunkSize)
118
 , m_buf(new char[m_chunk_size])
119
{
120
  SetName(".");
121
}
122

    
123
void CGlobalItems::decrement_and_wait(int nr_sem)
124
{
125
  if(sem.decrement_and_wait(nr_sem))
126
    exit(1);
127
}
128

    
129
int TestDirOps(int directory_size, int max_size, int min_size
130
             , int num_directories, CGlobalItems &globals);
131
int TestFileOps(int file_size, CGlobalItems &globals);
132

    
133
static bool exitNow;
134
static bool already_printed_error;
135

    
136
#ifdef USE_SA_SIGACTION
137
#define SIGNAL_NUMBER siginf->si_signo
138
#else
139
#define SIGNAL_NUMBER sig
140
#endif
141

    
142
extern "C"
143
{
144
  void ctrl_c_handler(int sig
145
#ifdef USE_SA_SIGACTION
146
		    , siginfo_t *siginf, void *unused
147
#endif
148
		     )
149
  {
150
    if(SIGNAL_NUMBER == SIGXCPU)
151
      fprintf(stderr, "Exceeded CPU usage.\n");
152
    else if(SIGNAL_NUMBER == SIGXFSZ)
153
      fprintf(stderr, "exceeded file storage limits.\n");
154
    exitNow = true;
155
  }
156
}
157

    
158
int main(int argc, char *argv[])
159
{
160
  int    file_size = DefaultFileSize;
161
  int    directory_size = DefaultDirectorySize;
162
  int    directory_max_size = DefaultDirectoryMaxSize;
163
  int    directory_min_size = DefaultDirectoryMinSize;
164
  int    num_bonnie_procs = 0;
165
  int    num_directories = 1;
166
  int    count = -1;
167

    
168
  int nprocs;
169
  MPI_Init(&argc,&argv); 
170
  MPI_Comm_size(MPI_COMM_WORLD,&nprocs);
171

    
172
  time_t as_start, as_end;
173
  double as_timediff;
174

    
175
  time(&as_start);
176

    
177
  const char * machine = NULL;
178
  char *userName = NULL, *groupName = NULL;
179
  CGlobalItems globals(&exitNow);
180
  bool setSize = false;
181

    
182
  exitNow = false;
183
  already_printed_error = false;
184

    
185
  struct sigaction sa;
186
#ifdef USE_SA_SIGACTION
187
  sa.sa_sigaction = &ctrl_c_handler;
188
  sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
189
#else
190
  sa.sa_handler = ctrl_c_handler;
191
  sa.sa_flags = SA_RESETHAND;
192
#endif
193
  if(sigaction(SIGINT, &sa, NULL)
194
   || sigaction(SIGXCPU, &sa, NULL)
195
   || sigaction(SIGXFSZ, &sa, NULL))
196
  {
197
    printf("Can't handle SIGINT.\n");
198
    return 1;
199
  }
200
#ifdef USE_SA_SIGACTION
201
  sa.sa_sigaction = NULL;
202
#endif
203
  sa.sa_handler = SIG_IGN;
204
  if(sigaction(SIGHUP, &sa, NULL))
205
  {
206
    printf("Can't handle SIGHUP.\n");
207
    return 1;
208
  }
209

    
210
#ifdef _SC_PHYS_PAGES
211
  int page_size = sysconf(_SC_PAGESIZE);
212
  int num_pages = sysconf(_SC_PHYS_PAGES);
213
  if(page_size != -1 && num_pages != -1)
214
  {
215
    globals.ram = page_size/1024 * (num_pages/1024);
216
  }
217
#else
218

    
219
#ifdef AIX_MEM_SIZE
220
  struct CuAt *odm_obj;
221
  int how_many;
222

    
223
  odm_set_path("/etc/objrepos");
224
  odm_obj = getattr("sys0", "realmem", 0, &how_many);
225
  globals.ram = atoi(odm_obj->value) / 1024;
226
  odm_terminate();
227
  printf("Memory = %d MiB\n", globals.ram);
228
#endif
229

    
230
#endif
231

    
232
  int int_c;
233
  while(-1 != (int_c = getopt(argc, argv, "bd:fg:m:n:p:qr:s:u:x:y")) )
234
  {
235
    switch(char(int_c))
236
    {
237
      case '?':
238
      case ':':
239
        usage();
240
      break;
241
      case 'b':
242
        globals.bufSync = true;
243
      break;
244
      case 'd':
245
        if(chdir(optarg))
246
        {
247
          fprintf(stderr, "Can't change to directory \"%s\".\n", optarg);
248
          usage();
249
        }
250
      break;
251
      case 'f':
252
        globals.fast = true;
253
      break;
254
      case 'm':
255
        machine = optarg;
256
      break;
257
      case 'n':
258
        sscanf(optarg, "%d:%d:%d:%d", &directory_size
259
                     , &directory_max_size, &directory_min_size
260
                     , &num_directories);
261
      break;
262
      case 'p':
263
        num_bonnie_procs = atoi(optarg);
264
                        /* Set semaphore to # of bonnie++ procs 
265
                           to synchronize */
266
      break;
267
      case 'q':
268
        globals.quiet = true;
269
      break;
270
      case 'r':
271
        globals.ram = atoi(optarg);
272
      break;
273
      case 's':
274
      {
275
        char *sbuf = strdup(optarg);
276
        char *size = strtok(sbuf, ":");
277
        file_size = atoi(size);
278
        char c = size[strlen(size) - 1];
279
        if(c == 'g' || c == 'G')
280
          file_size *= 1024;
281
        size = strtok(NULL, "");
282
        if(size)
283
        {
284
          int tmp = atoi(size);
285
          c = size[strlen(size) - 1];
286
          if(c == 'k' || c == 'K')
287
            tmp *= 1024;
288
          globals.set_chunk_size(tmp);
289
        }
290
        setSize = true;
291
      }
292
      break;
293
      case 'g':
294
        if(groupName)
295
          usage();
296
        groupName = optarg;
297
      break;
298
      case 'u':
299
      {
300
        if(userName)
301
          usage();
302
        userName = strdup(optarg);
303
        int i;
304
        for(i = 0; userName[i] && userName[i] != ':'; i++)
305
        {}
306
        if(userName[i] == ':')
307
        {
308
          if(groupName)
309
            usage();
310
          userName[i] = '\0';
311
          groupName = &userName[i + 1];
312
        }
313
      }
314
      break;
315
      case 'x':
316
        count = atoi(optarg);
317
      break;
318
      case 'y':
319
                        /* tell procs to synchronize via previous
320
                           defined semaphore */
321
        globals.sync_bonnie = true;
322
      break;
323
    }
324
  }
325
  if(optind < argc)
326
    usage();
327

    
328
  if(globals.ram && !setSize)
329
  {
330
    if(file_size < (globals.ram * 2))
331
      file_size = globals.ram * 2;
332
    // round up to the nearest gig
333
    if(file_size % 1024 > 512)
334
      file_size = file_size + 1024 - (file_size % 1024);
335
  }
336

    
337
  if(machine == NULL)
338
  {
339
    struct utsname utsBuf;
340
    if(uname(&utsBuf) != -1)
341
      machine = utsBuf.nodename;
342
  }
343

    
344
  if(userName || groupName)
345
  {
346
    if(bon_setugid(userName, groupName, globals.quiet))
347
      return 1;
348
    if(userName)
349
      free(userName);
350
  }
351
  else if(geteuid() == 0)
352
  {
353
    fprintf(stderr, "You must use the \"-u\" switch when running as root.\n");
354
    usage();
355
  }
356

    
357
  if(num_bonnie_procs && globals.sync_bonnie)
358
    usage();
359

    
360
  if(num_bonnie_procs)
361
  {
362
    if(num_bonnie_procs == -1)
363
    {
364
      return globals.sem.clear_sem();
365
    }
366
    else
367
    {
368
      return globals.sem.create(num_bonnie_procs);
369
    }
370
  }
371

    
372
  if(globals.sync_bonnie)
373
  {
374
    if(globals.sem.get_semid())
375
      return 1;
376
  }
377

    
378
  if(file_size < 0 || directory_size < 0 || (!file_size && !directory_size) )
379
    usage();
380
  if(globals.chunk_size() < 256 || globals.chunk_size() > Unit)
381
    usage();
382
  int i;
383
  globals.chunk_bits = 0;
384
  for(i = globals.chunk_size(); i > 1; i = i >> 1, globals.chunk_bits++)
385
  {}
386
  if(1 << globals.chunk_bits != globals.chunk_size())
387
    usage();
388

    
389
  if( (directory_max_size != -1 && directory_max_size != -2)
390
     && (directory_max_size < directory_min_size || directory_max_size < 0
391
     || directory_min_size < 0) )
392
    usage();
393
  /* If the storage size is too big for the maximum number of files (1000G) */
394
  if(file_size > IOFileSize * MaxIOFiles)
395
    usage();
396
  /* If the file size is so large and the chunk size is so small that we have
397
   * more than 2G of chunks */
398
  if(globals.chunk_bits < 20 && file_size > (1 << (31 - 20 + globals.chunk_bits)) )
399
    usage();
400
  // if doing more than one test run then we print a header before the
401
  // csv format output.
402
//  if(count > 1)
403
//  {
404
    globals.timer.SetType(BonTimer::csv);
405
    globals.timer.PrintHeader(stdout);
406
//  }
407
#ifdef OS2
408
    ULONG myPid = 0;
409
    DosQuerySysInfo(QSV_FOREGROUND_PROCESS, QSV_FOREGROUND_PROCESS
410
                  , &myPid, sizeof(myPid));
411
#else
412
  pid_t myPid = getpid();
413
#endif
414
  srand(myPid ^ time(NULL));
415
  for(; count > 0 || count == -1; count--)
416
  {
417
    globals.timer.Initialize();
418
    int rc;
419
    rc = TestFileOps(file_size, globals);
420
    if(rc) return rc;
421
    rc = TestDirOps(directory_size, directory_max_size, directory_min_size
422
                  , num_directories, globals);
423
    if(rc) return rc;
424
    // if we are only doing one test run then print a plain-text version of
425
    // the results before printing a csv version.
426
    if(count == -1)
427
/*    {
428
      globals.timer.SetType(BonTimer::txt);
429
      rc = globals.timer.DoReport(machine, file_size, directory_size
430
                                , directory_max_size, directory_min_size
431
                                , num_directories, globals.chunk_size()
432
                                , globals.quiet ? stderr : stdout);
433
    }*/
434
    // print a csv version in every case
435
    globals.timer.SetType(BonTimer::csv);
436
    
437
    rc = globals.timer.DoReport(machine, file_size, directory_size
438
                              , directory_max_size, directory_min_size
439
                              , num_directories, globals.chunk_size(), stdout);
440
    if(rc) return rc;
441
  }
442
  time(&as_end);
443
  as_timediff = difftime (as_end,as_start);
444
  fprintf(stdout,  "Walltime: %6.4f s\n", as_timediff);
445
  fprintf(stdout, "\nbonnie terminated normally!\n");
446
}
447

    
448
int
449
TestFileOps(int file_size, CGlobalItems &globals)
450
{
451
  if(file_size)
452
  {
453
    CFileOp file(globals.timer, file_size, globals.chunk_bits, globals.bufSync);
454
    int    num_chunks;
455
    int    words;
456
    char  *buf = globals.buf();
457
    int    bufindex;
458
    int    i;
459

    
460
    if(globals.ram && file_size < globals.ram * 2)
461
    {
462
      fprintf(stderr
463
            , "File size should be double RAM for good results, RAM is %dM.\n"
464
            , globals.ram);
465
      return 1;
466
    }
467
    // default is we have 1M / 8K * 200 chunks = 25600
468
    num_chunks = Unit / globals.chunk_size() * file_size;
469

    
470
    int rc;
471
    rc = file.open(globals.name, true, true);
472
    if(rc)
473
      return rc;
474
    if(exitNow)
475
      return EXIT_CTRL_C;
476
    globals.timer.timestamp();
477

    
478
    if(!globals.fast)
479
    {
480
      globals.decrement_and_wait(Putc);
481
      // Fill up a file, writing it a char at a time with the stdio putc() call
482
      if(!globals.quiet) fprintf(stderr, "Writing with putc()...");
483
      for(words = 0; words < num_chunks; words++)
484
      {
485
        if(file.write_block_putc() == -1)
486
          return 1;
487
        if(exitNow)
488
          return EXIT_CTRL_C;
489
      }
490
      fflush(NULL);
491
      /*
492
       * note that we always close the file before measuring time, in an
493
       *  effort to force as much of the I/O out as we can
494
       */
495
      file.close();
496
      globals.timer.get_delta_t(Putc);
497
      if(!globals.quiet) fprintf(stderr, "done\n");
498
    }
499
    /* Write the whole file from scratch, again, with block I/O */
500
    if(file.reopen(true))
501
      return 1;
502
    globals.decrement_and_wait(FastWrite);
503
    if(!globals.quiet) fprintf(stderr, "Writing intelligently...");
504
    memset(buf, 0, globals.chunk_size());
505
    globals.timer.timestamp();
506
    bufindex = 0;
507
    // for the number of chunks of file data
508
    for(i = 0; i < num_chunks; i++)
509
    {
510
      if(exitNow)
511
        return EXIT_CTRL_C;
512
      // for each chunk in the Unit
513
      buf[bufindex]++;
514
      bufindex = (bufindex + 1) % globals.chunk_size();
515
      if(file.write_block(PVOID(buf)) == -1)
516
        return io_error("write(2)");
517
    }
518
    file.close();
519
    globals.timer.get_delta_t(FastWrite);
520
    if(!globals.quiet) fprintf(stderr, "done\n");
521

    
522

    
523
    /* Now read & rewrite it using block I/O.  Dirty one word in each block */
524
    if(file.reopen(false))
525
      return 1;
526
    if (file.seek(0, SEEK_SET) == -1)
527
    {
528
      if(!globals.quiet) fprintf(stderr, "error in lseek(2) before rewrite\n");
529
      return 1;
530
    }
531
    globals.decrement_and_wait(ReWrite);
532
    if(!globals.quiet) fprintf(stderr, "Rewriting...");
533
    globals.timer.timestamp();
534
    bufindex = 0;
535
    for(words = 0; words < num_chunks; words++)
536
    { // for each chunk in the file
537
      if (file.read_block(PVOID(buf)) == -1)
538
        return 1;
539
      bufindex = bufindex % globals.chunk_size();
540
      buf[bufindex]++;
541
      bufindex++;
542
      if (file.seek(-1, SEEK_CUR) == -1)
543
        return 1;
544
      if (file.write_block(PVOID(buf)) == -1)
545
        return io_error("re write(2)");
546
      if(exitNow)
547
        return EXIT_CTRL_C;
548
    }
549
    file.close();
550
    globals.timer.get_delta_t(ReWrite);
551
    if(!globals.quiet) fprintf(stderr, "done\n");
552

    
553

    
554
    if(!globals.fast)
555
    {
556
      // read them all back with getc()
557
      if(file.reopen(false, true))
558
        return 1;
559
      globals.decrement_and_wait(Getc);
560
      if(!globals.quiet) fprintf(stderr, "Reading with getc()...");
561
      globals.timer.timestamp();
562

    
563
      for(words = 0; words < num_chunks; words++)
564
      {
565
        if(file.read_block_getc(buf) == -1)
566
          return 1;
567
        if(exitNow)
568
          return EXIT_CTRL_C;
569
      }
570

    
571
      file.close();
572
      globals.timer.get_delta_t(Getc);
573
      if(!globals.quiet) fprintf(stderr, "done\n");
574
    }
575

    
576
    /* Now suck it in, Chunk at a time, as fast as we can */
577
    if(file.reopen(false))
578
      return 1;
579
    if (file.seek(0, SEEK_SET) == -1)
580
      return io_error("lseek before read");
581
    globals.decrement_and_wait(FastRead);
582
    if(!globals.quiet) fprintf(stderr, "Reading intelligently...");
583
    globals.timer.timestamp();
584
    for(i = 0; i < num_chunks; i++)
585
    { /* per block */
586
      if ((words = file.read_block(PVOID(buf))) == -1)
587
        return io_error("read(2)");
588
      if(exitNow)
589
        return EXIT_CTRL_C;
590
    } /* per block */
591
    file.close();
592
    globals.timer.get_delta_t(FastRead);
593
    if(!globals.quiet) fprintf(stderr, "done\n");
594

    
595
    globals.timer.timestamp();
596
    if(file.seek_test(globals.quiet, globals.sem))
597
      return 1;
598

    
599
    /*
600
     * Now test random seeks; first, set up for communicating with children.
601
     * The object of the game is to do "Seeks" lseek() calls as quickly
602
     *  as possible.  So we'll farm them out among SeekProcCount processes.
603
     *  We'll control them by writing 1-byte tickets down a pipe which
604
     *  the children all read.  We write "Seeks" bytes with val 1, whichever
605
     *  child happens to get them does it and the right number of seeks get
606
     *  done.
607
     * The idea is that since the write() of the tickets is probably
608
     *  atomic, the parent process likely won't get scheduled while the
609
     *  children are seeking away.  If you draw a picture of the likely
610
     *  timelines for three children, it seems likely that the seeks will
611
     *  overlap very nicely with the process scheduling with the effect
612
     *  that there will *always* be a seek() outstanding on the file.
613
     * Question: should the file be opened *before* the fork, so that
614
     *  all the children are lseeking on the same underlying file object?
615
     */
616
  }
617
  return 0;
618
}
619

    
620
int
621
TestDirOps(int directory_size, int max_size, int min_size
622
         , int num_directories, CGlobalItems &globals)
623
{
624
  COpenTest open_test(globals.chunk_size(), globals.bufSync, globals.doExit);
625
  if(!directory_size)
626
  {
627
    return 0;
628
  }
629
  // if directory_size (in K) * data per file*2 > (ram << 10) (IE memory /1024)
630
  // then the storage of file names will take more than half RAM and there
631
  // won't be enough RAM to have Bonnie++ paged in and to have a reasonable
632
  // meta-data cache.
633
  if(globals.ram && directory_size * MaxDataPerFile * 2 > (globals.ram << 10))
634
  {
635
    fprintf(stderr
636
        , "When testing %dK of files in %d MiB of RAM the system is likely to\n"
637
           "start paging Bonnie++ data and the test will give suspect\n"
638
           "results, use less files or install more RAM for this test.\n"
639
          , directory_size, globals.ram);
640
    return 1;
641
  }
642
  // Can't use more than 1G of RAM
643
  if(directory_size * MaxDataPerFile > (1 << 20))
644
  {
645
    fprintf(stderr, "Not enough ram to test with %dK files.\n"
646
                  , directory_size);
647
    return 1;
648
  }
649
  globals.decrement_and_wait(CreateSeq);
650
  if(!globals.quiet&&globals.rank==0) fprintf(stderr, "Create files in sequential order...");
651
  MPI_Barrier(MPI_COMM_WORLD);
652
  if(open_test.create(globals.name, globals.timer, directory_size
653
                    , max_size, min_size, num_directories, false))
654
    return 1;
655
  
656
  globals.decrement_and_wait(StatSeq);
657
  if(!globals.quiet&&globals.rank==0) fprintf(stderr, "done.\nStat files in sequential order...");
658
  MPI_Barrier(MPI_COMM_WORLD);
659
  if(open_test.stat_sequential(globals.timer))
660
    return 1;
661
  globals.decrement_and_wait(DelSeq);
662
  if(!globals.quiet&&globals.rank==0) fprintf(stderr, "done.\nDelete files in sequential order...");
663
  MPI_Barrier(MPI_COMM_WORLD);
664
  if(open_test.delete_sequential(globals.timer))
665
    return 1;
666
  if(!globals.quiet&&globals.rank==0) fprintf(stderr, "done.\n");
667

    
668
  globals.decrement_and_wait(CreateRand);
669
  if(!globals.quiet&&globals.rank==0) fprintf(stderr, "Create files in random order...");
670
  MPI_Barrier(MPI_COMM_WORLD);
671
  if(open_test.create(globals.name, globals.timer, directory_size
672
                    , max_size, min_size, num_directories, true))
673
    return 1;
674
  globals.decrement_and_wait(StatRand);
675
  if(!globals.quiet&&globals.rank==0) fprintf(stderr, "done.\nStat files in random order...");
676
  MPI_Barrier(MPI_COMM_WORLD);
677
  if(open_test.stat_random(globals.timer))
678
    return 1;
679
  globals.decrement_and_wait(DelRand);
680
  if(!globals.quiet&&globals.rank==0) fprintf(stderr, "done.\nDelete files in random order...");
681
  MPI_Barrier(MPI_COMM_WORLD);
682
  if(open_test.delete_random(globals.timer))
683
    return 1;
684
  if(!globals.quiet&&globals.rank==0) fprintf(stderr, "done.\n");
685
  MPI_Barrier(MPI_COMM_WORLD);
686
  MPI_Finalize();
687
  return 0;
688
}
689

    
690
void
691
usage()
692
{
693
  fprintf(stderr,
694
    "usage: bonnie++ [-d scratch-dir] [-s size(MiB)[:chunk-size(b)]]\n"
695
    "                [-n number-to-stat[:max-size[:min-size][:num-directories]]]\n"
696
    "                [-m machine-name]\n"
697
    "                [-r ram-size-in-MiB]\n"
698
    "                [-x number-of-tests] [-u uid-to-use:gid-to-use] [-g gid-to-use]\n"
699
    "                [-q] [-f] [-b] [-p processes | -y]\n"
700
    "\nVersion: " BON_VERSION "\n");
701
  exit(1);
702
}
703

    
704
int
705
io_error(CPCCHAR message, bool do_exit)
706
{
707
  char buf[1024];
708

    
709
  if(!already_printed_error && !do_exit)
710
  {
711
    sprintf(buf, "Bonnie: drastic I/O error (%s)", message);
712
    perror(buf);
713
    already_printed_error = 1;
714
  }
715
  if(do_exit)
716
    exit(1);
717
  return(1);
718
}
719