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