Home | History | Annotate | Download | only in ioshark
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdio.h>
     18 #include <sys/time.h>
     19 #include <sys/types.h>
     20 #include <unistd.h>
     21 #include <stdlib.h>
     22 #include <signal.h>
     23 #include <string.h>
     24 #include <sys/stat.h>
     25 #include <sys/errno.h>
     26 #include <fcntl.h>
     27 #include <ctype.h>
     28 #include "ioshark.h"
     29 #include "compile_ioshark.h"
     30 
     31 char *progname;
     32 
     33 char in_buf[2048];
     34 
     35 struct flags_map_s {
     36 	char *flag_str;
     37 	int flag;
     38 };
     39 
     40 #define ARRAY_SIZE(a)	(sizeof(a) / sizeof(a[0]))
     41 
     42 struct flags_map_s open_flags_map[] = {
     43 	{ "O_RDONLY", O_RDONLY },
     44 	{ "O_WRONLY", O_WRONLY },
     45 	{ "O_RDWR", O_RDWR },
     46 	{ "O_CREAT", O_CREAT },
     47 	{ "O_SYNC", O_SYNC },
     48 	{ "O_TRUNC", O_TRUNC },
     49 	{ "O_EXCL", O_EXCL },
     50 	{ "O_APPEND", O_APPEND },
     51 	{ "O_NOATIME", O_NOATIME },
     52 	{ "O_ASYNC", O_ASYNC },
     53 	{ "O_CLOEXEC", O_CLOEXEC },
     54 	{ "O_DIRECT", O_DIRECT },
     55 	{ "O_DIRECTORY", O_DIRECTORY },
     56 	{ "O_LARGEFILE", O_LARGEFILE },
     57 	{ "O_NOCTTY", O_NOCTTY },
     58 	{ "O_NOFOLLOW", O_NOFOLLOW },
     59 	{ "O_NONBLOCK", O_NONBLOCK },
     60 	{ "O_NDELAY", O_NDELAY },
     61 	{ "O_PATH", O_PATH }
     62 };
     63 
     64 struct flags_map_s lseek_action_map[] = {
     65 	{ "SEEK_SET", SEEK_SET },
     66 	{ "SEEK_CUR", SEEK_CUR },
     67 	{ "SEEK_END", SEEK_END }
     68 };
     69 
     70 struct flags_map_s fileop_map[] = {
     71 	{ "lseek", IOSHARK_LSEEK },
     72 	{ "_llseek", IOSHARK_LLSEEK },
     73 	{ "pread64", IOSHARK_PREAD64 },
     74 	{ "pwrite64", IOSHARK_PWRITE64 },
     75 	{ "read", IOSHARK_READ },
     76 	{ "write", IOSHARK_WRITE },
     77 	{ "mmap", IOSHARK_MMAP },
     78 	{ "mmap2", IOSHARK_MMAP2 },
     79 	{ "openat", IOSHARK_OPEN },
     80 	{ "fsync", IOSHARK_FSYNC },
     81 	{ "fdatasync", IOSHARK_FDATASYNC },
     82 	{ "close", IOSHARK_CLOSE },
     83 	{ "ftrace", IOSHARK_MAPPED_PREAD }
     84 };
     85 
     86 struct in_mem_file_op {
     87 	struct ioshark_file_operation disk_file_op;
     88 	struct in_mem_file_op *next;
     89 };
     90 
     91 struct in_mem_file_op *in_mem_file_op_head = NULL, *in_mem_file_op_tail = NULL;
     92 
     93 void usage(void)
     94 {
     95 	fprintf(stderr, "%s in_file out_file\n", progname);
     96 }
     97 
     98 void
     99 init_prev_time(struct timeval *tv)
    100 {
    101 	tv->tv_sec = tv->tv_usec = 0;
    102 }
    103 
    104 /*
    105  * delta ts is the time delta from the previous IO in this tracefile.
    106  */
    107 static u_int64_t
    108 get_delta_ts(char *buf, struct timeval *prev)
    109 {
    110 	struct timeval op_tv, tv_res;
    111 
    112 	sscanf(buf, "%lu.%lu", &op_tv.tv_sec, &op_tv.tv_usec);
    113 	/* First item */
    114 	if (prev->tv_sec == 0 && prev->tv_usec == 0)
    115 		tv_res.tv_sec = tv_res.tv_usec = 0;
    116 	else
    117 		timersub(&op_tv, prev, &tv_res);
    118 	*prev = op_tv;
    119 	return (tv_res.tv_usec + (tv_res.tv_sec * 1000000));
    120 }
    121 
    122 void
    123 get_tracetype(char *buf, char *trace_type)
    124 {
    125 	char *s, *s2;
    126 
    127 	*trace_type = '\0';
    128 	s = strchr(buf, ' ');
    129 	if (s == NULL) {
    130 		fprintf(stderr,
    131 			"%s Malformed Trace Type ? %s\n",
    132 			progname, __func__);
    133 		exit(EXIT_FAILURE);
    134 	}
    135 	while (*s == ' ')
    136 		s++;
    137 	if (sscanf(s, "%s", trace_type) != 1) {
    138 		fprintf(stderr,
    139 			"%s Malformed Trace Type ? %s\n",
    140 			progname, __func__);
    141 		exit(EXIT_FAILURE);
    142 	}
    143 	if (strcmp(trace_type, "strace") != 0 &&
    144 	    strcmp(trace_type, "ftrace") != 0) {
    145 		fprintf(stderr,
    146 			"%s Unknown/Missing Trace Type (has to be strace|ftrace) %s\n",
    147 			progname, __func__);
    148 		exit(EXIT_FAILURE);
    149 	}
    150 	/*
    151 	 * Remove the keyword "strace"/"ftrace" from the buffer
    152 	 */
    153 	s2 = strchr(s, ' ');
    154 	if (s2 == NULL) {
    155 		fprintf(stderr,
    156 			"%s Malformed Trace Type ? %s\n",
    157 			progname, __func__);
    158 		exit(EXIT_FAILURE);
    159 	}
    160 	while (*s2 == ' ')
    161 		s2++;
    162 	if (*s2  == '\0') {
    163 		/*
    164 		 * Premature end of input record
    165 		 */
    166 		fprintf(stderr,
    167 			"%s Mal-formed strace/ftrace record %s:%s\n",
    168 			progname, __func__, buf);
    169 		exit(EXIT_FAILURE);
    170 	}
    171 	/* strcpy() expects non-overlapping buffers, but bcopy doesn't */
    172 	bcopy(s2, s, strlen(s2) + 1);
    173 }
    174 
    175 void
    176 get_pathname(char *buf, char *pathname, enum file_op file_op)
    177 {
    178 	char *s, *s2, save;
    179 
    180 	if (file_op == IOSHARK_MAPPED_PREAD) {
    181 		s = strchr(buf, '/');
    182 		if (s == NULL) {
    183 			fprintf(stderr, "%s: Malformed line: %s\n",
    184 				__func__, buf);
    185 			exit(EXIT_FAILURE);
    186 		}
    187 		s2 = strchr(s, ' ');
    188 		if (s2 == NULL) {
    189 			fprintf(stderr, "%s: Malformed line: %s\n",
    190 				__func__, buf);
    191 			exit(EXIT_FAILURE);
    192 		}
    193 	} else {
    194 		if (file_op == IOSHARK_OPEN)
    195 			s = strchr(buf, '"');
    196 		else
    197 			s = strchr(buf, '<');
    198 		if (s == NULL) {
    199 			fprintf(stderr, "%s: Malformed line: %s\n",
    200 				__func__, buf);
    201 			exit(EXIT_FAILURE);
    202 		}
    203 		s += 1;
    204 		if (file_op == IOSHARK_OPEN)
    205 			s2 = strchr(s, '"');
    206 		else
    207 			s2 = strchr(s, '>');
    208 		if (s2 == NULL) {
    209 			fprintf(stderr, "%s: Malformed line: %s\n",
    210 				__func__, buf);
    211 			exit(EXIT_FAILURE);
    212 		}
    213 	}
    214 	save = *s2;
    215 	*s2 = '\0';
    216 	strcpy(pathname, s);
    217 	*s2 = save;
    218 }
    219 
    220 void
    221 get_syscall(char *buf, char *syscall)
    222 {
    223 	char *s, *s2;
    224 
    225 	s = strchr(buf, ' ');
    226 	if (s == NULL) {
    227 		fprintf(stderr, "%s: Malformed line: %s\n",
    228 			__func__, buf);
    229 		exit(EXIT_FAILURE);
    230 	}
    231 	s += 1;
    232 	s2 = strchr(s, '(');
    233 	if (s2 == NULL) {
    234 		fprintf(stderr, "%s: Malformed line: %s\n",
    235 			__func__, buf);
    236 		exit(EXIT_FAILURE);
    237 	}
    238 	*s2 = '\0';
    239 	strcpy(syscall, s);
    240 	*s2 = '(';
    241 }
    242 
    243 void
    244 get_mmap_offset_len_prot(char *buf, int *prot, off_t *offset, u_int64_t *len)
    245 {
    246 	char *s, *s1;
    247 	int i;
    248 	char protstr[128];
    249 
    250 	s = strchr(buf, ',');
    251 	if (s == NULL) {
    252 		fprintf(stderr, "%s: Malformed line: %s\n",
    253 			__func__, buf);
    254 		exit(EXIT_FAILURE);
    255 	}
    256 	s += 2;
    257 	sscanf(s, "%ju", len);
    258 	s1 = strchr(s, ',');
    259 	if (s1 == NULL) {
    260 		fprintf(stderr, "%s: Malformed line: %s\n",
    261 			__func__, buf);
    262 		exit(EXIT_FAILURE);
    263 	}
    264 	s1 += 2;
    265 	s = strchr(s1, ',');
    266 	if (s == NULL) {
    267 		fprintf(stderr, "%s: Malformed line: %s\n",
    268 			__func__, buf);
    269 		exit(EXIT_FAILURE);
    270 	}
    271 	*s = '\0';
    272 	strcpy(protstr, s1);
    273 	*prot = 0;
    274 	if (strstr(protstr, "PROT_READ"))
    275 		*prot |= IOSHARK_PROT_READ;
    276 	if (strstr(protstr, "PROT_WRITE"))
    277 		*prot |= IOSHARK_PROT_WRITE;
    278 	*s = ',';
    279 	s += 2;
    280 	for (i = 0 ; i < 2 ; i++) {
    281 		s = strchr(s, ',');
    282 		if (s == NULL) {
    283 			fprintf(stderr, "%s: Malformed line: %s\n",
    284 				__func__, buf);
    285 			exit(EXIT_FAILURE);
    286 		}
    287 		s += 2;
    288 	}
    289 	sscanf(s, "%jx", offset);
    290 }
    291 
    292 void
    293 get_lseek_offset_action(char *buf, enum file_op op,
    294 			off_t *offset, char *action)
    295 {
    296 	char *s, *s2;
    297 
    298 	s = strchr(buf, ',');
    299 	if (s == NULL) {
    300 		fprintf(stderr, "%s: Malformed line: %s\n",
    301 			__func__, buf);
    302 		exit(EXIT_FAILURE);
    303 	}
    304 	s += 2;
    305 	sscanf(s, "%ju", offset);
    306 	s = strchr(s, ',');
    307 	if (s == NULL) {
    308 		fprintf(stderr, "%s: Malformed line: %s\n",
    309 			__func__, buf);
    310 		exit(EXIT_FAILURE);
    311 	}
    312 	s += 2;
    313 	if (op == IOSHARK_LLSEEK) {
    314 		s = strchr(s, ',');
    315 		if (s == NULL) {
    316 			fprintf(stderr, "%s: Malformed line: %s\n",
    317 				__func__, buf);
    318 			exit(EXIT_FAILURE);
    319 		}
    320 		s += 2;
    321 	}
    322 	s2 = strchr(s, ')');
    323 	if (s2 == NULL) {
    324 		fprintf(stderr, "%s: Malformed line: %s\n",
    325 			__func__, buf);
    326 		exit(EXIT_FAILURE);
    327 	}
    328 	*s2 = '\0';
    329 	strcpy(action, s);
    330 	*s2 = ')';
    331 }
    332 
    333 void
    334 get_rw_len(char *buf,
    335 	   u_int64_t *len)
    336 {
    337 	char *s_len;
    338 
    339 	s_len = strrchr(buf, ',');
    340 	if (s_len == NULL) {
    341 		fprintf(stderr, "%s: Malformed line: %s\n",
    342 			__func__, buf);
    343 		exit(EXIT_FAILURE);
    344 	}
    345 	sscanf(s_len + 2, "%ju", len);
    346 }
    347 
    348 void
    349 get_prw64_offset_len(char *buf,
    350 		     off_t *offset,
    351 		     u_int64_t *len)
    352 {
    353 	char *s_offset, *s_len;
    354 
    355 	s_offset = strrchr(buf, ',');
    356 	if (s_offset == NULL) {
    357 		fprintf(stderr, "%s: Malformed line 1: %s\n",
    358 			__func__, buf);
    359 		exit(EXIT_FAILURE);
    360 	}
    361 	*s_offset = '\0';
    362 	s_len = strrchr(buf, ',');
    363 	if (s_len == NULL) {
    364 		fprintf(stderr, "%s: Malformed line 2: %s\n",
    365 			__func__, buf);
    366 		exit(EXIT_FAILURE);
    367 	}
    368 	*s_offset = ',';
    369 	sscanf(s_len + 2, "%ju", len);
    370 	sscanf(s_offset + 2, "%ju", offset);
    371 }
    372 
    373 
    374 void
    375 get_ftrace_offset_len(char *buf,
    376 		      off_t *offset,
    377 		      u_int64_t *len)
    378 {
    379 	char *s_offset;
    380 
    381 	s_offset = strchr(buf, '/');
    382 	if (s_offset == NULL) {
    383 		fprintf(stderr, "%s: Malformed line 1: %s\n",
    384 			__func__, buf);
    385 		exit(EXIT_FAILURE);
    386 	}
    387 	s_offset = strchr(s_offset, ' ');
    388 	if (s_offset == NULL) {
    389 		fprintf(stderr, "%s: Malformed line 2: %s\n",
    390 			__func__, buf);
    391 		exit(EXIT_FAILURE);
    392 	}
    393 	while (*s_offset == ' ')
    394 		s_offset++;
    395 	if (sscanf(s_offset, "%ju %ju", offset, len) != 2) {
    396 		fprintf(stderr, "%s: Malformed line 3: %s\n",
    397 			__func__, buf);
    398 		exit(EXIT_FAILURE);
    399 	}
    400 }
    401 
    402 void
    403 get_openat_flags_mode(char *buf, char *flags, mode_t *mode)
    404 {
    405 	char *s, *s2, lookfor;
    406 
    407 	s = strchr(buf, ',');
    408 	if (s == NULL) {
    409 		fprintf(stderr, "%s: Malformed line: %s\n",
    410 			__func__, buf);
    411 		exit(EXIT_FAILURE);
    412 	}
    413 	s += 2;
    414 	s = strchr(s, ',');
    415 	if (s == NULL) {
    416 		fprintf(stderr, "%s: Malformed line: %s\n",
    417 			__func__, buf);
    418 		exit(EXIT_FAILURE);
    419 	}
    420 	s += 2;
    421 	if (strstr(s, "O_CREAT") == NULL)
    422 		lookfor = ')';
    423 	else
    424 		lookfor = ',';
    425 	s2 = strchr(s, lookfor);
    426 	if (s2 == NULL) {
    427 		fprintf(stderr, "%s: Malformed line: %s\n",
    428 			__func__, buf);
    429 		exit(EXIT_FAILURE);
    430 	}
    431 	*s2 = '\0';
    432 	strcpy(flags, s);
    433 	*s2 = lookfor;
    434 	if (strstr(s, "O_CREAT") != NULL) {
    435 		s = s2 + 2;
    436 		s2 = strchr(s, ')');
    437 		if (s2 == NULL) {
    438 			fprintf(stderr, "%s: Malformed line: %s\n",
    439 				__func__, buf);
    440 			exit(EXIT_FAILURE);
    441 		}
    442 		*s2 = '\0';
    443 		sscanf(s, "%o", mode);
    444 		*s2 = ')';
    445 	}
    446 }
    447 
    448 int
    449 lookup_map(char *s, struct flags_map_s *flags_map, int maplen)
    450 {
    451 	int found = 0, flag = 0;
    452 	int i;
    453 
    454 	while (isspace(*s))
    455 		s++;
    456 	for (i = 0 ; i < maplen ; i++) {
    457 		if (strcmp(flags_map[i].flag_str, s) == 0) {
    458 			flag = flags_map[i].flag;
    459 			found = 1;
    460 			break;
    461 		}
    462 	}
    463 	if (found == 0) {
    464 		fprintf(stderr, "%s: Unknown syscall %s\n",
    465 			__func__, s);
    466 		exit(1);
    467 	}
    468 	return flag;
    469 }
    470 
    471 int
    472 map_open_flags(char *s)
    473 {
    474 	int flags = 0;
    475 	char *s1;
    476 
    477 	while ((s1 = strchr(s, '|'))) {
    478 		*s1 = '\0';
    479 		flags |= lookup_map(s, open_flags_map,
    480 				    ARRAY_SIZE(open_flags_map));
    481 		*s1 = '|';
    482 		s = s1 + 1;
    483 	}
    484 	/* Last option */
    485 	flags |= lookup_map(s, open_flags_map,
    486 			    ARRAY_SIZE(open_flags_map));
    487 	return flags;
    488 }
    489 
    490 int
    491 map_lseek_action(char *s)
    492 {
    493 	int flags = 0;
    494 	char *s1;
    495 
    496 	while ((s1 = strchr(s, '|'))) {
    497 		*s1 = '\0';
    498 		flags |= lookup_map(s, lseek_action_map,
    499 				    ARRAY_SIZE(lseek_action_map));
    500 		*s1 = '|';
    501 		s = s1 + 1;
    502 	}
    503 	/* Last option */
    504 	flags |= lookup_map(s, lseek_action_map,
    505 			    ARRAY_SIZE(lseek_action_map));
    506 	return flags;
    507 }
    508 
    509 enum file_op
    510 map_syscall(char *syscall)
    511 {
    512 	return lookup_map(syscall, fileop_map,
    513 			  ARRAY_SIZE(fileop_map));
    514 }
    515 
    516 /*
    517  * For each tracefile, we first create in-memory structures, then once
    518  * we've processed each tracefile completely, we write out the in-memory
    519  * structures and free them.
    520  */
    521 int main(int argc, char **argv)
    522 {
    523 	FILE *fp;
    524 	char path[512];
    525 	char syscall[512];
    526 	char lseek_action_str[512];
    527 	char *s;
    528 	char open_flags_str[64];
    529 	void *db_node;
    530 	int num_io_operations = 0;
    531 	struct ioshark_header header;
    532 	struct ioshark_file_operation *disk_file_op;
    533 	struct in_mem_file_op *in_mem_fop;
    534 	struct stat st;
    535 	char *infile, *outfile;
    536 	struct timeval prev_time;
    537 	char trace_type[64];
    538 
    539 	progname = argv[0];
    540 	if (argc != 3) {
    541 		usage();
    542 		exit(EXIT_FAILURE);
    543 	}
    544 	infile = argv[1];
    545 	outfile = argv[2];
    546 	if (stat(infile, &st) < 0) {
    547 		fprintf(stderr, "%s Can't stat %s\n",
    548 			progname, infile);
    549 		exit(EXIT_FAILURE);
    550 	}
    551 	if (st.st_size == 0) {
    552 		fprintf(stderr, "%s Empty file %s\n",
    553 			progname, infile);
    554 		exit(EXIT_FAILURE);
    555 	}
    556 	init_prev_time(&prev_time);
    557 	init_filename_cache();
    558 	fp = fopen(infile, "r");
    559 	if (fp == NULL) {
    560 		fprintf(stderr, "%s Can't open %s\n",
    561 			progname, infile);
    562 		exit(EXIT_FAILURE);
    563 	}
    564 	while (fgets(in_buf, 2048, fp)) {
    565 		s = in_buf;
    566 		while (isspace(*s))
    567 			s++;
    568 		in_mem_fop = malloc(sizeof(struct in_mem_file_op));
    569 		disk_file_op = &in_mem_fop->disk_file_op;
    570 		disk_file_op->delta_us = get_delta_ts(s, &prev_time);
    571 		get_tracetype(s, trace_type);
    572 		if (strcmp(trace_type, "strace") == 0) {
    573 			get_syscall(s, syscall);
    574 			disk_file_op->file_op = map_syscall(syscall);
    575 		} else
    576 			disk_file_op->file_op = map_syscall("ftrace");
    577 		get_pathname(s, path, disk_file_op->file_op);
    578 		db_node = files_db_add(path);
    579 		disk_file_op->fileno = files_db_get_fileno(db_node);
    580 		switch (disk_file_op->file_op) {
    581 		case IOSHARK_LLSEEK:
    582 		case IOSHARK_LSEEK:
    583 			get_lseek_offset_action(s,
    584 					disk_file_op->file_op,
    585 					&disk_file_op->lseek_offset,
    586 					lseek_action_str);
    587 			disk_file_op->lseek_action =
    588 				map_lseek_action(lseek_action_str);
    589 			if (disk_file_op->lseek_action == SEEK_SET)
    590 				files_db_update_size(db_node,
    591 						     disk_file_op->lseek_offset);
    592 			break;
    593 		case IOSHARK_PREAD64:
    594 		case IOSHARK_PWRITE64:
    595 			get_prw64_offset_len(s,
    596 					     &disk_file_op->prw_offset,
    597 					     (u_int64_t *)&disk_file_op->prw_len);
    598 			files_db_update_size(db_node,
    599 					     disk_file_op->prw_offset +
    600 					     disk_file_op->prw_len);
    601 			break;
    602 		case IOSHARK_READ:
    603 		case IOSHARK_WRITE:
    604 			get_rw_len(s, (u_int64_t *)&disk_file_op->rw_len);
    605 			files_db_add_to_size(db_node,
    606 					     disk_file_op->rw_len);
    607 			break;
    608 		case IOSHARK_MMAP:
    609 		case IOSHARK_MMAP2:
    610 			get_mmap_offset_len_prot(s,
    611 				    &disk_file_op->mmap_prot,
    612 				    &disk_file_op->mmap_offset,
    613 				    (u_int64_t *)&disk_file_op->mmap_len);
    614 			files_db_update_size(db_node,
    615 				     disk_file_op->mmap_offset +
    616 				     disk_file_op->mmap_len);
    617 			break;
    618 		case IOSHARK_OPEN:
    619 			disk_file_op->open_mode = 0;
    620 			get_openat_flags_mode(s, open_flags_str,
    621 				      &disk_file_op->open_mode);
    622 			disk_file_op->open_flags =
    623 				map_open_flags(open_flags_str);
    624 			break;
    625 		case IOSHARK_FSYNC:
    626 		case IOSHARK_FDATASYNC:
    627 			break;
    628 		case IOSHARK_CLOSE:
    629 			break;
    630 		case IOSHARK_MAPPED_PREAD:
    631 			/* Convert a mmap'ed read into a PREAD64 */
    632 			disk_file_op->file_op = IOSHARK_PREAD64;
    633 			get_ftrace_offset_len(s,
    634 					      &disk_file_op->prw_offset,
    635 					      (u_int64_t *)&disk_file_op->prw_len);
    636 			files_db_update_size(db_node,
    637 					     disk_file_op->prw_offset +
    638 					     disk_file_op->prw_len);
    639 			break;
    640 		default:
    641 			break;
    642 		}
    643 		/* Chain at the end */
    644 		num_io_operations++;
    645 		in_mem_fop->next = NULL;
    646 		if (in_mem_file_op_head == NULL) {
    647 			in_mem_file_op_tail = in_mem_fop;
    648 			in_mem_file_op_head = in_mem_fop;
    649 		} else {
    650 			in_mem_file_op_tail->next = in_mem_fop;
    651 			in_mem_file_op_tail = in_mem_fop;
    652 		}
    653 	}
    654 	fclose(fp);
    655 	/*
    656 	 * Now we can write everything out to the output tracefile.
    657 	 */
    658 	fp = fopen(outfile, "w+");
    659 	if (fp == NULL) {
    660 		fprintf(stderr, "%s Can't open trace.outfile\n",
    661 			progname);
    662 		exit(EXIT_FAILURE);
    663 	}
    664 	header.num_io_operations = num_io_operations;
    665 	header.num_files = files_db_get_total_obj();
    666 	if (fwrite(&header, sizeof(struct ioshark_header), 1, fp) != 1) {
    667 		fprintf(stderr, "%s Write error trace.outfile\n",
    668 			progname);
    669 		exit(EXIT_FAILURE);
    670 	}
    671 	files_db_write_objects(fp);
    672 	while (in_mem_file_op_head != NULL) {
    673 		struct in_mem_file_op *temp;
    674 
    675 		disk_file_op = &in_mem_file_op_head->disk_file_op;
    676 		if (fwrite(disk_file_op,
    677 			   sizeof(struct ioshark_file_operation), 1, fp) != 1) {
    678 			fprintf(stderr, "%s Write error trace.outfile\n",
    679 				progname);
    680 			exit(EXIT_FAILURE);
    681 		}
    682 		temp = in_mem_file_op_head;
    683 		in_mem_file_op_head = in_mem_file_op_head->next;
    684 		free(temp);
    685 	}
    686 	store_filename_cache();
    687 }
    688