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