/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include "ioshark.h" #include "compile_ioshark.h" char *progname; char in_buf[2048]; struct flags_map_s { char *flag_str; int flag; }; #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) struct flags_map_s open_flags_map[] = { { "O_RDONLY", O_RDONLY }, { "O_WRONLY", O_WRONLY }, { "O_RDWR", O_RDWR }, { "O_CREAT", O_CREAT }, { "O_SYNC", O_SYNC }, { "O_TRUNC", O_TRUNC }, { "O_EXCL", O_EXCL }, { "O_APPEND", O_APPEND }, { "O_NOATIME", O_NOATIME }, { "O_ASYNC", O_ASYNC }, { "O_CLOEXEC", O_CLOEXEC }, { "O_DIRECT", O_DIRECT }, { "O_DIRECTORY", O_DIRECTORY }, { "O_LARGEFILE", O_LARGEFILE }, { "O_NOCTTY", O_NOCTTY }, { "O_NOFOLLOW", O_NOFOLLOW }, { "O_NONBLOCK", O_NONBLOCK }, { "O_NDELAY", O_NDELAY }, { "O_PATH", O_PATH } }; struct flags_map_s lseek_action_map[] = { { "SEEK_SET", SEEK_SET }, { "SEEK_CUR", SEEK_CUR }, { "SEEK_END", SEEK_END } }; struct flags_map_s fileop_map[] = { { "lseek", IOSHARK_LSEEK }, { "_llseek", IOSHARK_LLSEEK }, { "pread64", IOSHARK_PREAD64 }, { "pwrite64", IOSHARK_PWRITE64 }, { "read", IOSHARK_READ }, { "write", IOSHARK_WRITE }, { "mmap", IOSHARK_MMAP }, { "mmap2", IOSHARK_MMAP2 }, { "openat", IOSHARK_OPEN }, { "fsync", IOSHARK_FSYNC }, { "fdatasync", IOSHARK_FDATASYNC }, { "close", IOSHARK_CLOSE }, { "ftrace", IOSHARK_MAPPED_PREAD } }; struct in_mem_file_op { struct ioshark_file_operation disk_file_op; struct in_mem_file_op *next; }; struct in_mem_file_op *in_mem_file_op_head = NULL, *in_mem_file_op_tail = NULL; void usage(void) { fprintf(stderr, "%s in_file out_file\n", progname); } void init_prev_time(struct timeval *tv) { tv->tv_sec = tv->tv_usec = 0; } /* * delta ts is the time delta from the previous IO in this tracefile. */ static u_int64_t get_delta_ts(char *buf, struct timeval *prev) { struct timeval op_tv, tv_res; sscanf(buf, "%lu.%lu", &op_tv.tv_sec, &op_tv.tv_usec); /* First item */ if (prev->tv_sec == 0 && prev->tv_usec == 0) tv_res.tv_sec = tv_res.tv_usec = 0; else timersub(&op_tv, prev, &tv_res); *prev = op_tv; return (tv_res.tv_usec + (tv_res.tv_sec * 1000000)); } void get_tracetype(char *buf, char *trace_type) { char *s, *s2; *trace_type = '\0'; s = strchr(buf, ' '); if (s == NULL) { fprintf(stderr, "%s Malformed Trace Type ? %s\n", progname, __func__); exit(EXIT_FAILURE); } while (*s == ' ') s++; if (sscanf(s, "%s", trace_type) != 1) { fprintf(stderr, "%s Malformed Trace Type ? %s\n", progname, __func__); exit(EXIT_FAILURE); } if (strcmp(trace_type, "strace") != 0 && strcmp(trace_type, "ftrace") != 0) { fprintf(stderr, "%s Unknown/Missing Trace Type (has to be strace|ftrace) %s\n", progname, __func__); exit(EXIT_FAILURE); } /* * Remove the keyword "strace"/"ftrace" from the buffer */ s2 = strchr(s, ' '); if (s2 == NULL) { fprintf(stderr, "%s Malformed Trace Type ? %s\n", progname, __func__); exit(EXIT_FAILURE); } while (*s2 == ' ') s2++; if (*s2 == '\0') { /* * Premature end of input record */ fprintf(stderr, "%s Mal-formed strace/ftrace record %s:%s\n", progname, __func__, buf); exit(EXIT_FAILURE); } /* strcpy() expects non-overlapping buffers, but bcopy doesn't */ bcopy(s2, s, strlen(s2) + 1); } void get_pathname(char *buf, char *pathname, enum file_op file_op) { char *s, *s2, save; if (file_op == IOSHARK_MAPPED_PREAD) { s = strchr(buf, '/'); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s2 = strchr(s, ' '); if (s2 == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } } else { if (file_op == IOSHARK_OPEN) s = strchr(buf, '"'); else s = strchr(buf, '<'); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s += 1; if (file_op == IOSHARK_OPEN) s2 = strchr(s, '"'); else s2 = strchr(s, '>'); if (s2 == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } } save = *s2; *s2 = '\0'; strcpy(pathname, s); *s2 = save; } void get_syscall(char *buf, char *syscall) { char *s, *s2; s = strchr(buf, ' '); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s += 1; s2 = strchr(s, '('); if (s2 == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } *s2 = '\0'; strcpy(syscall, s); *s2 = '('; } void get_mmap_offset_len_prot(char *buf, int *prot, off_t *offset, u_int64_t *len) { char *s, *s1; int i; char protstr[128]; s = strchr(buf, ','); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s += 2; sscanf(s, "%ju", len); s1 = strchr(s, ','); if (s1 == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s1 += 2; s = strchr(s1, ','); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } *s = '\0'; strcpy(protstr, s1); *prot = 0; if (strstr(protstr, "PROT_READ")) *prot |= IOSHARK_PROT_READ; if (strstr(protstr, "PROT_WRITE")) *prot |= IOSHARK_PROT_WRITE; *s = ','; s += 2; for (i = 0 ; i < 2 ; i++) { s = strchr(s, ','); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s += 2; } sscanf(s, "%jx", offset); } void get_lseek_offset_action(char *buf, enum file_op op, off_t *offset, char *action) { char *s, *s2; s = strchr(buf, ','); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s += 2; sscanf(s, "%ju", offset); s = strchr(s, ','); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s += 2; if (op == IOSHARK_LLSEEK) { s = strchr(s, ','); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s += 2; } s2 = strchr(s, ')'); if (s2 == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } *s2 = '\0'; strcpy(action, s); *s2 = ')'; } void get_rw_len(char *buf, u_int64_t *len) { char *s_len; s_len = strrchr(buf, ','); if (s_len == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } sscanf(s_len + 2, "%ju", len); } void get_prw64_offset_len(char *buf, off_t *offset, u_int64_t *len) { char *s_offset, *s_len; s_offset = strrchr(buf, ','); if (s_offset == NULL) { fprintf(stderr, "%s: Malformed line 1: %s\n", __func__, buf); exit(EXIT_FAILURE); } *s_offset = '\0'; s_len = strrchr(buf, ','); if (s_len == NULL) { fprintf(stderr, "%s: Malformed line 2: %s\n", __func__, buf); exit(EXIT_FAILURE); } *s_offset = ','; sscanf(s_len + 2, "%ju", len); sscanf(s_offset + 2, "%ju", offset); } void get_ftrace_offset_len(char *buf, off_t *offset, u_int64_t *len) { char *s_offset; s_offset = strchr(buf, '/'); if (s_offset == NULL) { fprintf(stderr, "%s: Malformed line 1: %s\n", __func__, buf); exit(EXIT_FAILURE); } s_offset = strchr(s_offset, ' '); if (s_offset == NULL) { fprintf(stderr, "%s: Malformed line 2: %s\n", __func__, buf); exit(EXIT_FAILURE); } while (*s_offset == ' ') s_offset++; if (sscanf(s_offset, "%ju %ju", offset, len) != 2) { fprintf(stderr, "%s: Malformed line 3: %s\n", __func__, buf); exit(EXIT_FAILURE); } } void get_openat_flags_mode(char *buf, char *flags, mode_t *mode) { char *s, *s2, lookfor; s = strchr(buf, ','); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s += 2; s = strchr(s, ','); if (s == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } s += 2; if (strstr(s, "O_CREAT") == NULL) lookfor = ')'; else lookfor = ','; s2 = strchr(s, lookfor); if (s2 == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } *s2 = '\0'; strcpy(flags, s); *s2 = lookfor; if (strstr(s, "O_CREAT") != NULL) { s = s2 + 2; s2 = strchr(s, ')'); if (s2 == NULL) { fprintf(stderr, "%s: Malformed line: %s\n", __func__, buf); exit(EXIT_FAILURE); } *s2 = '\0'; sscanf(s, "%o", mode); *s2 = ')'; } } int lookup_map(char *s, struct flags_map_s *flags_map, int maplen) { int found = 0, flag = 0; int i; while (isspace(*s)) s++; for (i = 0 ; i < maplen ; i++) { if (strcmp(flags_map[i].flag_str, s) == 0) { flag = flags_map[i].flag; found = 1; break; } } if (found == 0) { fprintf(stderr, "%s: Unknown syscall %s\n", __func__, s); exit(1); } return flag; } int map_open_flags(char *s) { int flags = 0; char *s1; while ((s1 = strchr(s, '|'))) { *s1 = '\0'; flags |= lookup_map(s, open_flags_map, ARRAY_SIZE(open_flags_map)); *s1 = '|'; s = s1 + 1; } /* Last option */ flags |= lookup_map(s, open_flags_map, ARRAY_SIZE(open_flags_map)); return flags; } int map_lseek_action(char *s) { int flags = 0; char *s1; while ((s1 = strchr(s, '|'))) { *s1 = '\0'; flags |= lookup_map(s, lseek_action_map, ARRAY_SIZE(lseek_action_map)); *s1 = '|'; s = s1 + 1; } /* Last option */ flags |= lookup_map(s, lseek_action_map, ARRAY_SIZE(lseek_action_map)); return flags; } enum file_op map_syscall(char *syscall) { return lookup_map(syscall, fileop_map, ARRAY_SIZE(fileop_map)); } /* * For each tracefile, we first create in-memory structures, then once * we've processed each tracefile completely, we write out the in-memory * structures and free them. */ int main(int argc, char **argv) { FILE *fp; char path[512]; char syscall[512]; char lseek_action_str[512]; char *s; char open_flags_str[64]; void *db_node; int num_io_operations = 0; struct ioshark_header header; struct ioshark_file_operation *disk_file_op; struct in_mem_file_op *in_mem_fop; struct stat st; char *infile, *outfile; struct timeval prev_time; char trace_type[64]; progname = argv[0]; if (argc != 3) { usage(); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; if (stat(infile, &st) < 0) { fprintf(stderr, "%s Can't stat %s\n", progname, infile); exit(EXIT_FAILURE); } if (st.st_size == 0) { fprintf(stderr, "%s Empty file %s\n", progname, infile); exit(EXIT_FAILURE); } init_prev_time(&prev_time); init_filename_cache(); fp = fopen(infile, "r"); if (fp == NULL) { fprintf(stderr, "%s Can't open %s\n", progname, infile); exit(EXIT_FAILURE); } while (fgets(in_buf, 2048, fp)) { s = in_buf; while (isspace(*s)) s++; in_mem_fop = malloc(sizeof(struct in_mem_file_op)); disk_file_op = &in_mem_fop->disk_file_op; disk_file_op->delta_us = get_delta_ts(s, &prev_time); get_tracetype(s, trace_type); if (strcmp(trace_type, "strace") == 0) { get_syscall(s, syscall); disk_file_op->ioshark_io_op = map_syscall(syscall); } else disk_file_op->ioshark_io_op = map_syscall("ftrace"); get_pathname(s, path, disk_file_op->ioshark_io_op); db_node = files_db_add(path); disk_file_op->fileno = files_db_get_fileno(db_node); switch (disk_file_op->ioshark_io_op) { case IOSHARK_LLSEEK: case IOSHARK_LSEEK: get_lseek_offset_action(s, disk_file_op->ioshark_io_op, (off_t *)&disk_file_op->lseek_offset, lseek_action_str); disk_file_op->lseek_action = map_lseek_action(lseek_action_str); if (disk_file_op->lseek_action == SEEK_SET) files_db_update_size(db_node, disk_file_op->lseek_offset); break; case IOSHARK_PREAD64: case IOSHARK_PWRITE64: get_prw64_offset_len(s, (off_t *)&disk_file_op->prw_offset, (u_int64_t *)&disk_file_op->prw_len); files_db_update_size(db_node, disk_file_op->prw_offset + disk_file_op->prw_len); break; case IOSHARK_READ: case IOSHARK_WRITE: get_rw_len(s, (u_int64_t *)&disk_file_op->rw_len); files_db_add_to_size(db_node, disk_file_op->rw_len); break; case IOSHARK_MMAP: case IOSHARK_MMAP2: get_mmap_offset_len_prot(s, (int *)&disk_file_op->mmap_prot, (off_t *)&disk_file_op->mmap_offset, (u_int64_t *)&disk_file_op->mmap_len); files_db_update_size(db_node, disk_file_op->mmap_offset + disk_file_op->mmap_len); break; case IOSHARK_OPEN: disk_file_op->open_mode = 0; get_openat_flags_mode(s, open_flags_str, &disk_file_op->open_mode); disk_file_op->open_flags = map_open_flags(open_flags_str); break; case IOSHARK_FSYNC: case IOSHARK_FDATASYNC: break; case IOSHARK_CLOSE: break; case IOSHARK_MAPPED_PREAD: /* Convert a mmap'ed read into a PREAD64 */ disk_file_op->ioshark_io_op = IOSHARK_PREAD64; get_ftrace_offset_len(s, (off_t *)&disk_file_op->prw_offset, (u_int64_t *)&disk_file_op->prw_len); files_db_update_size(db_node, disk_file_op->prw_offset + disk_file_op->prw_len); break; default: break; } /* Chain at the end */ num_io_operations++; in_mem_fop->next = NULL; if (in_mem_file_op_head == NULL) { in_mem_file_op_tail = in_mem_fop; in_mem_file_op_head = in_mem_fop; } else { in_mem_file_op_tail->next = in_mem_fop; in_mem_file_op_tail = in_mem_fop; } } fclose(fp); /* * Now we can write everything out to the output tracefile. */ fp = fopen(outfile, "w+"); if (fp == NULL) { fprintf(stderr, "%s Can't open trace.outfile\n", progname); exit(EXIT_FAILURE); } header.num_io_operations = num_io_operations; header.num_files = files_db_get_total_obj(); if (ioshark_write_header(fp, &header) != 1) { fprintf(stderr, "%s Write error trace.outfile\n", progname); exit(EXIT_FAILURE); } files_db_write_objects(fp); while (in_mem_file_op_head != NULL) { struct in_mem_file_op *temp; disk_file_op = &in_mem_file_op_head->disk_file_op; if (ioshark_write_file_op(fp, disk_file_op) != 1) { fprintf(stderr, "%s Write error trace.outfile\n", progname); exit(EXIT_FAILURE); } temp = in_mem_file_op_head; in_mem_file_op_head = in_mem_file_op_head->next; free(temp); } store_filename_cache(); }