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