#include "defs.h"

#define	COUNT	1000
#define	FCOUNT	1

typedef struct ForkMap {
    int id;
    int type;
    int count;
    struct timeval tstart;
    struct timeval tend;
} ForkMap;

void DoIt(int Action, int count, ForkMap *smap);

ForkMap *StatsMap;
int AddForks = 0;
int AddCount = COUNT;
int LookupForks = 0;
int LookupCount = COUNT;
char StatsPath[PATH_MAX];

void
Usage(void)
{
    fprintf(stderr, "A simple history performance tester\n\n");
    fprintf(stderr, "Usage: dhisbench [-ac n] [-af n] [-F] [-f historyfile] [-l]\n");
    fprintf(stderr, "                 [-lc n] [-lf n] [-m map_file]\n");
    fprintf(stderr, "  where:\n");
    fprintf(stderr, "\t-ac\tspecify number if history additions (default:%d)\n",
							AddCount);
    fprintf(stderr, "\t-af\tspecify number if addition forks (default: %d)\n",
							AddForks);
    fprintf(stderr, "\t-F\tfast mode - no locking or duplicate checking for add\n");
    fprintf(stderr, "\t-Fl\tdon't perform locking on history adds\n");
    fprintf(stderr, "\t-Fs\tdon't check for duplicates on history adds\n");
    fprintf(stderr, "\t-f FILE\tuse FILE has the history file (default: %s)\n",
					PatDbExpand(DHistoryPat));
    fprintf(stderr, "\t-h n\tspecify history hash size\n");
    fprintf(stderr, "\t-lc\tspecify number of history lookups (default: %d)\n",
							LookupCount);
    fprintf(stderr, "\t-lf\tspecify number of lookup forks (default: %d)\n",
							LookupForks);
    fprintf(stderr, "\t-m FILE\tuse FILE for the temp stats mmap (default: %s)\n",
					StatsPath);
    fprintf(stderr, "\nWARNING: This program writes garbage entries to the history file\n\n");
    exit(1);
}

const char *HistoryFile = NULL;
int HOFlags = 0;

int
main(int ac, char **av)
{
    int i;
    int n;
    int rpid;
    int mapfd;
    int mapindex = 0;
    struct timeval tstart;
    struct timeval tend;
    double elapsed;
    int LookupTotal = 0;
    double LookupTime = 0.0;
    int AddTotal = 0;
    double AddTime = 0.0;

    LoadDiabloConfig(ac, av);
 
    strncpy(StatsPath, PatRunExpand("%s/dhisbench.map"), sizeof(StatsPath) - 1);
    StatsPath[sizeof(StatsPath) - 1] = '\0';

    for (i = 1; i < ac; ++i) {
	char *ptr = av[i];

	if (*ptr == '-') {
	    ptr += 2;
	    switch(ptr[-1]) {
	    case 'a':
		if (*ptr == 'f') {
		    ptr++;
		    AddForks = strtol(((*ptr) ? ptr : av[++i]), NULL, 0);
		} else if (*ptr == 'c') {
		    ptr++;
		    AddCount = strtol(((*ptr) ? ptr : av[++i]), NULL, 0);
		}
		break;
	    case 'C':
		if (*ptr == 0)
		    ++i;
		break;
	    case 'F':
		if (*ptr == 'l')
		    HOFlags = HGF_FAST;
		else if (*ptr == 's')
		    HOFlags = HGF_NOSEARCH;
		else
		    HOFlags = HGF_FAST|HGF_NOSEARCH;
		break;
	    case 'f':
		HistoryFile = (*ptr) ? ptr : av[++i];
		break;
	    case 'h':
		n = bsizetol((*ptr) ? ptr : av[++i]);
		if (n < 256 * 1024 || n > 128 * 1024 * 1024) {
		    fprintf(stderr, "Illegal Hash size: %d\n", n);
		    exit(1);
		}
		if ((n ^ (n - 1)) != (n << 1) - 1) {
		    fprintf(stderr, "Hash size not a power of 2: %d\n", n); 
		    exit(1);
		}
		DOpts.HashSize = n;
	    case 'l':
		if (*ptr == 'f') {
		    ptr++;
		    LookupForks = strtol(((*ptr) ? ptr : av[++i]), NULL, 0);
		} else if (*ptr == 'c') {
		    ptr++;
		    LookupCount = strtol(((*ptr) ? ptr : av[++i]), NULL, 0);
		}
		break;
	    case 'm':
		if (!*ptr)
		    ptr = av[++i];
		if (ptr == NULL || !*ptr)
		    Usage();
		strncpy(StatsPath, ptr, sizeof(StatsPath) - 1);
		StatsPath[sizeof(StatsPath) - 1] = '\0';
		break;
	    default:
		Usage();
	    }
	}
    }

    if (AddForks == 0 && LookupForks == 0)
	Usage();
    if (AddForks == 0)
	HOFlags |= HGF_READONLY;

    if (HistoryFile == NULL)
	HistoryFile = PatDbExpand(DHistoryPat);

    printf("History File: %s\n", HistoryFile);
    printf("Flags       : ");
    if (HOFlags & HGF_READONLY)
	printf("RO ");
    if (HOFlags & HGF_FAST)
	printf("NOLOCK ");
    if (HOFlags & HGF_NOSEARCH)
	printf("NOSEARCH ");
    printf("\n");
    printf("Hash Size   : %d\n", DOpts.HashSize);

    /*
     * Create history if it doesn't exist
     */
    HistoryOpen(HistoryFile, HOFlags);
    HistoryClose();

    remove(StatsPath);
    mapfd = open(StatsPath, O_RDWR|O_CREAT|O_EXCL, 0600);
    if (mapfd == -1) {
	fprintf(stderr, "Cannot create %s: %s", StatsPath, strerror(errno));
	exit(1);
    }
    for (i = 0; i < AddForks + LookupForks; i++) {
	ForkMap tsmap = { 0 };
	write(mapfd, &tsmap, sizeof(tsmap));
    }
    StatsMap = xmap(NULL, sizeof(ForkMap) * (AddForks + LookupForks),
				PROT_READ|PROT_WRITE, MAP_SHARED, mapfd, 0);
    if (StatsMap == NULL) {
	perror("mmap");
	exit(1);
    }
    gettimeofday(&tstart, NULL);
    for (i = 0; i < AddForks; i++)
	if (fork() == 0) {
	    DoIt(2, AddCount, &StatsMap[mapindex]);
	} else {
	    mapindex++;
	}
    for (i = 0; i < LookupForks; i++)
	if (fork() == 0) {
	    DoIt(1, LookupCount, &StatsMap[mapindex]);
	} else {
	    mapindex++;
	}
    i = AddForks + LookupForks;
    while (i > 0)
	if (waitpid(-1, &rpid, 0) != -1)
	    i--;
    gettimeofday(&tend, NULL);

    for (i = 0; i < mapindex; i++) {
	ForkMap *smap = &StatsMap[i];
	elapsed = (smap->tend.tv_sec + smap->tend.tv_usec / 1000000.0) -
		(smap->tstart.tv_sec + smap->tstart.tv_usec / 1000000.0);
	if (smap->type == 1) {
	    printf("L ");
	    LookupTotal += smap->count;
	    if (elapsed > LookupTime)
		LookupTime = elapsed;
	} else {
	    printf("A ");
	    AddTotal += smap->count;
	    if (elapsed > AddTime)
		AddTime = elapsed;
	}
	printf("%10d %10.3f %10.0f\n", smap->count, elapsed, smap->count / elapsed);
    }
    elapsed = (tend.tv_sec + tend.tv_usec / 1000000.0) -
			(tstart.tv_sec + tstart.tv_usec / 1000000.0);
    printf("Lookups: %d\n", LookupTotal);
    printf("   Adds: %d\n", AddTotal);
    printf("Lookup Time: %.3f seconds\n", LookupTime);
    printf("   Add Time: %.3f seconds\n", AddTime);
    printf("Total  Time: %.3f seconds\n", elapsed);
    if (LookupTime == 0)
	LookupTime = 1;
    if (AddTime == 0)
	AddTime = 1;
    printf("%.0f lookups per second\n", LookupTotal / LookupTime);
    printf("%.0f adds per second\n", AddTotal / AddTime);
    close(mapfd);
    remove(StatsPath);
    exit(0);
}

void
DoIt(int Action, int count, ForkMap *smap)
{
    int i;
    char msgid[512];
    hash_t hv;
    History h = { 0 };

    HistoryOpen(HistoryFile, HOFlags);

    printf("pid=%d  action=%d count=%d\n", getpid(), Action, count);

    srandom(time(NULL) + getpid());

    smap->type = Action;
    gettimeofday(&smap->tstart, NULL);
    for (i = 0; i < count; i++) {
	sprintf(msgid,"<%d%08lx$%08lx@%08lx.%08lx.%08lx>", i,
			random(), random(), random(), random(), random());
	hv = hhash(msgid);
	if (Action == 1) {
	    HistoryLookupByHash(hv, NULL);
	} else if (Action == 2) {
	    h.hv = hv;
	    h.gmt = 1;
	    HistoryAdd((char *)msgid, &h);
	}
	smap->count++;
    }
    gettimeofday(&smap->tend, NULL);
    exit(0);
}



syntax highlighted by Code2HTML, v. 0.9.1