Whamcloud - gitweb
land b1_5 onto HEAD
[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 __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 *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         ftruncate(fd, (off_t)0);
533         ftruncate(fd, (off_t)100000);
534         if (fstat(fd, &statbuf)) {
535                 prterr("trunc_hack: fstat");
536                 statbuf.st_size = -1;
537         }
538         if (statbuf.st_size != (off_t)100000) {
539                 prt("no extend on truncate! not posix!\n");
540                 exit(130);
541         }
542         ftruncate(fd, 0);
543 }
544
545 static char *tf_buf = NULL;
546 static int max_tf_len = 0;
547
548 void
549 alloc_tf_buf(void)
550 {
551         char dummy = '\0';
552         int highest = num_test_files - 1;
553         int len;
554
555         len = snprintf(&dummy, 0, "%u ", highest);
556         if (len < 1) {
557                 prterr("finding max tf_buf");
558                 exit(1);
559         }
560         len++;
561         tf_buf = malloc(len);
562         if (tf_buf == NULL) {
563                 prterr("allocating tf_buf");
564                 exit(1);
565         }
566         max_tf_len = snprintf(tf_buf, len, "%u ", highest);
567         if (max_tf_len < 1) {
568                 prterr("fiding max_tv_len\n");
569                 exit(1);
570         }
571         if (max_tf_len != len - 1) {
572                 warn("snprintf() gave %d instead of %d?\n",
573                                 max_tf_len, len - 1);
574                 exit(1);
575         }
576 }
577
578 char * 
579 fill_tf_buf(struct test_file *tf)
580 {
581         if (tf_buf == NULL)
582                 alloc_tf_buf();
583
584         sprintf(tf_buf,"%lu ", (unsigned long)(tf - test_files));
585         return tf_buf;
586 }
587
588 void
589 output_line(struct test_file *tf, int op, unsigned offset, 
590                 unsigned size, struct timeval *tv)
591 {
592         char *tf_num = "";
593
594         char *ops[] = {
595                 [OP_READ] = "read",
596                 [OP_WRITE] = "write",
597                 [OP_TRUNCATE] = "trunc from",
598                 [OP_MAPREAD] = "mapread",
599                 [OP_MAPWRITE] = "mapwrite",
600         };
601
602         if (fd_policy != FD_SINGLE)
603                 tf_num = fill_tf_buf(tf);
604
605         /* W. */
606         if (!(!quiet && ((progressinterval &&
607                         testcalls % progressinterval == 0) ||
608                        (debug &&
609                         (monitorstart == -1 ||
610                          (offset + size > monitorstart &&
611                           (monitorend == -1 || offset <= monitorend)))))))
612                 return;
613
614         prt("%06lu %lu.%06lu %.*s%-10s %#08x %s %#08x\t(0x%x bytes)\n",
615                 testcalls, tv->tv_sec, tv->tv_usec, max_tf_len,
616                 tf_num, ops[op], 
617                 offset, op == OP_TRUNCATE ? " to " : "thru",
618                 offset + size - 1, size);
619 }
620
621 void
622 doread(unsigned offset, unsigned size)
623 {
624         struct timeval t;
625         off_t ret;
626         unsigned iret;
627         struct test_file *tf = get_tf();
628         int fd = tf->fd;
629
630         offset -= offset % readbdy;
631         gettimeofday(&t, NULL);
632         if (size == 0) {
633                 if (!quiet && testcalls > simulatedopcount)
634                         prt("skipping zero size read\n");
635                 log4(OP_SKIPPED, OP_READ, offset, size, &t);
636                 return;
637         }
638         if (size + offset > file_size) {
639                 if (!quiet && testcalls > simulatedopcount)
640                         prt("skipping seek/read past end of file\n");
641                 log4(OP_SKIPPED, OP_READ, offset, size, &t);
642                 return;
643         }
644
645         log4(OP_READ, offset, size, 0, &t);
646
647         if (testcalls <= simulatedopcount)
648                 return;
649
650         output_line(tf, OP_READ, offset, size, &t);
651
652         ret = lseek(fd, (off_t)offset, SEEK_SET);
653         if (ret == (off_t)-1) {
654                 prterr("doread: lseek");
655                 report_failure(140);
656         }
657         iret = read(fd, temp_buf, size);
658         if (!quiet && (debug > 1 &&
659                         (monitorstart == -1 ||
660                          (offset + size > monitorstart &&
661                           (monitorend == -1 || offset <= monitorend))))) {
662                 gettimeofday(&t, NULL);
663                 prt("       %lu.%06lu read done\n", t.tv_sec, t.tv_usec);
664         }
665         if (iret != size) {
666                 if (iret == -1)
667                         prterr("doread: read");
668                 else
669                         prt("short read: 0x%x bytes instead of 0x%x\n",
670                             iret, size);
671                 report_failure(141);
672         }
673         check_buffers(offset, size);
674 }
675
676
677 void
678 domapread(unsigned offset, unsigned size)
679 {
680         struct timeval t;
681         unsigned pg_offset;
682         unsigned map_size;
683         char    *p;
684         struct test_file *tf = get_tf();
685         int fd = tf->fd;
686
687         offset -= offset % readbdy;
688         gettimeofday(&t, NULL);
689         if (size == 0) {
690                 if (!quiet && testcalls > simulatedopcount)
691                         prt("skipping zero size read\n");
692                 log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t);
693                 return;
694         }
695         if (size + offset > file_size) {
696                 if (!quiet && testcalls > simulatedopcount)
697                         prt("skipping seek/read past end of file\n");
698                 log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t);
699                 return;
700         }
701
702         log4(OP_MAPREAD, offset, size, 0, &t);
703
704         if (testcalls <= simulatedopcount)
705                 return;
706
707         output_line(tf, OP_MAPREAD, offset, size, &t);
708
709         pg_offset = offset & page_mask;
710         map_size  = pg_offset + size;
711
712         if ((p = mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd,
713                       (off_t)(offset - pg_offset))) == MAP_FAILED) {
714                 prterr("domapread: mmap");
715                 report_failure(190);
716         }
717         if (!quiet && (debug > 1 &&
718                         (monitorstart == -1 ||
719                          (offset + size > monitorstart &&
720                           (monitorend == -1 || offset <= monitorend))))) {
721                 gettimeofday(&t, NULL);
722                 prt("       %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec);
723         }
724         memcpy(temp_buf, p + pg_offset, size);
725         if (!quiet && (debug > 1 &&
726                         (monitorstart == -1 ||
727                          (offset + size > monitorstart &&
728                           (monitorend == -1 || offset <= monitorend))))) {
729                 gettimeofday(&t, NULL);
730                 prt("       %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec);
731         }
732         if (munmap(p, map_size) != 0) {
733                 prterr("domapread: munmap");
734                 report_failure(191);
735         }
736         if (!quiet && (debug > 1 &&
737                         (monitorstart == -1 ||
738                          (offset + size > monitorstart &&
739                           (monitorend == -1 || offset <= monitorend))))) {
740                 gettimeofday(&t, NULL);
741                 prt("       %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec);
742         }
743
744         check_buffers(offset, size);
745 }
746
747
748 void
749 gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
750 {
751         while (size--) {
752                 good_buf[offset] = testcalls % 256;
753                 if (offset % 2)
754                         good_buf[offset] += original_buf[offset];
755                 offset++;
756         }
757 }
758
759
760 void
761 dowrite(unsigned offset, unsigned size)
762 {
763         struct timeval t;
764         off_t ret;
765         unsigned iret;
766         struct test_file *tf = get_tf();
767         int fd = tf->fd;
768
769         offset -= offset % writebdy;
770         gettimeofday(&t, NULL);
771         if (size == 0) {
772                 if (!quiet && testcalls > simulatedopcount)
773                         prt("skipping zero size write\n");
774                 log4(OP_SKIPPED, OP_WRITE, offset, size, &t);
775                 return;
776         }
777
778         log4(OP_WRITE, offset, size, file_size, &t);
779
780         gendata(original_buf, good_buf, offset, size);
781         if (file_size < offset + size) {
782                 if (file_size < offset)
783                         memset(good_buf + file_size, '\0', offset - file_size);
784                 file_size = offset + size;
785                 if (lite) {
786                         warn("Lite file size bug in fsx!");
787                         report_failure(149);
788                 }
789         }
790
791         if (testcalls <= simulatedopcount)
792                 return;
793
794         output_line(tf, OP_WRITE, offset, size, &t);
795
796         ret = lseek(fd, (off_t)offset, SEEK_SET);
797         if (ret == (off_t)-1) {
798                 prterr("dowrite: lseek");
799                 report_failure(150);
800         }
801         iret = write(fd, good_buf + offset, size);
802         if (!quiet && (debug > 1 &&
803                         (monitorstart == -1 ||
804                          (offset + size > monitorstart &&
805                           (monitorend == -1 || offset <= monitorend))))) {
806                 gettimeofday(&t, NULL);
807                 prt("       %lu.%06lu write done\n", t.tv_sec, t.tv_usec);
808         }
809         if (iret != size) {
810                 if (iret == -1)
811                         prterr("dowrite: write");
812                 else
813                         prt("short write: 0x%x bytes instead of 0x%x\n",
814                             iret, size);
815                 report_failure(151);
816         }
817 }
818
819
820 void
821 domapwrite(unsigned offset, unsigned size)
822 {
823         struct timeval t;
824         unsigned pg_offset;
825         unsigned map_size;
826         off_t    cur_filesize;
827         char    *p;
828         struct test_file *tf = get_tf();
829         int fd = tf->fd;
830
831         offset -= offset % writebdy;
832         gettimeofday(&t, NULL);
833         if (size == 0) {
834                 if (!quiet && testcalls > simulatedopcount)
835                         prt("skipping zero size write\n");
836                 log4(OP_SKIPPED, OP_MAPWRITE, offset, size, &t);
837                 return;
838         }
839         cur_filesize = file_size;
840
841         log4(OP_MAPWRITE, offset, size, 0, &t);
842
843         gendata(original_buf, good_buf, offset, size);
844         if (file_size < offset + size) {
845                 if (file_size < offset)
846                         memset(good_buf + file_size, '\0', offset - file_size);
847                 file_size = offset + size;
848                 if (lite) {
849                         warn("Lite file size bug in fsx!");
850                         report_failure(200);
851                 }
852         }
853
854         if (testcalls <= simulatedopcount)
855                 return;
856
857         output_line(tf, OP_MAPWRITE, offset, size, &t);
858
859         if (file_size > cur_filesize) {
860                 if (ftruncate(fd, file_size) == -1) {
861                         prterr("domapwrite: ftruncate");
862                         exit(201);
863                 }
864                 if (!quiet && (debug > 1 &&
865                                (monitorstart == -1 ||
866                                 (offset + size > monitorstart &&
867                                  (monitorend == -1 || offset <= monitorend))))) {
868                         gettimeofday(&t, NULL);
869                         prt("       %lu.%06lu truncate done\n", t.tv_sec, t.tv_usec);
870         }
871         }
872         pg_offset = offset & page_mask;
873         map_size  = pg_offset + size;
874
875         if ((p = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_FILE|MAP_SHARED,
876                       fd, (off_t)(offset - pg_offset))) == MAP_FAILED) {
877                 prterr("domapwrite: mmap");
878                 report_failure(202);
879         }
880         if (!quiet && (debug > 1 &&
881                         (monitorstart == -1 ||
882                          (offset + size > monitorstart &&
883                           (monitorend == -1 || offset <= monitorend))))) {
884                 gettimeofday(&t, NULL);
885                 prt("       %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec);
886         }
887         memcpy(p + pg_offset, good_buf + offset, size);
888         if (!quiet && (debug > 1 &&
889                         (monitorstart == -1 ||
890                          (offset + size > monitorstart &&
891                           (monitorend == -1 || offset <= monitorend))))) {
892                 gettimeofday(&t, NULL);
893                 prt("       %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec);
894         }
895         if (msync(p, map_size, 0) != 0) {
896                 prterr("domapwrite: msync");
897                 report_failure(203);
898         }
899         if (!quiet && (debug > 1 &&
900                         (monitorstart == -1 ||
901                          (offset + size > monitorstart &&
902                           (monitorend == -1 || offset <= monitorend))))) {
903                 gettimeofday(&t, NULL);
904                 prt("       %lu.%06lu msync done\n", t.tv_sec, t.tv_usec);
905         }
906         if (munmap(p, map_size) != 0) {
907                 prterr("domapwrite: munmap");
908                 report_failure(204);
909         }
910         if (!quiet && (debug > 1 &&
911                         (monitorstart == -1 ||
912                          (offset + size > monitorstart &&
913                           (monitorend == -1 || offset <= monitorend))))) {
914                 gettimeofday(&t, NULL);
915                 prt("       %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec);
916         }
917 }
918
919
920 void
921 dotruncate(unsigned size)
922 {
923         struct timeval t;
924         int oldsize = file_size;
925         struct test_file *tf = get_tf();
926         int fd = tf->fd;
927
928         size -= size % truncbdy;
929         gettimeofday(&t, NULL);
930         if (size > biggest) {
931                 biggest = size;
932                 if (!quiet && testcalls > simulatedopcount)
933                         prt("truncating to largest ever: 0x%x\n", size);
934         }
935
936         log4(OP_TRUNCATE, size, (unsigned)file_size, 0, &t);
937
938         if (size > file_size)
939                 memset(good_buf + file_size, '\0', size - file_size);
940         file_size = size;
941
942         if (testcalls <= simulatedopcount)
943                 return;
944
945         output_line(tf, OP_TRUNCATE, oldsize, size, &t);
946
947         if (ftruncate(fd, (off_t)size) == -1) {
948                 prt("ftruncate1: %x\n", size);
949                 prterr("dotruncate: ftruncate");
950                 report_failure(160);
951         }
952         if (!quiet && debug > 1) {
953                 gettimeofday(&t, NULL);
954                 prt("       %lu.%06lu trunc done\n", t.tv_sec, t.tv_usec);
955         }
956 }
957
958
959 void
960 writefileimage()
961 {
962         ssize_t iret;
963         int fd = get_fd();
964
965         if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
966                 prterr("writefileimage: lseek");
967                 report_failure(171);
968         }
969         iret = write(fd, good_buf, file_size);
970         if ((off_t)iret != file_size) {
971                 if (iret == -1)
972                         prterr("writefileimage: write");
973                 else
974                         prt("short write: 0x%lx bytes instead of 0x%llx\n",
975                             (unsigned long)iret, 
976                             (unsigned long long)file_size);
977                 report_failure(172);
978         }
979         if (lite ? 0 : ftruncate(fd, file_size) == -1) {
980                 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
981                 prterr("writefileimage: ftruncate");
982                 report_failure(173);
983         }
984 }
985
986
987 void
988 docloseopen(void)
989 {
990         struct timeval t;
991         struct test_file *tf = get_tf();
992
993         if (testcalls <= simulatedopcount)
994                 return;
995
996         gettimeofday(&t, NULL);
997         log4(OP_CLOSEOPEN, file_size, (unsigned)file_size, 0, &t);
998
999         if (debug)
1000                 prt("%06lu %lu.%06lu close/open\n", testcalls, t.tv_sec,
1001                     t.tv_usec);
1002         if (close(tf->fd)) {
1003                 prterr("docloseopen: close");
1004                 report_failure(180);
1005         }
1006         if (!quiet && debug > 1) {
1007                 gettimeofday(&t, NULL);
1008                 prt("       %lu.%06lu close done\n", t.tv_sec, t.tv_usec);
1009         }
1010         tf->fd = open(tf->path, O_RDWR, 0);
1011         if (tf->fd < 0) {
1012                 prterr("docloseopen: open");
1013                 report_failure(181);
1014         }
1015         if (!quiet && debug > 1) {
1016                 gettimeofday(&t, NULL);
1017                 prt("       %lu.%06lu open done\n", t.tv_sec, t.tv_usec);
1018         }
1019 }
1020
1021
1022 void
1023 test(void)
1024 {
1025         unsigned long   offset;
1026         unsigned long   size = maxoplen;
1027         unsigned long   rv = random();
1028         unsigned long   op = rv % (3 + !lite + mapped_writes);
1029
1030         /* turn off the map read if necessary */
1031
1032         if (op == 2 && !mapped_reads)
1033             op = 0;
1034
1035         if (simulatedopcount > 0 && testcalls == simulatedopcount)
1036                 writefileimage();
1037
1038         testcalls++;
1039
1040         if (debugstart > 0 && testcalls >= debugstart)
1041                 debug = 1;
1042
1043         if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
1044                 prt("%lu...\n", testcalls);
1045
1046         /*
1047          * READ:        op = 0
1048          * WRITE:       op = 1
1049          * MAPREAD:     op = 2
1050          * TRUNCATE:    op = 3
1051          * MAPWRITE:    op = 3 or 4
1052          */
1053         if (lite ? 0 : op == 3 && (style & 1) == 0) /* vanilla truncate? */
1054                 dotruncate(random() % maxfilelen);
1055         else {
1056                 if (randomoplen)
1057                         size = random() % (maxoplen+1);
1058                 if (lite ? 0 : op == 3)
1059                         dotruncate(size);
1060                 else {
1061                         offset = random();
1062                         if (op == 1 || op == (lite ? 3 : 4)) {
1063                                 offset %= maxfilelen;
1064                                 if (offset + size > maxfilelen)
1065                                         size = maxfilelen - offset;
1066                                 if (op != 1)
1067                                         domapwrite(offset, size);
1068                                 else
1069                                         dowrite(offset, size);
1070                         } else {
1071                                 if (file_size)
1072                                         offset %= file_size;
1073                                 else
1074                                         offset = 0;
1075                                 if (offset + size > file_size)
1076                                         size = file_size - offset;
1077                                 if (op != 0)
1078                                         domapread(offset, size);
1079                                 else
1080                                         doread(offset, size);
1081                         }
1082                 }
1083         }
1084         if (sizechecks && testcalls > simulatedopcount)
1085                 check_size();
1086         if (closeprob && (rv >> 3) < (1 << 28) / closeprob)
1087                 docloseopen();
1088 }
1089
1090
1091 void
1092 cleanup(sig)
1093         int     sig;
1094 {
1095         if (sig)
1096                 prt("signal %d\n", sig);
1097         prt("testcalls = %lu\n", testcalls);
1098         exit(sig);
1099 }
1100
1101
1102 void
1103 usage(void)
1104 {
1105         fprintf(stdout, "usage: %s",
1106                 "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m "
1107 "start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t "
1108 "truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] "
1109 "[ -I random|rotate ] fname [additional paths to fname..]\n"
1110 "       -b opnum: beginning operation number (default 1)\n"
1111 "       -c P: 1 in P chance of file close+open at each op (default infinity)\n"
1112 "       -d: debug output for all operations [-d -d = more debugging]\n"
1113 "       -l flen: the upper bound on file size (default 262144)\n"
1114 "       -m startop:endop: monitor (print debug output) specified byte rang"
1115 "(default 0:infinity)\n"
1116 "       -n: no verifications of file size\n"
1117 "       -o oplen: the upper bound on operation size (default 65536)\n"
1118 "       -p progressinterval: debug output at specified operation interval\n"
1119 "       -q: quieter operation\n"
1120 "       -r readbdy: 4096 would make reads page aligned (default 1)\n"
1121 "       -s style: 1 gives smaller truncates (default 0)\n"
1122 "       -t truncbdy: 4096 would make truncates page aligned (default 1)\n"
1123 "       -w writebdy: 4096 would make writes page aligned (default 1)\n"
1124 "       -D startingop: debug output starting at specified operation\n"
1125 "       -L: fsxLite - no file creations & no file size changes\n"
1126 "       -N numops: total # operations to do (default infinity)\n"
1127 "       -O: use oplen (see -o flag) for every op (default random)\n"
1128 "       -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n"
1129 "       -S seed: for random # generator (default 1) 0 gets timestamp\n"
1130 "       -W: mapped write operations DISabled\n"
1131 "        -R: read() system calls only (mapped reads disabled)\n"
1132 "       -I: When multiple paths to the file are given each operation uses"
1133 "           a different path.  Iterate through them in order with 'rotate'"
1134 "           or chose then at 'random'.  (defaults to random)\n"
1135 "       fname: this filename is REQUIRED (no default)\n");
1136         exit(90);
1137 }
1138
1139
1140 int
1141 getnum(char *s, char **e)
1142 {
1143         int ret = -1;
1144
1145         *e = (char *) 0;
1146         ret = strtol(s, e, 0);
1147         if (*e)
1148                 switch (**e) {
1149                 case 'b':
1150                 case 'B':
1151                         ret *= 512;
1152                         *e = *e + 1;
1153                         break;
1154                 case 'k':
1155                 case 'K':
1156                         ret *= 1024;
1157                         *e = *e + 1;
1158                         break;
1159                 case 'm':
1160                 case 'M':
1161                         ret *= 1024*1024;
1162                         *e = *e + 1;
1163                         break;
1164                 case 'w':
1165                 case 'W':
1166                         ret *= 4;
1167                         *e = *e + 1;
1168                         break;
1169                 }
1170         return (ret);
1171 }
1172
1173 int
1174 main(int argc, char **argv)
1175 {
1176         int     i, style, ch;
1177         char    *endp;
1178         int  dirpath = 0;
1179
1180         goodfile[0] = 0;
1181         logfile[0] = 0;
1182
1183         page_size = getpagesize();
1184         page_mask = page_size - 1;
1185
1186         setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
1187
1188         while ((ch = getopt(argc, argv, 
1189                                 "b:c:dl:m:no:p:qr:s:t:w:D:I:LN:OP:RS:W"))
1190                != EOF)
1191                 switch (ch) {
1192                 case 'b':
1193                         simulatedopcount = getnum(optarg, &endp);
1194                         if (!quiet)
1195                                 fprintf(stdout, "Will begin at operation"
1196                                         "%ld\n",
1197                                         simulatedopcount);
1198                         if (simulatedopcount == 0)
1199                                 usage();
1200                         simulatedopcount -= 1;
1201                         break;
1202                 case 'c':
1203                         closeprob = getnum(optarg, &endp);
1204                         if (!quiet)
1205                                 fprintf(stdout,
1206                                         "Chance of close/open is 1 in %d\n",
1207                                         closeprob);
1208                         if (closeprob <= 0)
1209                                 usage();
1210                         break;
1211                 case 'd':
1212                         debug++;
1213                         break;
1214                 case 'l':
1215                         maxfilelen = getnum(optarg, &endp);
1216                         if (maxfilelen <= 0)
1217                                 usage();
1218                         break;
1219                 case 'm':
1220                         monitorstart = getnum(optarg, &endp);
1221                         if (monitorstart < 0)
1222                                 usage();
1223                         if (!endp || *endp++ != ':')
1224                                 usage();
1225                         monitorend = getnum(endp, &endp);
1226                         if (monitorend < 0)
1227                                 usage();
1228                         if (monitorend == 0)
1229                                 monitorend = -1; /* aka infinity */
1230                         debug = 1;
1231                 case 'n':
1232                         sizechecks = 0;
1233                         break;
1234                 case 'o':
1235                         maxoplen = getnum(optarg, &endp);
1236                         if (maxoplen <= 0)
1237                                 usage();
1238                         break;
1239                 case 'p':
1240                         progressinterval = getnum(optarg, &endp);
1241                         if (progressinterval < 0)
1242                                 usage();
1243                         break;
1244                 case 'q':
1245                         quiet = 1;
1246                         break;
1247                 case 'r':
1248                         readbdy = getnum(optarg, &endp);
1249                         if (readbdy <= 0)
1250                                 usage();
1251                         break;
1252                 case 's':
1253                         style = getnum(optarg, &endp);
1254                         if (style < 0 || style > 1)
1255                                 usage();
1256                         break;
1257                 case 't':
1258                         truncbdy = getnum(optarg, &endp);
1259                         if (truncbdy <= 0)
1260                                 usage();
1261                         break;
1262                 case 'w':
1263                         writebdy = getnum(optarg, &endp);
1264                         if (writebdy <= 0)
1265                                 usage();
1266                         break;
1267                 case 'D':
1268                         debugstart = getnum(optarg, &endp);
1269                         if (debugstart < 1)
1270                                 usage();
1271                         break;
1272                 case 'I':
1273                         assign_fd_policy(optarg);
1274                         break;
1275                 case 'L':
1276                         lite = 1;
1277                         break;
1278                 case 'N':
1279                         numops = getnum(optarg, &endp);
1280                         if (numops < 0)
1281                                 usage();
1282                         break;
1283                 case 'O':
1284                         randomoplen = 0;
1285                         break;
1286                 case 'P':
1287                         strncpy(goodfile, optarg, sizeof(goodfile));
1288                         strcat(goodfile, "/");
1289                         strncpy(logfile, optarg, sizeof(logfile));
1290                         strcat(logfile, "/");
1291                         dirpath = 1;
1292                         break;
1293                 case 'R':
1294                         mapped_reads = 0;
1295                         break;
1296                 case 'S':
1297                         seed = getnum(optarg, &endp);
1298                         if (seed == 0)
1299                                 seed = time(0) % 10000;
1300                         if (!quiet)
1301                                 fprintf(stdout, "Seed set to %d\n", seed);
1302                         if (seed < 0)
1303                                 usage();
1304                         break;
1305                 case 'W':
1306                         mapped_writes = 0;
1307                         if (!quiet)
1308                                 fprintf(stdout, "mapped writes DISABLED\n");
1309                         break;
1310
1311                 default:
1312                         usage();
1313                         /* NOTREACHED */
1314                 }
1315         argc -= optind;
1316         argv += optind;
1317         if (argc < 1)
1318                 usage();
1319         fname = argv[0];
1320
1321         signal(SIGHUP,  cleanup);
1322         signal(SIGINT,  cleanup);
1323         signal(SIGPIPE, cleanup);
1324         signal(SIGALRM, cleanup);
1325         signal(SIGTERM, cleanup);
1326         signal(SIGXCPU, cleanup);
1327         signal(SIGXFSZ, cleanup);
1328         signal(SIGVTALRM,       cleanup);
1329         signal(SIGUSR1, cleanup);
1330         signal(SIGUSR2, cleanup);
1331
1332         initstate(seed, state, 256);
1333         setstate(state);
1334
1335         open_test_files(argv, argc);
1336
1337         strncat(goodfile, dirpath ? basename(fname) : fname, 256);
1338         strcat (goodfile, ".fsxgood");
1339         fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
1340         if (fsxgoodfd < 0) {
1341                 prterr(goodfile);
1342                 exit(92);
1343         }
1344         strncat(logfile, dirpath ? basename(fname) : fname, 256);
1345         strcat (logfile, ".fsxlog");
1346         fsxlogf = fopen(logfile, "w");
1347         if (fsxlogf == NULL) {
1348                 prterr(logfile);
1349                 exit(93);
1350         }
1351         if (lite) {
1352                 off_t ret;
1353                 int fd = get_fd();
1354                 file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END);
1355                 if (file_size == (off_t)-1) {
1356                         prterr(fname);
1357                         warn("main: lseek eof");
1358                         exit(94);
1359                 }
1360                 ret = lseek(fd, (off_t)0, SEEK_SET);
1361                 if (ret == (off_t)-1) {
1362                         prterr(fname);
1363                         warn("main: lseek 0");
1364                         exit(95);
1365                 }
1366         }
1367         original_buf = (char *) malloc(maxfilelen);
1368         for (i = 0; i < maxfilelen; i++)
1369                 original_buf[i] = random() % 256;
1370         good_buf = (char *) malloc(maxfilelen);
1371         memset(good_buf, '\0', maxfilelen);
1372         temp_buf = (char *) malloc(maxoplen);
1373         memset(temp_buf, '\0', maxoplen);
1374         if (lite) {     /* zero entire existing file */
1375                 ssize_t written;
1376                 int fd = get_fd();
1377
1378                 written = write(fd, good_buf, (size_t)maxfilelen);
1379                 if (written != maxfilelen) {
1380                         if (written == -1) {
1381                                 prterr(fname);
1382                                 warn("main: error on write");
1383                         } else
1384                                 warn("main: short write, 0x%x bytes instead"
1385                                         "of 0x%x\n",
1386                                      (unsigned)written, maxfilelen);
1387                         exit(98);
1388                 }
1389         } else
1390                 check_trunc_hack();
1391
1392         while (numops == -1 || numops--)
1393                 test();
1394
1395         close_test_files();
1396         prt("All operations completed A-OK!\n");
1397
1398         exit(0);
1399         return 0;
1400 }