Whamcloud - gitweb
LU-1347 style: removes obsolete EXPORT_SYMTAB macros
[fs/lustre-release.git] / lustre / tests / fsx.c
1 /*
2  * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
6  * The contents of this file constitute Original Code as defined in and
7  * are subject to the Apple Public Source License Version 1.1 (the
8  * "License").  You may not use this file except in compliance with the
9  * License.  Please obtain a copy of the License at
10  * http://www.apple.com/publicsource and read it before using this file.
11  *
12  * This Original Code and all software distributed under the License are
13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17  * License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * @APPLE_LICENSE_HEADER_END@
21  *
22  *      File:   fsx.c
23  *      Author: Avadis Tevanian, Jr.
24  *
25  *      File system exerciser.
26  *
27  *      Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com
28  *
29  *      Various features from Joe Sokol, Pat Dirks, and Clark Warner.
30  *
31  *      Small changes to work under Linux -- davej@suse.de
32  *
33  *      Sundry porting patches from Guy Harris 12/2001
34  * $FreeBSD: src/tools/regression/fsx/fsx.c,v 1.1 2001/12/20 04:15:57 jkh Exp $
35  */
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #if defined(_UWIN) || defined(__linux__)
40 # include <sys/param.h>
41 # include <limits.h>
42 # include <time.h>
43 # include <strings.h>
44 # include <sys/time.h>
45 #endif
46 #include <fcntl.h>
47 #include <sys/mman.h>
48 #ifndef MAP_FILE
49 # define MAP_FILE 0
50 #endif
51 #include <limits.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <stdarg.h>
58 #include <errno.h>
59
60 #define NUMPRINTCOLUMNS 32      /* # columns of data to print on each line */
61
62 /*
63  *      A log entry is an operation and a bunch of arguments.
64  */
65
66 struct log_entry {
67         int     operation;
68         struct timeval tv;
69         int     args[3];
70 };
71
72 #define LOGSIZE 100000
73
74 struct log_entry        oplog[LOGSIZE]; /* the log */
75 int                     logptr = 0;     /* current position in log */
76 int                     logcount = 0;   /* total ops */
77
78 /*
79  *      Define operations
80  */
81
82 #define OP_READ         1
83 #define OP_WRITE        2
84 #define OP_TRUNCATE     3
85 #define OP_CLOSEOPEN    4
86 #define OP_MAPREAD      5
87 #define OP_MAPWRITE     6
88 #define OP_SKIPPED      7
89
90 int page_size;
91 int page_mask;
92
93 char    *original_buf;                  /* a pointer to the original data */
94 char    *good_buf;                      /* a pointer to the correct data */
95 char    *temp_buf;                      /* a pointer to the current data */
96 char    *fname;                         /* name of our test file */
97 char    logfile[1024];                  /* name of our log file */
98 char    goodfile[1024];                 /* name of our test file */
99
100 off_t           file_size = 0;
101 off_t           biggest = 0;
102 char            state[256];
103 unsigned long   testcalls = 0;          /* calls to function "test" */
104
105 unsigned long   simulatedopcount = 0;   /* -b flag */
106 int     closeprob = 0;                  /* -c flag */
107 int     debug = 0;                      /* -d flag */
108 unsigned long   debugstart = 0;         /* -D flag */
109 unsigned long   maxfilelen = 256 * 1024;        /* -l flag */
110 int     sizechecks = 1;                 /* -n flag disables them */
111 int     maxoplen = 64 * 1024;           /* -o flag */
112 int     quiet = 0;                      /* -q flag */
113 unsigned long progressinterval = 0;     /* -p flag */
114 int     readbdy = 1;                    /* -r flag */
115 int     style = 0;                      /* -s flag */
116 int     truncbdy = 1;                   /* -t flag */
117 int     writebdy = 1;                   /* -w flag */
118 long    monitorstart = -1;              /* -m flag */
119 long    monitorend = -1;                /* -m flag */
120 int     lite = 0;                       /* -L flag */
121 long    numops = -1;                    /* -N flag */
122 int     randomoplen = 1;                /* -O flag disables it */
123 int     seed = 1;                       /* -S flag */
124 int     mapped_writes = 1;              /* -W flag disables */
125 int     mapped_reads = 1;               /* -R flag disables it */
126 int     fsxgoodfd = 0;
127 FILE *  fsxlogf = NULL;
128 int badoff = -1;
129
130
131 void
132 vwarnc(code, fmt, ap)
133         int code;
134         const char *fmt;
135         va_list ap;
136 {
137         fprintf(stderr, "fsx: ");
138         if (fmt != NULL) {
139                 vfprintf(stderr, fmt, ap);
140                 fprintf(stderr, ": ");
141         }
142         fprintf(stderr, "%s\n", strerror(code));
143 }
144
145
146 void
147 warn(const char * fmt, ...)
148 {
149         va_list ap;
150         va_start(ap, fmt);
151         vwarnc(errno, fmt, ap);
152         va_end(ap);
153 }
154
155
156 void
157 __attribute__((format(printf, 1, 2)))
158 prt(char *fmt, ...)
159 {
160         va_list args;
161
162         va_start(args, fmt);
163         vfprintf(stdout, fmt, args);
164         va_end(args);
165
166         if (fsxlogf) {
167                 va_start(args, fmt);
168                 vfprintf(fsxlogf, fmt, args);
169                 va_end(args);
170         }
171 }
172
173 void
174 prterr(char *prefix)
175 {
176         prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
177 }
178
179
180 void
181 log4(int operation, int arg0, int arg1, int arg2, struct timeval *tv)
182 {
183         struct log_entry *le;
184
185         le = &oplog[logptr];
186         le->tv = *tv;
187         le->operation = operation;
188         le->args[0] = arg0;
189         le->args[1] = arg1;
190         le->args[2] = arg2;
191         logptr++;
192         logcount++;
193         if (logptr >= LOGSIZE)
194                 logptr = 0;
195 }
196
197
198 void
199 logdump(void)
200 {
201         int     i, count, down;
202         struct log_entry        *lp;
203
204         prt("LOG DUMP (%d total operations):\n", logcount);
205         if (logcount < LOGSIZE) {
206                 i = 0;
207                 count = logcount;
208         } else {
209                 i = logptr;
210                 count = LOGSIZE;
211         }
212         for ( ; count > 0; count--) {
213                 int opnum;
214
215                 opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE;
216                 lp = &oplog[i];
217                 prt("%d: %lu.%06lu ", opnum,
218                     lp->tv.tv_sec, lp->tv.tv_usec);
219
220                 switch (lp->operation) {
221                 case OP_MAPREAD:
222                         prt("MAPREAD  0x%x thru 0x%x (0x%x bytes)",
223                             lp->args[0], lp->args[0] + lp->args[1] - 1,
224                             lp->args[1]);
225                         if (badoff >= lp->args[0] && badoff <
226                                                      lp->args[0] + lp->args[1])
227                                 prt("\t***RRRR***");
228                         break;
229                 case OP_MAPWRITE:
230                         prt("MAPWRITE 0x%x thru 0x%x (0x%x bytes)",
231                             lp->args[0], lp->args[0] + lp->args[1] - 1,
232                             lp->args[1]);
233                         if (badoff >= lp->args[0] && badoff <
234                                                      lp->args[0] + lp->args[1])
235                                 prt("\t******WWWW");
236                         break;
237                 case OP_READ:
238                         prt("READ     0x%x thru 0x%x (0x%x bytes)",
239                             lp->args[0], lp->args[0] + lp->args[1] - 1,
240                             lp->args[1]);
241                         if (badoff >= lp->args[0] &&
242                             badoff < lp->args[0] + lp->args[1])
243                                 prt("\t***RRRR***");
244                         break;
245                 case OP_WRITE:
246                         prt("WRITE    0x%x thru 0x%x (0x%x bytes)",
247                             lp->args[0], lp->args[0] + lp->args[1] - 1,
248                             lp->args[1]);
249                         if (lp->args[0] > lp->args[2])
250                                 prt(" HOLE");
251                         else if (lp->args[0] + lp->args[1] > lp->args[2])
252                                 prt(" EXTEND");
253                         if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
254                             badoff < lp->args[0] + lp->args[1])
255                                 prt("\t***WWWW");
256                         break;
257                 case OP_TRUNCATE:
258                         down = lp->args[0] < lp->args[1];
259                         prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
260                             down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
261                         if (badoff >= lp->args[!down] &&
262                             badoff < lp->args[!!down])
263                                 prt("\t******WWWW");
264                         break;
265                 case OP_CLOSEOPEN:
266                         prt("CLOSE/OPEN");
267                         break;
268                 case OP_SKIPPED:
269                         prt("SKIPPED (no operation)");
270                         break;
271                 default:
272                         prt("BOGUS LOG ENTRY (operation code = %d)!",
273                             lp->operation);
274                 }
275                 prt("\n");
276                 i++;
277                 if (i == LOGSIZE)
278                         i = 0;
279         }
280 }
281
282
283 void
284 save_buffer(char *buffer, off_t bufferlength, int fd)
285 {
286         off_t ret;
287         ssize_t byteswritten;
288
289         if (fd <= 0 || bufferlength == 0)
290                 return;
291
292         if (bufferlength > SSIZE_MAX) {
293                 prt("fsx flaw: overflow in save_buffer\n");
294                 exit(67);
295         }
296         if (lite) {
297                 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
298                 if (size_by_seek == (off_t)-1)
299                         prterr("save_buffer: lseek eof");
300                 else if (bufferlength > size_by_seek) {
301                         warn("save_buffer: .fsxgood file too short... will"
302                                 "save 0x%llx bytes instead of 0x%llx\n",
303                                 (unsigned long long)size_by_seek,
304                                 (unsigned long long)bufferlength);
305                         bufferlength = size_by_seek;
306                 }
307         }
308
309         ret = lseek(fd, (off_t)0, SEEK_SET);
310         if (ret == (off_t)-1)
311                 prterr("save_buffer: lseek 0");
312
313         byteswritten = write(fd, buffer, (size_t)bufferlength);
314         if (byteswritten != bufferlength) {
315                 if (byteswritten == -1)
316                         prterr("save_buffer write");
317                 else
318                         warn("save_buffer: short write, 0x%x bytes instead"
319                                 "of 0x%llx\n",
320                              (unsigned)byteswritten,
321                              (unsigned long long)bufferlength);
322         }
323 }
324
325
326 void
327 report_failure(int status)
328 {
329         logdump();
330
331         if (fsxgoodfd) {
332                 if (good_buf) {
333                         save_buffer(good_buf, file_size, fsxgoodfd);
334                         prt("Correct content saved for comparison\n");
335                         prt("(maybe hexdump \"%s\" vs \"%s\")\n",
336                             fname, goodfile);
337                 }
338                 close(fsxgoodfd);
339         }
340         exit(status);
341 }
342
343
344 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
345                                         *(((unsigned char *)(cp)) + 1)))
346
347 void
348 check_buffers(unsigned offset, unsigned size)
349 {
350         unsigned char c, t;
351         unsigned i = 0;
352         unsigned n = 0;
353         unsigned op = 0;
354         unsigned bad = 0;
355
356         if (memcmp(good_buf + offset, temp_buf, size) != 0) {
357                 prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n",
358                     offset, size);
359                 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
360                 while (size > 0) {
361                         c = good_buf[offset];
362                         t = temp_buf[i];
363                         if (c != t) {
364                                 if (n == 0) {
365                                         bad = short_at(&temp_buf[i]);
366                                         prt("%#07x\t%#06x\t%#06x", offset,
367                                             short_at(&good_buf[offset]), bad);
368                                         op = temp_buf[offset & 1 ? i+1 : i];
369                                 }
370                                 n++;
371                                 badoff = offset;
372                         }
373                         offset++;
374                         i++;
375                         size--;
376                 }
377                 if (n) {
378                         prt("\t%#7x\n", n);
379                         if (bad)
380                                 prt("operation# (mod 256) for the bad data"
381                                         "may be %u\n", ((unsigned)op & 0xff));
382                         else
383                                 prt("operation# (mod 256) for the bad data"
384                                         "unknown, check HOLE and EXTEND ops\n");
385                 } else
386                         prt("????????????????\n");
387                 report_failure(110);
388         }
389 }
390
391 struct test_file {
392         char *path;
393         int fd;
394 } *test_files = NULL;
395
396 int num_test_files = 0;
397 enum fd_iteration_policy {
398         FD_SINGLE,
399         FD_ROTATE,
400         FD_RANDOM,
401 };
402 int fd_policy = FD_RANDOM;
403 int fd_last = 0;
404
405 struct test_file *
406 get_tf(void)
407 {
408         unsigned index = 0;
409
410         switch (fd_policy) {
411                 case FD_ROTATE:
412                         index = fd_last++;
413                         break;
414                 case FD_RANDOM:
415                         index = random();
416                         break;
417                 case FD_SINGLE:
418                         index = 0;
419                         break;
420                 default:
421                         prt("unknown policy");
422                         exit(1);
423                         break;
424         }
425         return &test_files[ index % num_test_files ];
426 }
427
428 void
429 assign_fd_policy(char *policy)
430 {
431         if (!strcmp(policy, "random"))
432                 fd_policy = FD_RANDOM;
433         else if (!strcmp(policy, "rotate"))
434                 fd_policy = FD_ROTATE;
435         else {
436                 prt("unknown -I policy: '%s'\n", policy);
437                 exit(1);
438         }
439 }
440
441 int
442 get_fd(void)
443 {
444         struct test_file *tf = get_tf();
445         return tf->fd;
446 }
447
448 static const char *my_basename(const char *path)
449 {
450         char *c = strrchr(path, '/');
451
452         return c ? c++ : path;
453 }
454
455 void
456 open_test_files(char **argv, int argc)
457 {
458         struct test_file *tf;
459         int i;
460
461         num_test_files = argc;
462         if (num_test_files == 1)
463                 fd_policy = FD_SINGLE;
464
465         test_files = calloc(num_test_files, sizeof(*test_files));
466         if (test_files == NULL) {
467                 prterr("reallocating space for test files");
468                 exit(1);
469         }
470
471         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
472
473                 tf->path = argv[i];
474                 tf->fd = open(tf->path, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC),
475                                 0666);
476                 if (tf->fd < 0) {
477                         prterr(tf->path);
478                         exit(91);
479                 }
480         }
481
482         if (quiet || fd_policy == FD_SINGLE)
483                 return;
484
485         for (i = 0, tf = test_files; i < num_test_files; i++, tf++)
486                 prt("fd %d: %s\n", i, tf->path);
487 }
488
489 void
490 close_test_files(void)
491 {
492         int i;
493         struct test_file *tf;
494
495         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
496                 if (close(tf->fd)) {
497                         prterr("close");
498                         report_failure(99);
499                 }
500         }
501 }
502
503
504 void
505 check_size(void)
506 {
507         struct stat     statbuf;
508         off_t   size_by_seek;
509         int fd = get_fd();
510
511         if (fstat(fd, &statbuf)) {
512                 prterr("check_size: fstat");
513                 statbuf.st_size = -1;
514         }
515         size_by_seek = lseek(fd, (off_t)0, SEEK_END);
516         if (file_size != statbuf.st_size || file_size != size_by_seek) {
517                 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
518                     (unsigned long long)file_size,
519                     (unsigned long long)statbuf.st_size,
520                     (unsigned long long)size_by_seek);
521                 report_failure(120);
522         }
523 }
524
525
526 void
527 check_trunc_hack(void)
528 {
529         struct stat statbuf;
530         int fd = get_fd();
531
532         /* should not ignore ftruncate(2)'s return value */
533         if (ftruncate(fd, (off_t)0) < 0) {
534                 prterr("trunc_hack: ftruncate(0)");
535                 exit(1);
536         }
537         if (ftruncate(fd, (off_t)100000) < 0) {
538                 prterr("trunc_hack: ftruncate(100000)");
539                 exit(1);
540         }
541         if (fstat(fd, &statbuf)) {
542                 prterr("trunc_hack: fstat");
543                 statbuf.st_size = -1;
544         }
545         if (statbuf.st_size != (off_t)100000) {
546                 prt("no extend on truncate! not posix!\n");
547                 exit(130);
548         }
549         if (ftruncate(fd, 0) < 0) {
550                 prterr("trunc_hack: ftruncate(0) (2nd call)");
551                 exit(1);
552         }
553 }
554
555 static char *tf_buf = NULL;
556 static int max_tf_len = 0;
557
558 void
559 alloc_tf_buf(void)
560 {
561         char dummy = '\0';
562         int highest = num_test_files - 1;
563         int len;
564
565         len = snprintf(&dummy, 0, "%u ", highest);
566         if (len < 1) {
567                 prterr("finding max tf_buf");
568                 exit(1);
569         }
570         len++;
571         tf_buf = malloc(len);
572         if (tf_buf == NULL) {
573                 prterr("allocating tf_buf");
574                 exit(1);
575         }
576         max_tf_len = snprintf(tf_buf, len, "%u ", highest);
577         if (max_tf_len < 1) {
578                 prterr("fiding max_tv_len\n");
579                 exit(1);
580         }
581         if (max_tf_len != len - 1) {
582                 warn("snprintf() gave %d instead of %d?\n",
583                                 max_tf_len, len - 1);
584                 exit(1);
585         }
586 }
587
588 char *
589 fill_tf_buf(struct test_file *tf)
590 {
591         if (tf_buf == NULL)
592                 alloc_tf_buf();
593
594         sprintf(tf_buf,"%lu ", (unsigned long)(tf - test_files));
595         return tf_buf;
596 }
597
598 void
599 output_line(struct test_file *tf, int op, unsigned offset,
600                 unsigned size, struct timeval *tv)
601 {
602         char *tf_num = "";
603
604         char *ops[] = {
605                 [OP_READ] = "read",
606                 [OP_WRITE] = "write",
607                 [OP_TRUNCATE] = "trunc from",
608                 [OP_MAPREAD] = "mapread",
609                 [OP_MAPWRITE] = "mapwrite",
610         };
611
612         if (fd_policy != FD_SINGLE)
613                 tf_num = fill_tf_buf(tf);
614
615         /* W. */
616         if (!(!quiet && ((progressinterval &&
617                         testcalls % progressinterval == 0) ||
618                        (debug &&
619                         (monitorstart == -1 ||
620                          (offset + size > monitorstart &&
621                           (monitorend == -1 || offset <= monitorend)))))))
622                 return;
623
624         prt("%06lu %lu.%06lu %.*s%-10s %#08x %s %#08x\t(0x%x bytes)\n",
625                 testcalls, tv->tv_sec, tv->tv_usec, max_tf_len,
626                 tf_num, ops[op],
627                 offset, op == OP_TRUNCATE ? " to " : "thru",
628                 offset + size - 1, size);
629 }
630
631 void
632 doread(unsigned offset, unsigned size)
633 {
634         struct timeval t;
635         off_t ret;
636         unsigned iret;
637         struct test_file *tf = get_tf();
638         int fd = tf->fd;
639
640         offset -= offset % readbdy;
641         gettimeofday(&t, NULL);
642         if (size == 0) {
643                 if (!quiet && testcalls > simulatedopcount)
644                         prt("skipping zero size read\n");
645                 log4(OP_SKIPPED, OP_READ, offset, size, &t);
646                 return;
647         }
648         if (size + offset > file_size) {
649                 if (!quiet && testcalls > simulatedopcount)
650                         prt("skipping seek/read past end of file\n");
651                 log4(OP_SKIPPED, OP_READ, offset, size, &t);
652                 return;
653         }
654
655         log4(OP_READ, offset, size, 0, &t);
656
657         if (testcalls <= simulatedopcount)
658                 return;
659
660         output_line(tf, OP_READ, offset, size, &t);
661
662         ret = lseek(fd, (off_t)offset, SEEK_SET);
663         if (ret == (off_t)-1) {
664                 prterr("doread: lseek");
665                 report_failure(140);
666         }
667         iret = read(fd, temp_buf, size);
668         if (!quiet && (debug > 1 &&
669                         (monitorstart == -1 ||
670                          (offset + size > monitorstart &&
671                           (monitorend == -1 || offset <= monitorend))))) {
672                 gettimeofday(&t, NULL);
673                 prt("       %lu.%06lu read done\n", t.tv_sec, t.tv_usec);
674         }
675         if (iret != size) {
676                 if (iret == -1)
677                         prterr("doread: read");
678                 else
679                         prt("short read: 0x%x bytes instead of 0x%x\n",
680                             iret, size);
681                 report_failure(141);
682         }
683         check_buffers(offset, size);
684 }
685
686
687 void
688 domapread(unsigned offset, unsigned size)
689 {
690         struct timeval t;
691         unsigned pg_offset;
692         unsigned map_size;
693         char    *p;
694         struct test_file *tf = get_tf();
695         int fd = tf->fd;
696
697         offset -= offset % readbdy;
698         gettimeofday(&t, NULL);
699         if (size == 0) {
700                 if (!quiet && testcalls > simulatedopcount)
701                         prt("skipping zero size read\n");
702                 log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t);
703                 return;
704         }
705         if (size + offset > file_size) {
706                 if (!quiet && testcalls > simulatedopcount)
707                         prt("skipping seek/read past end of file\n");
708                 log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t);
709                 return;
710         }
711
712         log4(OP_MAPREAD, offset, size, 0, &t);
713
714         if (testcalls <= simulatedopcount)
715                 return;
716
717         output_line(tf, OP_MAPREAD, offset, size, &t);
718
719         pg_offset = offset & page_mask;
720         map_size  = pg_offset + size;
721
722         if ((p = mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd,
723                       (off_t)(offset - pg_offset))) == MAP_FAILED) {
724                 prterr("domapread: mmap");
725                 report_failure(190);
726         }
727         if (!quiet && (debug > 1 &&
728                         (monitorstart == -1 ||
729                          (offset + size > monitorstart &&
730                           (monitorend == -1 || offset <= monitorend))))) {
731                 gettimeofday(&t, NULL);
732                 prt("       %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec);
733         }
734         memcpy(temp_buf, p + pg_offset, size);
735         if (!quiet && (debug > 1 &&
736                         (monitorstart == -1 ||
737                          (offset + size > monitorstart &&
738                           (monitorend == -1 || offset <= monitorend))))) {
739                 gettimeofday(&t, NULL);
740                 prt("       %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec);
741         }
742         if (munmap(p, map_size) != 0) {
743                 prterr("domapread: munmap");
744                 report_failure(191);
745         }
746         if (!quiet && (debug > 1 &&
747                         (monitorstart == -1 ||
748                          (offset + size > monitorstart &&
749                           (monitorend == -1 || offset <= monitorend))))) {
750                 gettimeofday(&t, NULL);
751                 prt("       %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec);
752         }
753
754         check_buffers(offset, size);
755 }
756
757
758 void
759 gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
760 {
761         while (size--) {
762                 good_buf[offset] = testcalls % 256;
763                 if (offset % 2)
764                         good_buf[offset] += original_buf[offset];
765                 offset++;
766         }
767 }
768
769
770 void
771 dowrite(unsigned offset, unsigned size)
772 {
773         struct timeval t;
774         off_t ret;
775         unsigned iret;
776         struct test_file *tf = get_tf();
777         int fd = tf->fd;
778
779         offset -= offset % writebdy;
780         gettimeofday(&t, NULL);
781         if (size == 0) {
782                 if (!quiet && testcalls > simulatedopcount)
783                         prt("skipping zero size write\n");
784                 log4(OP_SKIPPED, OP_WRITE, offset, size, &t);
785                 return;
786         }
787
788         log4(OP_WRITE, offset, size, file_size, &t);
789
790         gendata(original_buf, good_buf, offset, size);
791         if (file_size < offset + size) {
792                 if (file_size < offset)
793                         memset(good_buf + file_size, '\0', offset - file_size);
794                 file_size = offset + size;
795                 if (lite) {
796                         warn("Lite file size bug in fsx!");
797                         report_failure(149);
798                 }
799         }
800
801         if (testcalls <= simulatedopcount)
802                 return;
803
804         output_line(tf, OP_WRITE, offset, size, &t);
805
806         ret = lseek(fd, (off_t)offset, SEEK_SET);
807         if (ret == (off_t)-1) {
808                 prterr("dowrite: lseek");
809                 report_failure(150);
810         }
811         iret = write(fd, good_buf + offset, size);
812         if (!quiet && (debug > 1 &&
813                         (monitorstart == -1 ||
814                          (offset + size > monitorstart &&
815                           (monitorend == -1 || offset <= monitorend))))) {
816                 gettimeofday(&t, NULL);
817                 prt("       %lu.%06lu write done\n", t.tv_sec, t.tv_usec);
818         }
819         if (iret != size) {
820                 if (iret == -1)
821                         prterr("dowrite: write");
822                 else
823                         prt("short write: 0x%x bytes instead of 0x%x\n",
824                             iret, size);
825                 report_failure(151);
826         }
827 }
828
829
830 void
831 domapwrite(unsigned offset, unsigned size)
832 {
833         struct timeval t;
834         unsigned pg_offset;
835         unsigned map_size;
836         off_t    cur_filesize;
837         char    *p;
838         struct test_file *tf = get_tf();
839         int fd = tf->fd;
840
841         offset -= offset % writebdy;
842         gettimeofday(&t, NULL);
843         if (size == 0) {
844                 if (!quiet && testcalls > simulatedopcount)
845                         prt("skipping zero size write\n");
846                 log4(OP_SKIPPED, OP_MAPWRITE, offset, size, &t);
847                 return;
848         }
849         cur_filesize = file_size;
850
851         log4(OP_MAPWRITE, offset, size, 0, &t);
852
853         gendata(original_buf, good_buf, offset, size);
854         if (file_size < offset + size) {
855                 if (file_size < offset)
856                         memset(good_buf + file_size, '\0', offset - file_size);
857                 file_size = offset + size;
858                 if (lite) {
859                         warn("Lite file size bug in fsx!");
860                         report_failure(200);
861                 }
862         }
863
864         if (testcalls <= simulatedopcount)
865                 return;
866
867         output_line(tf, OP_MAPWRITE, offset, size, &t);
868
869         if (file_size > cur_filesize) {
870                 if (ftruncate(fd, file_size) == -1) {
871                         prterr("domapwrite: ftruncate");
872                         exit(201);
873                 }
874                 if (!quiet && (debug > 1 &&
875                                (monitorstart == -1 ||
876                                 (offset + size > monitorstart &&
877                                  (monitorend == -1 || offset <= monitorend))))) {
878                         gettimeofday(&t, NULL);
879                         prt("       %lu.%06lu truncate done\n", t.tv_sec, t.tv_usec);
880         }
881         }
882         pg_offset = offset & page_mask;
883         map_size  = pg_offset + size;
884
885         if ((p = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_FILE|MAP_SHARED,
886                       fd, (off_t)(offset - pg_offset))) == MAP_FAILED) {
887                 prterr("domapwrite: mmap");
888                 report_failure(202);
889         }
890         if (!quiet && (debug > 1 &&
891                         (monitorstart == -1 ||
892                          (offset + size > monitorstart &&
893                           (monitorend == -1 || offset <= monitorend))))) {
894                 gettimeofday(&t, NULL);
895                 prt("       %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec);
896         }
897         memcpy(p + pg_offset, good_buf + offset, size);
898         if (!quiet && (debug > 1 &&
899                         (monitorstart == -1 ||
900                          (offset + size > monitorstart &&
901                           (monitorend == -1 || offset <= monitorend))))) {
902                 gettimeofday(&t, NULL);
903                 prt("       %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec);
904         }
905         if (msync(p, map_size, 0) != 0) {
906                 prterr("domapwrite: msync");
907                 report_failure(203);
908         }
909         if (!quiet && (debug > 1 &&
910                         (monitorstart == -1 ||
911                          (offset + size > monitorstart &&
912                           (monitorend == -1 || offset <= monitorend))))) {
913                 gettimeofday(&t, NULL);
914                 prt("       %lu.%06lu msync done\n", t.tv_sec, t.tv_usec);
915         }
916         if (munmap(p, map_size) != 0) {
917                 prterr("domapwrite: munmap");
918                 report_failure(204);
919         }
920         if (!quiet && (debug > 1 &&
921                         (monitorstart == -1 ||
922                          (offset + size > monitorstart &&
923                           (monitorend == -1 || offset <= monitorend))))) {
924                 gettimeofday(&t, NULL);
925                 prt("       %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec);
926         }
927 }
928
929
930 void
931 dotruncate(unsigned size)
932 {
933         struct timeval t;
934         int oldsize = file_size;
935         struct test_file *tf = get_tf();
936         int fd = tf->fd;
937
938         size -= size % truncbdy;
939         gettimeofday(&t, NULL);
940         if (size > biggest) {
941                 biggest = size;
942                 if (!quiet && testcalls > simulatedopcount)
943                         prt("truncating to largest ever: 0x%x\n", size);
944         }
945
946         log4(OP_TRUNCATE, size, (unsigned)file_size, 0, &t);
947
948         if (size > file_size)
949                 memset(good_buf + file_size, '\0', size - file_size);
950         file_size = size;
951
952         if (testcalls <= simulatedopcount)
953                 return;
954
955         output_line(tf, OP_TRUNCATE, oldsize, size, &t);
956
957         if (ftruncate(fd, (off_t)size) == -1) {
958                 prt("ftruncate1: %x\n", size);
959                 prterr("dotruncate: ftruncate");
960                 report_failure(160);
961         }
962         if (!quiet && debug > 1) {
963                 gettimeofday(&t, NULL);
964                 prt("       %lu.%06lu trunc done\n", t.tv_sec, t.tv_usec);
965         }
966 }
967
968
969 void
970 writefileimage()
971 {
972         ssize_t iret;
973         int fd = get_fd();
974
975         if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
976                 prterr("writefileimage: lseek");
977                 report_failure(171);
978         }
979         iret = write(fd, good_buf, file_size);
980         if ((off_t)iret != file_size) {
981                 if (iret == -1)
982                         prterr("writefileimage: write");
983                 else
984                         prt("short write: 0x%lx bytes instead of 0x%llx\n",
985                             (unsigned long)iret,
986                             (unsigned long long)file_size);
987                 report_failure(172);
988         }
989         if (lite ? 0 : ftruncate(fd, file_size) == -1) {
990                 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
991                 prterr("writefileimage: ftruncate");
992                 report_failure(173);
993         }
994 }
995
996
997 void
998 docloseopen(void)
999 {
1000         struct timeval t;
1001         struct test_file *tf = get_tf();
1002
1003         if (testcalls <= simulatedopcount)
1004                 return;
1005
1006         gettimeofday(&t, NULL);
1007         log4(OP_CLOSEOPEN, file_size, (unsigned)file_size, 0, &t);
1008
1009         if (debug)
1010                 prt("%06lu %lu.%06lu close/open\n", testcalls, t.tv_sec,
1011                     t.tv_usec);
1012         if (close(tf->fd)) {
1013                 prterr("docloseopen: close");
1014                 report_failure(180);
1015         }
1016         if (!quiet && debug > 1) {
1017                 gettimeofday(&t, NULL);
1018                 prt("       %lu.%06lu close done\n", t.tv_sec, t.tv_usec);
1019         }
1020         tf->fd = open(tf->path, O_RDWR, 0);
1021         if (tf->fd < 0) {
1022                 prterr("docloseopen: open");
1023                 report_failure(181);
1024         }
1025         if (!quiet && debug > 1) {
1026                 gettimeofday(&t, NULL);
1027                 prt("       %lu.%06lu open done\n", t.tv_sec, t.tv_usec);
1028         }
1029 }
1030
1031
1032 void
1033 test(void)
1034 {
1035         unsigned long   offset;
1036         unsigned long   size = maxoplen;
1037         unsigned long   rv = random();
1038         unsigned long   op = rv % (3 + !lite + mapped_writes);
1039
1040         /* turn off the map read if necessary */
1041
1042         if (op == 2 && !mapped_reads)
1043             op = 0;
1044
1045         if (simulatedopcount > 0 && testcalls == simulatedopcount)
1046                 writefileimage();
1047
1048         testcalls++;
1049
1050         if (debugstart > 0 && testcalls >= debugstart)
1051                 debug = 1;
1052
1053         if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
1054                 prt("%lu...\n", testcalls);
1055
1056         /*
1057          * READ:        op = 0
1058          * WRITE:       op = 1
1059          * MAPREAD:     op = 2
1060          * TRUNCATE:    op = 3
1061          * MAPWRITE:    op = 3 or 4
1062          */
1063         if (lite ? 0 : op == 3 && (style & 1) == 0) /* vanilla truncate? */
1064                 dotruncate(random() % maxfilelen);
1065         else {
1066                 if (randomoplen)
1067                         size = random() % (maxoplen+1);
1068                 if (lite ? 0 : op == 3)
1069                         dotruncate(size);
1070                 else {
1071                         offset = random();
1072                         if (op == 1 || op == (lite ? 3 : 4)) {
1073                                 offset %= maxfilelen;
1074                                 if (offset + size > maxfilelen)
1075                                         size = maxfilelen - offset;
1076                                 if (op != 1)
1077                                         domapwrite(offset, size);
1078                                 else
1079                                         dowrite(offset, size);
1080                         } else {
1081                                 if (file_size)
1082                                         offset %= file_size;
1083                                 else
1084                                         offset = 0;
1085                                 if (offset + size > file_size)
1086                                         size = file_size - offset;
1087                                 if (op != 0)
1088                                         domapread(offset, size);
1089                                 else
1090                                         doread(offset, size);
1091                         }
1092                 }
1093         }
1094         if (sizechecks && testcalls > simulatedopcount)
1095                 check_size();
1096         if (closeprob && (rv >> 3) < (1 << 28) / closeprob)
1097                 docloseopen();
1098 }
1099
1100
1101 void
1102 cleanup(sig)
1103         int     sig;
1104 {
1105         if (sig)
1106                 prt("signal %d\n", sig);
1107         prt("testcalls = %lu\n", testcalls);
1108         exit(sig);
1109 }
1110
1111
1112 void
1113 usage(void)
1114 {
1115         fprintf(stdout, "usage: %s",
1116                 "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m "
1117 "start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t "
1118 "truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] "
1119 "[ -I random|rotate ] fname [additional paths to fname..]\n"
1120 "       -b opnum: beginning operation number (default 1)\n"
1121 "       -c P: 1 in P chance of file close+open at each op (default infinity)\n"
1122 "       -d: debug output for all operations [-d -d = more debugging]\n"
1123 "       -l flen: the upper bound on file size (default 262144)\n"
1124 "       -m startop:endop: monitor (print debug output) specified byte rang"
1125 "(default 0:infinity)\n"
1126 "       -n: no verifications of file size\n"
1127 "       -o oplen: the upper bound on operation size (default 65536)\n"
1128 "       -p progressinterval: debug output at specified operation interval\n"
1129 "       -q: quieter operation\n"
1130 "       -r readbdy: 4096 would make reads page aligned (default 1)\n"
1131 "       -s style: 1 gives smaller truncates (default 0)\n"
1132 "       -t truncbdy: 4096 would make truncates page aligned (default 1)\n"
1133 "       -w writebdy: 4096 would make writes page aligned (default 1)\n"
1134 "       -D startingop: debug output starting at specified operation\n"
1135 "       -L: fsxLite - no file creations & no file size changes\n"
1136 "       -N numops: total # operations to do (default infinity)\n"
1137 "       -O: use oplen (see -o flag) for every op (default random)\n"
1138 "       -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n"
1139 "       -S seed: for random # generator (default 1) 0 gets timestamp\n"
1140 "       -W: mapped write operations DISabled\n"
1141 "        -R: read() system calls only (mapped reads disabled)\n"
1142 "       -I: When multiple paths to the file are given each operation uses"
1143 "           a different path.  Iterate through them in order with 'rotate'"
1144 "           or chose then at 'random'.  (defaults to random)\n"
1145 "       fname: this filename is REQUIRED (no default)\n");
1146         exit(90);
1147 }
1148
1149
1150 int
1151 getnum(char *s, char **e)
1152 {
1153         int ret = -1;
1154
1155         *e = (char *) 0;
1156         ret = strtol(s, e, 0);
1157         if (*e)
1158                 switch (**e) {
1159                 case 'b':
1160                 case 'B':
1161                         ret *= 512;
1162                         *e = *e + 1;
1163                         break;
1164                 case 'k':
1165                 case 'K':
1166                         ret *= 1024;
1167                         *e = *e + 1;
1168                         break;
1169                 case 'm':
1170                 case 'M':
1171                         ret *= 1024*1024;
1172                         *e = *e + 1;
1173                         break;
1174                 case 'w':
1175                 case 'W':
1176                         ret *= 4;
1177                         *e = *e + 1;
1178                         break;
1179                 }
1180         return (ret);
1181 }
1182
1183 int
1184 main(int argc, char **argv)
1185 {
1186         int     i, style, ch;
1187         char    *endp;
1188         int  dirpath = 0;
1189
1190         goodfile[0] = 0;
1191         logfile[0] = 0;
1192
1193         page_size = getpagesize();
1194         page_mask = page_size - 1;
1195
1196         setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
1197
1198         while ((ch = getopt(argc, argv,
1199                                 "b:c:dl:m:no:p:qr:s:t:w:D:I:LN:OP:RS:W"))
1200                != EOF)
1201                 switch (ch) {
1202                 case 'b':
1203                         simulatedopcount = getnum(optarg, &endp);
1204                         if (!quiet)
1205                                 fprintf(stdout, "Will begin at operation"
1206                                         "%ld\n",
1207                                         simulatedopcount);
1208                         if (simulatedopcount == 0)
1209                                 usage();
1210                         simulatedopcount -= 1;
1211                         break;
1212                 case 'c':
1213                         closeprob = getnum(optarg, &endp);
1214                         if (!quiet)
1215                                 fprintf(stdout,
1216                                         "Chance of close/open is 1 in %d\n",
1217                                         closeprob);
1218                         if (closeprob <= 0)
1219                                 usage();
1220                         break;
1221                 case 'd':
1222                         debug++;
1223                         break;
1224                 case 'l':
1225                         maxfilelen = getnum(optarg, &endp);
1226                         if (maxfilelen <= 0)
1227                                 usage();
1228                         break;
1229                 case 'm':
1230                         monitorstart = getnum(optarg, &endp);
1231                         if (monitorstart < 0)
1232                                 usage();
1233                         if (!endp || *endp++ != ':')
1234                                 usage();
1235                         monitorend = getnum(endp, &endp);
1236                         if (monitorend < 0)
1237                                 usage();
1238                         if (monitorend == 0)
1239                                 monitorend = -1; /* aka infinity */
1240                         debug = 1;
1241                 case 'n':
1242                         sizechecks = 0;
1243                         break;
1244                 case 'o':
1245                         maxoplen = getnum(optarg, &endp);
1246                         if (maxoplen <= 0)
1247                                 usage();
1248                         break;
1249                 case 'p':
1250                         progressinterval = getnum(optarg, &endp);
1251                         if (progressinterval < 0)
1252                                 usage();
1253                         break;
1254                 case 'q':
1255                         quiet = 1;
1256                         break;
1257                 case 'r':
1258                         readbdy = getnum(optarg, &endp);
1259                         if (readbdy <= 0)
1260                                 usage();
1261                         break;
1262                 case 's':
1263                         style = getnum(optarg, &endp);
1264                         if (style < 0 || style > 1)
1265                                 usage();
1266                         break;
1267                 case 't':
1268                         truncbdy = getnum(optarg, &endp);
1269                         if (truncbdy <= 0)
1270                                 usage();
1271                         break;
1272                 case 'w':
1273                         writebdy = getnum(optarg, &endp);
1274                         if (writebdy <= 0)
1275                                 usage();
1276                         break;
1277                 case 'D':
1278                         debugstart = getnum(optarg, &endp);
1279                         if (debugstart < 1)
1280                                 usage();
1281                         break;
1282                 case 'I':
1283                         assign_fd_policy(optarg);
1284                         break;
1285                 case 'L':
1286                         lite = 1;
1287                         break;
1288                 case 'N':
1289                         numops = getnum(optarg, &endp);
1290                         if (numops < 0)
1291                                 usage();
1292                         break;
1293                 case 'O':
1294                         randomoplen = 0;
1295                         break;
1296                 case 'P':
1297                         strncpy(goodfile, optarg, sizeof(goodfile));
1298                         strcat(goodfile, "/");
1299                         strncpy(logfile, optarg, sizeof(logfile));
1300                         strcat(logfile, "/");
1301                         dirpath = 1;
1302                         break;
1303                 case 'R':
1304                         mapped_reads = 0;
1305                         break;
1306                 case 'S':
1307                         seed = getnum(optarg, &endp);
1308                         if (seed == 0)
1309                                 seed = time(0) % 10000;
1310                         if (!quiet)
1311                                 fprintf(stdout, "Seed set to %d\n", seed);
1312                         if (seed < 0)
1313                                 usage();
1314                         break;
1315                 case 'W':
1316                         mapped_writes = 0;
1317                         if (!quiet)
1318                                 fprintf(stdout, "mapped writes DISABLED\n");
1319                         break;
1320
1321                 default:
1322                         usage();
1323                         /* NOTREACHED */
1324                 }
1325         argc -= optind;
1326         argv += optind;
1327         if (argc < 1)
1328                 usage();
1329         fname = argv[0];
1330
1331         signal(SIGHUP,  cleanup);
1332         signal(SIGINT,  cleanup);
1333         signal(SIGPIPE, cleanup);
1334         signal(SIGALRM, cleanup);
1335         signal(SIGTERM, cleanup);
1336         signal(SIGXCPU, cleanup);
1337         signal(SIGXFSZ, cleanup);
1338         signal(SIGVTALRM,       cleanup);
1339         signal(SIGUSR1, cleanup);
1340         signal(SIGUSR2, cleanup);
1341
1342         initstate(seed, state, 256);
1343         setstate(state);
1344
1345         open_test_files(argv, argc);
1346
1347         strncat(goodfile, dirpath ? my_basename(fname) : fname, 256);
1348         strcat (goodfile, ".fsxgood");
1349         fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
1350         if (fsxgoodfd < 0) {
1351                 prterr(goodfile);
1352                 exit(92);
1353         }
1354         strncat(logfile, dirpath ? my_basename(fname) : fname, 256);
1355         strcat (logfile, ".fsxlog");
1356         fsxlogf = fopen(logfile, "w");
1357         if (fsxlogf == NULL) {
1358                 prterr(logfile);
1359                 exit(93);
1360         }
1361         if (lite) {
1362                 off_t ret;
1363                 int fd = get_fd();
1364                 file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END);
1365                 if (file_size == (off_t)-1) {
1366                         prterr(fname);
1367                         warn("main: lseek eof");
1368                         exit(94);
1369                 }
1370                 ret = lseek(fd, (off_t)0, SEEK_SET);
1371                 if (ret == (off_t)-1) {
1372                         prterr(fname);
1373                         warn("main: lseek 0");
1374                         exit(95);
1375                 }
1376         }
1377         original_buf = (char *) malloc(maxfilelen);
1378         for (i = 0; i < maxfilelen; i++)
1379                 original_buf[i] = random() % 256;
1380         good_buf = (char *) malloc(maxfilelen);
1381         memset(good_buf, '\0', maxfilelen);
1382         temp_buf = (char *) malloc(maxoplen);
1383         memset(temp_buf, '\0', maxoplen);
1384         if (lite) {     /* zero entire existing file */
1385                 ssize_t written;
1386                 int fd = get_fd();
1387
1388                 written = write(fd, good_buf, (size_t)maxfilelen);
1389                 if (written != maxfilelen) {
1390                         if (written == -1) {
1391                                 prterr(fname);
1392                                 warn("main: error on write");
1393                         } else
1394                                 warn("main: short write, 0x%x bytes instead"
1395                                         "of 0x%x\n",
1396                                      (unsigned)written, maxfilelen);
1397                         exit(98);
1398                 }
1399         } else
1400                 check_trunc_hack();
1401
1402         while (numops == -1 || numops--)
1403                 test();
1404
1405         close_test_files();
1406         prt("All operations completed A-OK!\n");
1407
1408         exit(0);
1409         return 0;
1410 }