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 
usage()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
update_time(struct timeval * aggr_time,struct timeval * delta_time)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
update_op_counts(u_int64_t * op_counts)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
update_byte_counts(struct rw_bytes_s * dest,struct rw_bytes_s * delta)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
init_work(int next_file,int num_files)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 *
get_work()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
create_files(struct thread_state_s * state)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
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)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
do_io(struct thread_state_s * state)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 			goto fail;
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 			goto fail;
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 				goto fail;
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 
480 	free(buf);
481 	files_db_fsync_discard_files(state->db_handle);
482 	files_db_close_files(state->db_handle);
483 	update_time(&aggregate_delay_time, &total_delay_time);
484 	update_op_counts(op_counts);
485 	update_byte_counts(&aggr_io_rw_bytes, &rw_bytes);
486 	return;
487 
488 fail:
489 	free(buf);
490 	exit(EXIT_FAILURE);
491 }
492 
493 void *
io_thread(void * unused)494 io_thread(void *unused __attribute__((unused)))
495 {
496 	struct thread_state_s *state;
497 
498 	srand(gettid());
499 	while ((state = get_work()))
500 		do_io(state);
501 	pthread_exit(NULL);
502         return(NULL);
503 }
504 
505 static void
do_create(struct thread_state_s * state)506 do_create(struct thread_state_s *state)
507 {
508 	struct ioshark_header header;
509 
510 	if (ioshark_read_header(state->fp, &header) != 1) {
511 		fprintf(stderr, "%s read error %s\n",
512 			progname, state->filename);
513 		exit(EXIT_FAILURE);
514 	}
515 	state->num_files = header.num_files;
516 	state->db_handle = files_db_create_handle();
517 	create_files(state);
518 }
519 
520 void *
create_files_thread(void * unused)521 create_files_thread(void *unused __attribute__((unused)))
522 {
523 	struct thread_state_s *state;
524 
525 	while ((state = get_work()))
526 		do_create(state);
527 	pthread_exit(NULL);
528 	return(NULL);
529 }
530 
531 int
get_start_end(int * start_ix)532 get_start_end(int *start_ix)
533 {
534 	int i, j, ret_numfiles;
535 	u_int64_t free_fs_bytes;
536 	char *infile;
537 	FILE *fp;
538 	struct ioshark_header header;
539 	struct ioshark_file_state file_state;
540 	struct statfs fsstat;
541 	static int fssize_clamp_next_index = 0;
542 	static int chunk = 0;
543 
544 	if (fssize_clamp_next_index == num_input_files)
545 		return 0;
546 	if (statfs("/data/local/tmp", &fsstat) < 0) {
547 		fprintf(stderr, "%s: Can't statfs /data/local/tmp\n",
548 			progname);
549 		exit(EXIT_FAILURE);
550 	}
551 	free_fs_bytes = (fsstat.f_bavail * fsstat.f_bsize) * 9 /10;
552 	for (i = fssize_clamp_next_index; i < num_input_files; i++) {
553 		infile = thread_state[i].filename;
554 		fp = fopen(infile, "r");
555 		if (fp == NULL) {
556 			fprintf(stderr, "%s: Can't open %s\n",
557 				progname, infile);
558 			exit(EXIT_FAILURE);
559 		}
560 		if (ioshark_read_header(fp, &header) != 1) {
561 			fprintf(stderr, "%s read error %s\n",
562 				progname, infile);
563 			exit(EXIT_FAILURE);
564 		}
565 		for (j = 0 ; j < (int)header.num_files ; j++) {
566 			if (ioshark_read_file_state(fp, &file_state) != 1) {
567 				fprintf(stderr, "%s read error tracefile\n",
568 					progname);
569 				exit(EXIT_FAILURE);
570 			}
571 			if (quick_mode == 0 ||
572 			    !is_readonly_mount(
573 				    get_ro_filename(file_state.global_filename_ix),
574 				    file_state.size)) {
575 				if (file_state.size > free_fs_bytes) {
576 					fclose(fp);
577 					goto out;
578 				}
579 				free_fs_bytes -= file_state.size;
580 			}
581 		}
582 		fclose(fp);
583 	}
584 out:
585 	if (verbose) {
586 		if (chunk > 0 || i < num_input_files) {
587 			printf("Breaking up input files, Chunk %d: %d to %d\n",
588 			       chunk++, fssize_clamp_next_index, i - 1);
589 		} else {
590 			printf("Entire Dataset fits start = %d to %d, free_bytes = %ju\n",
591 			       fssize_clamp_next_index,
592 			       i - fssize_clamp_next_index,
593 			       free_fs_bytes);
594 		}
595 	}
596 	*start_ix = fssize_clamp_next_index;
597 	ret_numfiles = i - fssize_clamp_next_index;
598 	fssize_clamp_next_index = i;
599 	return ret_numfiles;
600 }
601 
602 int
ioshark_pthread_create(pthread_t * tidp,void * (* start_routine)(void *))603 ioshark_pthread_create(pthread_t *tidp, void *(*start_routine)(void *))
604 {
605 	pthread_attr_t attr;
606 
607 	pthread_attr_init(&attr);
608 	pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
609 	pthread_attr_setstacksize(&attr, (size_t)(1024*1024));
610 	return pthread_create(tidp, &attr, start_routine, (void *)NULL);
611 }
612 
613 void
wait_for_threads(int num_threads)614 wait_for_threads(int num_threads)
615 {
616 	int i;
617 
618 	for (i = 0; i < num_threads; i++) {
619 		pthread_join(tid[i], NULL);
620 		tid[i] = 0;
621 	}
622 }
623 
624 #define IOSHARK_FD_LIM		8192
625 
626 static void
sizeup_fd_limits(void)627 sizeup_fd_limits(void)
628 {
629 	struct rlimit r;
630 
631 	getrlimit(RLIMIT_NOFILE, &r);
632 	if (r.rlim_cur >= IOSHARK_FD_LIM)
633 		/* cur limit already at what we want */
634 		return;
635 	/*
636 	 * Size up both the Max and Cur to IOSHARK_FD_LIM.
637 	 * If we are not running as root, this will fail,
638 	 * catch that below and exit.
639 	 */
640 	if (r.rlim_max < IOSHARK_FD_LIM)
641 		r.rlim_max = IOSHARK_FD_LIM;
642 	r.rlim_cur = IOSHARK_FD_LIM;
643 	if (setrlimit(RLIMIT_NOFILE, &r) < 0) {
644 		fprintf(stderr, "%s: Can't setrlimit (RLIMIT_NOFILE, 8192)\n",
645 			progname);
646 		exit(EXIT_FAILURE);
647 	}
648 	getrlimit(RLIMIT_NOFILE, &r);
649 	if (r.rlim_cur < IOSHARK_FD_LIM) {
650 		fprintf(stderr, "%s: Can't setrlimit up to 8192\n",
651 			progname);
652 		fprintf(stderr, "%s: Running as root ?\n",
653 			progname);
654 		exit(EXIT_FAILURE);
655 	}
656 }
657 
658 int
main(int argc,char ** argv)659 main(int argc, char **argv)
660 {
661 	int i;
662 	FILE *fp;
663 	struct stat st;
664 	char *infile;
665 	int num_threads = 0;
666 	int num_iterations = 1;
667 	int c;
668 	int num_files, start_file;
669 	struct thread_state_s *state;
670 
671 	progname = argv[0];
672         while ((c = getopt(argc, argv, "b:dn:st:qv")) != EOF) {
673                 switch (c) {
674                 case 'b':
675 			blockdev_name = strdup(optarg);
676 			break;
677                 case 'd':
678 			do_delay = 1;
679 			break;
680                 case 'n':
681 			num_iterations = atoi(optarg);
682 			break;
683                 case 's':
684 			/* Non-verbose summary mode for nightly runs */
685 			summary_mode = 1;
686 			break;
687                 case 't':
688 			num_threads = atoi(optarg);
689 			break;
690                 case 'q':
691 			/*
692 			 * If quick mode is enabled, then we won't
693 			 * pre-create files that we are doing IO on that
694 			 * live in readonly partitions (/system, /vendor etc)
695 			 */
696 			quick_mode = 1;
697 			break;
698                 case 'v':
699 			verbose = 1;
700 			break;
701  	        default:
702 			usage();
703 		}
704 	}
705 
706 	if ((verbose + summary_mode) == 2)
707 		usage();
708 
709 	if (num_threads > MAX_THREADS)
710 		usage();
711 
712 	if (optind == argc)
713                 usage();
714 
715 	sizeup_fd_limits();
716 
717 	for (i = optind; i < argc; i++) {
718 		infile = argv[i];
719 		if (stat(infile, &st) < 0) {
720 			fprintf(stderr, "%s: Can't stat %s\n",
721 				progname, infile);
722 			exit(EXIT_FAILURE);
723 		}
724 		if (st.st_size == 0) {
725 			fprintf(stderr, "%s: Empty file %s\n",
726 				progname, infile);
727 			continue;
728 		}
729 		fp = fopen(infile, "r");
730 		if (fp == NULL) {
731 			fprintf(stderr, "%s: Can't open %s\n",
732 				progname, infile);
733 			continue;
734 		}
735 		thread_state[num_input_files].filename = infile;
736 		thread_state[num_input_files].fp = fp;
737 		num_input_files++;
738 	}
739 
740 	if (num_input_files == 0) {
741 		exit(EXIT_SUCCESS);
742 	}
743 	if (verbose) {
744 		printf("Total Input Files = %d\n", num_input_files);
745 		printf("Num Iterations = %d\n", num_iterations);
746 	}
747 	timerclear(&aggregate_file_create_time);
748 	timerclear(&aggregate_file_remove_time);
749 	timerclear(&aggregate_IO_time);
750 
751 	if (quick_mode)
752 		init_filename_cache();
753 
754 	capture_util_state_before();
755 
756 	/*
757 	 * We pre-create the files that we need once and then we
758 	 * loop around N times doing IOs on the pre-created files.
759 	 *
760 	 * get_start_end() breaks up the total work here to make sure
761 	 * that all the files we need to pre-create fit into the
762 	 * available space in /data/local/tmp (hardcoded for now).
763 	 *
764 	 * If it won't fit, then we do several sweeps.
765 	 */
766 	while ((num_files = get_start_end(&start_file))) {
767 		struct timeval time_for_pass;
768 
769 		/* Create files once */
770 		if (!summary_mode)
771 			printf("Doing Pre-creation of Files\n");
772 		if (quick_mode && !summary_mode)
773 			printf("Skipping Pre-creation of read-only Files\n");
774 		if (num_threads == 0 || num_threads > num_files)
775 			num_threads = num_files;
776 		(void)system("echo 3 > /proc/sys/vm/drop_caches");
777 		init_work(start_file, num_files);
778 		(void)gettimeofday(&time_for_pass,
779 				   (struct timezone *)NULL);
780 		for (i = 0; i < num_threads; i++) {
781 			if (ioshark_pthread_create(&(tid[i]),
782 						   create_files_thread)) {
783 				fprintf(stderr,
784 					"%s: Can't create creator thread %d\n",
785 					progname, i);
786 				exit(EXIT_FAILURE);
787 			}
788 		}
789 		wait_for_threads(num_threads);
790 		update_delta_time(&time_for_pass, &aggregate_file_create_time);
791 		/* Do the IOs N times */
792 		for (i = 0 ; i < num_iterations ; i++) {
793 			(void)system("echo 3 > /proc/sys/vm/drop_caches");
794 			if (!summary_mode) {
795 				if (num_iterations > 1)
796 					printf("Starting Test. Iteration %d...\n",
797 					       i);
798 				else
799 					printf("Starting Test...\n");
800 			}
801 			init_work(start_file, num_files);
802 			(void)gettimeofday(&time_for_pass,
803 					   (struct timezone *)NULL);
804 			for (c = 0; c < num_threads; c++) {
805 				if (ioshark_pthread_create(&(tid[c]),
806 							   io_thread)) {
807 					fprintf(stderr,
808 						"%s: Can't create thread %d\n",
809 						progname, c);
810 					exit(EXIT_FAILURE);
811 				}
812 			}
813 			wait_for_threads(num_threads);
814 			update_delta_time(&time_for_pass,
815 					  &aggregate_IO_time);
816 		}
817 
818 		/*
819 		 * We are done with the N iterations of IO.
820 		 * Destroy the files we pre-created.
821 		 */
822 		init_work(start_file, num_files);
823 		while ((state = get_work())) {
824 			struct timeval start;
825 
826 			(void)gettimeofday(&start, (struct timezone *)NULL);
827 			files_db_unlink_files(state->db_handle);
828 			update_delta_time(&start, &aggregate_file_remove_time);
829 			files_db_free_memory(state->db_handle);
830 		}
831 	}
832 	if (!summary_mode) {
833 		printf("Total Creation time = %ju.%ju (msecs.usecs)\n",
834 		       get_msecs(&aggregate_file_create_time),
835 		       get_usecs(&aggregate_file_create_time));
836 		printf("Total Remove time = %ju.%ju (msecs.usecs)\n",
837 		       get_msecs(&aggregate_file_remove_time),
838 		       get_usecs(&aggregate_file_remove_time));
839 		if (do_delay)
840 			printf("Total delay time = %ju.%ju (msecs.usecs)\n",
841 			       get_msecs(&aggregate_delay_time),
842 			       get_usecs(&aggregate_delay_time));
843 		printf("Total Test (IO) time = %ju.%ju (msecs.usecs)\n",
844 		       get_msecs(&aggregate_IO_time),
845 		       get_usecs(&aggregate_IO_time));
846 		if (verbose)
847 			print_bytes("Upfront File Creation bytes",
848 				    &aggr_create_rw_bytes);
849 		print_bytes("Total Test (IO) bytes", &aggr_io_rw_bytes);
850 		if (verbose)
851 			print_op_stats(aggr_op_counts);
852 		report_cpu_disk_util();
853 	} else {
854 		printf("%ju.%ju ",
855 		       get_msecs(&aggregate_file_create_time),
856 		       get_usecs(&aggregate_file_create_time));
857 		printf("%ju.%ju ",
858 		       get_msecs(&aggregate_file_remove_time),
859 		       get_usecs(&aggregate_file_remove_time));
860 		if (do_delay)
861 			printf("%ju.%ju ",
862 			       get_msecs(&aggregate_delay_time),
863 			       get_usecs(&aggregate_delay_time));
864 		printf("%ju.%ju ",
865 		       get_msecs(&aggregate_IO_time),
866 		       get_usecs(&aggregate_IO_time));
867 		print_bytes(NULL, &aggr_io_rw_bytes);
868 		report_cpu_disk_util();
869 		printf("\n");
870 	}
871 	if (quick_mode)
872 		free_filename_cache();
873 }
874