0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
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;
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') {
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;
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) {
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;
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 }