/*
 * Copyright (c) 2004 Roger Seguin <roger_seguin@msn.com>
 *				http://rmlx.dyndns.org
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

static char     rcsid[] =
    "$Id: diskiobench.C,v 1.2 2004/08/15 01:53:31 RogerSeguin Exp $"
    "\nCopyright (c) 2004 Roger Seguin <roger_seguin@msn.com,"
    " http://rmlx.dyndns.org>";

/*
Description:
	Indicate how much time it takes for reading/writing disk data
	(disk I/O benchmark)
	Usage:	diskiobench <data size in MB> 
*/

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <memory.h>
#include <strings.h>		/* Solaris */
#include <stdio.h>
#include <sys/stat.h>
#include <sys/time.h>


#define TMPFILE	"./diskiobench.dat"


int64_t CurrentTime_ms ()
{
    struct timeval  oTime;
    int             status;

    status = gettimeofday (&oTime, 0);
    if (status == -1) {
	perror ("gettimeofday()");
	return (-1);
    }
    return (oTime.tv_sec * 1000 + oTime.tv_usec / 1000);
}


int Write (const size_t p_DataSize_MB, const char *p_FileName,
	   const size_t p_BlockDize_B)
{
    char            acBuffer[p_BlockDize_B];
    const int       nloops = p_DataSize_MB * 1024 * 1024 / p_BlockDize_B;
    int             iFile;
    int             i;
    int64_t         t1, t2;	// ms

    memset (acBuffer, 0, sizeof (acBuffer));

    t1 = CurrentTime_ms ();
    iFile = open (p_FileName, O_CREAT | O_RDWR, S_IRUSR + S_IWUSR);
    if (iFile == -1) {
	perror (p_FileName);
	return (-1);
    }
    for (i = 0; i < nloops; i++)
	write (iFile, acBuffer, sizeof (acBuffer));
    fsync (iFile);
    close (iFile);
    t2 = CurrentTime_ms ();

    return (static_cast < int >(t2 - t1));
}


int Read (const size_t p_DataSize_MB, const char *p_FileName,
	  const size_t p_BlockDize_B)
{
    char            acBuffer[p_BlockDize_B];
    const int       nloops = p_DataSize_MB * 1024 * 1024 / p_BlockDize_B;
    int             iFile;
    int             i;
    int64_t         t1, t2;	// ms

    t1 = CurrentTime_ms ();
    iFile = open (p_FileName, O_RDONLY);
    if (iFile == -1) {
	perror (p_FileName);
	return (-1);
    }
    for (i = 0; i < nloops; i++)
	read (iFile, acBuffer, sizeof (acBuffer));
    close (iFile);
    t2 = CurrentTime_ms ();

    return (static_cast < int >(t2 - t1));
}


int Usage (const char *const This, FILE * p_pFOut)
{
    FILE           *pFOut = p_pFOut;
    fprintf (stdout, "%s\n", rcsid + 5);
    fprintf (stdout, "Disk benchmarking tool\n");
    fprintf (pFOut, "Usage:\n");
    fprintf (pFOut, "\t%s [-b] [-f filename] <MB's to read/write>\n",
	     This);
    fprintf (pFOut, "\t%s -h\n", This);
    fprintf (pFOut,
	     "\t\t-b\tBlock:\tread/write by 512-byte blocks"
	     " instead of 1024's\n");
    fprintf (pFOut, "\t\t-f file\tFile:\t"
	     "temporary file used for disk benchmarking\n"
	     "\t\t\t\t(default=\"%s\")\n", TMPFILE);
    fprintf (pFOut, "\t\t-h\tHelp:\toutput this help and exit\n");
    return (0);
}


int main (int argc, char **argv)
{
    size_t          DataSize_MB = 0;	/* How much to read/write? */
    char            FileName[256] = TMPFILE;	/* where? */
    size_t          BlockSize_B = 1024;	/* by blocks of what size? */
    char            c;
    int             fError = 0, fUsage = 0;
    int             d;		// ms
    int64_t         t1, t2;	// ms
    int             status;

    /* Parse command line options */
    putenv ("POSIXLY_CORRECT=1");
    for (; !fUsage && !fError && (c = getopt (argc, argv, "bf:h")) != EOF;)
	switch (c) {
	    case 'b':		/* 512-Byte blocks */
		BlockSize_B = 512;
		break;
	    case 'f':		/* Name of temporary file to read/write */
		strcpy (FileName, optarg);
		break;
	    case 'h':		/* Help */
		fUsage = 1;
		break;
	    case '?':
	    default:
		fError = 1;
	}
    if (!fUsage) {
	fError |= (argc != (optind + 1));
	if (!fError) {
	    DataSize_MB = atoi (argv[optind]);
	    fError = (DataSize_MB <= 0);
	}
    }
    fUsage |= fError;

    /* Display usage message whether requested or error detected */
    if (fUsage) {
	Usage (argv[0], fError ? stderr : stdout);
	return (fError ? ~0 : 0);
    }

    printf ("Using %s\n", FileName);

    unlink (FileName);

    d = Write (DataSize_MB, FileName, BlockSize_B);
    if (d == -1)
	return (-1);
    printf ("Wrote: %d MB by %d-byte blocks in %d ms (%d MB/s)\n",
	    DataSize_MB, BlockSize_B, d, (1000 * DataSize_MB / d));

    d = Read (DataSize_MB, FileName, BlockSize_B);
    if (d == -1)
	return (-1);
    printf ("Read:  %d MB by %d-byte blocks in %d ms (%d MB/s)\n",
	    DataSize_MB, BlockSize_B, d, (1000 * DataSize_MB / d));

    d = Write (DataSize_MB, FileName, BlockSize_B);
    if (d == -1)
	return (-1);
    printf ("Rewrote: %d MB by %d-byte blocks in %d ms (%d MB/s)\n",
	    DataSize_MB, BlockSize_B, d, (1000 * DataSize_MB / d));

    t1 = CurrentTime_ms ();
    status = unlink (FileName);
    if (status == -1)
	perror ("unlink()");
    t2 = CurrentTime_ms ();
    d = (int) (t2 - t1);
    printf ("Deleted: %d MB in %d ms\n", DataSize_MB, d);

    return (0);
}


/* $Log: diskiobench.C,v $
 * Revision 1.2  2004/08/15 01:53:31  RogerSeguin
 *
 * Revision 1.1  2004/07/26 12:45:16  RogerSeguin
 * Initial revision
 * */
