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