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
usage(void)93 void usage(void)
94 {
95 fprintf(stderr, "%s in_file out_file\n", progname);
96 }
97
98 void
init_prev_time(struct timeval * tv)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
get_delta_ts(char * buf,struct timeval * prev)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
get_tracetype(char * buf,char * trace_type)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
get_pathname(char * buf,char * pathname,enum file_op file_op)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
get_syscall(char * buf,char * syscall)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
get_mmap_offset_len_prot(char * buf,int * prot,off_t * offset,u_int64_t * len)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
get_lseek_offset_action(char * buf,enum file_op op,off_t * offset,char * action)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
get_rw_len(char * buf,u_int64_t * len)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
get_prw64_offset_len(char * buf,off_t * offset,u_int64_t * len)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
get_ftrace_offset_len(char * buf,off_t * offset,u_int64_t * len)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
get_openat_flags_mode(char * buf,char * flags,mode_t * mode)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
lookup_map(char * s,struct flags_map_s * flags_map,int maplen)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
map_open_flags(char * s)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
map_lseek_action(char * s)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
map_syscall(char * syscall)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 */
main(int argc,char ** argv)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