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