Home | History | Annotate | Download | only in ioshark
      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