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