/* rawio.c: measure raw disk I/O throughput */
/*-
 * Copyright (c) 1999
 *	Nan Yang Computer Services Limited.  All rights reserved.
 *
 *  This software is distributed under the so-called ``Berkeley
 *  License'':
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Nan Yang Computer
 *      Services Limited.
 * 4. Neither the name of the Company nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * This software is provided ``as is'', and any express or implied
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose are disclaimed.
 * In no event shall the company or contributors be liable for any
 * direct, indirect, incidental, special, exemplary, or consequential
 * damages (including, but not limited to, procurement of substitute
 * goods or services; loss of use, data, or profits; or business
 * interruption) however caused and on any theory of liability, whether
 * in contract, strict liability, or tort (including negligence or
 * otherwise) arising in any way out of the use of this software, even if
 * advised of the possibility of such damage.
 *
 * $Id: rawio.c,v 1.7 1999/07/21 02:10:09 grog Exp grog $
 */

#define LINELENGTH 200					    /* max length of program line */
#define MAXLINES 54					    /* number of lines on page */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <signal.h>
#ifdef __osf__
#include <sys/ioctl.h>
#endif
#ifdef BSD4_4
#include <sys/disklabel.h>
#if __FreeBSD__ >= 5
#include <sys/disk.h>
#endif
#endif
#include "randoms.h"

/* submitted by Andy Doran <ad@psn.ie> */
#ifdef __NetBSD__
#include <errno.h>
#endif
#if defined(__NetBSD__) || defined(__osf__) || defined(linux)
#define srandomdev() srand ((unsigned) time (NULL))
#endif

#ifdef __alpha
#define        Int64           long
#define        Quad           "l"
#define        atoi64(x)       strtol(x, NULL, 10)
#else
#define        Int64   long long
#define        Quad   "ll"
#define        atoi64(x)       strtoq(x, NULL, 10)
#endif

#define SKIPSTART 16384					    /* amount to leave untouched at start of dev */

#ifdef __osf__
pid_t wait4 (pid_t process_id, int *status_location, int options, struct rusage *resource_usage);
#endif

void checklines (int increment);
void usage (void);
void sigcatch (int signo);
int myrandom (int i);
int myrandom2 (int i);

#ifndef MAXPHYS
#define        MAXPHYS (128*1024)
#endif

char physbuf [MAXPHYS * 2];				    /* buffer we're allocating from */
char *buf;						    /* and what we're using, for alignment */
int file;
size_t length;
u_int sectorsize;

int count;
enum operation
{
  RandomRead = 1,
  SequentialRead = 2,
  RandomWrite = 4,
  SequentialWrite = 8
  }
op;
enum
{
  RRtest = 0,
  SRtest,
  RWtest,
  SWtest,
  };
int openflags;						    /* flags to pass to open(2) */
enum operation thisop;					    /* current one we're looking at */
int test;						    /* number of test */

char *args;
char *myname;						    /* argv [0] for posterity */
off_t offset;
int lines;
#define MAXCHILD 256					    /* maximum number of processes to start */
pid_t apid [MAXCHILD];
int nproc = 8;						    /* (default) number to start */
int fixedoffset = 0;					    /* set if we start all sequential tests
							     * at the same offset (-f flag) */
int fixedlength = 0;					    /* set if random transfers are all full
							     * length (-F flag) */
int staticrandom = 0;					    /* set if we read our random values from
							     * the internal table (-S flag) */
struct timeval start_time;
struct timeval end_time [MAXCHILD];			    /* one end time per process */
float elapsed_time;
float user_time;
float system_time;

struct rusage rusage [MAXCHILD];			    /* and associated rusage info */
struct rusage total_rusage;				    /* total values */

pid_t endproc [MAXCHILD];				    /* note the pid of each proc as it finishes */

/* Shared memory segment for communicating with
 * the children.  Each child accesses only its
 * element of the array.  The parent doesn't
 * touch them until they exit */
struct childinfo
{
  int reads;
  int writes;
  Int64 bytes_read;
  Int64 bytes_written;
  }
*childinfo;

int total_reads;
int total_writes;
Int64 total_bytes_read;
Int64 total_bytes_written;

void header ();

/* child code */
void dochild (int, int);

#define PAGELENGTH 60					    /* get this from termcap */
void
  checklines (int increment)
{
  char prompt [2];
  if ((lines += increment) >= PAGELENGTH)
    {
    fprintf (stderr, "More...");
    fgets (prompt, 2, stdin);
    lines = 0;
    }
  }

int alignment = 1;					    /* buffer alignment */
size_t maxchunk = (size_t) 16384;			    /* maximum size of transfer */
off_t filesize = 0;
int recs = 16384;					    /* number of records to transfer */
char *device;						    /* name of device to open */
int proc;						    /* index of this process in childinfo (child) */
							    /* index of current child (parent) */
int verbose = 0;					    /* verbosity flag */
int heading = 1;					    /* print heading if != 0 */
int exitimmediately = 0;				    /* exit after headers if set (-x flag) */
int SWfrac;						    /* percentage sequential writes */
int RWfrac;						    /* percentage random writes */
char *id;						    /* name of test */

void
  usage ()
{
  fprintf (stderr,
	   "Usage: %s [options] device\n"
	   "\n"
	   "Options may be:\n"
	   "\n"
	   "\t-a\tPerform all tests (read and write, random and sequential)\n"
	   "\t-A <alignment>\tEnsure that all buffers are aligned on an <alignment> boundary\n"
	   "\t-c\tMaximum I/O transfer size in sectors or bytes.\n"
	   "\t\t\tMust be an integral number of sectors, default %d bytes\n"
	   "\t-F\tPerform fixed-length random I/O\n"
	   "\t-f\tStart sequential tests at fixed offset\n"
	   "\t-h\tSuppress heading\n"
	   "\t-I <name>\tIdentification for output\n"
	   "\t-n\tTotal number of records to write, defaults to %d\n"
	   "\t-p\tNumber of processes to start, defaults to %d\n"
	   "\t-R\tPerform random read test\n"
	   "\t-r\tPerform sequential read test\n"
	   "\t-s\tSize of file system, no default\n"
	   "\t-S\tUse static random table\n"
	   "\t-v\tProduce verbose output\n"
	   "\t-W\tPerform random write test\n"
	   "\t-Wx\tPerform random read/write test, x%% write\n"
	   "\t-w\tPerform sequential write test\n"
	   "\t-wx\tPerform sequential read/write test, x%% write\n"
	   "\t-x\tPrint headers and exit\n"
	   "\n",
	   myname,
	   maxchunk,
	   recs,
	   nproc);
  exit (1);
  }

/*
 * Take a number with an optional scale factor and convert
 * it to a number of bytes.
 *
 * The scale factors are:
 *
 * s	sectors (of 512 bytes)
 * k	kilobytes (1024 bytes)
 * m	megabytes (of 1024 * 1024 bytes)
 * g	gigabytes (of 1024 * 1024 * 1024 bytes)
 */
u_int64_t sizespec (char *spec)
{
  u_int64_t size;
  char *s;
  int sign = 1;						    /* -1 if negative */

  size = 0;
  if (spec != NULL)					    /* we have a parameter */
    {
    s = spec;
    if (*s == '-')					    /* negative, */
      {
      sign = -1;
      s++;						    /* skip */
      }
    if ((*s >= '0') && (*s <= '9'))			    /* it's numeric */
      {
      while ((*s >= '0') && (*s <= '9'))		    /* it's numeric */
	size = size * 10 + *s++ - '0';			    /* convert it */
      switch (*s)
	{
      case '\0':
	return size * sign;
	
      case 'S':
      case 's':
	return size * sign * 512;
	
      case 'K':
      case 'k':
	return size * sign * 1024;
	
      case 'M':
      case 'm':
	return size * sign * 1024 * 1024;
	
      case 'G':
      case 'g':
	return size * sign * 1024 * 1024 * 1024;
	}
      }
    }
  fprintf (stderr, "Invalid length specification: %s", spec);
  exit (1);
  }

int main (int argc, unsigned char *argv [])
{
  int i;
  char *arg;

  myname = argv [0];					    /* for usage () */
  if (argc < 2)
    usage ();
  for (i = 1; i < argc; i++)
    {
    if (argv [i][0] == '-')
      {
      switch (argv [i][1])
	{
      case 'a':						    /* do it all */
	op = SequentialRead | RandomRead | SequentialWrite | RandomWrite;
	break;

      case 'A':
	if (argv [i] [2] != '\0')
	  arg = &argv [i] [2];
	else if (++i < argc)
	  arg = argv [i];
	else
	  {
	  printf ("No arg to A flag\n");
	  break;
	  }
	alignment = atoi (arg);
	break;

      case 'c':						    /* maxchunk */
	if (argv [i] [2] != '\0')
	  arg = &argv [i] [2];
	else if (++i < argc)
	  arg = argv [i];
	else
	  {
	  printf ("No arg to c flag\n");
	  break;
	  }
	maxchunk = (size_t) atoi (arg);
	if (maxchunk < (size_t) 512)
	  {
	  maxchunk *= (size_t) 512;
	  }
	if (maxchunk > (size_t) MAXPHYS)
	  {
	  printf ("Maximum transfer size limited to %d\n", MAXPHYS);
	  maxchunk = (size_t) MAXPHYS;
	  }
	break;

      case 'f':
	fixedoffset = 1;
	break;

      case 'F':
	fixedlength = 1;
	break;

      case 'h':
	heading = 0;
	break;

      case 'I':
	if (argv [i] [2] != '\0')
	  arg = &argv [i] [2];
	else if (++i < argc)
	  arg = argv [i];
	else
	  {
	  printf ("No arg to I flag\n");
	  break;
	  }
	id = arg;
	break;

      case 'n':
	if (argv [i] [2] != '\0')
	  arg = &argv [i] [2];
	else if (++i < argc)
	  arg = argv [i];
	else
	  {
	  printf ("No arg to n flag\n");
	  break;
	  }
	recs = atoi (arg);
	break;

      case 'p':
	if (argv [i] [2] != '\0')
	  arg = &argv [i] [2];
	else if (++i < argc)
	  arg = argv [i];
	else
	  {
	  printf ("No arg to n flag\n");
	  break;
	  }
	nproc = atoi (arg);
	if (nproc > MAXCHILD)				    /* too many */
	  {
	  fprintf (stderr,
		   "Can't start %d processes, maximum is %d\n",
		   nproc,
		   MAXCHILD );
	  nproc = MAXCHILD;
	  }
	break;

      case 'R':
	op |= RandomRead;
	break;

      case 'r':
	op |= SequentialRead;
	break;

      case 's':
	if (argv [i] [2] != '\0')
	  arg = &argv [i] [2];
	else if (++i < argc)
	  arg = argv [i];
	else
	  {
	  printf ("No arg to n flag\n");
	  break;
	  }
	filesize = sizespec (arg);
	break;

      case 'S':
	staticrandom = 1;				    /* read from our internal table */
	break;

      case 'v':
	if (argv [i] [2] != '\0')
	  arg = &argv [i] [2];
	else if (++i < argc)
	  arg = argv [i];
	else
	  {
	  printf ("No arg to v flag\n");
	  break;
	  }
	verbose = atoi (arg);
	break;

      case 'W':
	op |= RandomWrite;
	if (argv [i] [2] != '\0')
	  RWfrac = atoi (&argv [i] [2]);
	else if (((i + 1) < argc) && isdigit (argv [i + 1] [0]))
	  {
	  RWfrac = atoi (argv [i + 1]);
	  i++;
	  }
	else
	  RWfrac = 100;
	if ((RWfrac < 0) || (RWfrac > 100))
	  {
	  fprintf (stderr, "Invalid random write percentage specification\n");
	  exit (1);
	  }
	break;

      case 'w':
	op |= SequentialWrite;
	if (argv [i] [2] != '\0')
	  SWfrac = atoi (&argv [i] [2]);
	else if (((i + 1) < argc) && isdigit (argv [i + 1] [0]))
	  {
	  SWfrac = atoi (argv [i + 1]);
	  i++;
	  }
	else
	  SWfrac = 100;
	if ((SWfrac <= 0) || (SWfrac > 100))
	  {
	  fprintf (stderr, "Invalid sequential write percentage specification\n");
	  exit (1);
	  }
	break;

      case 'x':
	exitimmediately = 1;
	break;

      default:
	usage ();
	}
      }
    else if (device != NULL)
      usage ();
    else
      device = argv [i];
    }
  if (exitimmediately)
    {
    header ();
    exit (0);
    }
  buf = (char *) (((int) &physbuf [MAXPHYS]) & ~ (alignment - 1)); /* where to put the aligned buffer */
  if (op == 0)						    /* no ops specified, */
    op = RandomRead | SequentialRead;			    /* default to the read tests */
  if (device == NULL)
    {
    fprintf (stderr, "No file name specified\n");
    usage ();
    }
  if (op & ((SequentialWrite | RandomWrite) == 0))	    /* only read tests */
    openflags = O_RDONLY;				    /* open read-only */
  else
    openflags = O_RDWR;					    /* open read-write */
  if ((file = open (device, openflags, 0)) < 0)
    {
    fprintf (stderr, "Can't open %s: %s\n", device, strerror (errno));
    usage ();
    }
  if (filesize == 0)
    {
    struct stat filestat;

    if (stat (device, &filestat) < 0)
      {
      fprintf (stderr, "Can't stat %s: %s\n", device, strerror (errno));
      usage ();
      }
    else
      filesize = filestat.st_size;
    if ((filestat.st_mode & S_IFMT) == S_IFBLK)
      fprintf (stderr,
	       "Warning: %s is a block device.  Results will be meaningless\n",
	       device);
    else if ((filestat.st_mode & S_IFMT) != S_IFCHR)
      {
      fprintf (stderr, "%s is not a device\n", device);
      usage ();
      }

    /* stat didn't do it; try lseek */
    if (filesize == 0)
      {
      filesize = lseek (file, (off_t) 0, SEEK_END);	    /* try to get the end */
      if (filesize < 0)
	fprintf (stderr, "Can't seek %s: %s\n", device, strerror (errno));
      }

#ifdef BSD4_4
    /* lseek didn't do it; look at the disk label */
    if (filesize == 0)
      {
      struct disklabel label;
      if (ioctl (file, DIOCGDINFO, &label) != -1)	    /* got the label */
	filesize = ((off_t) label.d_secperunit) * DEV_BSIZE;
      else if (verbose)
	fprintf (stderr, "Can't get label of %s: %s\n", device, strerror (errno));
      }
#endif
    }
  if (filesize == 0)
    {
    fprintf (stderr, "No file size specified\n");
    usage ();
    }
#ifndef DIOCGSECTORSIZE
  sectorsize = DEV_BSIZE;
#else
  if (ioctl (file, DIOCGSECTORSIZE, &sectorsize) == -1)		/* failed to find native sector size */
    {
    if (verbose)
      fprintf (stderr, "Can't get sector size of %s: %s\n", device, strerror (errno));
    sectorsize = DEV_BSIZE;
    }

#endif
  if (maxchunk <= sectorsize)
    {
    fprintf (stderr, "I/O transfer max must be at least 1 sector\n");
    usage ();
    }
  if (maxchunk & (sectorsize - 1))
    {
    fprintf (stderr, "Invalid transfer size, must be multiple of %d: %d\n", sectorsize, maxchunk);
    usage ();
    }
  if (id == NULL)					    /* no ID specified, */
    {
    id = strrchr (device, '/');				    /* find the basename */
    if (id == NULL)					    /* no / */
      id = device;					    /* take the whole name */
    else
      id++;
    if ((id [0] == 'r') && (id [1] != '\0'))		    /* raw name? */
      id++;
    }
  
#ifdef BSD4_4
  childinfo = mmap (NULL,
		    nproc * sizeof (struct childinfo),
		    PROT_READ | PROT_WRITE,
#ifdef MAP_INHERIT
		    MAP_INHERIT | MAP_SHARED | MAP_ANON,
#else
		    MAP_SHARED | MAP_ANON,
#endif
		    -1,
		    (off_t) 0 );
#else
  childinfo = mmap (NULL,
		    nproc * sizeof (struct childinfo),
		    PROT_READ | PROT_WRITE,
		    MAP_SHARED | MAP_ANON,
		    -1,
		    (off_t) 0 );
#endif
  if (childinfo == (struct childinfo *) MAP_FAILED)
    {
    fprintf (stderr, "Can't mmap shared memory: %s\n", strerror (errno));
    exit (1);
    }
  
  /* OK, we're ready to go */
  header ();
  if (! verbose)					    /* print the ID now */
    printf ("%-8s ", id);
  
  for (test = 0; test < 4; test++)			    /* check for each test */
    {
    if (op & 1 << test)					    /* do this test */
      {
      fflush (stdout);					    /* to stop the children doing it */
      /* First, start up the procs */
      for (proc = 0; proc < nproc; proc++)		    /* spawn some processes */
	{
	pid_t pid = fork ();
	if (pid == 0)
	  dochild (test, proc);
	else if (pid < 0)
	  {
	  fprintf (stderr, "Can't fork process %d: %s\n", proc, strerror (errno));
	  exit (1);
	  }
	else
	  apid [proc] = pid;
	}
      sleep (1);					    /* give them a sec to get their act together */
      
      /* Now initialize the totals */
      bzero ((char *) &total_rusage, sizeof (struct rusage));
      total_reads = 0;
      total_writes = 0;
      total_bytes_read = 0;
      total_bytes_written = 0;
      /* Now we're ready to start: note the time */
      gettimeofday (&start_time, NULL);
      /* And let them go */
      for (proc = 0; proc < nproc; proc++)
	{
	if (kill (apid [proc], SIGUSR1) < 0)
	  fprintf (stderr, "Can't kill process %d: %s\n", apid [proc], strerror (errno));
	}
      
      /* Now wait for them to finish */
      for (proc = 0; proc < nproc; proc++)
	{
	int status;
	pid_t pid;
	
	pid = wait4 (-1, &status, 0, &rusage [proc]);
	gettimeofday (&end_time [proc], NULL);
	if (pid < 0)
	  {
	  fprintf (stderr, "Can't wait4: %s\n", strerror (errno));
	  exit (1);
	  }
	endproc [proc] = pid;				    /* note the pid */
	}
      
      for (proc = 0; proc < nproc; proc++)
	{
	total_rusage.ru_utime.tv_sec += rusage [proc].ru_utime.tv_sec; /* accumulate totals */
	total_rusage.ru_utime.tv_usec += rusage [proc].ru_utime.tv_usec; /* accumulate totals */
	total_rusage.ru_stime.tv_sec += rusage [proc].ru_stime.tv_sec; /* accumulate totals */
	total_rusage.ru_stime.tv_usec += rusage [proc].ru_stime.tv_usec; /* accumulate totals */
	total_rusage.ru_minflt += rusage [proc].ru_minflt;  /* page reclaims */
	total_rusage.ru_majflt += rusage [proc].ru_majflt;  /* page faults */
	total_rusage.ru_nswap += rusage [proc].ru_nswap;    /* swaps */
	total_rusage.ru_inblock += rusage [proc].ru_inblock; /* block input operations */
	total_rusage.ru_oublock += rusage [proc].ru_oublock; /* block output operations */
	total_rusage.ru_msgsnd += rusage [proc].ru_msgsnd;  /* messages sent */
	total_rusage.ru_msgrcv += rusage [proc].ru_msgrcv;  /* messages received */
	total_rusage.ru_nsignals += rusage [proc].ru_nsignals; /* signals received */
	total_rusage.ru_nvcsw += rusage [proc].ru_nvcsw;    /* voluntary context switches */
	total_rusage.ru_nivcsw += rusage [proc].ru_nivcsw;  /* involuntary " */
	total_reads += childinfo [proc].reads;
	total_writes += childinfo [proc].writes;
	total_bytes_read += childinfo [proc].bytes_read;
	total_bytes_written += childinfo [proc].bytes_written;
	}
      
      /* Reaped all children.  Print results */
      proc = nproc - 1;					    /* look at the last child to complete */
      elapsed_time = (end_time [proc].tv_sec - start_time.tv_sec)
	+ ((float) (end_time [proc].tv_usec - start_time.tv_usec) / 1000000);
      user_time = total_rusage.ru_utime.tv_sec + ((float) total_rusage.ru_utime.tv_usec) / 1000000;
      system_time = total_rusage.ru_stime.tv_sec + ((float) total_rusage.ru_stime.tv_usec) / 1000000;
      if (verbose)
	{
	
	/* First print the test names */
	switch (test)
	  {
	case RRtest:
	  printf ("RR  ");
	  break;
	case SRtest:
	  printf ("SR  ");
	  break;
	  
	case RWtest:
	  if (RWfrac != 100)
	    printf ("RW%02d", RWfrac);
	  else
	    printf ("RW  ");
	  break;
	  
	case SWtest:
	  if (SWfrac != 100)
	    printf ("SW%02d", SWfrac);
	  else
	    printf ("SW  ");
	  break;
	  }
	if (verbose > 1)
	  printf ("%8s\t%12.6f\t%10.1f\t  %5.0f %6.1f\t%6.1f\t%6.1f\t%d\t%d\n",
		  id,
		  elapsed_time,
		  (float) (total_bytes_read + total_bytes_written) / elapsed_time / 1000,
		  ((float) (total_reads + total_writes) / elapsed_time),
		  user_time / elapsed_time * 100,
		  system_time / elapsed_time * 100,
		  (user_time + system_time) / elapsed_time * 100,
		  total_reads,
		  total_writes );
	else
	  {
	  if (test < 2)					    /* read ops, */
	    printf ("%8s\t%10.1f\t  %5.0f %6.1f\t%6.1f\t%6.1f\t%d\n",
		    id,
		    (float) (total_bytes_read + total_bytes_written) / elapsed_time / 1000,
		    ((float) (total_reads + total_writes) / elapsed_time),
		    user_time / elapsed_time * 100,
		    system_time / elapsed_time * 100,
		    (user_time + system_time) / elapsed_time * 100,
		    total_reads );
	  else
	    printf ("%8s\t%10.1f\t  %5.0f %6.1f\t%6.1f\t%6.1f\t%d\n",
		    id,
		    (float) (total_bytes_read + total_bytes_written) / elapsed_time / 1000,
		    ((float) (total_reads + total_writes) / elapsed_time),
		    user_time / elapsed_time * 100,
		    system_time / elapsed_time * 100,
		    (user_time + system_time) / elapsed_time * 100,
		    total_writes );
	  }
	}
      else						    /* not verbose, */
	printf ("%8.1f %5.0f\t",
		(float) (total_bytes_read + total_bytes_written) / elapsed_time / 1000,
		((float) (total_reads + total_writes) / elapsed_time) );
      }
    else if (! verbose)
      printf ("\t\t");					    /* skip the column */
    }
  if (! verbose)
    printf ("\n");
  return 0;
  }

void header ()
{
  if (heading)
    {
    if (verbose > 1)
      {
      printf ("Test name:\t%16s\n"
	      "Transfer size:\t%16d\n"
	      "Record count:\t\t%8d\n"
	      "Process count:\t%16d\n"
	      "Device size:\t%16" Quad "d\n",
	      id,
	      maxchunk,
	      recs,
	      nproc,
	      filesize );
      /*     printf ("Test    ID\t\tTime\t    KB/sec\t %%User\t  %%Sys\t%%Total\tReads\tI/O in\tWrites\tI/O out\n"); */
      printf ("Test    ID\t\tTime\t    KB/sec\t    /sec %%User\t  %%Sys\t%%Total\tReads\tWrites\n");
      }
    else if (verbose)
      printf ("Test    ID\t     K/sec\t    /sec %%User\t  %%Sys\t%%Total\n");
    else
      printf ("\t   Random read\tSequential read\t   Random write\tSequential write\n"
	      "ID\t    K/sec  /sec\t   K/sec  /sec\t   K/sec  /sec\t   K/sec  /sec\n");
    }
  }

void sigcatch (int signo)
{
  }

/*
 * Calculate a random number.  If we're static, just pick it from a
 * table, otherwise do the real thing.
 */
int myrandom (int i)
{
  if (staticrandom)
    return randoms [i % MAXRANDOM];
  else
    return random ();
  }

/* Another random number calculation.  Do we really need this? */
int myrandom2 (int i)
{
  if (staticrandom)
    return random2 [i % MAXRANDOM];
  else
    return random ();
  }

/* Child code */
void dochild (int test, int proc)
{
  sigset_t allsigs;
  struct sigaction ignore;
  int mycount;
  int iocount;
  int i;
  int maxrecs;
  off_t pos;						    /* where we actually seek to */

  ignore.sa_handler = sigcatch;
  ignore.sa_flags = 0;
  ignore.sa_mask = allsigs;

  /* first, calculate the number of records to transfer.
   * If we're one of the first <remainder> processes,
   * transfer one more */
  mycount = recs / nproc;				    /* quotient */
  if (recs % nproc > proc)
    mycount++;

  /* Initialize the counters */
  childinfo [proc].reads = 0;
  childinfo [proc].bytes_read = 0;
  childinfo [proc].writes = 0;
  childinfo [proc].bytes_written = 0;

  /* Don't jump the gun */
  sigemptyset (&allsigs);
  if (sigaction (SIGUSR1, &ignore, NULL) < 0)
    {
    perror ("Can't sigaction");
    exit (1);
    }
  sigsuspend (&allsigs);
  srandomdev ();

  /* OK, we're allowed to start now */
  switch (test)
    {
  case 0:						    /* random read */
    for (i = 1; i <= mycount; i++)
      {
      if (fixedlength)
	length = maxchunk;
      else
	length = (myrandom (proc + nproc * i * 2)
		  % (maxchunk * 2) + sectorsize) & ~(sectorsize - 1);	    /* length of this transfer */
      offset = (((((off_t) myrandom2 (proc + nproc * i)) * sectorsize)
		 % (filesize - SKIPSTART - length)) & ~((off_t)sectorsize - 1)) + SKIPSTART;
      if ((iocount = pread (file, buf, length, pos = offset)) != length)
	{
	int Errno = errno;
	pos = offset + (iocount > 0 ? iocount : 0);

	fprintf (stderr,
		 "offset %" Quad "d, filesize %" Quad "d\n",
		 offset,
		 filesize);
	if (iocount < 0)				    /* error */
	  fprintf (stderr,
		   "Child %d Bad read at %" Quad "d: %s (%d), iocount %d\n",
		   proc,
		   pos,
		   strerror (Errno),
		   Errno,
		   iocount );
	else
	  fprintf (stderr,
		   "Child %d Bad read at %" Quad "d: %d of %d bytes\n",
		   proc,
		   pos,
		   iocount,
		   length );
	exit (1);
	}
      else
	{
	childinfo [proc].reads++;
	childinfo [proc].bytes_read += iocount;
	}
      }
    break;

  case 1:						    /* Sequential Read */
    maxrecs = mycount;
    length = maxchunk;					    /* length of this transfer */
    if (length * maxrecs > (filesize - SKIPSTART))	    /* too much, */
       maxrecs  = (filesize - SKIPSTART) / length;	    /* reduce */
    if (fixedoffset)
      offset = SKIPSTART;				    /* start at the beginning */
    else						    /* random start */
      offset = (((((off_t) myrandom (proc + nproc)) * sectorsize)
		 % (filesize - SKIPSTART		    /* decide where to start */
		    - (maxrecs * length))) & ~((off_t)sectorsize - 1)) + SKIPSTART;
    if ((offset + maxrecs * length) > filesize)		    /* XXX */
      {
      printf ("Overrun: offset %" Quad "d, end %" Quad "d, file size %" Quad "d\n",
	      offset,
	      offset + maxrecs * length,
	      filesize);
      exit (1);
      }
    if ((verbose > 2) && ! fixedoffset)
      printf ("Child %d reading from %" Quad "d\n", proc, offset);
    for (i = 1; i <= maxrecs; i++)
      {
      if ((iocount = pread (file, buf, length, offset)) != length)
	{
	int Errno = errno;
	off_t pos = offset + (iocount > 0 ? iocount : 0);

	fprintf (stderr,
		 "offset %" Quad "d, filesize %" Quad "d\n",
		 offset,
		 filesize);
	if (iocount < 0)				    /* error */
	  fprintf (stderr,
		   "Child %d Bad read at %" Quad "d: %s (%d)\n",
		   proc,
		   pos,
		   strerror (Errno),
		   Errno );
	else
	  fprintf (stderr,
		   "Child %d Bad read at %" Quad "d: %d of %d bytes\n",
		   proc,
		   pos,
		   iocount,
		   length );
	exit (1);
	}
      else
	{
	childinfo [proc].reads++;
	childinfo [proc].bytes_read += iocount;
	offset += iocount;
	}
      }
    break;

  case 2:						    /* Random Write */
    for (i = 1; i <= mycount; i++)
      {
      if (fixedlength)
	length = maxchunk;
      else
	length = (myrandom (proc + nproc * i * 2)
		  % (maxchunk * 2) + sectorsize) & ~(sectorsize - 1);	    /* length of this transfer */
      offset = (((((off_t) myrandom2 (proc + nproc * i)) * sectorsize)
		 % (filesize - SKIPSTART - length)) & ~((off_t)sectorsize - 1)) + SKIPSTART;
      if (1)
	{
	if ((RWfrac == 100)
	    || ((myrandom (proc + nproc * i * 2 + 1) % 100) < RWfrac) )
	  {
	  iocount = pwrite (file, buf, length, offset);
	  childinfo [proc].writes++;
	  childinfo [proc].bytes_written += iocount;
	  }
	else
	  {
	  iocount = pread (file, buf, length, offset);
	  childinfo [proc].reads++;
	  childinfo [proc].bytes_read += iocount;
	  }
	if (iocount != length)
	  {
	  int Errno = errno;
	  off_t pos = offset + (iocount > 0 ? iocount : 0);

	  fprintf (stderr,
		   "offset %" Quad "d, filesize %" Quad "d\n", offset,
		   filesize);
	  if (iocount < 0)				    /* error */
	    fprintf (stderr,
		     "Child %d Bad write at %" Quad "d: %s (%d)\n",
		     proc,
		     pos,
		     strerror (Errno),
		     Errno );
	  else
	    fprintf (stderr,
		     "Child %d Bad write at %" Quad "d: %d of %d bytes\n",
		     proc,
		     pos,
		     iocount,
		     length );
	  exit (1);
	  }
	}
      }
    break;

  case 3:						    /* Sequential Write */
    maxrecs = mycount;
    length = maxchunk;					    /* length of this transfer */
    if (length * maxrecs > (filesize - SKIPSTART))	    /* too much, */
       maxrecs  = (filesize - SKIPSTART) / length;	    /* reduce */
    if (fixedoffset)
      offset = SKIPSTART;				    /* start at the beginning */
    else						    /* random start */
      offset = (((((off_t) myrandom (proc + nproc)) * sectorsize)
		 % (filesize - SKIPSTART		    /* decide where to start */
		    - (maxrecs * length))) & ~((off_t)sectorsize - 1)) + SKIPSTART;
    if ((verbose > 2) && ! fixedoffset)
      printf ("Child %d writing to %" Quad "d\n", proc, offset);
    for (i = 1; i <= maxrecs; i++)
      {
      if ((SWfrac == 100)
	|| ((myrandom (proc + nproc * i * 2 + 1) % 100) < SWfrac) )
	{
	iocount = pwrite (file, buf, length, offset);
	childinfo [proc].writes++;
	childinfo [proc].bytes_written += iocount;
	}
      else
	{
	iocount = pread (file, buf, length, offset);
	childinfo [proc].reads++;
	childinfo [proc].bytes_read += iocount;
	}
      if (iocount != length)
	{
	int Errno = errno;
	off_t pos = offset + (iocount > 0 ? iocount : 0);

	fprintf (stderr,
		 "offset %" Quad "d, filesize %" Quad "d\n",
		 offset,
		 filesize);
	if (iocount < 0)				    /* error */
	  fprintf (stderr,
		   "Child %d Bad write at %" Quad "d: %s (%d)\n",
		   proc,
		   pos,
		   strerror (Errno),
		   Errno );
	else
	  fprintf (stderr,
		   "Child %d Bad write at %" Quad "d: %d of %d bytes\n",
		   proc,
		   pos,
		   iocount,
		   length );
	exit (1);
	}
      else
	{
	offset += iocount;
	}
      }
    break;
    }
  if (verbose > 3)					    /* really just debug */
    fprintf (stderr,
	     "Child %d: %8d %8lld %8d %8" Quad "d\n",
	     proc,
	     childinfo [proc].reads,
	     childinfo [proc].bytes_read,
	     childinfo [proc].writes,
	     childinfo [proc].bytes_written );
  exit (0);
  }


syntax highlighted by Code2HTML, v. 0.9.1