/* * lmdd, lm@sun.com (Larry McVoy) (now lm@slovax.engr.sgi.com) * * Uncopyrighted, do whatever you want with it. * * defaults: * bs=8k * count=forever * if=internal * of=internal * ipat=0 * opat=0 * mismatch=0 * rusage=0 * flush=0 * shorthands: * recognizes 'k' or 'm' at the end of a number for 1024 & 1024^2 * recognizes "internal" as an internal /dev/zero /dev/null file. */ /* * Compile time configs */ #include #include #include #include #include #include #ifdef linux #include #endif #ifdef USCSI_LIBRARY #include "uscsi.h" #define RWFUNC scsi_rw #define LSEEK scsi_lseek #else /* USCSI_LIBRARY */ #define LSEEK lseek #define RWFUNC(fd, buf, amt, rw) \ rw? read(fd, buf, amt) : write(fd, buf, amt) #endif /* USCSI_LIBRARY */ #define USECS_PER_SEC 1000000.0 #define USECS_PER_TICK 1000 #define uchar unsigned char int out, Fsync, Sync, Flush, ru, c;/* done needs it */ char *cmds[] = { "if", /* input file */ "of", /* output file */ "ipat", /* check input for pattern */ "opat", /* generate pattern on output */ "mismatch", /* stop at first mismatch */ "bs", /* block size */ "count", /* number of blocks */ "skip", /* skip this number of blocks on input */ "fsync", /* fsync output before exit */ "sync", /* sync output before exit */ 0, }; static void start(void); static int chkarg(char *); static void done(int); static void rusage(void); static int getarg(char *, int, char **); static int getfile(char *, int, char **); static int warning(char *); static void error(char *); static void ptime(int); static double timespent(void); static void ptransfer(int, struct timeval *, struct timeval *); static void tvsub(struct timeval *, struct timeval *, struct timeval *); int main(int ac, char **av) { uint *buf; int misses, mismatch, outpat, inpat, bs, in, gotcnt, count; int skip; int i; for (i = 1; i < ac; ++i) { chkarg(av[i]); } signal(SIGINT, done); misses = mismatch = getarg("mismatch=", ac, av); inpat = getarg("ipat=", ac, av); outpat = getarg("opat=", ac, av); bs = getarg("bs=", ac, av); if (bs < 0) bs = 8192; Fsync = getarg("fsync=", ac, av); Sync = getarg("sync=", ac, av); count = getarg("count=", ac, av); if (count < 0) gotcnt = 0; else gotcnt = 1; c = 0; skip = getarg("skip=", ac, av); if (inpat != -1 || outpat != -1 && (bs & 3)) { (void) printf("Block size must be word aligned\n"); return (1); } if (!(buf = (uint *) valloc((unsigned) bs))) { perror("valloc"); return (1); } bzero((char *) buf, bs); start(); /* * We want this here because otherwise the accounting gets screwed up */ in = getfile("if=", ac, av); out = getfile("of=", ac, av); if (skip > 0) LSEEK(in, skip * bs, 0); for (;;) { register x; if (gotcnt && count-- <= 0) done(0); if (in >= 0) x = RWFUNC(in, buf, bs, 1); else x = bs; if (x <= 0) done(0); if (inpat != -1) { register foo, cnt; for (foo = 0, cnt = x >> 2; cnt--; foo++) { if (buf[foo] != (uint) (c + foo)) { printf("ERR: off=%d want=%x got=%x\n", c + foo, c + foo, buf[foo]); if (mismatch != -1 && --misses == 0) done(0); } } } if (outpat != -1) { register foo, cnt; for (foo = 0, cnt = x >> 2; cnt--; foo++) buf[foo] = c + foo; } if (out >= 0) if (RWFUNC(out, buf, x, 0) != x) done(0); c += x >> 2; } } int chkarg(char *arg) { int i; char *a, *b; for (i = 0; cmds[i]; ++i) { for (a = arg, b = cmds[i]; *a && *b && *a == *b; a++, b++) ; if (*a == '=') return (0); } (void) printf("Bad arg: %s\n", arg); return (1); } void done(int ignored) { close(1); open("/dev/tty", 1); if (Sync > 0) sync(); if (Fsync > 0) fsync(out); stop(); ptime(c << 2); exit(0); } #define secs(tv) (tv.tv_sec + tv.tv_usec / USECS_PER_SEC) #define mine(f) (r.f - ru_start.f) static int getarg(char *s, int ac, char **av) { register len, i; len = strlen(s); for (i = 1; i < ac; ++i) if (!strncmp(av[i], s, len)) { register bs = atoi(&av[i][len]); if (rindex(&av[i][len], 'k')) bs *= 1024; else if (rindex(&av[i][len], 'm')) bs *= (1024 * 1024); return (bs); } return (-1); } static int getfile(char *s, int ac, char **av) { register ret, len, i; len = strlen(s); for (i = 1; i < ac; ++i) { if (!strncmp(av[i], s, len)) { if (av[i][0] == 'o') { if (!strcmp("of=internal", av[i])) return (-2); ret = creat(&av[i][len], 0644); if (ret == -1) error(&av[i][len]); return (ret); } else { if (!strcmp("if=internal", av[i])) return (-2); ret = open(&av[i][len], 0); if (ret == -1) error(&av[i][len]); #ifdef linux if (ioctl(ret, BLKFLSBUF, 0) < 0) { perror("BLKFSLBUF"); return (-1); } #endif return (ret); } } } return (-2); } /* * utilities for timing */ static struct timeval t1, t2; static int warning(char *s) { perror(s); return (-1); } static void error(char *s) { perror(s); exit (1); /* NOTREACHED */ } static void start(void) { gettimeofday(&t1, (struct timezone *) 0); } stop(void) { gettimeofday(&t2, (struct timezone *) 0); } static void ptime(int bytes) { ptransfer(bytes, &t1, &t2); } static double timespent(void) { struct timeval td; tvsub(&td, &t2, &t1); return (td.tv_sec + td.tv_usec/USECS_PER_SEC); } static void ptransfer(int bytes, struct timeval *t0, struct timeval *t1) { struct timeval td; float s, bs; tvsub(&td, t1, t0); s = td.tv_sec + td.tv_usec/USECS_PER_SEC; #define nz(x) ((x) == 0 ? 1 : (x)) #define MB (1024*1024.0) bs = bytes / nz(s); (void) fprintf(stdout, "%.2f MB in %.2f seconds (%.4f MB/sec)\n", bytes / MB, s, bs / MB); } static void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0) { tdiff->tv_sec = t1->tv_sec - t0->tv_sec; if ( t0->tv_usec > t1->tv_usec ) { tdiff->tv_sec--; tdiff->tv_usec = (USECS_PER_SEC - t0->tv_usec) + t1->tv_usec; } else { tdiff->tv_usec = t1->tv_usec - t0->tv_usec; } }