1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <stdio.h> 18 #include <sys/time.h> 19 #include <sys/types.h> 20 #include <unistd.h> 21 #include <sys/syscall.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <sys/stat.h> 25 #include <sys/errno.h> 26 #include <fcntl.h> 27 #include <string.h> 28 #include <assert.h> 29 #include <pthread.h> 30 #include <sys/statfs.h> 31 #include <sys/resource.h> 32 #include <inttypes.h> 33 #include "ioshark.h" 34 #define IOSHARK_MAIN 35 #include "ioshark_bench.h" 36 37 /* 38 * Note on "quick" mode where we do reads on existing /system, 39 * /vendor and other files in ro partitions, instead of creating 40 * them. The ioshark compiler builds up a table of all the files 41 * in /system, /vendor and other ro partitions. For files in this 42 * list, the benchmark skips the pre-creation of these files and 43 * reads them directly. 44 * The code relevant to this is in *filename_cache*. 45 */ 46 47 char *progname; 48 49 #define MAX_INPUT_FILES 8192 50 #define MAX_THREADS 8192 51 52 struct thread_state_s { 53 char *filename; 54 FILE *fp; 55 int num_files; 56 void *db_handle; 57 }; 58 59 struct thread_state_s thread_state[MAX_INPUT_FILES]; 60 int num_input_files = 0; 61 int next_input_file; 62 63 pthread_t tid[MAX_THREADS]; 64 65 /* 66 * Global options 67 */ 68 int do_delay = 0; 69 int verbose = 0; 70 int summary_mode = 0; 71 int quick_mode = 0; 72 char *blockdev_name = NULL; /* if user would like to specify blockdev */ 73 74 #if 0 75 static long gettid() 76 { 77 return syscall(__NR_gettid); 78 } 79 #endif 80 81 void usage() 82 { 83 fprintf(stderr, "%s [-b blockdev_name] [-d preserve_delays] [-n num_iterations] [-t num_threads] -q -v | -s <list of parsed input files>\n", 84 progname); 85 fprintf(stderr, "%s -s, -v are mutually exclusive\n", 86 progname); 87 exit(EXIT_FAILURE); 88 } 89 90 pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER; 91 pthread_mutex_t stats_mutex = PTHREAD_MUTEX_INITIALIZER; 92 pthread_mutex_t work_mutex = PTHREAD_MUTEX_INITIALIZER; 93 struct timeval aggregate_file_create_time; 94 struct timeval debug_file_create_time; 95 struct timeval aggregate_file_remove_time; 96 struct timeval aggregate_IO_time; 97 struct timeval aggregate_delay_time; 98 99 u_int64_t aggr_op_counts[IOSHARK_MAX_FILE_OP]; 100 struct rw_bytes_s aggr_io_rw_bytes; 101 struct rw_bytes_s aggr_create_rw_bytes; 102 103 /* 104 * Locking needed here because aggregate_delay_time is updated 105 * from multiple threads concurrently. 106 */ 107 static void 108 update_time(struct timeval *aggr_time, 109 struct timeval *delta_time) 110 { 111 struct timeval tmp; 112 113 pthread_mutex_lock(&time_mutex); 114 timeradd(aggr_time, delta_time, &tmp); 115 *aggr_time = tmp; 116 pthread_mutex_unlock(&time_mutex); 117 } 118 119 static void 120 update_op_counts(u_int64_t *op_counts) 121 { 122 int i; 123 124 pthread_mutex_lock(&stats_mutex); 125 for (i = IOSHARK_LSEEK ; i < IOSHARK_MAX_FILE_OP ; i++) 126 aggr_op_counts[i] += op_counts[i]; 127 pthread_mutex_unlock(&stats_mutex); 128 } 129 130 static void 131 update_byte_counts(struct rw_bytes_s *dest, struct rw_bytes_s *delta) 132 { 133 pthread_mutex_lock(&stats_mutex); 134 dest->bytes_read += delta->bytes_read; 135 dest->bytes_written += delta->bytes_written; 136 pthread_mutex_unlock(&stats_mutex); 137 } 138 139 static int work_next_file; 140 static int work_num_files; 141 142 void 143 init_work(int next_file, int num_files) 144 { 145 pthread_mutex_lock(&work_mutex); 146 work_next_file = next_file; 147 work_num_files = work_next_file + num_files; 148 pthread_mutex_unlock(&work_mutex); 149 } 150 151 /* Dole out the next file to work on to the thread */ 152 static struct thread_state_s * 153 get_work() 154 { 155 struct thread_state_s *work = NULL; 156 157 pthread_mutex_lock(&work_mutex); 158 if (work_next_file < work_num_files) 159 work = &thread_state[work_next_file++]; 160 pthread_mutex_unlock(&work_mutex); 161 return work; 162 } 163 164 static void 165 create_files(struct thread_state_s *state) 166 { 167 int i; 168 struct ioshark_file_state file_state; 169 char path[MAX_IOSHARK_PATHLEN]; 170 void *db_node; 171 struct rw_bytes_s rw_bytes; 172 char *filename; 173 int readonly; 174 175 memset(&rw_bytes, 0, sizeof(struct rw_bytes_s)); 176 for (i = 0 ; i < state->num_files ; i++) { 177 if (ioshark_read_file_state(state->fp, &file_state) != 1) { 178 fprintf(stderr, "%s read error tracefile\n", 179 progname); 180 exit(EXIT_FAILURE); 181 } 182 /* 183 * Check to see if the file is in a readonly partition, 184 * in which case, we don't have to pre-create the file 185 * we can just read the existing file. 186 */ 187 filename = 188 get_ro_filename(file_state.global_filename_ix); 189 if (quick_mode) 190 assert(filename != NULL); 191 if (quick_mode == 0 || 192 is_readonly_mount(filename, file_state.size) == 0) { 193 sprintf(path, "file.%d.%"PRIu64"", 194 (int)(state - thread_state), 195 file_state.fileno); 196 create_file(path, file_state.size, 197 &rw_bytes); 198 filename = path; 199 readonly = 0; 200 } else { 201 readonly = 1; 202 } 203 db_node = files_db_add_byfileno(state->db_handle, 204 file_state.fileno, 205 readonly); 206 files_db_update_size(db_node, file_state.size); 207 files_db_update_filename(db_node, filename); 208 } 209 update_byte_counts(&aggr_create_rw_bytes, &rw_bytes); 210 } 211 212 static void 213 do_one_io(void *db_node, 214 struct ioshark_file_operation *file_op, 215 u_int64_t *op_counts, 216 struct rw_bytes_s *rw_bytes, 217 char **bufp, int *buflen) 218 { 219 assert(file_op->ioshark_io_op < IOSHARK_MAX_FILE_OP); 220 op_counts[file_op->ioshark_io_op]++; 221 switch (file_op->ioshark_io_op) { 222 int ret; 223 char *p; 224 int fd; 225 226 case IOSHARK_LSEEK: 227 case IOSHARK_LLSEEK: 228 ret = lseek(files_db_get_fd(db_node), 229 file_op->lseek_offset, 230 file_op->lseek_action); 231 if (ret < 0) { 232 fprintf(stderr, 233 "%s: lseek(%s %"PRIu64" %d) returned error %d\n", 234 progname, files_db_get_filename(db_node), 235 file_op->lseek_offset, 236 file_op->lseek_action, errno); 237 exit(EXIT_FAILURE); 238 } 239 break; 240 case IOSHARK_PREAD64: 241 p = get_buf(bufp, buflen, file_op->prw_len, 0); 242 ret = pread(files_db_get_fd(db_node), p, 243 file_op->prw_len, file_op->prw_offset); 244 rw_bytes->bytes_read += file_op->prw_len; 245 if (ret < 0) { 246 fprintf(stderr, 247 "%s: pread(%s %"PRIu64" %"PRIu64") error %d\n", 248 progname, 249 files_db_get_filename(db_node), 250 file_op->prw_len, 251 file_op->prw_offset, errno); 252 exit(EXIT_FAILURE); 253 } 254 break; 255 case IOSHARK_PWRITE64: 256 p = get_buf(bufp, buflen, file_op->prw_len, 1); 257 ret = pwrite(files_db_get_fd(db_node), p, 258 file_op->prw_len, file_op->prw_offset); 259 rw_bytes->bytes_written += file_op->prw_len; 260 if (ret < 0) { 261 fprintf(stderr, 262 "%s: pwrite(%s %"PRIu64" %"PRIu64") error %d\n", 263 progname, 264 files_db_get_filename(db_node), 265 file_op->prw_len, 266 file_op->prw_offset, errno); 267 exit(EXIT_FAILURE); 268 } 269 break; 270 case IOSHARK_READ: 271 p = get_buf(bufp, buflen, file_op->rw_len, 0); 272 ret = read(files_db_get_fd(db_node), p, 273 file_op->rw_len); 274 rw_bytes->bytes_read += file_op->rw_len; 275 if (ret < 0) { 276 fprintf(stderr, 277 "%s: read(%s %"PRIu64") error %d\n", 278 progname, 279 files_db_get_filename(db_node), 280 file_op->rw_len, 281 errno); 282 exit(EXIT_FAILURE); 283 } 284 break; 285 case IOSHARK_WRITE: 286 p = get_buf(bufp, buflen, file_op->rw_len, 1); 287 ret = write(files_db_get_fd(db_node), p, 288 file_op->rw_len); 289 rw_bytes->bytes_written += file_op->rw_len; 290 if (ret < 0) { 291 fprintf(stderr, 292 "%s: write(%s %"PRIu64") error %d\n", 293 progname, 294 files_db_get_filename(db_node), 295 file_op->rw_len, 296 errno); 297 exit(EXIT_FAILURE); 298 } 299 break; 300 case IOSHARK_MMAP: 301 case IOSHARK_MMAP2: 302 ioshark_handle_mmap(db_node, file_op, 303 bufp, buflen, op_counts, 304 rw_bytes); 305 break; 306 case IOSHARK_OPEN: 307 if (file_op->open_flags & O_CREAT) { 308 fd = open(files_db_get_filename(db_node), 309 file_op->open_flags, 310 file_op->open_mode); 311 if (fd < 0) { 312 /* 313 * EEXIST error acceptable, others are fatal. 314 * Although we failed to O_CREAT the file (O_EXCL) 315 * We will force an open of the file before any 316 * IO. 317 */ 318 if (errno == EEXIST) { 319 return; 320 } else { 321 fprintf(stderr, 322 "%s: O_CREAT open(%s %x %o) error %d\n", 323 progname, 324 files_db_get_filename(db_node), 325 file_op->open_flags, 326 file_op->open_mode, errno); 327 exit(EXIT_FAILURE); 328 } 329 } 330 } else { 331 fd = open(files_db_get_filename(db_node), 332 file_op->open_flags); 333 if (fd < 0) { 334 if (file_op->open_flags & O_DIRECTORY) { 335 /* O_DIRECTORY open()s should fail */ 336 return; 337 } else { 338 fprintf(stderr, 339 "%s: open(%s %x) error %d\n", 340 progname, 341 files_db_get_filename(db_node), 342 file_op->open_flags, 343 errno); 344 exit(EXIT_FAILURE); 345 } 346 } 347 } 348 files_db_close_fd(db_node); 349 files_db_update_fd(db_node, fd); 350 break; 351 case IOSHARK_FSYNC: 352 case IOSHARK_FDATASYNC: 353 if (file_op->ioshark_io_op == IOSHARK_FSYNC) { 354 ret = fsync(files_db_get_fd(db_node)); 355 if (ret < 0) { 356 fprintf(stderr, 357 "%s: fsync(%s) error %d\n", 358 progname, 359 files_db_get_filename(db_node), 360 errno); 361 exit(EXIT_FAILURE); 362 } 363 } else { 364 ret = fdatasync(files_db_get_fd(db_node)); 365 if (ret < 0) { 366 fprintf(stderr, 367 "%s: fdatasync(%s) error %d\n", 368 progname, 369 files_db_get_filename(db_node), 370 errno); 371 exit(EXIT_FAILURE); 372 } 373 } 374 break; 375 case IOSHARK_CLOSE: 376 ret = close(files_db_get_fd(db_node)); 377 if (ret < 0) { 378 fprintf(stderr, 379 "%s: close(%s) error %d\n", 380 progname, 381 files_db_get_filename(db_node), errno); 382 exit(EXIT_FAILURE); 383 } 384 files_db_update_fd(db_node, -1); 385 break; 386 default: 387 fprintf(stderr, "%s: unknown FILE_OP %d\n", 388 progname, file_op->ioshark_io_op); 389 exit(EXIT_FAILURE); 390 break; 391 } 392 } 393 394 static void 395 do_io(struct thread_state_s *state) 396 { 397 void *db_node; 398 struct ioshark_header header; 399 struct ioshark_file_operation file_op; 400 int fd; 401 int i; 402 char *buf = NULL; 403 int buflen = 0; 404 struct timeval total_delay_time; 405 u_int64_t op_counts[IOSHARK_MAX_FILE_OP]; 406 struct rw_bytes_s rw_bytes; 407 408 rewind(state->fp); 409 if (ioshark_read_header(state->fp, &header) != 1) { 410 fprintf(stderr, "%s read error %s\n", 411 progname, state->filename); 412 exit(EXIT_FAILURE); 413 } 414 /* 415 * First open and pre-create all the files. Indexed by fileno. 416 */ 417 timerclear(&total_delay_time); 418 memset(&rw_bytes, 0, sizeof(struct rw_bytes_s)); 419 memset(op_counts, 0, sizeof(op_counts)); 420 fseek(state->fp, 421 sizeof(struct ioshark_header) + 422 header.num_files * sizeof(struct ioshark_file_state), 423 SEEK_SET); 424 /* 425 * Loop over all the IOs, and launch each 426 */ 427 for (i = 0 ; i < (int)header.num_io_operations ; i++) { 428 if (ioshark_read_file_op(state->fp, &file_op) != 1) { 429 fprintf(stderr, "%s read error trace.outfile\n", 430 progname); 431 exit(EXIT_FAILURE); 432 } 433 if (do_delay) { 434 struct timeval start; 435 436 (void)gettimeofday(&start, (struct timezone *)NULL); 437 usleep(file_op.delta_us); 438 update_delta_time(&start, &total_delay_time); 439 } 440 db_node = files_db_lookup_byfileno(state->db_handle, 441 file_op.fileno); 442 if (db_node == NULL) { 443 fprintf(stderr, 444 "%s Can't lookup fileno %"PRIu64", fatal error\n", 445 progname, file_op.fileno); 446 fprintf(stderr, 447 "%s state filename %s, i %d\n", 448 progname, state->filename, i); 449 exit(EXIT_FAILURE); 450 } 451 if (file_op.ioshark_io_op != IOSHARK_OPEN && 452 files_db_get_fd(db_node) == -1) { 453 int openflags; 454 455 /* 456 * This is a hack to workaround the fact that we did not 457 * see an open() for this file until now. open() the 458 * file O_RDWR, so that we can perform the IO. 459 */ 460 if (files_db_readonly(db_node)) 461 openflags = O_RDONLY; 462 else 463 openflags = O_RDWR; 464 fd = open(files_db_get_filename(db_node), 465 openflags); 466 if (fd < 0) { 467 fprintf(stderr, "%s: open(%s %x) error %d\n", 468 progname, 469 files_db_get_filename(db_node), 470 openflags, 471 errno); 472 exit(EXIT_FAILURE); 473 } 474 files_db_update_fd(db_node, fd); 475 } 476 do_one_io(db_node, &file_op, 477 op_counts, &rw_bytes, &buf, &buflen); 478 } 479 files_db_fsync_discard_files(state->db_handle); 480 files_db_close_files(state->db_handle); 481 update_time(&aggregate_delay_time, &total_delay_time); 482 update_op_counts(op_counts); 483 update_byte_counts(&aggr_io_rw_bytes, &rw_bytes); 484 } 485 486 void * 487 io_thread(void *unused __attribute__((unused))) 488 { 489 struct thread_state_s *state; 490 491 srand(gettid()); 492 while ((state = get_work())) 493 do_io(state); 494 pthread_exit(NULL); 495 return(NULL); 496 } 497 498 static void 499 do_create(struct thread_state_s *state) 500 { 501 struct ioshark_header header; 502 503 if (ioshark_read_header(state->fp, &header) != 1) { 504 fprintf(stderr, "%s read error %s\n", 505 progname, state->filename); 506 exit(EXIT_FAILURE); 507 } 508 state->num_files = header.num_files; 509 state->db_handle = files_db_create_handle(); 510 create_files(state); 511 } 512 513 void * 514 create_files_thread(void *unused __attribute__((unused))) 515 { 516 struct thread_state_s *state; 517 518 while ((state = get_work())) 519 do_create(state); 520 pthread_exit(NULL); 521 return(NULL); 522 } 523 524 int 525 get_start_end(int *start_ix) 526 { 527 int i, j, ret_numfiles; 528 u_int64_t free_fs_bytes; 529 char *infile; 530 FILE *fp; 531 struct ioshark_header header; 532 struct ioshark_file_state file_state; 533 struct statfs fsstat; 534 static int fssize_clamp_next_index = 0; 535 static int chunk = 0; 536 537 if (fssize_clamp_next_index == num_input_files) 538 return 0; 539 if (statfs("/data/local/tmp", &fsstat) < 0) { 540 fprintf(stderr, "%s: Can't statfs /data/local/tmp\n", 541 progname); 542 exit(EXIT_FAILURE); 543 } 544 free_fs_bytes = (fsstat.f_bavail * fsstat.f_bsize) * 9 /10; 545 for (i = fssize_clamp_next_index; i < num_input_files; i++) { 546 infile = thread_state[i].filename; 547 fp = fopen(infile, "r"); 548 if (fp == NULL) { 549 fprintf(stderr, "%s: Can't open %s\n", 550 progname, infile); 551 exit(EXIT_FAILURE); 552 } 553 if (ioshark_read_header(fp, &header) != 1) { 554 fprintf(stderr, "%s read error %s\n", 555 progname, infile); 556 exit(EXIT_FAILURE); 557 } 558 for (j = 0 ; j < (int)header.num_files ; j++) { 559 if (ioshark_read_file_state(fp, &file_state) != 1) { 560 fprintf(stderr, "%s read error tracefile\n", 561 progname); 562 exit(EXIT_FAILURE); 563 } 564 if (quick_mode == 0 || 565 !is_readonly_mount( 566 get_ro_filename(file_state.global_filename_ix), 567 file_state.size)) { 568 if (file_state.size > free_fs_bytes) { 569 fclose(fp); 570 goto out; 571 } 572 free_fs_bytes -= file_state.size; 573 } 574 } 575 fclose(fp); 576 } 577 out: 578 if (verbose) { 579 if (chunk > 0 || i < num_input_files) { 580 printf("Breaking up input files, Chunk %d: %d to %d\n", 581 chunk++, fssize_clamp_next_index, i - 1); 582 } else { 583 printf("Entire Dataset fits start = %d to %d, free_bytes = %ju\n", 584 fssize_clamp_next_index, 585 i - fssize_clamp_next_index, 586 free_fs_bytes); 587 } 588 } 589 *start_ix = fssize_clamp_next_index; 590 ret_numfiles = i - fssize_clamp_next_index; 591 fssize_clamp_next_index = i; 592 return ret_numfiles; 593 } 594 595 int 596 ioshark_pthread_create(pthread_t *tidp, void *(*start_routine)(void *)) 597 { 598 pthread_attr_t attr; 599 600 pthread_attr_init(&attr); 601 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); 602 pthread_attr_setstacksize(&attr, (size_t)(1024*1024)); 603 return pthread_create(tidp, &attr, start_routine, (void *)NULL); 604 } 605 606 void 607 wait_for_threads(int num_threads) 608 { 609 int i; 610 611 for (i = 0; i < num_threads; i++) { 612 pthread_join(tid[i], NULL); 613 tid[i] = 0; 614 } 615 } 616 617 #define IOSHARK_FD_LIM 8192 618 619 static void 620 sizeup_fd_limits(void) 621 { 622 struct rlimit r; 623 624 getrlimit(RLIMIT_NOFILE, &r); 625 if (r.rlim_cur >= IOSHARK_FD_LIM) 626 /* cur limit already at what we want */ 627 return; 628 /* 629 * Size up both the Max and Cur to IOSHARK_FD_LIM. 630 * If we are not running as root, this will fail, 631 * catch that below and exit. 632 */ 633 if (r.rlim_max < IOSHARK_FD_LIM) 634 r.rlim_max = IOSHARK_FD_LIM; 635 r.rlim_cur = IOSHARK_FD_LIM; 636 if (setrlimit(RLIMIT_NOFILE, &r) < 0) { 637 fprintf(stderr, "%s: Can't setrlimit (RLIMIT_NOFILE, 8192)\n", 638 progname); 639 exit(EXIT_FAILURE); 640 } 641 getrlimit(RLIMIT_NOFILE, &r); 642 if (r.rlim_cur < IOSHARK_FD_LIM) { 643 fprintf(stderr, "%s: Can't setrlimit up to 8192\n", 644 progname); 645 fprintf(stderr, "%s: Running as root ?\n", 646 progname); 647 exit(EXIT_FAILURE); 648 } 649 } 650 651 int 652 main(int argc, char **argv) 653 { 654 int i; 655 FILE *fp; 656 struct stat st; 657 char *infile; 658 int num_threads = 0; 659 int num_iterations = 1; 660 int c; 661 int num_files, start_file; 662 struct thread_state_s *state; 663 664 progname = argv[0]; 665 while ((c = getopt(argc, argv, "b:dn:st:qv")) != EOF) { 666 switch (c) { 667 case 'b': 668 blockdev_name = strdup(optarg); 669 break; 670 case 'd': 671 do_delay = 1; 672 break; 673 case 'n': 674 num_iterations = atoi(optarg); 675 break; 676 case 's': 677 /* Non-verbose summary mode for nightly runs */ 678 summary_mode = 1; 679 break; 680 case 't': 681 num_threads = atoi(optarg); 682 break; 683 case 'q': 684 /* 685 * If quick mode is enabled, then we won't 686 * pre-create files that we are doing IO on that 687 * live in readonly partitions (/system, /vendor etc) 688 */ 689 quick_mode = 1; 690 break; 691 case 'v': 692 verbose = 1; 693 break; 694 default: 695 usage(); 696 } 697 } 698 699 if ((verbose + summary_mode) == 2) 700 usage(); 701 702 if (num_threads > MAX_THREADS) 703 usage(); 704 705 if (optind == argc) 706 usage(); 707 708 sizeup_fd_limits(); 709 710 for (i = optind; i < argc; i++) { 711 infile = argv[i]; 712 if (stat(infile, &st) < 0) { 713 fprintf(stderr, "%s: Can't stat %s\n", 714 progname, infile); 715 exit(EXIT_FAILURE); 716 } 717 if (st.st_size == 0) { 718 fprintf(stderr, "%s: Empty file %s\n", 719 progname, infile); 720 continue; 721 } 722 fp = fopen(infile, "r"); 723 if (fp == NULL) { 724 fprintf(stderr, "%s: Can't open %s\n", 725 progname, infile); 726 continue; 727 } 728 thread_state[num_input_files].filename = infile; 729 thread_state[num_input_files].fp = fp; 730 num_input_files++; 731 } 732 733 if (num_input_files == 0) { 734 exit(EXIT_SUCCESS); 735 } 736 if (verbose) { 737 printf("Total Input Files = %d\n", num_input_files); 738 printf("Num Iterations = %d\n", num_iterations); 739 } 740 timerclear(&aggregate_file_create_time); 741 timerclear(&aggregate_file_remove_time); 742 timerclear(&aggregate_IO_time); 743 744 if (quick_mode) 745 init_filename_cache(); 746 747 capture_util_state_before(); 748 749 /* 750 * We pre-create the files that we need once and then we 751 * loop around N times doing IOs on the pre-created files. 752 * 753 * get_start_end() breaks up the total work here to make sure 754 * that all the files we need to pre-create fit into the 755 * available space in /data/local/tmp (hardcoded for now). 756 * 757 * If it won't fit, then we do several sweeps. 758 */ 759 while ((num_files = get_start_end(&start_file))) { 760 struct timeval time_for_pass; 761 762 /* Create files once */ 763 if (!summary_mode) 764 printf("Doing Pre-creation of Files\n"); 765 if (quick_mode && !summary_mode) 766 printf("Skipping Pre-creation of read-only Files\n"); 767 if (num_threads == 0 || num_threads > num_files) 768 num_threads = num_files; 769 (void)system("echo 3 > /proc/sys/vm/drop_caches"); 770 init_work(start_file, num_files); 771 (void)gettimeofday(&time_for_pass, 772 (struct timezone *)NULL); 773 for (i = 0; i < num_threads; i++) { 774 if (ioshark_pthread_create(&(tid[i]), 775 create_files_thread)) { 776 fprintf(stderr, 777 "%s: Can't create creator thread %d\n", 778 progname, i); 779 exit(EXIT_FAILURE); 780 } 781 } 782 wait_for_threads(num_threads); 783 update_delta_time(&time_for_pass, &aggregate_file_create_time); 784 /* Do the IOs N times */ 785 for (i = 0 ; i < num_iterations ; i++) { 786 (void)system("echo 3 > /proc/sys/vm/drop_caches"); 787 if (!summary_mode) { 788 if (num_iterations > 1) 789 printf("Starting Test. Iteration %d...\n", 790 i); 791 else 792 printf("Starting Test...\n"); 793 } 794 init_work(start_file, num_files); 795 (void)gettimeofday(&time_for_pass, 796 (struct timezone *)NULL); 797 for (c = 0; c < num_threads; c++) { 798 if (ioshark_pthread_create(&(tid[c]), 799 io_thread)) { 800 fprintf(stderr, 801 "%s: Can't create thread %d\n", 802 progname, c); 803 exit(EXIT_FAILURE); 804 } 805 } 806 wait_for_threads(num_threads); 807 update_delta_time(&time_for_pass, 808 &aggregate_IO_time); 809 } 810 811 /* 812 * We are done with the N iterations of IO. 813 * Destroy the files we pre-created. 814 */ 815 init_work(start_file, num_files); 816 while ((state = get_work())) { 817 struct timeval start; 818 819 (void)gettimeofday(&start, (struct timezone *)NULL); 820 files_db_unlink_files(state->db_handle); 821 update_delta_time(&start, &aggregate_file_remove_time); 822 files_db_free_memory(state->db_handle); 823 } 824 } 825 if (!summary_mode) { 826 printf("Total Creation time = %ju.%ju (msecs.usecs)\n", 827 get_msecs(&aggregate_file_create_time), 828 get_usecs(&aggregate_file_create_time)); 829 printf("Total Remove time = %ju.%ju (msecs.usecs)\n", 830 get_msecs(&aggregate_file_remove_time), 831 get_usecs(&aggregate_file_remove_time)); 832 if (do_delay) 833 printf("Total delay time = %ju.%ju (msecs.usecs)\n", 834 get_msecs(&aggregate_delay_time), 835 get_usecs(&aggregate_delay_time)); 836 printf("Total Test (IO) time = %ju.%ju (msecs.usecs)\n", 837 get_msecs(&aggregate_IO_time), 838 get_usecs(&aggregate_IO_time)); 839 if (verbose) 840 print_bytes("Upfront File Creation bytes", 841 &aggr_create_rw_bytes); 842 print_bytes("Total Test (IO) bytes", &aggr_io_rw_bytes); 843 if (verbose) 844 print_op_stats(aggr_op_counts); 845 report_cpu_disk_util(); 846 } else { 847 printf("%ju.%ju ", 848 get_msecs(&aggregate_file_create_time), 849 get_usecs(&aggregate_file_create_time)); 850 printf("%ju.%ju ", 851 get_msecs(&aggregate_file_remove_time), 852 get_usecs(&aggregate_file_remove_time)); 853 if (do_delay) 854 printf("%ju.%ju ", 855 get_msecs(&aggregate_delay_time), 856 get_usecs(&aggregate_delay_time)); 857 printf("%ju.%ju ", 858 get_msecs(&aggregate_IO_time), 859 get_usecs(&aggregate_IO_time)); 860 print_bytes(NULL, &aggr_io_rw_bytes); 861 report_cpu_disk_util(); 862 printf("\n"); 863 } 864 if (quick_mode) 865 free_filename_cache(); 866 } 867