/*    
 *  nfstest -- benchmark Inversion over the network.
 *
 *	This test performs the following operations:
 *		Create a 25MByte file (1MByte writes)
 *		Read 1MByte sequentially
 *		  + in a single transfer
 *		  + in transfers of page-sized units
 *		Overwrite 1MByte sequentially
 *		  + in a single transfer
 *		  + in transfers of page-sized units
 *		Read 1MByte in page-sized units, randomly distributed
 *		  throughout the file, aligned at page boundaries
 *		Overwrite 1MByte in page-sized units, randomly distributed
 *		  throughout the file, aligned at page boundaries
 *		Read 1 byte from a random location in the file
 *		Overwrite 1 byte at a random location in the file
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/resource.h>

#ifndef sprite
#include <sys/signal.h>
#endif /* !sprite */

#include "tmp/c.h"
#include "tmp/oid.h"
#include "tmp/libpq-fe.h"
#include "tmp/libpq-fs.h"

#define Inversion 0
#define BIG	1
#define	SMALL	0

RcsId("$Header: /private/postgres/sample/RCS/nfstest.c,v 1.1 1992/04/20 05:04:11 mao Exp $");

extern char	*getenv();
extern char	*get_attr();

extern char     *PQhost;     /* machine on which the backend is running */
extern char     *PQport;     /* comm port with the postgres backend */

#define DEFHOST		"toe.CS.Berkeley.EDU"
#define FILEDB		"mao"
#define FILENAME	"mao_nfs"

char QryBuf[512];

main(argc,argv)
     int argc;
     char **argv;
{
    int fd;
    int stat;

    if (argc == 1)
	PQhost = DEFHOST;
    else if (argc == 2)
	PQhost = argv[1];
    else {
	fprintf(stderr, "usage: %s [host]\n", *argv);
	exit (1);
    }

    srandom(time(0L));

    PQsetdb(FILEDB);

    printf("rem 0 setup|"); fflush(stdout);
    stat = setup();

    if (!stat) {
	flushcache();
	printf("rem 1 big read|"); fflush(stdout);
	stat = readtest(BIG);
    }

    if (!stat) {
	flushcache();
	printf("rem 2 small read|"); fflush(stdout);
	stat = readtest(SMALL);
    }

    if (!stat) {
	flushcache();
	printf("rem 3 random read|"); fflush(stdout);
	stat = randread();
    }

    if (!stat) {
	flushcache();
	printf("rem 4 single-byte read|"); fflush(stdout);
	stat = readone();
    }

    if (!stat) {
	flushcache();
	printf("rem 5 big write|"); fflush(stdout);
	stat = writetest(BIG);
    }

    if (!stat) {
	flushcache();
	printf("rem 6 small write|"); fflush(stdout);
	stat = writetest(SMALL);
    }

    if (!stat) {
	flushcache();
	printf("rem 7 random write|"); fflush(stdout);
	stat = randwrite();
    }

    if (!stat) {
	flushcache();
	printf("rem 8 single-byte write"); fflush(stdout);
	stat = writeone();
    }

    PQfinish();
    exit (stat);
}

flushcache()
{
    char *res;

    sprintf(QryBuf, "retrieve (x = userfntest(200))");

    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "%s\nflushcache failed\n", ++res);
	return;
    }
}

/* MByte == 2**20; MYMBYTE == 8092 * 129 (as close as i can get) */
#define	MEGABYTE	1048576
#define MYMEGABYTE	1043868
#define TWENTYFIVE	10

int
setup(fd)
    int fd;
{
    int nbytes;
    int want, nwrite;
    char *res;
    char *buf;
    int *varlen;

    if ((buf = (char *) palloc(MYMEGABYTE + 4)) == (char *) NULL) {
	fprintf(stderr, "cannot palloc buf\n");
	return (1);
    }

    varlen = (int *) buf;

    myResetUsage();
    sprintf(QryBuf, "begin");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot begin xact\n");
	return (1);
    }

    fd = p_creat(FILENAME, INV_WRITE, Inversion);

    want = TWENTYFIVE * MEGABYTE;
    while (want > 0) {
	if (want < MYMEGABYTE)
	    *varlen = want + sizeof(int);
	else
	    *varlen = MYMEGABYTE + sizeof(int);
	if ((nbytes = p_write(fd, buf, *varlen)) < *varlen - 4) {
	    fprintf(stderr, "setup: write failed\n");
	    fflush(stderr);
	    return (1);
	}
	want -= nbytes;
    }

    pfree (buf);

    sprintf(QryBuf, "end");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot end xact\n");
	return (1);
    }
    myShowUsage();

    return (0);
}

int
writetest(big)
    int big;
{
    int fd;
    int i, j;
    int nbytes;
    int nwritten;
    int size;
    char *res;
    char *buf;
    int *varlen;

    if (big) {
	size = MEGABYTE;
    } else {
	size = 8092;
    }

    if ((buf = (char *) palloc(size + 4)) == (char *) NULL) {
	fprintf(stderr, "cannot palloc buf\n");
	return (1);
    }
    varlen = (int *) buf;

    sprintf(QryBuf, "begin");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot begin xact\n");
	return (1);
    }

    if ((fd = p_open(FILENAME, INV_WRITE)) < 0) {
	fprintf(stderr, "cannot open file\n");
	return (1);
    }

    if (p_lseek(fd, 0L, L_SET) < 0) {
	fprintf(stderr, "cannot lseek\n");
	return (1);
    }

    myResetUsage();

    nwritten = 0;

    while (nwritten < MEGABYTE) {
	if (size > MEGABYTE - nwritten)
	     size = MEGABYTE - nwritten;
	*varlen = size + 4;
	if ((nbytes = p_write(fd, buf, *varlen)) != *varlen - 4) {
	    fprintf(stderr, "expected %d, got %d\n", size, nbytes);
	    fflush(stderr);
	    return (1);
	}
	nwritten += nbytes;
    }

    myShowUsage();

    sprintf(QryBuf, "end");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot end xact\n");
	return (1);
    }

    pfree(buf);
    return (0);
}
int
readtest(big)
    int big;
{
    int fd;
    int i, j;
    int nbytes;
    int nread;
    int size;
    char *res;
    char *buf;
    int *varlen;

    if (big) {
	size = MEGABYTE;
    } else {
	size = 8092;
    }

    if ((buf = (char *) palloc(size + 4)) == (char *) NULL) {
	fprintf(stderr, "cannot palloc buf\n");
	return (1);
    }

    sprintf(QryBuf, "begin");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot begin xact\n");
	return (1);
    }

    if ((fd = p_open(FILENAME, INV_READ)) < 0) {
	fprintf(stderr, "cannot open file\n");
	return (1);
    }

    if (p_lseek(fd, 0L, L_SET) < 0) {
	fprintf(stderr, "cannot lseek\n");
	return (1);
    }

    myResetUsage();

    nread = 0;

    while (nread < MEGABYTE) {
	if (size > MEGABYTE - nread)
	    size = MEGABYTE - nread;
	if ((nbytes = p_read(fd, buf, size)) != size) {
	    fprintf(stderr, "expected %d, got %d\n", size, nbytes);
	    fflush(stderr);
	    return (1);
	}
	nread += nbytes;
    }

    myShowUsage();

    sprintf(QryBuf, "end");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot end xact\n");
	return (1);
    }

    pfree(buf);
    return (0);
}

int
randread()
{
    int fd;
    int i, j;
    int nbytes;
    int nread;
    char *res;
    char *buf;
    int *varlen;
    int size;
    int off;

    size = 8092;
    if ((buf = (char *) palloc(size + 4)) == (char *) NULL) {
	fprintf(stderr, "cannot palloc buf\n");
	return (1);
    }

    sprintf(QryBuf, "begin");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot begin xact\n");
	return (1);
    }

    if ((fd = p_open(FILENAME, INV_READ)) < 0) {
	fprintf(stderr, "cannot open file\n");
	return (1);
    }

    myResetUsage();

    nread = 0;

    while (nread < MEGABYTE) {

	off = ((random() % ((TWENTYFIVE - 1) * MEGABYTE)) / 8092) * 8092;
	if (p_lseek(fd, off, L_SET) < 0) {
	    fprintf(stderr, "cannot lseek\n");
	    return (1);
	}

	if (size > MEGABYTE - nread)
	    size = MEGABYTE - nread;
	if ((nbytes = p_read(fd, buf, size)) != size) {
	    fprintf(stderr, "expected %d, got %d\n", size, nbytes);
	    fflush(stderr);
	    return (1);
	}
	nread += nbytes;
    }

    myShowUsage();

    sprintf(QryBuf, "end");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot end xact\n");
	return (1);
    }

    pfree(buf);
    return (0);
}

int
randwrite()
{
    int fd;
    int i, j;
    int nbytes;
    int nwritten;
    char *res;
    char *buf;
    int *varlen;
    int size;
    int off;

    size = 8092;
    if ((buf = (char *) palloc(size + 4)) == (char *) NULL) {
	fprintf(stderr, "cannot palloc buf\n");
	return (1);
    }

    sprintf(QryBuf, "begin");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot begin xact\n");
	return (1);
    }

    if ((fd = p_open(FILENAME, INV_READ)) < 0) {
	fprintf(stderr, "cannot open file\n");
	return (1);
    }

    myResetUsage();

    nwritten = 0;

    while (nwritten < MEGABYTE) {

	off = ((random() % ((TWENTYFIVE - 1) * MEGABYTE)) / 8092) * 8092;
	if (p_lseek(fd, off, L_SET) < 0) {
	    fprintf(stderr, "cannot lseek\n");
	    return (1);
	}

	if (size > MEGABYTE - nwritten)
	    size = MEGABYTE - nwritten;
	*varlen = size + sizeof(int);
	if ((nbytes = p_write(fd, buf, *varlen)) != *varlen - 4) {
	    fprintf(stderr, "expected %d, got %d\n", size, nbytes);
	    fflush(stderr);
	    return (1);
	}
	nwritten += nbytes;
    }

    myShowUsage();

    sprintf(QryBuf, "end");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot end xact\n");
	return (1);
    }

    pfree(buf);
    return (0);
}

int
readone()
{
    int fd;
    int i, j;
    int nbytes;
    int nwritten;
    char *res;
    char *buf;
    int *varlen;
    int size;
    int off;

    size = 8092;
    if ((buf = (char *) palloc(size + 4)) == (char *) NULL) {
	fprintf(stderr, "cannot palloc buf\n");
	return (1);
    }

    sprintf(QryBuf, "begin");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot begin xact\n");
	return (1);
    }

    if ((fd = p_open(FILENAME, INV_READ)) < 0) {
	fprintf(stderr, "cannot open file\n");
	return (1);
    }

    myResetUsage();

    off = random() % ((TWENTYFIVE - 1) * MEGABYTE);
    if (p_lseek(fd, off, L_SET) < 0) {
	fprintf(stderr, "cannot lseek\n");
	return (1);
    }

    if ((nbytes = p_read(fd, buf, 1)) != 1) {
	fprintf(stderr, "expected %d, got %d\n", 1, nbytes);
	fflush(stderr);
	return (1);
    }

    myShowUsage();

    sprintf(QryBuf, "end");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot end xact\n");
	return (1);
    }

    pfree(buf);
    return (0);
}

int
writeone()
{
    int fd;
    int i, j;
    int nbytes;
    int nwritten;
    char *res;
    char *buf;
    int *varlen;
    int size;
    int off;

    size = 8092;
    if ((buf = (char *) palloc(size + 4)) == (char *) NULL) {
	fprintf(stderr, "cannot palloc buf\n");
	return (1);
    }
    varlen = (int *) buf;
    *varlen = 5;

    sprintf(QryBuf, "begin");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot begin xact\n");
	return (1);
    }

    if ((fd = p_open(FILENAME, INV_READ)) < 0) {
	fprintf(stderr, "cannot open file\n");
	return (1);
    }

    myResetUsage();

    off = random() % ((TWENTYFIVE - 1) * MEGABYTE);
    if (p_lseek(fd, off, L_SET) < 0) {
	fprintf(stderr, "cannot lseek\n");
	return (1);
    }

    if ((nbytes = p_write(fd, buf, 5)) != 1) {
	fprintf(stderr, "expected %d, got %d\n", 1, nbytes);
	fflush(stderr);
	return (1);
    }

    myShowUsage();

    sprintf(QryBuf, "end");
    res = PQexec(QryBuf);

    if (*res == 'E') {
	fprintf(stderr, "cannot end xact\n");
	return (1);
    }

    pfree(buf);
    return (0);
}

struct rusage Save_r;
struct timeval Save_t;

myResetUsage()
{
    struct timezone tz;
    getrusage(RUSAGE_SELF, &Save_r);
    gettimeofday(&Save_t, &tz);
}

myShowUsage()
{
    struct rusage r;
    struct timeval user, sys;
    struct timeval elapse_t;
    struct timezone tz;

    getrusage(RUSAGE_SELF, &r);
    gettimeofday(&elapse_t, &tz);
    bcopy(&r.ru_utime, &user, sizeof(user));
    bcopy(&r.ru_stime, &sys, sizeof(sys));
    if (elapse_t.tv_usec < Save_t.tv_usec) {
	elapse_t.tv_sec--;
	elapse_t.tv_usec += 1000000;
    }
    if (r.ru_utime.tv_usec < Save_r.ru_utime.tv_usec) {
	r.ru_utime.tv_sec--;
	r.ru_utime.tv_usec += 1000000;
    }
    if (r.ru_stime.tv_usec < Save_r.ru_stime.tv_usec) {
	r.ru_stime.tv_sec--;
	r.ru_stime.tv_usec += 1000000;
    }

    /* print stats in the format grokked by jbstats.awk */
    printf(
	"!\t%ld.%06ld %ld.%06ld %ld.%06ld\n",
	elapse_t.tv_sec - Save_t.tv_sec,
	elapse_t.tv_usec - Save_t.tv_usec,
	r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec,
	r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec,
	r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec,
	r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec);
    fflush(stdout);
}
