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