Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * User-space helper to sort the output of /sys/kernel/debug/page_owner
0004  *
0005  * Example use:
0006  * cat /sys/kernel/debug/page_owner > page_owner_full.txt
0007  * ./page_owner_sort page_owner_full.txt sorted_page_owner.txt
0008  * Or sort by total memory:
0009  * ./page_owner_sort -m page_owner_full.txt sorted_page_owner.txt
0010  *
0011  * See Documentation/mm/page_owner.rst
0012 */
0013 
0014 #include <stdio.h>
0015 #include <stdlib.h>
0016 #include <sys/types.h>
0017 #include <sys/stat.h>
0018 #include <fcntl.h>
0019 #include <unistd.h>
0020 #include <string.h>
0021 #include <regex.h>
0022 #include <errno.h>
0023 #include <linux/types.h>
0024 #include <getopt.h>
0025 
0026 #define bool int
0027 #define true 1
0028 #define false 0
0029 #define TASK_COMM_LEN 16
0030 
0031 struct block_list {
0032     char *txt;
0033     char *comm; // task command name
0034     char *stacktrace;
0035     __u64 ts_nsec;
0036     __u64 free_ts_nsec;
0037     int len;
0038     int num;
0039     int page_num;
0040     pid_t pid;
0041     pid_t tgid;
0042     int allocator;
0043 };
0044 enum FILTER_BIT {
0045     FILTER_UNRELEASE = 1<<1,
0046     FILTER_PID = 1<<2,
0047     FILTER_TGID = 1<<3,
0048     FILTER_COMM = 1<<4
0049 };
0050 enum CULL_BIT {
0051     CULL_UNRELEASE = 1<<1,
0052     CULL_PID = 1<<2,
0053     CULL_TGID = 1<<3,
0054     CULL_COMM = 1<<4,
0055     CULL_STACKTRACE = 1<<5,
0056     CULL_ALLOCATOR = 1<<6
0057 };
0058 enum ALLOCATOR_BIT {
0059     ALLOCATOR_CMA = 1<<1,
0060     ALLOCATOR_SLAB = 1<<2,
0061     ALLOCATOR_VMALLOC = 1<<3,
0062     ALLOCATOR_OTHERS = 1<<4
0063 };
0064 enum ARG_TYPE {
0065     ARG_TXT, ARG_COMM, ARG_STACKTRACE, ARG_ALLOC_TS, ARG_FREE_TS,
0066     ARG_CULL_TIME, ARG_PAGE_NUM, ARG_PID, ARG_TGID, ARG_UNKNOWN, ARG_FREE,
0067     ARG_ALLOCATOR
0068 };
0069 enum SORT_ORDER {
0070     SORT_ASC = 1,
0071     SORT_DESC = -1,
0072 };
0073 struct filter_condition {
0074     pid_t *pids;
0075     pid_t *tgids;
0076     char **comms;
0077     int pids_size;
0078     int tgids_size;
0079     int comms_size;
0080 };
0081 struct sort_condition {
0082     int (**cmps)(const void *, const void *);
0083     int *signs;
0084     int size;
0085 };
0086 static struct filter_condition fc;
0087 static struct sort_condition sc;
0088 static regex_t order_pattern;
0089 static regex_t pid_pattern;
0090 static regex_t tgid_pattern;
0091 static regex_t comm_pattern;
0092 static regex_t ts_nsec_pattern;
0093 static regex_t free_ts_nsec_pattern;
0094 static struct block_list *list;
0095 static int list_size;
0096 static int max_size;
0097 static int cull;
0098 static int filter;
0099 static bool debug_on;
0100 
0101 static void set_single_cmp(int (*cmp)(const void *, const void *), int sign);
0102 
0103 int read_block(char *buf, char *ext_buf, int buf_size, FILE *fin)
0104 {
0105     char *curr = buf, *const buf_end = buf + buf_size;
0106 
0107     while (buf_end - curr > 1 && fgets(curr, buf_end - curr, fin)) {
0108         if (*curr == '\n') { /* empty line */
0109             return curr - buf;
0110         }
0111         if (!strncmp(curr, "PFN", 3)) {
0112             strcpy(ext_buf, curr);
0113             continue;
0114         }
0115         curr += strlen(curr);
0116     }
0117 
0118     return -1; /* EOF or no space left in buf. */
0119 }
0120 
0121 static int compare_txt(const void *p1, const void *p2)
0122 {
0123     const struct block_list *l1 = p1, *l2 = p2;
0124 
0125     return strcmp(l1->txt, l2->txt);
0126 }
0127 
0128 static int compare_stacktrace(const void *p1, const void *p2)
0129 {
0130     const struct block_list *l1 = p1, *l2 = p2;
0131 
0132     return strcmp(l1->stacktrace, l2->stacktrace);
0133 }
0134 
0135 static int compare_num(const void *p1, const void *p2)
0136 {
0137     const struct block_list *l1 = p1, *l2 = p2;
0138 
0139     return l1->num - l2->num;
0140 }
0141 
0142 static int compare_page_num(const void *p1, const void *p2)
0143 {
0144     const struct block_list *l1 = p1, *l2 = p2;
0145 
0146     return l1->page_num - l2->page_num;
0147 }
0148 
0149 static int compare_pid(const void *p1, const void *p2)
0150 {
0151     const struct block_list *l1 = p1, *l2 = p2;
0152 
0153     return l1->pid - l2->pid;
0154 }
0155 
0156 static int compare_tgid(const void *p1, const void *p2)
0157 {
0158     const struct block_list *l1 = p1, *l2 = p2;
0159 
0160     return l1->tgid - l2->tgid;
0161 }
0162 
0163 static int compare_allocator(const void *p1, const void *p2)
0164 {
0165     const struct block_list *l1 = p1, *l2 = p2;
0166 
0167     return l1->allocator - l2->allocator;
0168 }
0169 
0170 static int compare_comm(const void *p1, const void *p2)
0171 {
0172     const struct block_list *l1 = p1, *l2 = p2;
0173 
0174     return strcmp(l1->comm, l2->comm);
0175 }
0176 
0177 static int compare_ts(const void *p1, const void *p2)
0178 {
0179     const struct block_list *l1 = p1, *l2 = p2;
0180 
0181     return l1->ts_nsec < l2->ts_nsec ? -1 : 1;
0182 }
0183 
0184 static int compare_free_ts(const void *p1, const void *p2)
0185 {
0186     const struct block_list *l1 = p1, *l2 = p2;
0187 
0188     return l1->free_ts_nsec < l2->free_ts_nsec ? -1 : 1;
0189 }
0190 
0191 static int compare_release(const void *p1, const void *p2)
0192 {
0193     const struct block_list *l1 = p1, *l2 = p2;
0194 
0195     if (!l1->free_ts_nsec && !l2->free_ts_nsec)
0196         return 0;
0197     if (l1->free_ts_nsec && l2->free_ts_nsec)
0198         return 0;
0199     return l1->free_ts_nsec ? 1 : -1;
0200 }
0201 
0202 static int compare_cull_condition(const void *p1, const void *p2)
0203 {
0204     if (cull == 0)
0205         return compare_txt(p1, p2);
0206     if ((cull & CULL_STACKTRACE) && compare_stacktrace(p1, p2))
0207         return compare_stacktrace(p1, p2);
0208     if ((cull & CULL_PID) && compare_pid(p1, p2))
0209         return compare_pid(p1, p2);
0210     if ((cull & CULL_TGID) && compare_tgid(p1, p2))
0211         return compare_tgid(p1, p2);
0212     if ((cull & CULL_COMM) && compare_comm(p1, p2))
0213         return compare_comm(p1, p2);
0214     if ((cull & CULL_UNRELEASE) && compare_release(p1, p2))
0215         return compare_release(p1, p2);
0216     if ((cull & CULL_ALLOCATOR) && compare_allocator(p1, p2))
0217         return compare_allocator(p1, p2);
0218     return 0;
0219 }
0220 
0221 static int compare_sort_condition(const void *p1, const void *p2)
0222 {
0223     int cmp = 0;
0224 
0225     for (int i = 0; i < sc.size; ++i)
0226         if (cmp == 0)
0227             cmp = sc.signs[i] * sc.cmps[i](p1, p2);
0228     return cmp;
0229 }
0230 
0231 static int search_pattern(regex_t *pattern, char *pattern_str, char *buf)
0232 {
0233     int err, val_len;
0234     regmatch_t pmatch[2];
0235 
0236     err = regexec(pattern, buf, 2, pmatch, REG_NOTBOL);
0237     if (err != 0 || pmatch[1].rm_so == -1) {
0238         if (debug_on)
0239             fprintf(stderr, "no matching pattern in %s\n", buf);
0240         return -1;
0241     }
0242     val_len = pmatch[1].rm_eo - pmatch[1].rm_so;
0243 
0244     memcpy(pattern_str, buf + pmatch[1].rm_so, val_len);
0245 
0246     return 0;
0247 }
0248 
0249 static void check_regcomp(regex_t *pattern, const char *regex)
0250 {
0251     int err;
0252 
0253     err = regcomp(pattern, regex, REG_EXTENDED | REG_NEWLINE);
0254     if (err != 0 || pattern->re_nsub != 1) {
0255         fprintf(stderr, "Invalid pattern %s code %d\n", regex, err);
0256         exit(1);
0257     }
0258 }
0259 
0260 static char **explode(char sep, const char *str, int *size)
0261 {
0262     int count = 0, len = strlen(str);
0263     int lastindex = -1, j = 0;
0264 
0265     for (int i = 0; i < len; i++)
0266         if (str[i] == sep)
0267             count++;
0268     char **ret = calloc(++count, sizeof(char *));
0269 
0270     for (int i = 0; i < len; i++) {
0271         if (str[i] == sep) {
0272             ret[j] = calloc(i - lastindex, sizeof(char));
0273             memcpy(ret[j++], str + lastindex + 1, i - lastindex - 1);
0274             lastindex = i;
0275         }
0276     }
0277     if (lastindex <= len - 1) {
0278         ret[j] = calloc(len - lastindex, sizeof(char));
0279         memcpy(ret[j++], str + lastindex + 1, strlen(str) - 1 - lastindex);
0280     }
0281     *size = j;
0282     return ret;
0283 }
0284 
0285 static void free_explode(char **arr, int size)
0286 {
0287     for (int i = 0; i < size; i++)
0288         free(arr[i]);
0289     free(arr);
0290 }
0291 
0292 # define FIELD_BUFF 25
0293 
0294 static int get_page_num(char *buf)
0295 {
0296     int order_val;
0297     char order_str[FIELD_BUFF] = {0};
0298     char *endptr;
0299 
0300     search_pattern(&order_pattern, order_str, buf);
0301     errno = 0;
0302     order_val = strtol(order_str, &endptr, 10);
0303     if (order_val > 64 || errno != 0 || endptr == order_str || *endptr != '\0') {
0304         if (debug_on)
0305             fprintf(stderr, "wrong order in follow buf:\n%s\n", buf);
0306         return 0;
0307     }
0308 
0309     return 1 << order_val;
0310 }
0311 
0312 static pid_t get_pid(char *buf)
0313 {
0314     pid_t pid;
0315     char pid_str[FIELD_BUFF] = {0};
0316     char *endptr;
0317 
0318     search_pattern(&pid_pattern, pid_str, buf);
0319     errno = 0;
0320     pid = strtol(pid_str, &endptr, 10);
0321     if (errno != 0 || endptr == pid_str || *endptr != '\0') {
0322         if (debug_on)
0323             fprintf(stderr, "wrong/invalid pid in follow buf:\n%s\n", buf);
0324         return -1;
0325     }
0326 
0327     return pid;
0328 
0329 }
0330 
0331 static pid_t get_tgid(char *buf)
0332 {
0333     pid_t tgid;
0334     char tgid_str[FIELD_BUFF] = {0};
0335     char *endptr;
0336 
0337     search_pattern(&tgid_pattern, tgid_str, buf);
0338     errno = 0;
0339     tgid = strtol(tgid_str, &endptr, 10);
0340     if (errno != 0 || endptr == tgid_str || *endptr != '\0') {
0341         if (debug_on)
0342             fprintf(stderr, "wrong/invalid tgid in follow buf:\n%s\n", buf);
0343         return -1;
0344     }
0345 
0346     return tgid;
0347 
0348 }
0349 
0350 static __u64 get_ts_nsec(char *buf)
0351 {
0352     __u64 ts_nsec;
0353     char ts_nsec_str[FIELD_BUFF] = {0};
0354     char *endptr;
0355 
0356     search_pattern(&ts_nsec_pattern, ts_nsec_str, buf);
0357     errno = 0;
0358     ts_nsec = strtoull(ts_nsec_str, &endptr, 10);
0359     if (errno != 0 || endptr == ts_nsec_str || *endptr != '\0') {
0360         if (debug_on)
0361             fprintf(stderr, "wrong ts_nsec in follow buf:\n%s\n", buf);
0362         return -1;
0363     }
0364 
0365     return ts_nsec;
0366 }
0367 
0368 static __u64 get_free_ts_nsec(char *buf)
0369 {
0370     __u64 free_ts_nsec;
0371     char free_ts_nsec_str[FIELD_BUFF] = {0};
0372     char *endptr;
0373 
0374     search_pattern(&free_ts_nsec_pattern, free_ts_nsec_str, buf);
0375     errno = 0;
0376     free_ts_nsec = strtoull(free_ts_nsec_str, &endptr, 10);
0377     if (errno != 0 || endptr == free_ts_nsec_str || *endptr != '\0') {
0378         if (debug_on)
0379             fprintf(stderr, "wrong free_ts_nsec in follow buf:\n%s\n", buf);
0380         return -1;
0381     }
0382 
0383     return free_ts_nsec;
0384 }
0385 
0386 static char *get_comm(char *buf)
0387 {
0388     char *comm_str = malloc(TASK_COMM_LEN);
0389 
0390     memset(comm_str, 0, TASK_COMM_LEN);
0391 
0392     search_pattern(&comm_pattern, comm_str, buf);
0393     errno = 0;
0394     if (errno != 0) {
0395         if (debug_on)
0396             fprintf(stderr, "wrong comm in follow buf:\n%s\n", buf);
0397         return NULL;
0398     }
0399 
0400     return comm_str;
0401 }
0402 
0403 static int get_arg_type(const char *arg)
0404 {
0405     if (!strcmp(arg, "pid") || !strcmp(arg, "p"))
0406         return ARG_PID;
0407     else if (!strcmp(arg, "tgid") || !strcmp(arg, "tg"))
0408         return ARG_TGID;
0409     else if (!strcmp(arg, "name") || !strcmp(arg, "n"))
0410         return  ARG_COMM;
0411     else if (!strcmp(arg, "stacktrace") || !strcmp(arg, "st"))
0412         return ARG_STACKTRACE;
0413     else if (!strcmp(arg, "free") || !strcmp(arg, "f"))
0414         return ARG_FREE;
0415     else if (!strcmp(arg, "txt") || !strcmp(arg, "T"))
0416         return ARG_TXT;
0417     else if (!strcmp(arg, "free_ts") || !strcmp(arg, "ft"))
0418         return ARG_FREE_TS;
0419     else if (!strcmp(arg, "alloc_ts") || !strcmp(arg, "at"))
0420         return ARG_ALLOC_TS;
0421     else if (!strcmp(arg, "allocator") || !strcmp(arg, "ator"))
0422         return ARG_ALLOCATOR;
0423     else {
0424         return ARG_UNKNOWN;
0425     }
0426 }
0427 
0428 static int get_allocator(const char *buf, const char *migrate_info)
0429 {
0430     char *tmp, *first_line, *second_line;
0431     int allocator = 0;
0432 
0433     if (strstr(migrate_info, "CMA"))
0434         allocator |= ALLOCATOR_CMA;
0435     if (strstr(migrate_info, "slab"))
0436         allocator |= ALLOCATOR_SLAB;
0437     tmp = strstr(buf, "__vmalloc_node_range");
0438     if (tmp) {
0439         second_line = tmp;
0440         while (*tmp != '\n')
0441             tmp--;
0442         tmp--;
0443         while (*tmp != '\n')
0444             tmp--;
0445         first_line = ++tmp;
0446         tmp = strstr(tmp, "alloc_pages");
0447         if (tmp && first_line <= tmp && tmp < second_line)
0448             allocator |= ALLOCATOR_VMALLOC;
0449     }
0450     if (allocator == 0)
0451         allocator = ALLOCATOR_OTHERS;
0452     return allocator;
0453 }
0454 
0455 static bool match_num_list(int num, int *list, int list_size)
0456 {
0457     for (int i = 0; i < list_size; ++i)
0458         if (list[i] == num)
0459             return true;
0460     return false;
0461 }
0462 
0463 static bool match_str_list(const char *str, char **list, int list_size)
0464 {
0465     for (int i = 0; i < list_size; ++i)
0466         if (!strcmp(list[i], str))
0467             return true;
0468     return false;
0469 }
0470 
0471 static bool is_need(char *buf)
0472 {
0473     if ((filter & FILTER_UNRELEASE) && get_free_ts_nsec(buf) != 0)
0474         return false;
0475     if ((filter & FILTER_PID) && !match_num_list(get_pid(buf), fc.pids, fc.pids_size))
0476         return false;
0477     if ((filter & FILTER_TGID) &&
0478         !match_num_list(get_tgid(buf), fc.tgids, fc.tgids_size))
0479         return false;
0480 
0481     char *comm = get_comm(buf);
0482 
0483     if ((filter & FILTER_COMM) &&
0484     !match_str_list(comm, fc.comms, fc.comms_size)) {
0485         free(comm);
0486         return false;
0487     }
0488     free(comm);
0489     return true;
0490 }
0491 
0492 static void add_list(char *buf, int len, char *ext_buf)
0493 {
0494     if (list_size != 0 &&
0495         len == list[list_size-1].len &&
0496         memcmp(buf, list[list_size-1].txt, len) == 0) {
0497         list[list_size-1].num++;
0498         list[list_size-1].page_num += get_page_num(buf);
0499         return;
0500     }
0501     if (list_size == max_size) {
0502         fprintf(stderr, "max_size too small??\n");
0503         exit(1);
0504     }
0505     if (!is_need(buf))
0506         return;
0507     list[list_size].pid = get_pid(buf);
0508     list[list_size].tgid = get_tgid(buf);
0509     list[list_size].comm = get_comm(buf);
0510     list[list_size].txt = malloc(len+1);
0511     if (!list[list_size].txt) {
0512         fprintf(stderr, "Out of memory\n");
0513         exit(1);
0514     }
0515     memcpy(list[list_size].txt, buf, len);
0516     list[list_size].txt[len] = 0;
0517     list[list_size].len = len;
0518     list[list_size].num = 1;
0519     list[list_size].page_num = get_page_num(buf);
0520 
0521     list[list_size].stacktrace = strchr(list[list_size].txt, '\n') ?: "";
0522     if (*list[list_size].stacktrace == '\n')
0523         list[list_size].stacktrace++;
0524     list[list_size].ts_nsec = get_ts_nsec(buf);
0525     list[list_size].free_ts_nsec = get_free_ts_nsec(buf);
0526     list[list_size].allocator = get_allocator(buf, ext_buf);
0527     list_size++;
0528     if (list_size % 1000 == 0) {
0529         printf("loaded %d\r", list_size);
0530         fflush(stdout);
0531     }
0532 }
0533 
0534 static bool parse_cull_args(const char *arg_str)
0535 {
0536     int size = 0;
0537     char **args = explode(',', arg_str, &size);
0538 
0539     for (int i = 0; i < size; ++i) {
0540         int arg_type = get_arg_type(args[i]);
0541 
0542         if (arg_type == ARG_PID)
0543             cull |= CULL_PID;
0544         else if (arg_type == ARG_TGID)
0545             cull |= CULL_TGID;
0546         else if (arg_type == ARG_COMM)
0547             cull |= CULL_COMM;
0548         else if (arg_type == ARG_STACKTRACE)
0549             cull |= CULL_STACKTRACE;
0550         else if (arg_type == ARG_FREE)
0551             cull |= CULL_UNRELEASE;
0552         else if (arg_type == ARG_ALLOCATOR)
0553             cull |= CULL_ALLOCATOR;
0554         else {
0555             free_explode(args, size);
0556             return false;
0557         }
0558     }
0559     free_explode(args, size);
0560     if (sc.size == 0)
0561         set_single_cmp(compare_num, SORT_DESC);
0562     return true;
0563 }
0564 
0565 static void set_single_cmp(int (*cmp)(const void *, const void *), int sign)
0566 {
0567     if (sc.signs == NULL || sc.size < 1)
0568         sc.signs = calloc(1, sizeof(int));
0569     sc.signs[0] = sign;
0570     if (sc.cmps == NULL || sc.size < 1)
0571         sc.cmps = calloc(1, sizeof(int *));
0572     sc.cmps[0] = cmp;
0573     sc.size = 1;
0574 }
0575 
0576 static bool parse_sort_args(const char *arg_str)
0577 {
0578     int size = 0;
0579 
0580     if (sc.size != 0) { /* reset sort_condition */
0581         free(sc.signs);
0582         free(sc.cmps);
0583         size = 0;
0584     }
0585 
0586     char **args = explode(',', arg_str, &size);
0587 
0588     sc.signs = calloc(size, sizeof(int));
0589     sc.cmps = calloc(size, sizeof(int *));
0590     for (int i = 0; i < size; ++i) {
0591         int offset = 0;
0592 
0593         sc.signs[i] = SORT_ASC;
0594         if (args[i][0] == '-' || args[i][0] == '+') {
0595             if (args[i][0] == '-')
0596                 sc.signs[i] = SORT_DESC;
0597             offset = 1;
0598         }
0599 
0600         int arg_type = get_arg_type(args[i]+offset);
0601 
0602         if (arg_type == ARG_PID)
0603             sc.cmps[i] = compare_pid;
0604         else if (arg_type == ARG_TGID)
0605             sc.cmps[i] = compare_tgid;
0606         else if (arg_type == ARG_COMM)
0607             sc.cmps[i] = compare_comm;
0608         else if (arg_type == ARG_STACKTRACE)
0609             sc.cmps[i] = compare_stacktrace;
0610         else if (arg_type == ARG_ALLOC_TS)
0611             sc.cmps[i] = compare_ts;
0612         else if (arg_type == ARG_FREE_TS)
0613             sc.cmps[i] = compare_free_ts;
0614         else if (arg_type == ARG_TXT)
0615             sc.cmps[i] = compare_txt;
0616         else if (arg_type == ARG_ALLOCATOR)
0617             sc.cmps[i] = compare_allocator;
0618         else {
0619             free_explode(args, size);
0620             sc.size = 0;
0621             return false;
0622         }
0623     }
0624     sc.size = size;
0625     free_explode(args, size);
0626     return true;
0627 }
0628 
0629 static int *parse_nums_list(char *arg_str, int *list_size)
0630 {
0631     int size = 0;
0632     char **args = explode(',', arg_str, &size);
0633     int *list = calloc(size, sizeof(int));
0634 
0635     errno = 0;
0636     for (int i = 0; i < size; ++i) {
0637         char *endptr = NULL;
0638 
0639         list[i] = strtol(args[i], &endptr, 10);
0640         if (errno != 0 || endptr == args[i] || *endptr != '\0') {
0641             free(list);
0642             return NULL;
0643         }
0644     }
0645     *list_size = size;
0646     free_explode(args, size);
0647     return list;
0648 }
0649 
0650 static void print_allocator(FILE *out, int allocator)
0651 {
0652     fprintf(out, "allocated by ");
0653     if (allocator & ALLOCATOR_CMA)
0654         fprintf(out, "CMA ");
0655     if (allocator & ALLOCATOR_SLAB)
0656         fprintf(out, "SLAB ");
0657     if (allocator & ALLOCATOR_VMALLOC)
0658         fprintf(out, "VMALLOC ");
0659     if (allocator & ALLOCATOR_OTHERS)
0660         fprintf(out, "OTHERS ");
0661 }
0662 
0663 #define BUF_SIZE    (128 * 1024)
0664 
0665 static void usage(void)
0666 {
0667     printf("Usage: ./page_owner_sort [OPTIONS] <input> <output>\n"
0668         "-m\t\tSort by total memory.\n"
0669         "-s\t\tSort by the stack trace.\n"
0670         "-t\t\tSort by times (default).\n"
0671         "-p\t\tSort by pid.\n"
0672         "-P\t\tSort by tgid.\n"
0673         "-n\t\tSort by task command name.\n"
0674         "-a\t\tSort by memory allocate time.\n"
0675         "-r\t\tSort by memory release time.\n"
0676         "-f\t\tFilter out the information of blocks whose memory has been released.\n"
0677         "-d\t\tPrint debug information.\n"
0678         "--pid <pidlist>\tSelect by pid. This selects the information of blocks whose process ID numbers appear in <pidlist>.\n"
0679         "--tgid <tgidlist>\tSelect by tgid. This selects the information of blocks whose Thread Group ID numbers appear in <tgidlist>.\n"
0680         "--name <cmdlist>\n\t\tSelect by command name. This selects the information of blocks whose command name appears in <cmdlist>.\n"
0681         "--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"
0682         "--sort <order>\tSpecify sort order as: [+|-]key[,[+|-]key[,...]]\n"
0683     );
0684 }
0685 
0686 int main(int argc, char **argv)
0687 {
0688     FILE *fin, *fout;
0689     char *buf, *ext_buf;
0690     int i, count;
0691     struct stat st;
0692     int opt;
0693     struct option longopts[] = {
0694         { "pid", required_argument, NULL, 1 },
0695         { "tgid", required_argument, NULL, 2 },
0696         { "name", required_argument, NULL, 3 },
0697         { "cull",  required_argument, NULL, 4 },
0698         { "sort",  required_argument, NULL, 5 },
0699         { 0, 0, 0, 0},
0700     };
0701 
0702     while ((opt = getopt_long(argc, argv, "adfmnprstP", longopts, NULL)) != -1)
0703         switch (opt) {
0704         case 'a':
0705             set_single_cmp(compare_ts, SORT_ASC);
0706             break;
0707         case 'd':
0708             debug_on = true;
0709             break;
0710         case 'f':
0711             filter = filter | FILTER_UNRELEASE;
0712             break;
0713         case 'm':
0714             set_single_cmp(compare_page_num, SORT_DESC);
0715             break;
0716         case 'p':
0717             set_single_cmp(compare_pid, SORT_ASC);
0718             break;
0719         case 'r':
0720             set_single_cmp(compare_free_ts, SORT_ASC);
0721             break;
0722         case 's':
0723             set_single_cmp(compare_stacktrace, SORT_ASC);
0724             break;
0725         case 't':
0726             set_single_cmp(compare_num, SORT_DESC);
0727             break;
0728         case 'P':
0729             set_single_cmp(compare_tgid, SORT_ASC);
0730             break;
0731         case 'n':
0732             set_single_cmp(compare_comm, SORT_ASC);
0733             break;
0734         case 1:
0735             filter = filter | FILTER_PID;
0736             fc.pids = parse_nums_list(optarg, &fc.pids_size);
0737             if (fc.pids == NULL) {
0738                 fprintf(stderr, "wrong/invalid pid in from the command line:%s\n",
0739                         optarg);
0740                 exit(1);
0741             }
0742             break;
0743         case 2:
0744             filter = filter | FILTER_TGID;
0745             fc.tgids = parse_nums_list(optarg, &fc.tgids_size);
0746             if (fc.tgids == NULL) {
0747                 fprintf(stderr, "wrong/invalid tgid in from the command line:%s\n",
0748                         optarg);
0749                 exit(1);
0750             }
0751             break;
0752         case 3:
0753             filter = filter | FILTER_COMM;
0754             fc.comms = explode(',', optarg, &fc.comms_size);
0755             break;
0756         case 4:
0757             if (!parse_cull_args(optarg)) {
0758                 fprintf(stderr, "wrong argument after --cull option:%s\n",
0759                         optarg);
0760                 exit(1);
0761             }
0762             break;
0763         case 5:
0764             if (!parse_sort_args(optarg)) {
0765                 fprintf(stderr, "wrong argument after --sort option:%s\n",
0766                         optarg);
0767                 exit(1);
0768             }
0769             break;
0770         default:
0771             usage();
0772             exit(1);
0773         }
0774 
0775     if (optind >= (argc - 1)) {
0776         usage();
0777         exit(1);
0778     }
0779 
0780     fin = fopen(argv[optind], "r");
0781     fout = fopen(argv[optind + 1], "w");
0782     if (!fin || !fout) {
0783         usage();
0784         perror("open: ");
0785         exit(1);
0786     }
0787 
0788     check_regcomp(&order_pattern, "order\\s*([0-9]*),");
0789     check_regcomp(&pid_pattern, "pid\\s*([0-9]*),");
0790     check_regcomp(&tgid_pattern, "tgid\\s*([0-9]*) ");
0791     check_regcomp(&comm_pattern, "tgid\\s*[0-9]*\\s*\\((.*)\\),\\s*ts");
0792     check_regcomp(&ts_nsec_pattern, "ts\\s*([0-9]*)\\s*ns,");
0793     check_regcomp(&free_ts_nsec_pattern, "free_ts\\s*([0-9]*)\\s*ns");
0794     fstat(fileno(fin), &st);
0795     max_size = st.st_size / 100; /* hack ... */
0796 
0797     list = malloc(max_size * sizeof(*list));
0798     buf = malloc(BUF_SIZE);
0799     ext_buf = malloc(BUF_SIZE);
0800     if (!list || !buf || !ext_buf) {
0801         fprintf(stderr, "Out of memory\n");
0802         exit(1);
0803     }
0804 
0805     for ( ; ; ) {
0806         int buf_len = read_block(buf, ext_buf, BUF_SIZE, fin);
0807 
0808         if (buf_len < 0)
0809             break;
0810         add_list(buf, buf_len, ext_buf);
0811     }
0812 
0813     printf("loaded %d\n", list_size);
0814 
0815     printf("sorting ....\n");
0816 
0817     qsort(list, list_size, sizeof(list[0]), compare_cull_condition);
0818 
0819     printf("culling\n");
0820 
0821     for (i = count = 0; i < list_size; i++) {
0822         if (count == 0 ||
0823             compare_cull_condition((void *)(&list[count-1]), (void *)(&list[i])) != 0) {
0824             list[count++] = list[i];
0825         } else {
0826             list[count-1].num += list[i].num;
0827             list[count-1].page_num += list[i].page_num;
0828         }
0829     }
0830 
0831     qsort(list, count, sizeof(list[0]), compare_sort_condition);
0832 
0833     for (i = 0; i < count; i++) {
0834         if (cull == 0) {
0835             fprintf(fout, "%d times, %d pages, ", list[i].num, list[i].page_num);
0836             print_allocator(fout, list[i].allocator);
0837             fprintf(fout, ":\n%s\n", list[i].txt);
0838         }
0839         else {
0840             fprintf(fout, "%d times, %d pages",
0841                     list[i].num, list[i].page_num);
0842             if (cull & CULL_PID || filter & FILTER_PID)
0843                 fprintf(fout, ", PID %d", list[i].pid);
0844             if (cull & CULL_TGID || filter & FILTER_TGID)
0845                 fprintf(fout, ", TGID %d", list[i].pid);
0846             if (cull & CULL_COMM || filter & FILTER_COMM)
0847                 fprintf(fout, ", task_comm_name: %s", list[i].comm);
0848             if (cull & CULL_ALLOCATOR) {
0849                 fprintf(fout, ", ");
0850                 print_allocator(fout, list[i].allocator);
0851             }
0852             if (cull & CULL_UNRELEASE)
0853                 fprintf(fout, " (%s)",
0854                         list[i].free_ts_nsec ? "UNRELEASED" : "RELEASED");
0855             if (cull & CULL_STACKTRACE)
0856                 fprintf(fout, ":\n%s", list[i].stacktrace);
0857             fprintf(fout, "\n");
0858         }
0859     }
0860     regfree(&order_pattern);
0861     regfree(&pid_pattern);
0862     regfree(&tgid_pattern);
0863     regfree(&comm_pattern);
0864     regfree(&ts_nsec_pattern);
0865     regfree(&free_ts_nsec_pattern);
0866     return 0;
0867 }