1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * User-space helper to sort the output of /sys/kernel/debug/page_owner 4 * 5 * Example use: 6 * cat /sys/kernel/debug/page_owner > page_owner_full.txt 7 * ./page_owner_sort page_owner_full.txt sorted_page_owner.txt 8 * Or sort by total memory: 9 * ./page_owner_sort -m page_owner_full.txt sorted_page_owner.txt 10 * 11 * See Documentation/mm/page_owner.rst 12 */ 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <stdbool.h> 17 #include <sys/types.h> 18 #include <sys/stat.h> 19 #include <fcntl.h> 20 #include <unistd.h> 21 #include <string.h> 22 #include <regex.h> 23 #include <errno.h> 24 #include <linux/types.h> 25 #include <getopt.h> 26 27 #define TASK_COMM_LEN 16 28 29 struct block_list { 30 char *txt; 31 char *comm; // task command name 32 char *stacktrace; 33 __u64 ts_nsec; 34 int len; 35 int num; 36 int page_num; 37 pid_t pid; 38 pid_t tgid; 39 int allocator; 40 }; 41 enum FILTER_BIT { 42 FILTER_PID = 1<<1, 43 FILTER_TGID = 1<<2, 44 FILTER_COMM = 1<<3 45 }; 46 enum CULL_BIT { 47 CULL_PID = 1<<1, 48 CULL_TGID = 1<<2, 49 CULL_COMM = 1<<3, 50 CULL_STACKTRACE = 1<<4, 51 CULL_ALLOCATOR = 1<<5 52 }; 53 enum ALLOCATOR_BIT { 54 ALLOCATOR_CMA = 1<<1, 55 ALLOCATOR_SLAB = 1<<2, 56 ALLOCATOR_VMALLOC = 1<<3, 57 ALLOCATOR_OTHERS = 1<<4 58 }; 59 enum ARG_TYPE { 60 ARG_TXT, ARG_COMM, ARG_STACKTRACE, ARG_ALLOC_TS, ARG_CULL_TIME, 61 ARG_PAGE_NUM, ARG_PID, ARG_TGID, ARG_UNKNOWN, ARG_ALLOCATOR 62 }; 63 enum SORT_ORDER { 64 SORT_ASC = 1, 65 SORT_DESC = -1, 66 }; 67 enum COMP_FLAG { 68 COMP_NO_FLAG = 0, 69 COMP_ALLOC = 1<<0, 70 COMP_PAGE_NUM = 1<<1, 71 COMP_PID = 1<<2, 72 COMP_STACK = 1<<3, 73 COMP_NUM = 1<<4, 74 COMP_TGID = 1<<5, 75 COMP_COMM = 1<<6 76 }; 77 struct filter_condition { 78 pid_t *pids; 79 pid_t *tgids; 80 char **comms; 81 int pids_size; 82 int tgids_size; 83 int comms_size; 84 }; 85 struct sort_condition { 86 int (**cmps)(const void *, const void *); 87 int *signs; 88 int size; 89 }; 90 static struct filter_condition fc; 91 static struct sort_condition sc; 92 static regex_t order_pattern; 93 static regex_t pid_pattern; 94 static regex_t tgid_pattern; 95 static regex_t comm_pattern; 96 static regex_t ts_nsec_pattern; 97 static struct block_list *list; 98 static int list_size; 99 static int max_size; 100 static int cull; 101 static int filter; 102 static bool debug_on; 103 104 static void set_single_cmp(int (*cmp)(const void *, const void *), int sign); 105 106 int read_block(char *buf, char *ext_buf, int buf_size, FILE *fin) 107 { 108 char *curr = buf, *const buf_end = buf + buf_size; 109 110 while (buf_end - curr > 1 && fgets(curr, buf_end - curr, fin)) { 111 if (*curr == '\n') { /* empty line */ 112 return curr - buf; 113 } 114 if (!strncmp(curr, "PFN", 3)) { 115 strcpy(ext_buf, curr); 116 continue; 117 } 118 curr += strlen(curr); 119 } 120 121 return -1; /* EOF or no space left in buf. */ 122 } 123 124 static int compare_txt(const void *p1, const void *p2) 125 { 126 const struct block_list *l1 = p1, *l2 = p2; 127 128 return strcmp(l1->txt, l2->txt); 129 } 130 131 static int compare_stacktrace(const void *p1, const void *p2) 132 { 133 const struct block_list *l1 = p1, *l2 = p2; 134 135 return strcmp(l1->stacktrace, l2->stacktrace); 136 } 137 138 static int compare_num(const void *p1, const void *p2) 139 { 140 const struct block_list *l1 = p1, *l2 = p2; 141 142 return l1->num - l2->num; 143 } 144 145 static int compare_page_num(const void *p1, const void *p2) 146 { 147 const struct block_list *l1 = p1, *l2 = p2; 148 149 return l1->page_num - l2->page_num; 150 } 151 152 static int compare_pid(const void *p1, const void *p2) 153 { 154 const struct block_list *l1 = p1, *l2 = p2; 155 156 return l1->pid - l2->pid; 157 } 158 159 static int compare_tgid(const void *p1, const void *p2) 160 { 161 const struct block_list *l1 = p1, *l2 = p2; 162 163 return l1->tgid - l2->tgid; 164 } 165 166 static int compare_allocator(const void *p1, const void *p2) 167 { 168 const struct block_list *l1 = p1, *l2 = p2; 169 170 return l1->allocator - l2->allocator; 171 } 172 173 static int compare_comm(const void *p1, const void *p2) 174 { 175 const struct block_list *l1 = p1, *l2 = p2; 176 177 return strcmp(l1->comm, l2->comm); 178 } 179 180 static int compare_ts(const void *p1, const void *p2) 181 { 182 const struct block_list *l1 = p1, *l2 = p2; 183 184 return l1->ts_nsec < l2->ts_nsec ? -1 : 1; 185 } 186 187 static int compare_cull_condition(const void *p1, const void *p2) 188 { 189 if (cull == 0) 190 return compare_txt(p1, p2); 191 if ((cull & CULL_STACKTRACE) && compare_stacktrace(p1, p2)) 192 return compare_stacktrace(p1, p2); 193 if ((cull & CULL_PID) && compare_pid(p1, p2)) 194 return compare_pid(p1, p2); 195 if ((cull & CULL_TGID) && compare_tgid(p1, p2)) 196 return compare_tgid(p1, p2); 197 if ((cull & CULL_COMM) && compare_comm(p1, p2)) 198 return compare_comm(p1, p2); 199 if ((cull & CULL_ALLOCATOR) && compare_allocator(p1, p2)) 200 return compare_allocator(p1, p2); 201 return 0; 202 } 203 204 static int compare_sort_condition(const void *p1, const void *p2) 205 { 206 int cmp = 0; 207 208 for (int i = 0; i < sc.size; ++i) 209 if (cmp == 0) 210 cmp = sc.signs[i] * sc.cmps[i](p1, p2); 211 return cmp; 212 } 213 214 static int remove_pattern(regex_t *pattern, char *buf, int len) 215 { 216 regmatch_t pmatch[2]; 217 int err; 218 219 err = regexec(pattern, buf, 2, pmatch, REG_NOTBOL); 220 if (err != 0 || pmatch[1].rm_so == -1) 221 return len; 222 223 memcpy(buf + pmatch[1].rm_so, 224 buf + pmatch[1].rm_eo, len - pmatch[1].rm_eo); 225 226 return len - (pmatch[1].rm_eo - pmatch[1].rm_so); 227 } 228 229 static int search_pattern(regex_t *pattern, char *pattern_str, char *buf) 230 { 231 int err, val_len; 232 regmatch_t pmatch[2]; 233 234 err = regexec(pattern, buf, 2, pmatch, REG_NOTBOL); 235 if (err != 0 || pmatch[1].rm_so == -1) { 236 if (debug_on) 237 fprintf(stderr, "no matching pattern in %s\n", buf); 238 return -1; 239 } 240 val_len = pmatch[1].rm_eo - pmatch[1].rm_so; 241 242 memcpy(pattern_str, buf + pmatch[1].rm_so, val_len); 243 244 return 0; 245 } 246 247 static bool check_regcomp(regex_t *pattern, const char *regex) 248 { 249 int err; 250 251 err = regcomp(pattern, regex, REG_EXTENDED | REG_NEWLINE); 252 if (err != 0 || pattern->re_nsub != 1) { 253 fprintf(stderr, "Invalid pattern %s code %d\n", regex, err); 254 return false; 255 } 256 return true; 257 } 258 259 static char **explode(char sep, const char *str, int *size) 260 { 261 int count = 0, len = strlen(str); 262 int lastindex = -1, j = 0; 263 264 for (int i = 0; i < len; i++) 265 if (str[i] == sep) 266 count++; 267 char **ret = calloc(++count, sizeof(char *)); 268 269 for (int i = 0; i < len; i++) { 270 if (str[i] == sep) { 271 ret[j] = calloc(i - lastindex, sizeof(char)); 272 memcpy(ret[j++], str + lastindex + 1, i - lastindex - 1); 273 lastindex = i; 274 } 275 } 276 if (lastindex <= len - 1) { 277 ret[j] = calloc(len - lastindex, sizeof(char)); 278 memcpy(ret[j++], str + lastindex + 1, strlen(str) - 1 - lastindex); 279 } 280 *size = j; 281 return ret; 282 } 283 284 static void free_explode(char **arr, int size) 285 { 286 for (int i = 0; i < size; i++) 287 free(arr[i]); 288 free(arr); 289 } 290 291 # define FIELD_BUFF 25 292 293 static int get_page_num(char *buf) 294 { 295 int order_val; 296 char order_str[FIELD_BUFF] = {0}; 297 char *endptr; 298 299 search_pattern(&order_pattern, order_str, buf); 300 errno = 0; 301 order_val = strtol(order_str, &endptr, 10); 302 if (order_val > 64 || errno != 0 || endptr == order_str || *endptr != '\0') { 303 if (debug_on) 304 fprintf(stderr, "wrong order in follow buf:\n%s\n", buf); 305 return 0; 306 } 307 308 return 1 << order_val; 309 } 310 311 static pid_t get_pid(char *buf) 312 { 313 pid_t pid; 314 char pid_str[FIELD_BUFF] = {0}; 315 char *endptr; 316 317 search_pattern(&pid_pattern, pid_str, buf); 318 errno = 0; 319 pid = strtol(pid_str, &endptr, 10); 320 if (errno != 0 || endptr == pid_str || *endptr != '\0') { 321 if (debug_on) 322 fprintf(stderr, "wrong/invalid pid in follow buf:\n%s\n", buf); 323 return -1; 324 } 325 326 return pid; 327 328 } 329 330 static pid_t get_tgid(char *buf) 331 { 332 pid_t tgid; 333 char tgid_str[FIELD_BUFF] = {0}; 334 char *endptr; 335 336 search_pattern(&tgid_pattern, tgid_str, buf); 337 errno = 0; 338 tgid = strtol(tgid_str, &endptr, 10); 339 if (errno != 0 || endptr == tgid_str || *endptr != '\0') { 340 if (debug_on) 341 fprintf(stderr, "wrong/invalid tgid in follow buf:\n%s\n", buf); 342 return -1; 343 } 344 345 return tgid; 346 347 } 348 349 static __u64 get_ts_nsec(char *buf) 350 { 351 __u64 ts_nsec; 352 char ts_nsec_str[FIELD_BUFF] = {0}; 353 char *endptr; 354 355 search_pattern(&ts_nsec_pattern, ts_nsec_str, buf); 356 errno = 0; 357 ts_nsec = strtoull(ts_nsec_str, &endptr, 10); 358 if (errno != 0 || endptr == ts_nsec_str || *endptr != '\0') { 359 if (debug_on) 360 fprintf(stderr, "wrong ts_nsec in follow buf:\n%s\n", buf); 361 return -1; 362 } 363 364 return ts_nsec; 365 } 366 367 static char *get_comm(char *buf) 368 { 369 char *comm_str = malloc(TASK_COMM_LEN); 370 371 memset(comm_str, 0, TASK_COMM_LEN); 372 373 search_pattern(&comm_pattern, comm_str, buf); 374 errno = 0; 375 if (errno != 0) { 376 if (debug_on) 377 fprintf(stderr, "wrong comm in follow buf:\n%s\n", buf); 378 free(comm_str); 379 return NULL; 380 } 381 382 return comm_str; 383 } 384 385 static int get_arg_type(const char *arg) 386 { 387 if (!strcmp(arg, "pid") || !strcmp(arg, "p")) 388 return ARG_PID; 389 else if (!strcmp(arg, "tgid") || !strcmp(arg, "tg")) 390 return ARG_TGID; 391 else if (!strcmp(arg, "name") || !strcmp(arg, "n")) 392 return ARG_COMM; 393 else if (!strcmp(arg, "stacktrace") || !strcmp(arg, "st")) 394 return ARG_STACKTRACE; 395 else if (!strcmp(arg, "txt") || !strcmp(arg, "T")) 396 return ARG_TXT; 397 else if (!strcmp(arg, "alloc_ts") || !strcmp(arg, "at")) 398 return ARG_ALLOC_TS; 399 else if (!strcmp(arg, "allocator") || !strcmp(arg, "ator")) 400 return ARG_ALLOCATOR; 401 else { 402 return ARG_UNKNOWN; 403 } 404 } 405 406 static int get_allocator(const char *buf, const char *migrate_info) 407 { 408 char *tmp, *first_line, *second_line; 409 int allocator = 0; 410 411 if (strstr(migrate_info, "CMA")) 412 allocator |= ALLOCATOR_CMA; 413 if (strstr(migrate_info, "slab")) 414 allocator |= ALLOCATOR_SLAB; 415 tmp = strstr(buf, "__vmalloc_node_range"); 416 if (tmp) { 417 second_line = tmp; 418 while (*tmp != '\n') 419 tmp--; 420 tmp--; 421 while (*tmp != '\n') 422 tmp--; 423 first_line = ++tmp; 424 tmp = strstr(tmp, "alloc_pages"); 425 if (tmp && first_line <= tmp && tmp < second_line) 426 allocator |= ALLOCATOR_VMALLOC; 427 } 428 if (allocator == 0) 429 allocator = ALLOCATOR_OTHERS; 430 return allocator; 431 } 432 433 static bool match_num_list(int num, int *list, int list_size) 434 { 435 for (int i = 0; i < list_size; ++i) 436 if (list[i] == num) 437 return true; 438 return false; 439 } 440 441 static bool match_str_list(const char *str, char **list, int list_size) 442 { 443 for (int i = 0; i < list_size; ++i) 444 if (!strcmp(list[i], str)) 445 return true; 446 return false; 447 } 448 449 static bool is_need(char *buf) 450 { 451 if ((filter & FILTER_PID) && !match_num_list(get_pid(buf), fc.pids, fc.pids_size)) 452 return false; 453 if ((filter & FILTER_TGID) && 454 !match_num_list(get_tgid(buf), fc.tgids, fc.tgids_size)) 455 return false; 456 457 char *comm = get_comm(buf); 458 459 if ((filter & FILTER_COMM) && 460 !match_str_list(comm, fc.comms, fc.comms_size)) { 461 free(comm); 462 return false; 463 } 464 free(comm); 465 return true; 466 } 467 468 static bool add_list(char *buf, int len, char *ext_buf) 469 { 470 if (list_size == max_size) { 471 fprintf(stderr, "max_size too small??\n"); 472 return false; 473 } 474 if (!is_need(buf)) 475 return true; 476 list[list_size].pid = get_pid(buf); 477 list[list_size].tgid = get_tgid(buf); 478 list[list_size].comm = get_comm(buf); 479 list[list_size].txt = malloc(len+1); 480 if (!list[list_size].txt) { 481 fprintf(stderr, "Out of memory\n"); 482 return false; 483 } 484 memcpy(list[list_size].txt, buf, len); 485 if (sc.cmps[0] != compare_ts) { 486 len = remove_pattern(&ts_nsec_pattern, list[list_size].txt, len); 487 } 488 list[list_size].txt[len] = 0; 489 list[list_size].len = len; 490 list[list_size].num = 1; 491 list[list_size].page_num = get_page_num(buf); 492 493 list[list_size].stacktrace = strchr(list[list_size].txt, '\n') ?: ""; 494 if (*list[list_size].stacktrace == '\n') 495 list[list_size].stacktrace++; 496 list[list_size].ts_nsec = get_ts_nsec(buf); 497 list[list_size].allocator = get_allocator(buf, ext_buf); 498 list_size++; 499 if (list_size % 1000 == 0) { 500 printf("loaded %d\r", list_size); 501 fflush(stdout); 502 } 503 return true; 504 } 505 506 static bool parse_cull_args(const char *arg_str) 507 { 508 int size = 0; 509 char **args = explode(',', arg_str, &size); 510 511 for (int i = 0; i < size; ++i) { 512 int arg_type = get_arg_type(args[i]); 513 514 if (arg_type == ARG_PID) 515 cull |= CULL_PID; 516 else if (arg_type == ARG_TGID) 517 cull |= CULL_TGID; 518 else if (arg_type == ARG_COMM) 519 cull |= CULL_COMM; 520 else if (arg_type == ARG_STACKTRACE) 521 cull |= CULL_STACKTRACE; 522 else if (arg_type == ARG_ALLOCATOR) 523 cull |= CULL_ALLOCATOR; 524 else { 525 free_explode(args, size); 526 return false; 527 } 528 } 529 free_explode(args, size); 530 if (sc.size == 0) 531 set_single_cmp(compare_num, SORT_DESC); 532 return true; 533 } 534 535 static void set_single_cmp(int (*cmp)(const void *, const void *), int sign) 536 { 537 if (sc.signs == NULL || sc.size < 1) 538 sc.signs = calloc(1, sizeof(int)); 539 sc.signs[0] = sign; 540 if (sc.cmps == NULL || sc.size < 1) 541 sc.cmps = calloc(1, sizeof(int *)); 542 sc.cmps[0] = cmp; 543 sc.size = 1; 544 } 545 546 static bool parse_sort_args(const char *arg_str) 547 { 548 int size = 0; 549 550 if (sc.size != 0) { /* reset sort_condition */ 551 free(sc.signs); 552 free(sc.cmps); 553 size = 0; 554 } 555 556 char **args = explode(',', arg_str, &size); 557 558 sc.signs = calloc(size, sizeof(int)); 559 sc.cmps = calloc(size, sizeof(int *)); 560 for (int i = 0; i < size; ++i) { 561 int offset = 0; 562 563 sc.signs[i] = SORT_ASC; 564 if (args[i][0] == '-' || args[i][0] == '+') { 565 if (args[i][0] == '-') 566 sc.signs[i] = SORT_DESC; 567 offset = 1; 568 } 569 570 int arg_type = get_arg_type(args[i]+offset); 571 572 if (arg_type == ARG_PID) 573 sc.cmps[i] = compare_pid; 574 else if (arg_type == ARG_TGID) 575 sc.cmps[i] = compare_tgid; 576 else if (arg_type == ARG_COMM) 577 sc.cmps[i] = compare_comm; 578 else if (arg_type == ARG_STACKTRACE) 579 sc.cmps[i] = compare_stacktrace; 580 else if (arg_type == ARG_ALLOC_TS) 581 sc.cmps[i] = compare_ts; 582 else if (arg_type == ARG_TXT) 583 sc.cmps[i] = compare_txt; 584 else if (arg_type == ARG_ALLOCATOR) 585 sc.cmps[i] = compare_allocator; 586 else { 587 free_explode(args, size); 588 sc.size = 0; 589 return false; 590 } 591 } 592 sc.size = size; 593 free_explode(args, size); 594 return true; 595 } 596 597 static int *parse_nums_list(char *arg_str, int *list_size) 598 { 599 int size = 0; 600 char **args = explode(',', arg_str, &size); 601 int *list = calloc(size, sizeof(int)); 602 603 errno = 0; 604 for (int i = 0; i < size; ++i) { 605 char *endptr = NULL; 606 607 list[i] = strtol(args[i], &endptr, 10); 608 if (errno != 0 || endptr == args[i] || *endptr != '\0') { 609 free(list); 610 return NULL; 611 } 612 } 613 *list_size = size; 614 free_explode(args, size); 615 return list; 616 } 617 618 static void print_allocator(FILE *out, int allocator) 619 { 620 fprintf(out, "allocated by "); 621 if (allocator & ALLOCATOR_CMA) 622 fprintf(out, "CMA "); 623 if (allocator & ALLOCATOR_SLAB) 624 fprintf(out, "SLAB "); 625 if (allocator & ALLOCATOR_VMALLOC) 626 fprintf(out, "VMALLOC "); 627 if (allocator & ALLOCATOR_OTHERS) 628 fprintf(out, "OTHERS "); 629 } 630 631 #define BUF_SIZE (128 * 1024) 632 633 static void usage(void) 634 { 635 printf("Usage: ./page_owner_sort [OPTIONS] <input> <output>\n" 636 "-a\t\t\tSort by memory allocation time.\n" 637 "-m\t\t\tSort by total memory.\n" 638 "-n\t\t\tSort by task command name.\n" 639 "-p\t\t\tSort by pid.\n" 640 "-P\t\t\tSort by tgid.\n" 641 "-s\t\t\tSort by the stacktrace.\n" 642 "-t\t\t\tSort by number of times record is seen (default).\n\n" 643 "--pid <pidlist>\t\tSelect by pid. This selects the information" 644 " of\n\t\t\tblocks whose process ID numbers appear in <pidlist>.\n" 645 "--tgid <tgidlist>\tSelect by tgid. This selects the information" 646 " of\n\t\t\tblocks whose Thread Group ID numbers appear in " 647 "<tgidlist>.\n" 648 "--name <cmdlist>\tSelect by command name. This selects the" 649 " information\n\t\t\tof blocks whose command name appears in" 650 " <cmdlist>.\n" 651 "--cull <rules>\t\tCull by user-defined rules. <rules> is a " 652 "single\n\t\t\targument in the form of a comma-separated list " 653 "with some\n\t\t\tcommon fields predefined (pid, tgid, comm, " 654 "stacktrace, allocator)\n" 655 "--sort <order>\t\tSpecify sort order as: [+|-]key[,[+|-]key[,...]]\n" 656 ); 657 } 658 659 int main(int argc, char **argv) 660 { 661 FILE *fin, *fout; 662 char *buf, *ext_buf; 663 int i, count, compare_flag; 664 struct stat st; 665 int opt; 666 struct option longopts[] = { 667 { "pid", required_argument, NULL, 1 }, 668 { "tgid", required_argument, NULL, 2 }, 669 { "name", required_argument, NULL, 3 }, 670 { "cull", required_argument, NULL, 4 }, 671 { "sort", required_argument, NULL, 5 }, 672 { "help", no_argument, NULL, 'h' }, 673 { 0, 0, 0, 0}, 674 }; 675 676 compare_flag = COMP_NO_FLAG; 677 678 while ((opt = getopt_long(argc, argv, "admnpstPh", longopts, NULL)) != -1) 679 switch (opt) { 680 case 'a': 681 compare_flag |= COMP_ALLOC; 682 break; 683 case 'd': 684 debug_on = true; 685 break; 686 case 'm': 687 compare_flag |= COMP_PAGE_NUM; 688 break; 689 case 'p': 690 compare_flag |= COMP_PID; 691 break; 692 case 's': 693 compare_flag |= COMP_STACK; 694 break; 695 case 't': 696 compare_flag |= COMP_NUM; 697 break; 698 case 'P': 699 compare_flag |= COMP_TGID; 700 break; 701 case 'n': 702 compare_flag |= COMP_COMM; 703 break; 704 case 'h': 705 usage(); 706 exit(0); 707 case 1: 708 filter = filter | FILTER_PID; 709 fc.pids = parse_nums_list(optarg, &fc.pids_size); 710 if (fc.pids == NULL) { 711 fprintf(stderr, "wrong/invalid pid in from the command line:%s\n", 712 optarg); 713 exit(1); 714 } 715 break; 716 case 2: 717 filter = filter | FILTER_TGID; 718 fc.tgids = parse_nums_list(optarg, &fc.tgids_size); 719 if (fc.tgids == NULL) { 720 fprintf(stderr, "wrong/invalid tgid in from the command line:%s\n", 721 optarg); 722 exit(1); 723 } 724 break; 725 case 3: 726 filter = filter | FILTER_COMM; 727 fc.comms = explode(',', optarg, &fc.comms_size); 728 break; 729 case 4: 730 if (!parse_cull_args(optarg)) { 731 fprintf(stderr, "wrong argument after --cull option:%s\n", 732 optarg); 733 exit(1); 734 } 735 break; 736 case 5: 737 if (!parse_sort_args(optarg)) { 738 fprintf(stderr, "wrong argument after --sort option:%s\n", 739 optarg); 740 exit(1); 741 } 742 break; 743 default: 744 usage(); 745 exit(1); 746 } 747 748 if (optind >= (argc - 1)) { 749 usage(); 750 exit(1); 751 } 752 753 /* Only one compare option is allowed, yet we also want handle the 754 * default case were no option is provided, but we still want to 755 * match the behavior of the -t option (compare by number of times 756 * a record is seen 757 */ 758 switch (compare_flag) { 759 case COMP_ALLOC: 760 set_single_cmp(compare_ts, SORT_ASC); 761 break; 762 case COMP_PAGE_NUM: 763 set_single_cmp(compare_page_num, SORT_DESC); 764 break; 765 case COMP_PID: 766 set_single_cmp(compare_pid, SORT_ASC); 767 break; 768 case COMP_STACK: 769 set_single_cmp(compare_stacktrace, SORT_ASC); 770 break; 771 case COMP_NO_FLAG: 772 case COMP_NUM: 773 set_single_cmp(compare_num, SORT_DESC); 774 break; 775 case COMP_TGID: 776 set_single_cmp(compare_tgid, SORT_ASC); 777 break; 778 case COMP_COMM: 779 set_single_cmp(compare_comm, SORT_ASC); 780 break; 781 default: 782 usage(); 783 exit(1); 784 } 785 786 fin = fopen(argv[optind], "r"); 787 fout = fopen(argv[optind + 1], "w"); 788 if (!fin || !fout) { 789 usage(); 790 perror("open: "); 791 exit(1); 792 } 793 794 if (!check_regcomp(&order_pattern, "order\\s*([0-9]*),")) 795 goto out_order; 796 if (!check_regcomp(&pid_pattern, "pid\\s*([0-9]*),")) 797 goto out_pid; 798 if (!check_regcomp(&tgid_pattern, "tgid\\s*([0-9]*) ")) 799 goto out_tgid; 800 if (!check_regcomp(&comm_pattern, "tgid\\s*[0-9]*\\s*\\((.*)\\),\\s*ts")) 801 goto out_comm; 802 if (!check_regcomp(&ts_nsec_pattern, "ts\\s*([0-9]*)\\s*ns")) 803 goto out_ts; 804 805 fstat(fileno(fin), &st); 806 max_size = st.st_size / 100; /* hack ... */ 807 808 list = malloc(max_size * sizeof(*list)); 809 buf = malloc(BUF_SIZE); 810 ext_buf = malloc(BUF_SIZE); 811 if (!list || !buf || !ext_buf) { 812 fprintf(stderr, "Out of memory\n"); 813 goto out_free; 814 } 815 816 for ( ; ; ) { 817 int buf_len = read_block(buf, ext_buf, BUF_SIZE, fin); 818 819 if (buf_len < 0) 820 break; 821 if (!add_list(buf, buf_len, ext_buf)) 822 goto out_free; 823 } 824 825 printf("loaded %d\n", list_size); 826 827 printf("sorting ....\n"); 828 829 qsort(list, list_size, sizeof(list[0]), compare_cull_condition); 830 831 printf("culling\n"); 832 833 for (i = count = 0; i < list_size; i++) { 834 if (count == 0 || 835 compare_cull_condition((void *)(&list[count-1]), (void *)(&list[i])) != 0) { 836 list[count++] = list[i]; 837 } else { 838 list[count-1].num += list[i].num; 839 list[count-1].page_num += list[i].page_num; 840 } 841 } 842 843 qsort(list, count, sizeof(list[0]), compare_sort_condition); 844 845 for (i = 0; i < count; i++) { 846 if (cull == 0) { 847 fprintf(fout, "%d times, %d pages, ", list[i].num, list[i].page_num); 848 print_allocator(fout, list[i].allocator); 849 fprintf(fout, ":\n%s\n", list[i].txt); 850 } 851 else { 852 fprintf(fout, "%d times, %d pages", 853 list[i].num, list[i].page_num); 854 if (cull & CULL_PID || filter & FILTER_PID) 855 fprintf(fout, ", PID %d", list[i].pid); 856 if (cull & CULL_TGID || filter & FILTER_TGID) 857 fprintf(fout, ", TGID %d", list[i].tgid); 858 if (cull & CULL_COMM || filter & FILTER_COMM) 859 fprintf(fout, ", task_comm_name: %s", list[i].comm); 860 if (cull & CULL_ALLOCATOR) { 861 fprintf(fout, ", "); 862 print_allocator(fout, list[i].allocator); 863 } 864 if (cull & CULL_STACKTRACE) 865 fprintf(fout, ":\n%s", list[i].stacktrace); 866 fprintf(fout, "\n"); 867 } 868 } 869 870 out_free: 871 if (ext_buf) 872 free(ext_buf); 873 if (buf) 874 free(buf); 875 if (list) 876 free(list); 877 out_ts: 878 regfree(&ts_nsec_pattern); 879 out_comm: 880 regfree(&comm_pattern); 881 out_tgid: 882 regfree(&tgid_pattern); 883 out_pid: 884 regfree(&pid_pattern); 885 out_order: 886 regfree(&order_pattern); 887 888 return 0; 889 } 890