cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

page_owner_sort.c (20958B)


      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}