#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/gmon.h>
extern char *optarg;
extern int optind;
#ifndef O_DIRECT
#define O_DIRECT 0x00080000
#endif
int
main(int argc, char **argv)
{
int c, i, fd, ifd, ofd;
int mode, count, nrep, reps, seek, dofsync, gprof, rv;
char *file, *ifile, *ofile, *buf, *cp;
size_t size;
struct timeval tv1, tv2;
int mib[3];
int gprof_state, sz;
int value;
int direct, trunc, touch, openflags, pgsz;
size_t actual;
pgsz = getpagesize();
sz = sizeof(gprof_state);
file = NULL;
ifile = NULL;
ifd = 0;
ofile = NULL;
ofd = 0;
mode = 0;
count = 300;
size = 1024 * 1024;
seek = 0;
nrep = 1;
dofsync = 0;
direct = 0;
gprof = 0;
value = 'x';
trunc = 0;
touch = 0;
actual = 0;
while ((c = getopt(argc, argv, "rwdc:s:n:k:flpv:tTo:i:")) != -1) {
switch (c) {
case 'r':
mode = 1;
break;
case 'w':
mode = 2;
break;
case 'd':
direct = 1;
break;
case 'c':
count = strtol(optarg, NULL, 0);
break;
case 'n':
nrep = strtol(optarg, NULL, 0);
break;
case 's':
size = strtol(optarg, NULL, 0);
break;
case 'k':
seek = strtol(optarg, NULL, 0);
break;
case 'f':
dofsync = 1;
break;
case 'l':
if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
err(1, "mlockall");
}
break;
case 'p':
gprof = 1;
break;
case 'v':
value = *optarg;
break;
case 't':
trunc = 1;
break;
case 'T':
touch = 1;
break;
case 'i':
ifile = optarg;
break;
case 'o':
ofile = optarg;
break;
default:
errx(1, "bogus option `%c'", c);
}
}
if (optind == argc) {
errx(1, "must specify a file");
}
file = argv[optind];
if (mode == 0) {
errx(1, "must use one of `-r' or `-w'");
}
openflags = (mode == 1) ? O_RDONLY : (O_WRONLY|O_CREAT);
if (trunc) {
openflags |= O_TRUNC;
}
if (direct) {
openflags |= O_DIRECT;
}
fd = open(file, openflags, 0666);
if (fd < 0) {
err(1, "open");
}
if (ifile) {
ifd = open(ifile, O_RDONLY);
if (ifd < 0) {
err(1, "open ifile");
}
}
if (ofile) {
ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (ofd < 0) {
err(1, "open ofile");
}
}
buf = malloc(size);
if (buf == NULL) {
err(1, "malloc");
}
memset(buf, value, size);
if (gprof) {
mib[0] = CTL_KERN;
mib[1] = KERN_PROF;
mib[2] = GPROF_STATE;
gprof_state = GMON_PROF_ON;
if (sysctl(mib, 3, NULL, NULL, &gprof_state, sz) < 0) {
err(1, "sysctl gprof on");
}
}
if (gettimeofday(&tv1, NULL) < 0) {
err(1, "gettimeofday 1");
}
reps = nrep;
while (reps--) {
if (mode == 1) {
for (i = 0; i < count; i++) {
rv = read(fd, buf, size);
if (rv < 0) {
err(1, "read");
}
if (rv == 0) {
fprintf(stderr, "read hit EOF\n");
break;
}
actual += rv;
if (ofile) {
(void) write(ofd, buf, rv);
}
if (touch) {
for (cp = buf; cp < buf + size;
cp += pgsz) {
*cp = 1;
}
}
if (seek && lseek(fd, seek, SEEK_CUR) < 0) {
err(1, "lseek");
}
}
} else {
for (i = 0; i < count; i++) {
if (ifile) {
rv = read(ifd, buf, size);
if (rv < 0) {
err(1, "read ifile");
}
}
rv = write(fd, buf, size);
if (rv < 0) {
err(1, "write");
}
if (touch) {
for (cp = buf; cp < buf + size;
cp += pgsz) {
*cp = 1;
}
}
actual += rv;
if (seek && lseek(fd, seek, SEEK_CUR) < 0) {
err(1, "lseek");
}
}
}
if (dofsync) {
rv = fsync(fd);
if (rv < 0) {
err(1, "fsync");
}
}
if (lseek(fd, 0, SEEK_SET) < 0) {
err(1, "lseek");
}
}
if (gettimeofday(&tv2, NULL) < 0) {
err(1, "gettimeofday 1");
}
if (gprof) {
gprof_state = GMON_PROF_OFF;
if (sysctl(mib, 3, NULL, NULL, &gprof_state, sz) < 0) {
err(1, "sysctl gprof off");
}
}
timersub(&tv2, &tv1, &tv2);
printf("%lld bytes transferred in %ld.%03ld secs (%lld bytes/sec)\n",
((uint64_t)actual), tv2.tv_sec, tv2.tv_usec / 1000,
(((uint64_t)actual * 1000000) /
((uint64_t)tv2.tv_sec * 1000000 + tv2.tv_usec)));
exit(0);
}