2 * Copyright (C) 1991, NeXT Computer, Inc. All Rights Reserverd.
5 * Author: Avadis Tevanian, Jr.
7 * File system exerciser.
9 * Rewritten 8/98 by Conrad Minshall.
12 #include <sys/types.h>
14 #if defined(sun) || defined(_UWIN) || defined(__linux)
15 # include <sys/param.h>
21 # include <sys/dirent.h>
37 # define L_SET SEEK_SET
40 # define L_INCR SEEK_CUR
43 # define L_XTND SEEK_END
46 #define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */
49 * A log entry is an operation and a bunch of arguments.
59 struct log_entry oplog
[LOGSIZE
]; /* the log */
60 int logptr
= 0; /* current position in log */
61 int logcount
= 0; /* total ops */
70 #define OP_CLOSEOPEN 4
76 #define PAGE_SIZE 4096
78 #define PAGE_MASK (PAGE_SIZE - 1)
80 char *original_buf
; /* a pointer to the original data */
81 char *good_buf
; /* a pointer to the correct data */
82 char *temp_buf
; /* a pointer to the current data */
83 char *fname
; /* name of our test file */
84 int fd
; /* fd for our test file */
89 unsigned long testcalls
= 0; /* calls to function "test" */
91 unsigned long simulatedopcount
= 0; /* -b flag */
92 int closeprob
= 0; /* -c flag */
93 int debug
= 0; /* -d flag */
94 unsigned long debugstart
= 0; /* -D flag */
95 unsigned long maxfilelen
= 256 * 1024; /* -l flag */
96 int sizechecks
= 1; /* -n flag disables them */
97 int maxoplen
= 64 * 1024; /* -o flag */
98 int quiet
= 0; /* -q flag */
99 unsigned long progressinterval
= 0; /* -p flag */
100 int readbdy
= 1; /* -r flag */
101 int style
= 0; /* -s flag */
102 int truncbdy
= 1; /* -t flag */
103 int writebdy
= 1; /* -w flag */
104 long monitorstart
= -1; /* -m flag */
105 long monitorend
= -1; /* -m flag */
106 int lite
= 0; /* -L flag */
107 long numops
= -1; /* -N flag */
108 int randomoplen
= 1; /* -O flag disables it */
109 int seed
= 1; /* -S flag */
110 int mapped_writes
= 1; /* -W flag disables */
111 int mapped_reads
= 1; /* -R flag disables it */
113 FILE *fsxlogf
= NULL
;
124 vfprintf(stdout
, fmt
, args
);
126 vfprintf(fsxlogf
, fmt
, args
);
133 prt("%s%s%s\n", prefix
, prefix
? ": " : "", strerror(errno
));
138 log4(int operation
, int arg0
, int arg1
, int arg2
)
140 struct log_entry
*le
;
143 le
->operation
= operation
;
145 le
->operation
= ~le
->operation
;
151 if (logptr
>= LOGSIZE
)
160 struct log_entry
*lp
;
162 prt("LOG DUMP (%d total operations):\n", logcount
);
163 if (logcount
< LOGSIZE
) {
170 for (; count
> 0; count
--) {
173 opnum
= i
+ 1 + (logcount
/ LOGSIZE
) * LOGSIZE
;
174 prt("%d(%d mod 256): ", opnum
, opnum
% 256);
176 if ((closeopen
= lp
->operation
< 0))
177 lp
->operation
= ~lp
->operation
;
179 switch (lp
->operation
) {
181 prt("MAPREAD\t0x%x thru 0x%x\t(0x%x bytes)", lp
->args
[0],
182 lp
->args
[0] + lp
->args
[1] - 1, lp
->args
[1]);
183 if (badoff
>= lp
->args
[0] && badoff
< lp
->args
[0] + lp
->args
[1])
187 prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)", lp
->args
[0],
188 lp
->args
[0] + lp
->args
[1] - 1, lp
->args
[1]);
189 if (badoff
>= lp
->args
[0] && badoff
< lp
->args
[0] + lp
->args
[1])
193 prt("READ\t0x%x thru 0x%x\t(0x%x bytes)", lp
->args
[0],
194 lp
->args
[0] + lp
->args
[1] - 1, lp
->args
[1]);
195 if (badoff
>= lp
->args
[0] && badoff
< lp
->args
[0] + lp
->args
[1])
199 prt("WRITE\t0x%x thru 0x%x\t(0x%x bytes)", lp
->args
[0],
200 lp
->args
[0] + lp
->args
[1] - 1, lp
->args
[1]);
201 if (lp
->args
[0] > lp
->args
[2])
203 else if (lp
->args
[0] + lp
->args
[1] > lp
->args
[2])
205 if ((badoff
>= lp
->args
[0] || badoff
>= lp
->args
[2])
206 && badoff
< lp
->args
[0] + lp
->args
[1])
210 down
= lp
->args
[0] < lp
->args
[1];
211 prt("TRUNCATE %s\tfrom 0x%x to 0x%x", down
? "DOWN" : "UP",
212 lp
->args
[1], lp
->args
[0]);
213 if (badoff
>= lp
->args
[!down
] && badoff
< lp
->args
[!!down
])
217 prt("SKIPPED (no operation)");
220 prt("BOGUS LOG ENTRY (operation code = %d)!", lp
->operation
);
223 prt("\n\t\tCLOSE/OPEN");
233 save_buffer(char *buffer
, off_t bufferlength
, int fd
)
236 ssize_t byteswritten
;
238 if (fd
<= 0 || bufferlength
== 0)
241 if (bufferlength
> SSIZE_MAX
) {
242 prt("fsx flaw: overflow in save_buffer\n");
246 off_t size_by_seek
= lseek(fd
, (off_t
) 0, L_XTND
);
247 if (size_by_seek
== (off_t
) - 1)
248 prterr("save_buffer: lseek eof");
249 else if (bufferlength
> size_by_seek
) {
250 warn("save_buffer: .fsxgood file too short... will save 0x%qx bytes instead of 0x%qx\n", (unsigned long long)size_by_seek
, (unsigned long long)bufferlength
);
251 bufferlength
= size_by_seek
;
255 ret
= lseek(fd
, (off_t
) 0, SEEK_SET
);
256 if (ret
== (off_t
) - 1)
257 prterr("save_buffer: lseek 0");
259 byteswritten
= write(fd
, buffer
, (size_t) bufferlength
);
260 if (byteswritten
!= bufferlength
) {
261 if (byteswritten
== -1)
262 prterr("save_buffer write");
264 warn("save_buffer: short write, 0x%x bytes instead of 0x%qx\n",
265 (unsigned)byteswritten
, (unsigned long long)bufferlength
);
271 report_failure(int status
)
277 save_buffer(good_buf
, file_size
, fsxgoodfd
);
278 prt("Correct content saved for comparison\n");
279 prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n", fname
, fname
);
287 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
288 *(((unsigned char *)(cp)) + 1)))
291 check_buffers(unsigned offset
, unsigned size
)
299 if (bcmp(good_buf
+ offset
, temp_buf
, size
) != 0) {
300 prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n", offset
, size
);
301 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
303 c
= good_buf
[offset
];
307 bad
= short_at(&temp_buf
[i
]);
308 prt("0x%5x\t0x%04x\t0x%04x", offset
,
309 short_at(&good_buf
[offset
]), bad
);
310 op
= temp_buf
[offset
& 1 ? i
+ 1 : i
];
322 prt("operation# (mod 256) for the bad data may be %u\n",
323 ((unsigned)op
& 0xff));
325 prt("operation# (mod 256) for the bad data unknown, check HOLE and EXTEND ops\n");
327 prt("????????????????\n");
339 if (fstat(fd
, &statbuf
)) {
340 prterr("check_size: fstat");
341 statbuf
.st_size
= -1;
343 size_by_seek
= lseek(fd
, (off_t
) 0, L_XTND
);
344 if (file_size
!= statbuf
.st_size
|| file_size
!= size_by_seek
) {
345 prt("Size error: expected 0x%qx stat 0x%qx seek 0x%qx\n",
346 (unsigned long long)file_size
,
347 (unsigned long long)statbuf
.st_size
,
348 (unsigned long long)size_by_seek
);
355 check_trunc_hack(void)
359 ftruncate(fd
, (off_t
) 0);
360 ftruncate(fd
, (off_t
) 100000);
362 if (statbuf
.st_size
!= (off_t
) 100000) {
363 prt("no extend on truncate! not posix!\n");
371 doread(unsigned offset
, unsigned size
)
376 offset
-= offset
% readbdy
;
378 if (!quiet
&& testcalls
> simulatedopcount
)
379 prt("skipping zero size read\n");
380 log4(OP_SKIPPED
, OP_READ
, offset
, size
);
383 if (size
+ offset
> file_size
) {
384 if (!quiet
&& testcalls
> simulatedopcount
)
385 prt("skipping seek/read past end of file\n");
386 log4(OP_SKIPPED
, OP_READ
, offset
, size
);
390 log4(OP_READ
, offset
, size
, 0);
392 if (testcalls
<= simulatedopcount
)
396 && ((progressinterval
&& testcalls
% progressinterval
== 0)
398 && (monitorstart
== -1 || offset
+ size
> monitorstart
)
399 && (monitorend
== -1 || offset
<= monitorend
))))
400 prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls
, offset
,
401 offset
+ size
- 1, size
);
402 ret
= lseek(fd
, (off_t
) offset
, SEEK_SET
);
403 if (ret
== (off_t
) - 1) {
404 prterr("doread: lseek");
407 iret
= read(fd
, temp_buf
, size
);
410 prterr("doread: read");
412 prt("short read: 0x%x bytes instead of 0x%x\n", iret
, size
);
415 check_buffers(offset
, size
);
420 domapread(unsigned offset
, unsigned size
)
426 offset
-= offset
% readbdy
;
428 if (!quiet
&& testcalls
> simulatedopcount
)
429 prt("skipping zero size read\n");
430 log4(OP_SKIPPED
, OP_MAPREAD
, offset
, size
);
433 if (size
+ offset
> file_size
) {
434 if (!quiet
&& testcalls
> simulatedopcount
)
435 prt("skipping seek/read past end of file\n");
436 log4(OP_SKIPPED
, OP_MAPREAD
, offset
, size
);
440 log4(OP_MAPREAD
, offset
, size
, 0);
442 if (testcalls
<= simulatedopcount
)
446 && ((progressinterval
&& testcalls
% progressinterval
== 0)
448 && (monitorstart
== -1 || offset
+ size
> monitorstart
)
449 && (monitorend
== -1 || offset
<= monitorend
))))
450 prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls
, offset
,
451 offset
+ size
- 1, size
);
453 pg_offset
= offset
& PAGE_MASK
;
454 map_size
= pg_offset
+ size
;
457 (char *)mmap(0, map_size
, PROT_READ
, MAP_FILE
, fd
,
458 (off_t
) (offset
- pg_offset
))) == (char *)-1) {
459 prterr("domapread: mmap");
462 memcpy(temp_buf
, p
+ pg_offset
, size
);
463 if (munmap(p
, map_size
) != 0) {
464 prterr("domapread: munmap");
468 check_buffers(offset
, size
);
473 gendata(char *original_buf
, char *good_buf
, unsigned offset
, unsigned size
)
476 good_buf
[offset
] = testcalls
% 256;
478 good_buf
[offset
] += original_buf
[offset
];
485 dowrite(unsigned offset
, unsigned size
)
490 offset
-= offset
% writebdy
;
492 if (!quiet
&& testcalls
> simulatedopcount
)
493 prt("skipping zero size write\n");
494 log4(OP_SKIPPED
, OP_WRITE
, offset
, size
);
498 log4(OP_WRITE
, offset
, size
, file_size
);
500 gendata(original_buf
, good_buf
, offset
, size
);
501 if (file_size
< offset
+ size
) {
502 if (file_size
< offset
)
503 bzero(good_buf
+ file_size
, offset
- file_size
);
504 file_size
= offset
+ size
;
506 warn("Lite file size bug in fsx!");
511 if (testcalls
<= simulatedopcount
)
515 && ((progressinterval
&& testcalls
% progressinterval
== 0)
517 && (monitorstart
== -1 || offset
+ size
> monitorstart
)
518 && (monitorend
== -1 || offset
<= monitorend
))))
519 prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls
, offset
,
520 offset
+ size
- 1, size
);
521 ret
= lseek(fd
, (off_t
) offset
, SEEK_SET
);
522 if (ret
== (off_t
) - 1) {
523 prterr("dowrite: lseek");
526 iret
= write(fd
, good_buf
+ offset
, size
);
529 prterr("dowrite: write");
531 prt("short write: 0x%x bytes instead of 0x%x\n", iret
, size
);
538 domapwrite(unsigned offset
, unsigned size
)
545 offset
-= offset
% writebdy
;
547 if (!quiet
&& testcalls
> simulatedopcount
)
548 prt("skipping zero size write\n");
549 log4(OP_SKIPPED
, OP_MAPWRITE
, offset
, size
);
552 cur_filesize
= file_size
;
554 log4(OP_MAPWRITE
, offset
, size
, 0);
556 gendata(original_buf
, good_buf
, offset
, size
);
557 if (file_size
< offset
+ size
) {
558 if (file_size
< offset
)
559 bzero(good_buf
+ file_size
, offset
- file_size
);
560 file_size
= offset
+ size
;
562 warn("Lite file size bug in fsx!");
567 if (testcalls
<= simulatedopcount
)
571 && ((progressinterval
&& testcalls
% progressinterval
== 0)
573 && (monitorstart
== -1 || offset
+ size
> monitorstart
)
574 && (monitorend
== -1 || offset
<= monitorend
))))
575 prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls
,
576 offset
, offset
+ size
- 1, size
);
578 if (file_size
> cur_filesize
) {
579 if (ftruncate(fd
, file_size
) == -1) {
580 prterr("domapwrite: ftruncate");
584 pg_offset
= offset
& PAGE_MASK
;
585 map_size
= pg_offset
+ size
;
588 (char *)mmap(0, map_size
, PROT_READ
| PROT_WRITE
,
589 MAP_FILE
| MAP_SHARED
, fd
,
590 (off_t
) (offset
- pg_offset
))) == (char *)-1) {
591 prterr("domapwrite: mmap");
594 memcpy(p
+ pg_offset
, good_buf
+ offset
, size
);
595 if (msync(p
, map_size
, 0) != 0) {
596 prterr("domapwrite: msync");
599 if (munmap(p
, map_size
) != 0) {
600 prterr("domapwrite: munmap");
607 dotruncate(unsigned size
)
609 int oldsize
= file_size
;
611 size
-= size
% truncbdy
;
612 if (size
> biggest
) {
614 if (!quiet
&& testcalls
> simulatedopcount
)
615 prt("truncating to largest ever: 0x%x\n", size
);
618 log4(OP_TRUNCATE
, size
, (unsigned)file_size
, 0);
620 if (size
> file_size
)
621 bzero(good_buf
+ file_size
, size
- file_size
);
624 if (testcalls
<= simulatedopcount
)
627 if ((progressinterval
&& testcalls
% progressinterval
== 0)
629 && (monitorstart
== -1 || monitorend
== -1 || size
<= monitorend
)))
630 prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls
, oldsize
, size
);
631 if (ftruncate(fd
, (off_t
) size
) == -1) {
632 prt("ftruncate1: %x\n", size
);
633 prterr("dotruncate: ftruncate");
644 if (lseek(fd
, (off_t
) 0, SEEK_SET
) == (off_t
) - 1) {
645 prterr("writefileimage: lseek");
648 iret
= write(fd
, good_buf
, file_size
);
649 if ((off_t
) iret
!= file_size
) {
651 prterr("writefileimage: write");
653 prt("short write: 0x%x bytes instead of 0x%qx\n", iret
,
654 (unsigned long long)file_size
);
657 if (lite
? 0 : ftruncate(fd
, file_size
) == -1) {
658 prt("ftruncate2: %qx\n", (unsigned long long)file_size
);
659 prterr("writefileimage: ftruncate");
668 if (testcalls
<= simulatedopcount
)
672 prt("%lu close/open\n", testcalls
);
674 prterr("docloseopen: close");
677 fd
= open(fname
, O_RDWR
, 0);
679 prterr("docloseopen: open");
688 unsigned long offset
;
689 unsigned long size
= maxoplen
;
690 unsigned long rv
= random();
691 unsigned long op
= rv
% (3 + !lite
+ mapped_writes
);
693 /* turn off the map read if necessary */
695 if (op
== 2 && !mapped_reads
)
698 if (simulatedopcount
> 0 && testcalls
== simulatedopcount
)
703 closeopen
= (rv
>> 3) < (1 << 28) / closeprob
;
705 if (debugstart
> 0 && testcalls
>= debugstart
)
708 if (!quiet
&& testcalls
< simulatedopcount
&& testcalls
% 100000 == 0)
709 prt("%lu...\n", testcalls
);
716 * MAPWRITE: op = 3 or 4
718 if (lite
? 0 : op
== 3 && (style
& 1) == 0) /* vanilla truncate? */
719 dotruncate(random() % maxfilelen
);
722 size
= random() % (maxoplen
+ 1);
723 if (lite
? 0 : op
== 3)
727 if (op
== 1 || op
== (lite
? 3 : 4)) {
728 offset
%= maxfilelen
;
729 if (offset
+ size
> maxfilelen
)
730 size
= maxfilelen
- offset
;
732 domapwrite(offset
, size
);
734 dowrite(offset
, size
);
740 if (offset
+ size
> file_size
)
741 size
= file_size
- offset
;
743 domapread(offset
, size
);
745 doread(offset
, size
);
749 if (sizechecks
&& testcalls
> simulatedopcount
)
760 prt("signal %d\n", sig
);
761 prt("testcalls = %lu\n", testcalls
);
769 fprintf(stdout
, "usage: %s",
770 "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
771 -b opnum: beginning operation number (default 1)\n\
772 -c P: 1 in P chance of file close+open at each op (default infinity)\n\
773 -d: debug output for all operations\n\
774 -l flen: the upper bound on file size (default 262144)\n\
775 -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\
776 -n: no verifications of file size\n\
777 -o oplen: the upper bound on operation size (default 65536)\n\
778 -p progressinterval: debug output at specified operation interval\n\
779 -q: quieter operation\n\
780 -r readbdy: 4096 would make reads page aligned (default 1)\n\
781 -s style: 1 gives smaller truncates (default 0)\n\
782 -t truncbdy: 4096 would make truncates page aligned (default 1)\n\
783 -w writebdy: 4096 would make writes page aligned (default 1)\n\
784 -D startingop: debug output starting at specified operation\n\
785 -L: fsxLite - no file creations & no file size changes\n\
786 -N numops: total # operations to do (default infinity)\n\
787 -O: use oplen (see -o flag) for every op (default random)\n\
788 -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n\
789 -S seed: for random # generator (default 1) 0 gets timestamp\n\
790 -W: mapped write operations DISabled\n\
791 -R: read() system calls only (mapped reads disabled)\n\
792 fname: this filename is REQUIRED (no default)\n");
798 getnum(char *s
, char **e
)
803 ret
= strtol(s
, e
, 0);
832 main(int argc
, char **argv
)
842 setvbuf(stdout
, NULL
, _IOLBF
, 0); /* line buffered stdout */
844 while ((ch
= getopt(argc
, argv
, "b:c:dl:m:no:p:qr:s:t:w:D:LN:OP:RS:W"))
848 simulatedopcount
= getnum(optarg
, &endp
);
850 fprintf(stdout
, "Will begin at operation %ld\n",
852 if (simulatedopcount
== 0)
854 simulatedopcount
-= 1;
857 closeprob
= getnum(optarg
, &endp
);
859 fprintf(stdout
, "Chance of close/open is 1 in %d\n",
868 maxfilelen
= getnum(optarg
, &endp
);
873 monitorstart
= getnum(optarg
, &endp
);
874 if (monitorstart
< 0)
876 if (!endp
|| *endp
++ != ':')
878 monitorend
= getnum(endp
, &endp
);
882 monitorend
= -1; /* aka infinity */
888 maxoplen
= getnum(optarg
, &endp
);
893 progressinterval
= getnum(optarg
, &endp
);
894 if (progressinterval
< 0)
901 readbdy
= getnum(optarg
, &endp
);
906 style
= getnum(optarg
, &endp
);
907 if (style
< 0 || style
> 1)
911 truncbdy
= getnum(optarg
, &endp
);
916 writebdy
= getnum(optarg
, &endp
);
921 debugstart
= getnum(optarg
, &endp
);
929 numops
= getnum(optarg
, &endp
);
937 strncpy(goodfile
, optarg
, sizeof(goodfile
));
938 strcat(goodfile
, "/");
939 strncpy(logfile
, optarg
, sizeof(logfile
));
940 strcat(logfile
, "/");
946 seed
= getnum(optarg
, &endp
);
948 seed
= time(0) % 10000;
950 fprintf(stdout
, "Seed set to %d\n", seed
);
957 fprintf(stdout
, "mapped writes DISABLED\n");
970 signal(SIGHUP
, cleanup
);
971 signal(SIGINT
, cleanup
);
972 signal(SIGPIPE
, cleanup
);
973 signal(SIGALRM
, cleanup
);
974 signal(SIGTERM
, cleanup
);
975 signal(SIGXCPU
, cleanup
);
976 signal(SIGXFSZ
, cleanup
);
977 signal(SIGVTALRM
, cleanup
);
978 signal(SIGUSR1
, cleanup
);
979 signal(SIGUSR2
, cleanup
);
981 initstate(seed
, state
, 256);
983 fd
= open(fname
, O_RDWR
| (lite
? 0 : O_CREAT
| O_TRUNC
), 0666);
988 strncat(goodfile
, fname
, 256);
989 strcat(goodfile
, ".fsxgood");
990 fsxgoodfd
= open(goodfile
, O_RDWR
| O_CREAT
| O_TRUNC
, 0666);
995 strncat(logfile
, fname
, 256);
996 strcat(logfile
, ".fsxlog");
997 fsxlogf
= fopen(logfile
, "w");
998 if (fsxlogf
== NULL
) {
1004 file_size
= maxfilelen
= lseek(fd
, (off_t
) 0, L_XTND
);
1005 if (file_size
== (off_t
) - 1) {
1007 warn("main: lseek eof");
1010 ret
= lseek(fd
, (off_t
) 0, SEEK_SET
);
1011 if (ret
== (off_t
) - 1) {
1013 warn("main: lseek 0");
1017 original_buf
= (char *)malloc(maxfilelen
);
1018 for (i
= 0; i
< maxfilelen
; i
++)
1019 original_buf
[i
] = random() % 256;
1020 good_buf
= calloc(1, maxfilelen
);
1021 temp_buf
= calloc(1, maxoplen
);
1022 if (lite
) { /* zero entire existing file */
1025 written
= write(fd
, good_buf
, (size_t) maxfilelen
);
1026 if (written
!= maxfilelen
) {
1027 if (written
== -1) {
1029 warn("main: error on write");
1031 warn("main: short write, 0x%x bytes instead of 0x%x\n",
1032 (unsigned)written
, (unsigned)maxfilelen
);
1038 while (numops
== -1 || numops
--)
1045 prt("All operations completed A-OK!\n");