2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <sys/types.h>
25 #include <sys/errno.h>
29 #include "compile_ioshark.h"
40 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
42 struct flags_map_s open_flags_map[] = {
43 { "O_RDONLY", O_RDONLY },
44 { "O_WRONLY", O_WRONLY },
46 { "O_CREAT", O_CREAT },
48 { "O_TRUNC", O_TRUNC },
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 },
64 struct flags_map_s lseek_action_map[] = {
65 { "SEEK_SET", SEEK_SET },
66 { "SEEK_CUR", SEEK_CUR },
67 { "SEEK_END", SEEK_END }
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 }
86 struct in_mem_file_op {
87 struct ioshark_file_operation disk_file_op;
88 struct in_mem_file_op *next;
91 struct in_mem_file_op *in_mem_file_op_head = NULL, *in_mem_file_op_tail = NULL;
95 fprintf(stderr, "%s in_file out_file\n", progname);
99 init_prev_time(struct timeval *tv)
101 tv->tv_sec = tv->tv_usec = 0;
105 * delta ts is the time delta from the previous IO in this tracefile.
108 get_delta_ts(char *buf, struct timeval *prev)
110 struct timeval op_tv, tv_res;
112 sscanf(buf, "%lu.%lu", &op_tv.tv_sec, &op_tv.tv_usec);
114 if (prev->tv_sec == 0 && prev->tv_usec == 0)
115 tv_res.tv_sec = tv_res.tv_usec = 0;
117 timersub(&op_tv, prev, &tv_res);
119 return (tv_res.tv_usec + (tv_res.tv_sec * 1000000));
123 get_tracetype(char *buf, char *trace_type)
128 s = strchr(buf, ' ');
131 "%s Malformed Trace Type ? %s\n",
137 if (sscanf(s, "%s", trace_type) != 1) {
139 "%s Malformed Trace Type ? %s\n",
143 if (strcmp(trace_type, "strace") != 0 &&
144 strcmp(trace_type, "ftrace") != 0) {
146 "%s Unknown/Missing Trace Type (has to be strace|ftrace) %s\n",
151 * Remove the keyword "strace"/"ftrace" from the buffer
156 "%s Malformed Trace Type ? %s\n",
164 * Premature end of input record
167 "%s Mal-formed strace/ftrace record %s:%s\n",
168 progname, __func__, buf);
171 /* strcpy() expects non-overlapping buffers, but bcopy doesn't */
172 bcopy(s2, s, strlen(s2) + 1);
176 get_pathname(char *buf, char *pathname, enum file_op file_op)
180 if (file_op == IOSHARK_MAPPED_PREAD) {
181 s = strchr(buf, '/');
183 fprintf(stderr, "%s: Malformed line: %s\n",
189 fprintf(stderr, "%s: Malformed line: %s\n",
194 if (file_op == IOSHARK_OPEN)
195 s = strchr(buf, '"');
197 s = strchr(buf, '<');
199 fprintf(stderr, "%s: Malformed line: %s\n",
204 if (file_op == IOSHARK_OPEN)
209 fprintf(stderr, "%s: Malformed line: %s\n",
221 get_syscall(char *buf, char *syscall)
225 s = strchr(buf, ' ');
227 fprintf(stderr, "%s: Malformed line: %s\n",
234 fprintf(stderr, "%s: Malformed line: %s\n",
244 get_mmap_offset_len_prot(char *buf, int *prot, off_t *offset, u_int64_t *len)
250 s = strchr(buf, ',');
252 fprintf(stderr, "%s: Malformed line: %s\n",
257 sscanf(s, "%ju", len);
260 fprintf(stderr, "%s: Malformed line: %s\n",
267 fprintf(stderr, "%s: Malformed line: %s\n",
274 if (strstr(protstr, "PROT_READ"))
275 *prot |= IOSHARK_PROT_READ;
276 if (strstr(protstr, "PROT_WRITE"))
277 *prot |= IOSHARK_PROT_WRITE;
280 for (i = 0 ; i < 2 ; i++) {
283 fprintf(stderr, "%s: Malformed line: %s\n",
289 sscanf(s, "%jx", offset);
293 get_lseek_offset_action(char *buf, enum file_op op,
294 off_t *offset, char *action)
298 s = strchr(buf, ',');
300 fprintf(stderr, "%s: Malformed line: %s\n",
305 sscanf(s, "%ju", offset);
308 fprintf(stderr, "%s: Malformed line: %s\n",
313 if (op == IOSHARK_LLSEEK) {
316 fprintf(stderr, "%s: Malformed line: %s\n",
324 fprintf(stderr, "%s: Malformed line: %s\n",
334 get_rw_len(char *buf,
339 s_len = strrchr(buf, ',');
341 fprintf(stderr, "%s: Malformed line: %s\n",
345 sscanf(s_len + 2, "%ju", len);
349 get_prw64_offset_len(char *buf,
353 char *s_offset, *s_len;
355 s_offset = strrchr(buf, ',');
356 if (s_offset == NULL) {
357 fprintf(stderr, "%s: Malformed line 1: %s\n",
362 s_len = strrchr(buf, ',');
364 fprintf(stderr, "%s: Malformed line 2: %s\n",
369 sscanf(s_len + 2, "%ju", len);
370 sscanf(s_offset + 2, "%ju", offset);
375 get_ftrace_offset_len(char *buf,
381 s_offset = strchr(buf, '/');
382 if (s_offset == NULL) {
383 fprintf(stderr, "%s: Malformed line 1: %s\n",
387 s_offset = strchr(s_offset, ' ');
388 if (s_offset == NULL) {
389 fprintf(stderr, "%s: Malformed line 2: %s\n",
393 while (*s_offset == ' ')
395 if (sscanf(s_offset, "%ju %ju", offset, len) != 2) {
396 fprintf(stderr, "%s: Malformed line 3: %s\n",
403 get_openat_flags_mode(char *buf, char *flags, mode_t *mode)
405 char *s, *s2, lookfor;
407 s = strchr(buf, ',');
409 fprintf(stderr, "%s: Malformed line: %s\n",
416 fprintf(stderr, "%s: Malformed line: %s\n",
421 if (strstr(s, "O_CREAT") == NULL)
425 s2 = strchr(s, lookfor);
427 fprintf(stderr, "%s: Malformed line: %s\n",
434 if (strstr(s, "O_CREAT") != NULL) {
438 fprintf(stderr, "%s: Malformed line: %s\n",
443 sscanf(s, "%o", mode);
449 lookup_map(char *s, struct flags_map_s *flags_map, int maplen)
451 int found = 0, flag = 0;
456 for (i = 0 ; i < maplen ; i++) {
457 if (strcmp(flags_map[i].flag_str, s) == 0) {
458 flag = flags_map[i].flag;
464 fprintf(stderr, "%s: Unknown syscall %s\n",
472 map_open_flags(char *s)
477 while ((s1 = strchr(s, '|'))) {
479 flags |= lookup_map(s, open_flags_map,
480 ARRAY_SIZE(open_flags_map));
485 flags |= lookup_map(s, open_flags_map,
486 ARRAY_SIZE(open_flags_map));
491 map_lseek_action(char *s)
496 while ((s1 = strchr(s, '|'))) {
498 flags |= lookup_map(s, lseek_action_map,
499 ARRAY_SIZE(lseek_action_map));
504 flags |= lookup_map(s, lseek_action_map,
505 ARRAY_SIZE(lseek_action_map));
510 map_syscall(char *syscall)
512 return lookup_map(syscall, fileop_map,
513 ARRAY_SIZE(fileop_map));
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.
521 int main(int argc, char **argv)
526 char lseek_action_str[512];
528 char open_flags_str[64];
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;
535 char *infile, *outfile;
536 struct timeval prev_time;
546 if (stat(infile, &st) < 0) {
547 fprintf(stderr, "%s Can't stat %s\n",
551 if (st.st_size == 0) {
552 fprintf(stderr, "%s Empty file %s\n",
556 init_prev_time(&prev_time);
557 init_filename_cache();
558 fp = fopen(infile, "r");
560 fprintf(stderr, "%s Can't open %s\n",
564 while (fgets(in_buf, 2048, fp)) {
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);
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) {
583 get_lseek_offset_action(s,
584 disk_file_op->file_op,
585 &disk_file_op->lseek_offset,
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);
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);
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);
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);
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);
626 case IOSHARK_FDATASYNC:
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);
643 /* Chain at the end */
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;
650 in_mem_file_op_tail->next = in_mem_fop;
651 in_mem_file_op_tail = in_mem_fop;
656 * Now we can write everything out to the output tracefile.
658 fp = fopen(outfile, "w+");
660 fprintf(stderr, "%s Can't open trace.outfile\n",
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",
671 files_db_write_objects(fp);
672 while (in_mem_file_op_head != NULL) {
673 struct in_mem_file_op *temp;
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",
682 temp = in_mem_file_op_head;
683 in_mem_file_op_head = in_mem_file_op_head->next;
686 store_filename_cache();