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