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