int args[3];
};
-#define LOGSIZE 1000
+#define LOGSIZE 100000
struct log_entry oplog[LOGSIZE]; /* the log */
int logptr = 0; /* current position in log */
char *fname; /* name of our test file */
char logfile[1024]; /* name of our log file */
char goodfile[1024]; /* name of our test file */
-int fd; /* fd for our test file */
off_t file_size = 0;
off_t biggest = 0;
void
+__attribute__((format(printf, 1, 2)))
prt(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stdout, fmt, args);
- if (fsxlogf)
- vfprintf(fsxlogf, fmt, args);
va_end(args);
+
+ if (fsxlogf) {
+ va_start(args, fmt);
+ vfprintf(fsxlogf, fmt, args);
+ va_end(args);
+ }
}
void
prterr("save_buffer: lseek eof");
else if (bufferlength > size_by_seek) {
warn("save_buffer: .fsxgood file too short... will"
- "save 0x%llx bytes instead of 0x%llx\n",
+ "save 0x%llx bytes instead of 0x%llx\n",
(unsigned long long)size_by_seek,
(unsigned long long)bufferlength);
bufferlength = size_by_seek;
if (c != t) {
if (n == 0) {
bad = short_at(&temp_buf[i]);
- prt("0x%5x\t0x%04x\t0x%04x", offset,
+ prt("%#07x\t%#06x\t%#06x", offset,
short_at(&good_buf[offset]), bad);
op = temp_buf[offset & 1 ? i+1 : i];
}
size--;
}
if (n) {
- prt("\t0x%5x\n", n);
+ prt("\t%#7x\n", n);
if (bad)
prt("operation# (mod 256) for the bad data"
"may be %u\n", ((unsigned)op & 0xff));
}
}
+struct test_file {
+ char *path;
+ int fd;
+} *test_files = NULL;
+
+int num_test_files = 0;
+enum fd_iteration_policy {
+ FD_SINGLE,
+ FD_ROTATE,
+ FD_RANDOM,
+};
+int fd_policy = FD_RANDOM;
+int fd_last = 0;
+
+struct test_file *
+get_tf(void)
+{
+ unsigned index = 0;
+
+ switch (fd_policy) {
+ case FD_ROTATE:
+ index = fd_last++;
+ break;
+ case FD_RANDOM:
+ index = random();
+ break;
+ case FD_SINGLE:
+ index = 0;
+ break;
+ default:
+ prt("unknown policy");
+ exit(1);
+ break;
+ }
+ return &test_files[ index % num_test_files ];
+}
+
+void
+assign_fd_policy(char *policy)
+{
+ if (!strcmp(policy, "random"))
+ fd_policy = FD_RANDOM;
+ else if (!strcmp(policy, "rotate"))
+ fd_policy = FD_ROTATE;
+ else {
+ prt("unknown -I policy: '%s'\n", policy);
+ exit(1);
+ }
+}
+
+int
+get_fd(void)
+{
+ struct test_file *tf = get_tf();
+ return tf->fd;
+}
+
+static const char *my_basename(const char *path)
+{
+ char *c = strrchr(path, '/');
+
+ return c ? c++ : path;
+}
+
+void
+open_test_files(char **argv, int argc)
+{
+ struct test_file *tf;
+ int i;
+
+ num_test_files = argc;
+ if (num_test_files == 1)
+ fd_policy = FD_SINGLE;
+
+ test_files = calloc(num_test_files, sizeof(*test_files));
+ if (test_files == NULL) {
+ prterr("reallocating space for test files");
+ exit(1);
+ }
+
+ for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
+
+ tf->path = argv[i];
+ tf->fd = open(tf->path, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC),
+ 0666);
+ if (tf->fd < 0) {
+ prterr(tf->path);
+ exit(91);
+ }
+ }
+
+ if (quiet || fd_policy == FD_SINGLE)
+ return;
+
+ for (i = 0, tf = test_files; i < num_test_files; i++, tf++)
+ prt("fd %d: %s\n", i, tf->path);
+}
+
+void
+close_test_files(void)
+{
+ int i;
+ struct test_file *tf;
+
+ for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
+ if (close(tf->fd)) {
+ prterr("close");
+ report_failure(99);
+ }
+ }
+}
+
void
check_size(void)
{
struct stat statbuf;
off_t size_by_seek;
+ int fd = get_fd();
if (fstat(fd, &statbuf)) {
prterr("check_size: fstat");
check_trunc_hack(void)
{
struct stat statbuf;
+ int fd = get_fd();
ftruncate(fd, (off_t)0);
ftruncate(fd, (off_t)100000);
- fstat(fd, &statbuf);
+ if (fstat(fd, &statbuf)) {
+ prterr("trunc_hack: fstat");
+ statbuf.st_size = -1;
+ }
if (statbuf.st_size != (off_t)100000) {
prt("no extend on truncate! not posix!\n");
exit(130);
ftruncate(fd, 0);
}
+static char *tf_buf = NULL;
+static int max_tf_len = 0;
+
+void
+alloc_tf_buf(void)
+{
+ char dummy = '\0';
+ int highest = num_test_files - 1;
+ int len;
+
+ len = snprintf(&dummy, 0, "%u ", highest);
+ if (len < 1) {
+ prterr("finding max tf_buf");
+ exit(1);
+ }
+ len++;
+ tf_buf = malloc(len);
+ if (tf_buf == NULL) {
+ prterr("allocating tf_buf");
+ exit(1);
+ }
+ max_tf_len = snprintf(tf_buf, len, "%u ", highest);
+ if (max_tf_len < 1) {
+ prterr("fiding max_tv_len\n");
+ exit(1);
+ }
+ if (max_tf_len != len - 1) {
+ warn("snprintf() gave %d instead of %d?\n",
+ max_tf_len, len - 1);
+ exit(1);
+ }
+}
+
+char *
+fill_tf_buf(struct test_file *tf)
+{
+ if (tf_buf == NULL)
+ alloc_tf_buf();
+
+ sprintf(tf_buf,"%lu ", (unsigned long)(tf - test_files));
+ return tf_buf;
+}
+
+void
+output_line(struct test_file *tf, int op, unsigned offset,
+ unsigned size, struct timeval *tv)
+{
+ char *tf_num = "";
+
+ char *ops[] = {
+ [OP_READ] = "read",
+ [OP_WRITE] = "write",
+ [OP_TRUNCATE] = "trunc from",
+ [OP_MAPREAD] = "mapread",
+ [OP_MAPWRITE] = "mapwrite",
+ };
+
+ if (fd_policy != FD_SINGLE)
+ tf_num = fill_tf_buf(tf);
+
+ /* W. */
+ if (!(!quiet && ((progressinterval &&
+ testcalls % progressinterval == 0) ||
+ (debug &&
+ (monitorstart == -1 ||
+ (offset + size > monitorstart &&
+ (monitorend == -1 || offset <= monitorend)))))))
+ return;
+
+ prt("%06lu %lu.%06lu %.*s%-10s %#08x %s %#08x\t(0x%x bytes)\n",
+ testcalls, tv->tv_sec, tv->tv_usec, max_tf_len,
+ tf_num, ops[op],
+ offset, op == OP_TRUNCATE ? " to " : "thru",
+ offset + size - 1, size);
+}
void
doread(unsigned offset, unsigned size)
struct timeval t;
off_t ret;
unsigned iret;
+ struct test_file *tf = get_tf();
+ int fd = tf->fd;
offset -= offset % readbdy;
gettimeofday(&t, NULL);
if (testcalls <= simulatedopcount)
return;
- if (!quiet && ((progressinterval &&
- testcalls % progressinterval == 0) ||
- (debug &&
- (monitorstart == -1 ||
- (offset + size > monitorstart &&
- (monitorend == -1 || offset <= monitorend))))))
- prt("%06lu %lu.%06lu read %#08x thru %#08x\t(0x%x bytes)\n",
- testcalls, t.tv_sec, t.tv_usec, offset, offset + size - 1,
- size);
+ output_line(tf, OP_READ, offset, size, &t);
+
ret = lseek(fd, (off_t)offset, SEEK_SET);
if (ret == (off_t)-1) {
prterr("doread: lseek");
unsigned pg_offset;
unsigned map_size;
char *p;
+ struct test_file *tf = get_tf();
+ int fd = tf->fd;
offset -= offset % readbdy;
gettimeofday(&t, NULL);
if (testcalls <= simulatedopcount)
return;
- if (!quiet && ((progressinterval &&
- testcalls % progressinterval == 0) ||
- (debug &&
- (monitorstart == -1 ||
- (offset + size > monitorstart &&
- (monitorend == -1 || offset <= monitorend))))))
- prt("%06lu %lu.%06lu mapread %#08x thru %#08x\t(0x%x bytes)\n",
- testcalls, t.tv_sec, t.tv_usec, offset, offset + size - 1,
- size);
+ output_line(tf, OP_MAPREAD, offset, size, &t);
pg_offset = offset & page_mask;
map_size = pg_offset + size;
struct timeval t;
off_t ret;
unsigned iret;
+ struct test_file *tf = get_tf();
+ int fd = tf->fd;
offset -= offset % writebdy;
gettimeofday(&t, NULL);
if (testcalls <= simulatedopcount)
return;
- if (!quiet && ((progressinterval &&
- testcalls % progressinterval == 0) ||
- (debug &&
- (monitorstart == -1 ||
- (offset + size > monitorstart &&
- (monitorend == -1 || offset <= monitorend))))))
- prt("%06lu %lu.%06lu write %#08x thru %#08x\t(0x%x bytes)\n",
- testcalls, t.tv_sec, t.tv_usec, offset, offset + size - 1,
- size);
+ output_line(tf, OP_WRITE, offset, size, &t);
+
ret = lseek(fd, (off_t)offset, SEEK_SET);
if (ret == (off_t)-1) {
prterr("dowrite: lseek");
unsigned map_size;
off_t cur_filesize;
char *p;
+ struct test_file *tf = get_tf();
+ int fd = tf->fd;
offset -= offset % writebdy;
gettimeofday(&t, NULL);
if (testcalls <= simulatedopcount)
return;
- if (!quiet && ((progressinterval &&
- testcalls % progressinterval == 0) ||
- (debug &&
- (monitorstart == -1 ||
- (offset + size > monitorstart &&
- (monitorend == -1 || offset <= monitorend))))))
- prt("%06lu %lu.%06lu mapwrite %#08x thru %#08x\t(0x%x bytes)\n",
- testcalls, t.tv_sec, t.tv_usec, offset, offset + size - 1,
- size);
+ output_line(tf, OP_MAPWRITE, offset, size, &t);
if (file_size > cur_filesize) {
if (ftruncate(fd, file_size) == -1) {
{
struct timeval t;
int oldsize = file_size;
+ struct test_file *tf = get_tf();
+ int fd = tf->fd;
size -= size % truncbdy;
gettimeofday(&t, NULL);
if (testcalls <= simulatedopcount)
return;
- if ((progressinterval && testcalls % progressinterval == 0) ||
- (debug && (monitorstart == -1 || monitorend == -1 ||
- size <= monitorend)))
- prt("%06lu %lu.%06lu trunc from %#08x to %#08x\n",
- testcalls, t.tv_sec, t.tv_usec, oldsize, size);
+ output_line(tf, OP_TRUNCATE, oldsize, size, &t);
+
if (ftruncate(fd, (off_t)size) == -1) {
prt("ftruncate1: %x\n", size);
prterr("dotruncate: ftruncate");
writefileimage()
{
ssize_t iret;
+ int fd = get_fd();
if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
prterr("writefileimage: lseek");
if (iret == -1)
prterr("writefileimage: write");
else
- prt("short write: 0x%x bytes instead of 0x%llx\n",
- iret, (unsigned long long)file_size);
+ prt("short write: 0x%lx bytes instead of 0x%llx\n",
+ (unsigned long)iret,
+ (unsigned long long)file_size);
report_failure(172);
}
if (lite ? 0 : ftruncate(fd, file_size) == -1) {
docloseopen(void)
{
struct timeval t;
+ struct test_file *tf = get_tf();
if (testcalls <= simulatedopcount)
return;
if (debug)
prt("%06lu %lu.%06lu close/open\n", testcalls, t.tv_sec,
t.tv_usec);
- if (close(fd)) {
+ if (close(tf->fd)) {
prterr("docloseopen: close");
report_failure(180);
}
gettimeofday(&t, NULL);
prt(" %lu.%06lu close done\n", t.tv_sec, t.tv_usec);
}
- fd = open(fname, O_RDWR, 0);
- if (fd < 0) {
+ tf->fd = open(tf->path, O_RDWR, 0);
+ if (tf->fd < 0) {
prterr("docloseopen: open");
report_failure(181);
}
"fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m "
"start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t "
"truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] "
-"fname\n"
+"[ -I random|rotate ] fname [additional paths to fname..]\n"
" -b opnum: beginning operation number (default 1)\n"
" -c P: 1 in P chance of file close+open at each op (default infinity)\n"
" -d: debug output for all operations [-d -d = more debugging]\n"
" -S seed: for random # generator (default 1) 0 gets timestamp\n"
" -W: mapped write operations DISabled\n"
" -R: read() system calls only (mapped reads disabled)\n"
+" -I: When multiple paths to the file are given each operation uses"
+" a different path. Iterate through them in order with 'rotate'"
+" or chose then at 'random'. (defaults to random)\n"
" fname: this filename is REQUIRED (no default)\n");
exit(90);
}
return (ret);
}
-
-static const char *basename(const char *path)
-{
- char *c = strrchr(path, '/');
-
- return c ? c++ : path;
-}
-
int
main(int argc, char **argv)
{
setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
- while ((ch = getopt(argc, argv, "b:c:dl:m:no:p:qr:s:t:w:D:LN:OP:RS:W"))
+ while ((ch = getopt(argc, argv,
+ "b:c:dl:m:no:p:qr:s:t:w:D:I:LN:OP:RS:W"))
!= EOF)
switch (ch) {
case 'b':
if (debugstart < 1)
usage();
break;
+ case 'I':
+ assign_fd_policy(optarg);
+ break;
case 'L':
lite = 1;
break;
}
argc -= optind;
argv += optind;
- if (argc != 1)
+ if (argc < 1)
usage();
fname = argv[0];
initstate(seed, state, 256);
setstate(state);
- fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 0666);
- if (fd < 0) {
- prterr(fname);
- exit(91);
- }
- strncat(goodfile, dirpath ? basename(fname) : fname, 256);
+
+ open_test_files(argv, argc);
+
+ strncat(goodfile, dirpath ? my_basename(fname) : fname, 256);
strcat (goodfile, ".fsxgood");
fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
if (fsxgoodfd < 0) {
prterr(goodfile);
exit(92);
}
- strncat(logfile, dirpath ? basename(fname) : fname, 256);
+ strncat(logfile, dirpath ? my_basename(fname) : fname, 256);
strcat (logfile, ".fsxlog");
fsxlogf = fopen(logfile, "w");
if (fsxlogf == NULL) {
}
if (lite) {
off_t ret;
+ int fd = get_fd();
file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END);
if (file_size == (off_t)-1) {
prterr(fname);
memset(temp_buf, '\0', maxoplen);
if (lite) { /* zero entire existing file */
ssize_t written;
+ int fd = get_fd();
written = write(fd, good_buf, (size_t)maxfilelen);
if (written != maxfilelen) {
while (numops == -1 || numops--)
test();
- if (close(fd)) {
- prterr("close");
- report_failure(99);
- }
+ close_test_files();
prt("All operations completed A-OK!\n");
exit(0);