1799fb82aSSeongJae Park // SPDX-License-Identifier: GPL-2.0
2799fb82aSSeongJae Park /*
3799fb82aSSeongJae Park * User-space helper to sort the output of /sys/kernel/debug/page_owner
4799fb82aSSeongJae Park *
5799fb82aSSeongJae Park * Example use:
6799fb82aSSeongJae Park * cat /sys/kernel/debug/page_owner > page_owner_full.txt
7799fb82aSSeongJae Park * ./page_owner_sort page_owner_full.txt sorted_page_owner.txt
8799fb82aSSeongJae Park * Or sort by total memory:
9799fb82aSSeongJae Park * ./page_owner_sort -m page_owner_full.txt sorted_page_owner.txt
10799fb82aSSeongJae Park *
11799fb82aSSeongJae Park * See Documentation/mm/page_owner.rst
12799fb82aSSeongJae Park */
13799fb82aSSeongJae Park
14799fb82aSSeongJae Park #include <stdio.h>
15799fb82aSSeongJae Park #include <stdlib.h>
16799fb82aSSeongJae Park #include <sys/types.h>
17799fb82aSSeongJae Park #include <sys/stat.h>
18799fb82aSSeongJae Park #include <fcntl.h>
19799fb82aSSeongJae Park #include <unistd.h>
20799fb82aSSeongJae Park #include <string.h>
21799fb82aSSeongJae Park #include <regex.h>
22799fb82aSSeongJae Park #include <errno.h>
23799fb82aSSeongJae Park #include <linux/types.h>
24799fb82aSSeongJae Park #include <getopt.h>
25799fb82aSSeongJae Park
26799fb82aSSeongJae Park #define bool int
27799fb82aSSeongJae Park #define true 1
28799fb82aSSeongJae Park #define false 0
29799fb82aSSeongJae Park #define TASK_COMM_LEN 16
30799fb82aSSeongJae Park
31799fb82aSSeongJae Park struct block_list {
32799fb82aSSeongJae Park char *txt;
33799fb82aSSeongJae Park char *comm; // task command name
34799fb82aSSeongJae Park char *stacktrace;
35799fb82aSSeongJae Park __u64 ts_nsec;
36799fb82aSSeongJae Park __u64 free_ts_nsec;
37799fb82aSSeongJae Park int len;
38799fb82aSSeongJae Park int num;
39799fb82aSSeongJae Park int page_num;
40799fb82aSSeongJae Park pid_t pid;
41799fb82aSSeongJae Park pid_t tgid;
42799fb82aSSeongJae Park int allocator;
43799fb82aSSeongJae Park };
44799fb82aSSeongJae Park enum FILTER_BIT {
45799fb82aSSeongJae Park FILTER_UNRELEASE = 1<<1,
46799fb82aSSeongJae Park FILTER_PID = 1<<2,
47799fb82aSSeongJae Park FILTER_TGID = 1<<3,
48799fb82aSSeongJae Park FILTER_COMM = 1<<4
49799fb82aSSeongJae Park };
50799fb82aSSeongJae Park enum CULL_BIT {
51799fb82aSSeongJae Park CULL_UNRELEASE = 1<<1,
52799fb82aSSeongJae Park CULL_PID = 1<<2,
53799fb82aSSeongJae Park CULL_TGID = 1<<3,
54799fb82aSSeongJae Park CULL_COMM = 1<<4,
55799fb82aSSeongJae Park CULL_STACKTRACE = 1<<5,
56799fb82aSSeongJae Park CULL_ALLOCATOR = 1<<6
57799fb82aSSeongJae Park };
58799fb82aSSeongJae Park enum ALLOCATOR_BIT {
59799fb82aSSeongJae Park ALLOCATOR_CMA = 1<<1,
60799fb82aSSeongJae Park ALLOCATOR_SLAB = 1<<2,
61799fb82aSSeongJae Park ALLOCATOR_VMALLOC = 1<<3,
62799fb82aSSeongJae Park ALLOCATOR_OTHERS = 1<<4
63799fb82aSSeongJae Park };
64799fb82aSSeongJae Park enum ARG_TYPE {
65799fb82aSSeongJae Park ARG_TXT, ARG_COMM, ARG_STACKTRACE, ARG_ALLOC_TS, ARG_FREE_TS,
66799fb82aSSeongJae Park ARG_CULL_TIME, ARG_PAGE_NUM, ARG_PID, ARG_TGID, ARG_UNKNOWN, ARG_FREE,
67799fb82aSSeongJae Park ARG_ALLOCATOR
68799fb82aSSeongJae Park };
69799fb82aSSeongJae Park enum SORT_ORDER {
70799fb82aSSeongJae Park SORT_ASC = 1,
71799fb82aSSeongJae Park SORT_DESC = -1,
72799fb82aSSeongJae Park };
73799fb82aSSeongJae Park struct filter_condition {
74799fb82aSSeongJae Park pid_t *pids;
75799fb82aSSeongJae Park pid_t *tgids;
76799fb82aSSeongJae Park char **comms;
77799fb82aSSeongJae Park int pids_size;
78799fb82aSSeongJae Park int tgids_size;
79799fb82aSSeongJae Park int comms_size;
80799fb82aSSeongJae Park };
81799fb82aSSeongJae Park struct sort_condition {
82799fb82aSSeongJae Park int (**cmps)(const void *, const void *);
83799fb82aSSeongJae Park int *signs;
84799fb82aSSeongJae Park int size;
85799fb82aSSeongJae Park };
86799fb82aSSeongJae Park static struct filter_condition fc;
87799fb82aSSeongJae Park static struct sort_condition sc;
88799fb82aSSeongJae Park static regex_t order_pattern;
89799fb82aSSeongJae Park static regex_t pid_pattern;
90799fb82aSSeongJae Park static regex_t tgid_pattern;
91799fb82aSSeongJae Park static regex_t comm_pattern;
92799fb82aSSeongJae Park static regex_t ts_nsec_pattern;
93799fb82aSSeongJae Park static regex_t free_ts_nsec_pattern;
94799fb82aSSeongJae Park static struct block_list *list;
95799fb82aSSeongJae Park static int list_size;
96799fb82aSSeongJae Park static int max_size;
97799fb82aSSeongJae Park static int cull;
98799fb82aSSeongJae Park static int filter;
99799fb82aSSeongJae Park static bool debug_on;
100799fb82aSSeongJae Park
101799fb82aSSeongJae Park static void set_single_cmp(int (*cmp)(const void *, const void *), int sign);
102799fb82aSSeongJae Park
read_block(char * buf,char * ext_buf,int buf_size,FILE * fin)103799fb82aSSeongJae Park int read_block(char *buf, char *ext_buf, int buf_size, FILE *fin)
104799fb82aSSeongJae Park {
105799fb82aSSeongJae Park char *curr = buf, *const buf_end = buf + buf_size;
106799fb82aSSeongJae Park
107799fb82aSSeongJae Park while (buf_end - curr > 1 && fgets(curr, buf_end - curr, fin)) {
108799fb82aSSeongJae Park if (*curr == '\n') { /* empty line */
109799fb82aSSeongJae Park return curr - buf;
110799fb82aSSeongJae Park }
111799fb82aSSeongJae Park if (!strncmp(curr, "PFN", 3)) {
112799fb82aSSeongJae Park strcpy(ext_buf, curr);
113799fb82aSSeongJae Park continue;
114799fb82aSSeongJae Park }
115799fb82aSSeongJae Park curr += strlen(curr);
116799fb82aSSeongJae Park }
117799fb82aSSeongJae Park
118799fb82aSSeongJae Park return -1; /* EOF or no space left in buf. */
119799fb82aSSeongJae Park }
120799fb82aSSeongJae Park
compare_txt(const void * p1,const void * p2)121799fb82aSSeongJae Park static int compare_txt(const void *p1, const void *p2)
122799fb82aSSeongJae Park {
123799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
124799fb82aSSeongJae Park
125799fb82aSSeongJae Park return strcmp(l1->txt, l2->txt);
126799fb82aSSeongJae Park }
127799fb82aSSeongJae Park
compare_stacktrace(const void * p1,const void * p2)128799fb82aSSeongJae Park static int compare_stacktrace(const void *p1, const void *p2)
129799fb82aSSeongJae Park {
130799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
131799fb82aSSeongJae Park
132799fb82aSSeongJae Park return strcmp(l1->stacktrace, l2->stacktrace);
133799fb82aSSeongJae Park }
134799fb82aSSeongJae Park
compare_num(const void * p1,const void * p2)135799fb82aSSeongJae Park static int compare_num(const void *p1, const void *p2)
136799fb82aSSeongJae Park {
137799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
138799fb82aSSeongJae Park
139799fb82aSSeongJae Park return l1->num - l2->num;
140799fb82aSSeongJae Park }
141799fb82aSSeongJae Park
compare_page_num(const void * p1,const void * p2)142799fb82aSSeongJae Park static int compare_page_num(const void *p1, const void *p2)
143799fb82aSSeongJae Park {
144799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
145799fb82aSSeongJae Park
146799fb82aSSeongJae Park return l1->page_num - l2->page_num;
147799fb82aSSeongJae Park }
148799fb82aSSeongJae Park
compare_pid(const void * p1,const void * p2)149799fb82aSSeongJae Park static int compare_pid(const void *p1, const void *p2)
150799fb82aSSeongJae Park {
151799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
152799fb82aSSeongJae Park
153799fb82aSSeongJae Park return l1->pid - l2->pid;
154799fb82aSSeongJae Park }
155799fb82aSSeongJae Park
compare_tgid(const void * p1,const void * p2)156799fb82aSSeongJae Park static int compare_tgid(const void *p1, const void *p2)
157799fb82aSSeongJae Park {
158799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
159799fb82aSSeongJae Park
160799fb82aSSeongJae Park return l1->tgid - l2->tgid;
161799fb82aSSeongJae Park }
162799fb82aSSeongJae Park
compare_allocator(const void * p1,const void * p2)163799fb82aSSeongJae Park static int compare_allocator(const void *p1, const void *p2)
164799fb82aSSeongJae Park {
165799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
166799fb82aSSeongJae Park
167799fb82aSSeongJae Park return l1->allocator - l2->allocator;
168799fb82aSSeongJae Park }
169799fb82aSSeongJae Park
compare_comm(const void * p1,const void * p2)170799fb82aSSeongJae Park static int compare_comm(const void *p1, const void *p2)
171799fb82aSSeongJae Park {
172799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
173799fb82aSSeongJae Park
174799fb82aSSeongJae Park return strcmp(l1->comm, l2->comm);
175799fb82aSSeongJae Park }
176799fb82aSSeongJae Park
compare_ts(const void * p1,const void * p2)177799fb82aSSeongJae Park static int compare_ts(const void *p1, const void *p2)
178799fb82aSSeongJae Park {
179799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
180799fb82aSSeongJae Park
181799fb82aSSeongJae Park return l1->ts_nsec < l2->ts_nsec ? -1 : 1;
182799fb82aSSeongJae Park }
183799fb82aSSeongJae Park
compare_free_ts(const void * p1,const void * p2)184799fb82aSSeongJae Park static int compare_free_ts(const void *p1, const void *p2)
185799fb82aSSeongJae Park {
186799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
187799fb82aSSeongJae Park
188799fb82aSSeongJae Park return l1->free_ts_nsec < l2->free_ts_nsec ? -1 : 1;
189799fb82aSSeongJae Park }
190799fb82aSSeongJae Park
compare_release(const void * p1,const void * p2)191799fb82aSSeongJae Park static int compare_release(const void *p1, const void *p2)
192799fb82aSSeongJae Park {
193799fb82aSSeongJae Park const struct block_list *l1 = p1, *l2 = p2;
194799fb82aSSeongJae Park
195799fb82aSSeongJae Park if (!l1->free_ts_nsec && !l2->free_ts_nsec)
196799fb82aSSeongJae Park return 0;
197799fb82aSSeongJae Park if (l1->free_ts_nsec && l2->free_ts_nsec)
198799fb82aSSeongJae Park return 0;
199799fb82aSSeongJae Park return l1->free_ts_nsec ? 1 : -1;
200799fb82aSSeongJae Park }
201799fb82aSSeongJae Park
compare_cull_condition(const void * p1,const void * p2)202799fb82aSSeongJae Park static int compare_cull_condition(const void *p1, const void *p2)
203799fb82aSSeongJae Park {
204799fb82aSSeongJae Park if (cull == 0)
205799fb82aSSeongJae Park return compare_txt(p1, p2);
206799fb82aSSeongJae Park if ((cull & CULL_STACKTRACE) && compare_stacktrace(p1, p2))
207799fb82aSSeongJae Park return compare_stacktrace(p1, p2);
208799fb82aSSeongJae Park if ((cull & CULL_PID) && compare_pid(p1, p2))
209799fb82aSSeongJae Park return compare_pid(p1, p2);
210799fb82aSSeongJae Park if ((cull & CULL_TGID) && compare_tgid(p1, p2))
211799fb82aSSeongJae Park return compare_tgid(p1, p2);
212799fb82aSSeongJae Park if ((cull & CULL_COMM) && compare_comm(p1, p2))
213799fb82aSSeongJae Park return compare_comm(p1, p2);
214799fb82aSSeongJae Park if ((cull & CULL_UNRELEASE) && compare_release(p1, p2))
215799fb82aSSeongJae Park return compare_release(p1, p2);
216799fb82aSSeongJae Park if ((cull & CULL_ALLOCATOR) && compare_allocator(p1, p2))
217799fb82aSSeongJae Park return compare_allocator(p1, p2);
218799fb82aSSeongJae Park return 0;
219799fb82aSSeongJae Park }
220799fb82aSSeongJae Park
compare_sort_condition(const void * p1,const void * p2)221799fb82aSSeongJae Park static int compare_sort_condition(const void *p1, const void *p2)
222799fb82aSSeongJae Park {
223799fb82aSSeongJae Park int cmp = 0;
224799fb82aSSeongJae Park
225799fb82aSSeongJae Park for (int i = 0; i < sc.size; ++i)
226799fb82aSSeongJae Park if (cmp == 0)
227799fb82aSSeongJae Park cmp = sc.signs[i] * sc.cmps[i](p1, p2);
228799fb82aSSeongJae Park return cmp;
229799fb82aSSeongJae Park }
230799fb82aSSeongJae Park
search_pattern(regex_t * pattern,char * pattern_str,char * buf)231799fb82aSSeongJae Park static int search_pattern(regex_t *pattern, char *pattern_str, char *buf)
232799fb82aSSeongJae Park {
233799fb82aSSeongJae Park int err, val_len;
234799fb82aSSeongJae Park regmatch_t pmatch[2];
235799fb82aSSeongJae Park
236799fb82aSSeongJae Park err = regexec(pattern, buf, 2, pmatch, REG_NOTBOL);
237799fb82aSSeongJae Park if (err != 0 || pmatch[1].rm_so == -1) {
238799fb82aSSeongJae Park if (debug_on)
239799fb82aSSeongJae Park fprintf(stderr, "no matching pattern in %s\n", buf);
240799fb82aSSeongJae Park return -1;
241799fb82aSSeongJae Park }
242799fb82aSSeongJae Park val_len = pmatch[1].rm_eo - pmatch[1].rm_so;
243799fb82aSSeongJae Park
244799fb82aSSeongJae Park memcpy(pattern_str, buf + pmatch[1].rm_so, val_len);
245799fb82aSSeongJae Park
246799fb82aSSeongJae Park return 0;
247799fb82aSSeongJae Park }
248799fb82aSSeongJae Park
check_regcomp(regex_t * pattern,const char * regex)249799fb82aSSeongJae Park static bool check_regcomp(regex_t *pattern, const char *regex)
250799fb82aSSeongJae Park {
251799fb82aSSeongJae Park int err;
252799fb82aSSeongJae Park
253799fb82aSSeongJae Park err = regcomp(pattern, regex, REG_EXTENDED | REG_NEWLINE);
254799fb82aSSeongJae Park if (err != 0 || pattern->re_nsub != 1) {
255799fb82aSSeongJae Park fprintf(stderr, "Invalid pattern %s code %d\n", regex, err);
256799fb82aSSeongJae Park return false;
257799fb82aSSeongJae Park }
258799fb82aSSeongJae Park return true;
259799fb82aSSeongJae Park }
260799fb82aSSeongJae Park
explode(char sep,const char * str,int * size)261799fb82aSSeongJae Park static char **explode(char sep, const char *str, int *size)
262799fb82aSSeongJae Park {
263799fb82aSSeongJae Park int count = 0, len = strlen(str);
264799fb82aSSeongJae Park int lastindex = -1, j = 0;
265799fb82aSSeongJae Park
266799fb82aSSeongJae Park for (int i = 0; i < len; i++)
267799fb82aSSeongJae Park if (str[i] == sep)
268799fb82aSSeongJae Park count++;
269799fb82aSSeongJae Park char **ret = calloc(++count, sizeof(char *));
270799fb82aSSeongJae Park
271799fb82aSSeongJae Park for (int i = 0; i < len; i++) {
272799fb82aSSeongJae Park if (str[i] == sep) {
273799fb82aSSeongJae Park ret[j] = calloc(i - lastindex, sizeof(char));
274799fb82aSSeongJae Park memcpy(ret[j++], str + lastindex + 1, i - lastindex - 1);
275799fb82aSSeongJae Park lastindex = i;
276799fb82aSSeongJae Park }
277799fb82aSSeongJae Park }
278799fb82aSSeongJae Park if (lastindex <= len - 1) {
279799fb82aSSeongJae Park ret[j] = calloc(len - lastindex, sizeof(char));
280799fb82aSSeongJae Park memcpy(ret[j++], str + lastindex + 1, strlen(str) - 1 - lastindex);
281799fb82aSSeongJae Park }
282799fb82aSSeongJae Park *size = j;
283799fb82aSSeongJae Park return ret;
284799fb82aSSeongJae Park }
285799fb82aSSeongJae Park
free_explode(char ** arr,int size)286799fb82aSSeongJae Park static void free_explode(char **arr, int size)
287799fb82aSSeongJae Park {
288799fb82aSSeongJae Park for (int i = 0; i < size; i++)
289799fb82aSSeongJae Park free(arr[i]);
290799fb82aSSeongJae Park free(arr);
291799fb82aSSeongJae Park }
292799fb82aSSeongJae Park
293799fb82aSSeongJae Park # define FIELD_BUFF 25
294799fb82aSSeongJae Park
get_page_num(char * buf)295799fb82aSSeongJae Park static int get_page_num(char *buf)
296799fb82aSSeongJae Park {
297799fb82aSSeongJae Park int order_val;
298799fb82aSSeongJae Park char order_str[FIELD_BUFF] = {0};
299799fb82aSSeongJae Park char *endptr;
300799fb82aSSeongJae Park
301799fb82aSSeongJae Park search_pattern(&order_pattern, order_str, buf);
302799fb82aSSeongJae Park errno = 0;
303799fb82aSSeongJae Park order_val = strtol(order_str, &endptr, 10);
304799fb82aSSeongJae Park if (order_val > 64 || errno != 0 || endptr == order_str || *endptr != '\0') {
305799fb82aSSeongJae Park if (debug_on)
306799fb82aSSeongJae Park fprintf(stderr, "wrong order in follow buf:\n%s\n", buf);
307799fb82aSSeongJae Park return 0;
308799fb82aSSeongJae Park }
309799fb82aSSeongJae Park
310799fb82aSSeongJae Park return 1 << order_val;
311799fb82aSSeongJae Park }
312799fb82aSSeongJae Park
get_pid(char * buf)313799fb82aSSeongJae Park static pid_t get_pid(char *buf)
314799fb82aSSeongJae Park {
315799fb82aSSeongJae Park pid_t pid;
316799fb82aSSeongJae Park char pid_str[FIELD_BUFF] = {0};
317799fb82aSSeongJae Park char *endptr;
318799fb82aSSeongJae Park
319799fb82aSSeongJae Park search_pattern(&pid_pattern, pid_str, buf);
320799fb82aSSeongJae Park errno = 0;
321799fb82aSSeongJae Park pid = strtol(pid_str, &endptr, 10);
322799fb82aSSeongJae Park if (errno != 0 || endptr == pid_str || *endptr != '\0') {
323799fb82aSSeongJae Park if (debug_on)
324799fb82aSSeongJae Park fprintf(stderr, "wrong/invalid pid in follow buf:\n%s\n", buf);
325799fb82aSSeongJae Park return -1;
326799fb82aSSeongJae Park }
327799fb82aSSeongJae Park
328799fb82aSSeongJae Park return pid;
329799fb82aSSeongJae Park
330799fb82aSSeongJae Park }
331799fb82aSSeongJae Park
get_tgid(char * buf)332799fb82aSSeongJae Park static pid_t get_tgid(char *buf)
333799fb82aSSeongJae Park {
334799fb82aSSeongJae Park pid_t tgid;
335799fb82aSSeongJae Park char tgid_str[FIELD_BUFF] = {0};
336799fb82aSSeongJae Park char *endptr;
337799fb82aSSeongJae Park
338799fb82aSSeongJae Park search_pattern(&tgid_pattern, tgid_str, buf);
339799fb82aSSeongJae Park errno = 0;
340799fb82aSSeongJae Park tgid = strtol(tgid_str, &endptr, 10);
341799fb82aSSeongJae Park if (errno != 0 || endptr == tgid_str || *endptr != '\0') {
342799fb82aSSeongJae Park if (debug_on)
343799fb82aSSeongJae Park fprintf(stderr, "wrong/invalid tgid in follow buf:\n%s\n", buf);
344799fb82aSSeongJae Park return -1;
345799fb82aSSeongJae Park }
346799fb82aSSeongJae Park
347799fb82aSSeongJae Park return tgid;
348799fb82aSSeongJae Park
349799fb82aSSeongJae Park }
350799fb82aSSeongJae Park
get_ts_nsec(char * buf)351799fb82aSSeongJae Park static __u64 get_ts_nsec(char *buf)
352799fb82aSSeongJae Park {
353799fb82aSSeongJae Park __u64 ts_nsec;
354799fb82aSSeongJae Park char ts_nsec_str[FIELD_BUFF] = {0};
355799fb82aSSeongJae Park char *endptr;
356799fb82aSSeongJae Park
357799fb82aSSeongJae Park search_pattern(&ts_nsec_pattern, ts_nsec_str, buf);
358799fb82aSSeongJae Park errno = 0;
359799fb82aSSeongJae Park ts_nsec = strtoull(ts_nsec_str, &endptr, 10);
360799fb82aSSeongJae Park if (errno != 0 || endptr == ts_nsec_str || *endptr != '\0') {
361799fb82aSSeongJae Park if (debug_on)
362799fb82aSSeongJae Park fprintf(stderr, "wrong ts_nsec in follow buf:\n%s\n", buf);
363799fb82aSSeongJae Park return -1;
364799fb82aSSeongJae Park }
365799fb82aSSeongJae Park
366799fb82aSSeongJae Park return ts_nsec;
367799fb82aSSeongJae Park }
368799fb82aSSeongJae Park
get_free_ts_nsec(char * buf)369799fb82aSSeongJae Park static __u64 get_free_ts_nsec(char *buf)
370799fb82aSSeongJae Park {
371799fb82aSSeongJae Park __u64 free_ts_nsec;
372799fb82aSSeongJae Park char free_ts_nsec_str[FIELD_BUFF] = {0};
373799fb82aSSeongJae Park char *endptr;
374799fb82aSSeongJae Park
375799fb82aSSeongJae Park search_pattern(&free_ts_nsec_pattern, free_ts_nsec_str, buf);
376799fb82aSSeongJae Park errno = 0;
377799fb82aSSeongJae Park free_ts_nsec = strtoull(free_ts_nsec_str, &endptr, 10);
378799fb82aSSeongJae Park if (errno != 0 || endptr == free_ts_nsec_str || *endptr != '\0') {
379799fb82aSSeongJae Park if (debug_on)
380799fb82aSSeongJae Park fprintf(stderr, "wrong free_ts_nsec in follow buf:\n%s\n", buf);
381799fb82aSSeongJae Park return -1;
382799fb82aSSeongJae Park }
383799fb82aSSeongJae Park
384799fb82aSSeongJae Park return free_ts_nsec;
385799fb82aSSeongJae Park }
386799fb82aSSeongJae Park
get_comm(char * buf)387799fb82aSSeongJae Park static char *get_comm(char *buf)
388799fb82aSSeongJae Park {
389799fb82aSSeongJae Park char *comm_str = malloc(TASK_COMM_LEN);
390799fb82aSSeongJae Park
391799fb82aSSeongJae Park memset(comm_str, 0, TASK_COMM_LEN);
392799fb82aSSeongJae Park
393799fb82aSSeongJae Park search_pattern(&comm_pattern, comm_str, buf);
394799fb82aSSeongJae Park errno = 0;
395799fb82aSSeongJae Park if (errno != 0) {
396799fb82aSSeongJae Park if (debug_on)
397799fb82aSSeongJae Park fprintf(stderr, "wrong comm in follow buf:\n%s\n", buf);
398799fb82aSSeongJae Park return NULL;
399799fb82aSSeongJae Park }
400799fb82aSSeongJae Park
401799fb82aSSeongJae Park return comm_str;
402799fb82aSSeongJae Park }
403799fb82aSSeongJae Park
get_arg_type(const char * arg)404799fb82aSSeongJae Park static int get_arg_type(const char *arg)
405799fb82aSSeongJae Park {
406799fb82aSSeongJae Park if (!strcmp(arg, "pid") || !strcmp(arg, "p"))
407799fb82aSSeongJae Park return ARG_PID;
408799fb82aSSeongJae Park else if (!strcmp(arg, "tgid") || !strcmp(arg, "tg"))
409799fb82aSSeongJae Park return ARG_TGID;
410799fb82aSSeongJae Park else if (!strcmp(arg, "name") || !strcmp(arg, "n"))
411799fb82aSSeongJae Park return ARG_COMM;
412799fb82aSSeongJae Park else if (!strcmp(arg, "stacktrace") || !strcmp(arg, "st"))
413799fb82aSSeongJae Park return ARG_STACKTRACE;
414799fb82aSSeongJae Park else if (!strcmp(arg, "free") || !strcmp(arg, "f"))
415799fb82aSSeongJae Park return ARG_FREE;
416799fb82aSSeongJae Park else if (!strcmp(arg, "txt") || !strcmp(arg, "T"))
417799fb82aSSeongJae Park return ARG_TXT;
418799fb82aSSeongJae Park else if (!strcmp(arg, "free_ts") || !strcmp(arg, "ft"))
419799fb82aSSeongJae Park return ARG_FREE_TS;
420799fb82aSSeongJae Park else if (!strcmp(arg, "alloc_ts") || !strcmp(arg, "at"))
421799fb82aSSeongJae Park return ARG_ALLOC_TS;
422799fb82aSSeongJae Park else if (!strcmp(arg, "allocator") || !strcmp(arg, "ator"))
423799fb82aSSeongJae Park return ARG_ALLOCATOR;
424799fb82aSSeongJae Park else {
425799fb82aSSeongJae Park return ARG_UNKNOWN;
426799fb82aSSeongJae Park }
427799fb82aSSeongJae Park }
428799fb82aSSeongJae Park
get_allocator(const char * buf,const char * migrate_info)429799fb82aSSeongJae Park static int get_allocator(const char *buf, const char *migrate_info)
430799fb82aSSeongJae Park {
431799fb82aSSeongJae Park char *tmp, *first_line, *second_line;
432799fb82aSSeongJae Park int allocator = 0;
433799fb82aSSeongJae Park
434799fb82aSSeongJae Park if (strstr(migrate_info, "CMA"))
435799fb82aSSeongJae Park allocator |= ALLOCATOR_CMA;
436799fb82aSSeongJae Park if (strstr(migrate_info, "slab"))
437799fb82aSSeongJae Park allocator |= ALLOCATOR_SLAB;
438799fb82aSSeongJae Park tmp = strstr(buf, "__vmalloc_node_range");
439799fb82aSSeongJae Park if (tmp) {
440799fb82aSSeongJae Park second_line = tmp;
441799fb82aSSeongJae Park while (*tmp != '\n')
442799fb82aSSeongJae Park tmp--;
443799fb82aSSeongJae Park tmp--;
444799fb82aSSeongJae Park while (*tmp != '\n')
445799fb82aSSeongJae Park tmp--;
446799fb82aSSeongJae Park first_line = ++tmp;
447799fb82aSSeongJae Park tmp = strstr(tmp, "alloc_pages");
448799fb82aSSeongJae Park if (tmp && first_line <= tmp && tmp < second_line)
449799fb82aSSeongJae Park allocator |= ALLOCATOR_VMALLOC;
450799fb82aSSeongJae Park }
451799fb82aSSeongJae Park if (allocator == 0)
452799fb82aSSeongJae Park allocator = ALLOCATOR_OTHERS;
453799fb82aSSeongJae Park return allocator;
454799fb82aSSeongJae Park }
455799fb82aSSeongJae Park
match_num_list(int num,int * list,int list_size)456799fb82aSSeongJae Park static bool match_num_list(int num, int *list, int list_size)
457799fb82aSSeongJae Park {
458799fb82aSSeongJae Park for (int i = 0; i < list_size; ++i)
459799fb82aSSeongJae Park if (list[i] == num)
460799fb82aSSeongJae Park return true;
461799fb82aSSeongJae Park return false;
462799fb82aSSeongJae Park }
463799fb82aSSeongJae Park
match_str_list(const char * str,char ** list,int list_size)464799fb82aSSeongJae Park static bool match_str_list(const char *str, char **list, int list_size)
465799fb82aSSeongJae Park {
466799fb82aSSeongJae Park for (int i = 0; i < list_size; ++i)
467799fb82aSSeongJae Park if (!strcmp(list[i], str))
468799fb82aSSeongJae Park return true;
469799fb82aSSeongJae Park return false;
470799fb82aSSeongJae Park }
471799fb82aSSeongJae Park
is_need(char * buf)472799fb82aSSeongJae Park static bool is_need(char *buf)
473799fb82aSSeongJae Park {
474799fb82aSSeongJae Park __u64 ts_nsec, free_ts_nsec;
475799fb82aSSeongJae Park
476799fb82aSSeongJae Park ts_nsec = get_ts_nsec(buf);
477799fb82aSSeongJae Park free_ts_nsec = get_free_ts_nsec(buf);
478799fb82aSSeongJae Park
479799fb82aSSeongJae Park if ((filter & FILTER_UNRELEASE) && free_ts_nsec != 0 && ts_nsec < free_ts_nsec)
480799fb82aSSeongJae Park return false;
481799fb82aSSeongJae Park if ((filter & FILTER_PID) && !match_num_list(get_pid(buf), fc.pids, fc.pids_size))
482799fb82aSSeongJae Park return false;
483799fb82aSSeongJae Park if ((filter & FILTER_TGID) &&
484799fb82aSSeongJae Park !match_num_list(get_tgid(buf), fc.tgids, fc.tgids_size))
485799fb82aSSeongJae Park return false;
486799fb82aSSeongJae Park
487799fb82aSSeongJae Park char *comm = get_comm(buf);
488799fb82aSSeongJae Park
489799fb82aSSeongJae Park if ((filter & FILTER_COMM) &&
490799fb82aSSeongJae Park !match_str_list(comm, fc.comms, fc.comms_size)) {
491799fb82aSSeongJae Park free(comm);
492799fb82aSSeongJae Park return false;
493799fb82aSSeongJae Park }
494799fb82aSSeongJae Park free(comm);
495799fb82aSSeongJae Park return true;
496799fb82aSSeongJae Park }
497799fb82aSSeongJae Park
add_list(char * buf,int len,char * ext_buf)498799fb82aSSeongJae Park static bool add_list(char *buf, int len, char *ext_buf)
499799fb82aSSeongJae Park {
500799fb82aSSeongJae Park if (list_size != 0 &&
501799fb82aSSeongJae Park len == list[list_size-1].len &&
502799fb82aSSeongJae Park memcmp(buf, list[list_size-1].txt, len) == 0) {
503799fb82aSSeongJae Park list[list_size-1].num++;
504799fb82aSSeongJae Park list[list_size-1].page_num += get_page_num(buf);
505799fb82aSSeongJae Park return true;
506799fb82aSSeongJae Park }
507799fb82aSSeongJae Park if (list_size == max_size) {
508799fb82aSSeongJae Park fprintf(stderr, "max_size too small??\n");
509799fb82aSSeongJae Park return false;
510799fb82aSSeongJae Park }
511799fb82aSSeongJae Park if (!is_need(buf))
512799fb82aSSeongJae Park return true;
513799fb82aSSeongJae Park list[list_size].pid = get_pid(buf);
514799fb82aSSeongJae Park list[list_size].tgid = get_tgid(buf);
515799fb82aSSeongJae Park list[list_size].comm = get_comm(buf);
516799fb82aSSeongJae Park list[list_size].txt = malloc(len+1);
517799fb82aSSeongJae Park if (!list[list_size].txt) {
518799fb82aSSeongJae Park fprintf(stderr, "Out of memory\n");
519799fb82aSSeongJae Park return false;
520799fb82aSSeongJae Park }
521799fb82aSSeongJae Park memcpy(list[list_size].txt, buf, len);
522799fb82aSSeongJae Park list[list_size].txt[len] = 0;
523799fb82aSSeongJae Park list[list_size].len = len;
524799fb82aSSeongJae Park list[list_size].num = 1;
525799fb82aSSeongJae Park list[list_size].page_num = get_page_num(buf);
526799fb82aSSeongJae Park
527799fb82aSSeongJae Park list[list_size].stacktrace = strchr(list[list_size].txt, '\n') ?: "";
528799fb82aSSeongJae Park if (*list[list_size].stacktrace == '\n')
529799fb82aSSeongJae Park list[list_size].stacktrace++;
530799fb82aSSeongJae Park list[list_size].ts_nsec = get_ts_nsec(buf);
531799fb82aSSeongJae Park list[list_size].free_ts_nsec = get_free_ts_nsec(buf);
532799fb82aSSeongJae Park list[list_size].allocator = get_allocator(buf, ext_buf);
533799fb82aSSeongJae Park list_size++;
534799fb82aSSeongJae Park if (list_size % 1000 == 0) {
535799fb82aSSeongJae Park printf("loaded %d\r", list_size);
536799fb82aSSeongJae Park fflush(stdout);
537799fb82aSSeongJae Park }
538799fb82aSSeongJae Park return true;
539799fb82aSSeongJae Park }
540799fb82aSSeongJae Park
parse_cull_args(const char * arg_str)541799fb82aSSeongJae Park static bool parse_cull_args(const char *arg_str)
542799fb82aSSeongJae Park {
543799fb82aSSeongJae Park int size = 0;
544799fb82aSSeongJae Park char **args = explode(',', arg_str, &size);
545799fb82aSSeongJae Park
546799fb82aSSeongJae Park for (int i = 0; i < size; ++i) {
547799fb82aSSeongJae Park int arg_type = get_arg_type(args[i]);
548799fb82aSSeongJae Park
549799fb82aSSeongJae Park if (arg_type == ARG_PID)
550799fb82aSSeongJae Park cull |= CULL_PID;
551799fb82aSSeongJae Park else if (arg_type == ARG_TGID)
552799fb82aSSeongJae Park cull |= CULL_TGID;
553799fb82aSSeongJae Park else if (arg_type == ARG_COMM)
554799fb82aSSeongJae Park cull |= CULL_COMM;
555799fb82aSSeongJae Park else if (arg_type == ARG_STACKTRACE)
556799fb82aSSeongJae Park cull |= CULL_STACKTRACE;
557799fb82aSSeongJae Park else if (arg_type == ARG_FREE)
558799fb82aSSeongJae Park cull |= CULL_UNRELEASE;
559799fb82aSSeongJae Park else if (arg_type == ARG_ALLOCATOR)
560799fb82aSSeongJae Park cull |= CULL_ALLOCATOR;
561799fb82aSSeongJae Park else {
562799fb82aSSeongJae Park free_explode(args, size);
563799fb82aSSeongJae Park return false;
564799fb82aSSeongJae Park }
565799fb82aSSeongJae Park }
566799fb82aSSeongJae Park free_explode(args, size);
567799fb82aSSeongJae Park if (sc.size == 0)
568799fb82aSSeongJae Park set_single_cmp(compare_num, SORT_DESC);
569799fb82aSSeongJae Park return true;
570799fb82aSSeongJae Park }
571799fb82aSSeongJae Park
set_single_cmp(int (* cmp)(const void *,const void *),int sign)572799fb82aSSeongJae Park static void set_single_cmp(int (*cmp)(const void *, const void *), int sign)
573799fb82aSSeongJae Park {
574799fb82aSSeongJae Park if (sc.signs == NULL || sc.size < 1)
575799fb82aSSeongJae Park sc.signs = calloc(1, sizeof(int));
576799fb82aSSeongJae Park sc.signs[0] = sign;
577799fb82aSSeongJae Park if (sc.cmps == NULL || sc.size < 1)
578799fb82aSSeongJae Park sc.cmps = calloc(1, sizeof(int *));
579799fb82aSSeongJae Park sc.cmps[0] = cmp;
580799fb82aSSeongJae Park sc.size = 1;
581799fb82aSSeongJae Park }
582799fb82aSSeongJae Park
parse_sort_args(const char * arg_str)583799fb82aSSeongJae Park static bool parse_sort_args(const char *arg_str)
584799fb82aSSeongJae Park {
585799fb82aSSeongJae Park int size = 0;
586799fb82aSSeongJae Park
587799fb82aSSeongJae Park if (sc.size != 0) { /* reset sort_condition */
588799fb82aSSeongJae Park free(sc.signs);
589799fb82aSSeongJae Park free(sc.cmps);
590799fb82aSSeongJae Park size = 0;
591799fb82aSSeongJae Park }
592799fb82aSSeongJae Park
593799fb82aSSeongJae Park char **args = explode(',', arg_str, &size);
594799fb82aSSeongJae Park
595799fb82aSSeongJae Park sc.signs = calloc(size, sizeof(int));
596799fb82aSSeongJae Park sc.cmps = calloc(size, sizeof(int *));
597799fb82aSSeongJae Park for (int i = 0; i < size; ++i) {
598799fb82aSSeongJae Park int offset = 0;
599799fb82aSSeongJae Park
600799fb82aSSeongJae Park sc.signs[i] = SORT_ASC;
601799fb82aSSeongJae Park if (args[i][0] == '-' || args[i][0] == '+') {
602799fb82aSSeongJae Park if (args[i][0] == '-')
603799fb82aSSeongJae Park sc.signs[i] = SORT_DESC;
604799fb82aSSeongJae Park offset = 1;
605799fb82aSSeongJae Park }
606799fb82aSSeongJae Park
607799fb82aSSeongJae Park int arg_type = get_arg_type(args[i]+offset);
608799fb82aSSeongJae Park
609799fb82aSSeongJae Park if (arg_type == ARG_PID)
610799fb82aSSeongJae Park sc.cmps[i] = compare_pid;
611799fb82aSSeongJae Park else if (arg_type == ARG_TGID)
612799fb82aSSeongJae Park sc.cmps[i] = compare_tgid;
613799fb82aSSeongJae Park else if (arg_type == ARG_COMM)
614799fb82aSSeongJae Park sc.cmps[i] = compare_comm;
615799fb82aSSeongJae Park else if (arg_type == ARG_STACKTRACE)
616799fb82aSSeongJae Park sc.cmps[i] = compare_stacktrace;
617799fb82aSSeongJae Park else if (arg_type == ARG_ALLOC_TS)
618799fb82aSSeongJae Park sc.cmps[i] = compare_ts;
619799fb82aSSeongJae Park else if (arg_type == ARG_FREE_TS)
620799fb82aSSeongJae Park sc.cmps[i] = compare_free_ts;
621799fb82aSSeongJae Park else if (arg_type == ARG_TXT)
622799fb82aSSeongJae Park sc.cmps[i] = compare_txt;
623799fb82aSSeongJae Park else if (arg_type == ARG_ALLOCATOR)
624799fb82aSSeongJae Park sc.cmps[i] = compare_allocator;
625799fb82aSSeongJae Park else {
626799fb82aSSeongJae Park free_explode(args, size);
627799fb82aSSeongJae Park sc.size = 0;
628799fb82aSSeongJae Park return false;
629799fb82aSSeongJae Park }
630799fb82aSSeongJae Park }
631799fb82aSSeongJae Park sc.size = size;
632799fb82aSSeongJae Park free_explode(args, size);
633799fb82aSSeongJae Park return true;
634799fb82aSSeongJae Park }
635799fb82aSSeongJae Park
parse_nums_list(char * arg_str,int * list_size)636799fb82aSSeongJae Park static int *parse_nums_list(char *arg_str, int *list_size)
637799fb82aSSeongJae Park {
638799fb82aSSeongJae Park int size = 0;
639799fb82aSSeongJae Park char **args = explode(',', arg_str, &size);
640799fb82aSSeongJae Park int *list = calloc(size, sizeof(int));
641799fb82aSSeongJae Park
642799fb82aSSeongJae Park errno = 0;
643799fb82aSSeongJae Park for (int i = 0; i < size; ++i) {
644799fb82aSSeongJae Park char *endptr = NULL;
645799fb82aSSeongJae Park
646799fb82aSSeongJae Park list[i] = strtol(args[i], &endptr, 10);
647799fb82aSSeongJae Park if (errno != 0 || endptr == args[i] || *endptr != '\0') {
648799fb82aSSeongJae Park free(list);
649799fb82aSSeongJae Park return NULL;
650799fb82aSSeongJae Park }
651799fb82aSSeongJae Park }
652799fb82aSSeongJae Park *list_size = size;
653799fb82aSSeongJae Park free_explode(args, size);
654799fb82aSSeongJae Park return list;
655799fb82aSSeongJae Park }
656799fb82aSSeongJae Park
print_allocator(FILE * out,int allocator)657799fb82aSSeongJae Park static void print_allocator(FILE *out, int allocator)
658799fb82aSSeongJae Park {
659799fb82aSSeongJae Park fprintf(out, "allocated by ");
660799fb82aSSeongJae Park if (allocator & ALLOCATOR_CMA)
661799fb82aSSeongJae Park fprintf(out, "CMA ");
662799fb82aSSeongJae Park if (allocator & ALLOCATOR_SLAB)
663799fb82aSSeongJae Park fprintf(out, "SLAB ");
664799fb82aSSeongJae Park if (allocator & ALLOCATOR_VMALLOC)
665799fb82aSSeongJae Park fprintf(out, "VMALLOC ");
666799fb82aSSeongJae Park if (allocator & ALLOCATOR_OTHERS)
667799fb82aSSeongJae Park fprintf(out, "OTHERS ");
668799fb82aSSeongJae Park }
669799fb82aSSeongJae Park
670799fb82aSSeongJae Park #define BUF_SIZE (128 * 1024)
671799fb82aSSeongJae Park
usage(void)672799fb82aSSeongJae Park static void usage(void)
673799fb82aSSeongJae Park {
674799fb82aSSeongJae Park printf("Usage: ./page_owner_sort [OPTIONS] <input> <output>\n"
675799fb82aSSeongJae Park "-m\t\tSort by total memory.\n"
676799fb82aSSeongJae Park "-s\t\tSort by the stack trace.\n"
677799fb82aSSeongJae Park "-t\t\tSort by times (default).\n"
678799fb82aSSeongJae Park "-p\t\tSort by pid.\n"
679799fb82aSSeongJae Park "-P\t\tSort by tgid.\n"
680799fb82aSSeongJae Park "-n\t\tSort by task command name.\n"
681799fb82aSSeongJae Park "-a\t\tSort by memory allocate time.\n"
682799fb82aSSeongJae Park "-r\t\tSort by memory release time.\n"
683799fb82aSSeongJae Park "-f\t\tFilter out the information of blocks whose memory has been released.\n"
684799fb82aSSeongJae Park "-d\t\tPrint debug information.\n"
685799fb82aSSeongJae Park "--pid <pidlist>\tSelect by pid. This selects the information of blocks whose process ID numbers appear in <pidlist>.\n"
686799fb82aSSeongJae Park "--tgid <tgidlist>\tSelect by tgid. This selects the information of blocks whose Thread Group ID numbers appear in <tgidlist>.\n"
687799fb82aSSeongJae Park "--name <cmdlist>\n\t\tSelect by command name. This selects the information of blocks whose command name appears in <cmdlist>.\n"
688799fb82aSSeongJae Park "--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"
689799fb82aSSeongJae Park "--sort <order>\tSpecify sort order as: [+|-]key[,[+|-]key[,...]]\n"
690799fb82aSSeongJae Park );
691799fb82aSSeongJae Park }
692799fb82aSSeongJae Park
main(int argc,char ** argv)693799fb82aSSeongJae Park int main(int argc, char **argv)
694799fb82aSSeongJae Park {
695799fb82aSSeongJae Park FILE *fin, *fout;
696799fb82aSSeongJae Park char *buf, *ext_buf;
697799fb82aSSeongJae Park int i, count;
698799fb82aSSeongJae Park struct stat st;
699799fb82aSSeongJae Park int opt;
700799fb82aSSeongJae Park struct option longopts[] = {
701799fb82aSSeongJae Park { "pid", required_argument, NULL, 1 },
702799fb82aSSeongJae Park { "tgid", required_argument, NULL, 2 },
703799fb82aSSeongJae Park { "name", required_argument, NULL, 3 },
704799fb82aSSeongJae Park { "cull", required_argument, NULL, 4 },
705799fb82aSSeongJae Park { "sort", required_argument, NULL, 5 },
706799fb82aSSeongJae Park { 0, 0, 0, 0},
707799fb82aSSeongJae Park };
708799fb82aSSeongJae Park
709799fb82aSSeongJae Park while ((opt = getopt_long(argc, argv, "adfmnprstP", longopts, NULL)) != -1)
710799fb82aSSeongJae Park switch (opt) {
711799fb82aSSeongJae Park case 'a':
712799fb82aSSeongJae Park set_single_cmp(compare_ts, SORT_ASC);
713799fb82aSSeongJae Park break;
714799fb82aSSeongJae Park case 'd':
715799fb82aSSeongJae Park debug_on = true;
716799fb82aSSeongJae Park break;
717799fb82aSSeongJae Park case 'f':
718799fb82aSSeongJae Park filter = filter | FILTER_UNRELEASE;
719799fb82aSSeongJae Park break;
720799fb82aSSeongJae Park case 'm':
721799fb82aSSeongJae Park set_single_cmp(compare_page_num, SORT_DESC);
722799fb82aSSeongJae Park break;
723799fb82aSSeongJae Park case 'p':
724799fb82aSSeongJae Park set_single_cmp(compare_pid, SORT_ASC);
725799fb82aSSeongJae Park break;
726799fb82aSSeongJae Park case 'r':
727799fb82aSSeongJae Park set_single_cmp(compare_free_ts, SORT_ASC);
728799fb82aSSeongJae Park break;
729799fb82aSSeongJae Park case 's':
730799fb82aSSeongJae Park set_single_cmp(compare_stacktrace, SORT_ASC);
731799fb82aSSeongJae Park break;
732799fb82aSSeongJae Park case 't':
733799fb82aSSeongJae Park set_single_cmp(compare_num, SORT_DESC);
734799fb82aSSeongJae Park break;
735799fb82aSSeongJae Park case 'P':
736799fb82aSSeongJae Park set_single_cmp(compare_tgid, SORT_ASC);
737799fb82aSSeongJae Park break;
738799fb82aSSeongJae Park case 'n':
739799fb82aSSeongJae Park set_single_cmp(compare_comm, SORT_ASC);
740799fb82aSSeongJae Park break;
741799fb82aSSeongJae Park case 1:
742799fb82aSSeongJae Park filter = filter | FILTER_PID;
743799fb82aSSeongJae Park fc.pids = parse_nums_list(optarg, &fc.pids_size);
744799fb82aSSeongJae Park if (fc.pids == NULL) {
745799fb82aSSeongJae Park fprintf(stderr, "wrong/invalid pid in from the command line:%s\n",
746799fb82aSSeongJae Park optarg);
747799fb82aSSeongJae Park exit(1);
748799fb82aSSeongJae Park }
749799fb82aSSeongJae Park break;
750799fb82aSSeongJae Park case 2:
751799fb82aSSeongJae Park filter = filter | FILTER_TGID;
752799fb82aSSeongJae Park fc.tgids = parse_nums_list(optarg, &fc.tgids_size);
753799fb82aSSeongJae Park if (fc.tgids == NULL) {
754799fb82aSSeongJae Park fprintf(stderr, "wrong/invalid tgid in from the command line:%s\n",
755799fb82aSSeongJae Park optarg);
756799fb82aSSeongJae Park exit(1);
757799fb82aSSeongJae Park }
758799fb82aSSeongJae Park break;
759799fb82aSSeongJae Park case 3:
760799fb82aSSeongJae Park filter = filter | FILTER_COMM;
761799fb82aSSeongJae Park fc.comms = explode(',', optarg, &fc.comms_size);
762799fb82aSSeongJae Park break;
763799fb82aSSeongJae Park case 4:
764799fb82aSSeongJae Park if (!parse_cull_args(optarg)) {
765799fb82aSSeongJae Park fprintf(stderr, "wrong argument after --cull option:%s\n",
766799fb82aSSeongJae Park optarg);
767799fb82aSSeongJae Park exit(1);
768799fb82aSSeongJae Park }
769799fb82aSSeongJae Park break;
770799fb82aSSeongJae Park case 5:
771799fb82aSSeongJae Park if (!parse_sort_args(optarg)) {
772799fb82aSSeongJae Park fprintf(stderr, "wrong argument after --sort option:%s\n",
773799fb82aSSeongJae Park optarg);
774799fb82aSSeongJae Park exit(1);
775799fb82aSSeongJae Park }
776799fb82aSSeongJae Park break;
777799fb82aSSeongJae Park default:
778799fb82aSSeongJae Park usage();
779799fb82aSSeongJae Park exit(1);
780799fb82aSSeongJae Park }
781799fb82aSSeongJae Park
782799fb82aSSeongJae Park if (optind >= (argc - 1)) {
783799fb82aSSeongJae Park usage();
784799fb82aSSeongJae Park exit(1);
785799fb82aSSeongJae Park }
786799fb82aSSeongJae Park
787799fb82aSSeongJae Park fin = fopen(argv[optind], "r");
788799fb82aSSeongJae Park fout = fopen(argv[optind + 1], "w");
789799fb82aSSeongJae Park if (!fin || !fout) {
790799fb82aSSeongJae Park usage();
791799fb82aSSeongJae Park perror("open: ");
792799fb82aSSeongJae Park exit(1);
793799fb82aSSeongJae Park }
794799fb82aSSeongJae Park
795799fb82aSSeongJae Park if (!check_regcomp(&order_pattern, "order\\s*([0-9]*),"))
796799fb82aSSeongJae Park goto out_order;
797799fb82aSSeongJae Park if (!check_regcomp(&pid_pattern, "pid\\s*([0-9]*),"))
798799fb82aSSeongJae Park goto out_pid;
799799fb82aSSeongJae Park if (!check_regcomp(&tgid_pattern, "tgid\\s*([0-9]*) "))
800799fb82aSSeongJae Park goto out_tgid;
801799fb82aSSeongJae Park if (!check_regcomp(&comm_pattern, "tgid\\s*[0-9]*\\s*\\((.*)\\),\\s*ts"))
802799fb82aSSeongJae Park goto out_comm;
803799fb82aSSeongJae Park if (!check_regcomp(&ts_nsec_pattern, "ts\\s*([0-9]*)\\s*ns,"))
804799fb82aSSeongJae Park goto out_ts;
805799fb82aSSeongJae Park if (!check_regcomp(&free_ts_nsec_pattern, "free_ts\\s*([0-9]*)\\s*ns"))
806799fb82aSSeongJae Park goto out_free_ts;
807799fb82aSSeongJae Park
808799fb82aSSeongJae Park fstat(fileno(fin), &st);
809799fb82aSSeongJae Park max_size = st.st_size / 100; /* hack ... */
810799fb82aSSeongJae Park
811799fb82aSSeongJae Park list = malloc(max_size * sizeof(*list));
812799fb82aSSeongJae Park buf = malloc(BUF_SIZE);
813799fb82aSSeongJae Park ext_buf = malloc(BUF_SIZE);
814799fb82aSSeongJae Park if (!list || !buf || !ext_buf) {
815799fb82aSSeongJae Park fprintf(stderr, "Out of memory\n");
816799fb82aSSeongJae Park goto out_free;
817799fb82aSSeongJae Park }
818799fb82aSSeongJae Park
819799fb82aSSeongJae Park for ( ; ; ) {
820799fb82aSSeongJae Park int buf_len = read_block(buf, ext_buf, BUF_SIZE, fin);
821799fb82aSSeongJae Park
822799fb82aSSeongJae Park if (buf_len < 0)
823799fb82aSSeongJae Park break;
824799fb82aSSeongJae Park if (!add_list(buf, buf_len, ext_buf))
825799fb82aSSeongJae Park goto out_free;
826799fb82aSSeongJae Park }
827799fb82aSSeongJae Park
828799fb82aSSeongJae Park printf("loaded %d\n", list_size);
829799fb82aSSeongJae Park
830799fb82aSSeongJae Park printf("sorting ....\n");
831799fb82aSSeongJae Park
832799fb82aSSeongJae Park qsort(list, list_size, sizeof(list[0]), compare_cull_condition);
833799fb82aSSeongJae Park
834799fb82aSSeongJae Park printf("culling\n");
835799fb82aSSeongJae Park
836799fb82aSSeongJae Park for (i = count = 0; i < list_size; i++) {
837799fb82aSSeongJae Park if (count == 0 ||
838799fb82aSSeongJae Park compare_cull_condition((void *)(&list[count-1]), (void *)(&list[i])) != 0) {
839799fb82aSSeongJae Park list[count++] = list[i];
840799fb82aSSeongJae Park } else {
841799fb82aSSeongJae Park list[count-1].num += list[i].num;
842799fb82aSSeongJae Park list[count-1].page_num += list[i].page_num;
843799fb82aSSeongJae Park }
844799fb82aSSeongJae Park }
845799fb82aSSeongJae Park
846799fb82aSSeongJae Park qsort(list, count, sizeof(list[0]), compare_sort_condition);
847799fb82aSSeongJae Park
848799fb82aSSeongJae Park for (i = 0; i < count; i++) {
849799fb82aSSeongJae Park if (cull == 0) {
850799fb82aSSeongJae Park fprintf(fout, "%d times, %d pages, ", list[i].num, list[i].page_num);
851799fb82aSSeongJae Park print_allocator(fout, list[i].allocator);
852799fb82aSSeongJae Park fprintf(fout, ":\n%s\n", list[i].txt);
853799fb82aSSeongJae Park }
854799fb82aSSeongJae Park else {
855799fb82aSSeongJae Park fprintf(fout, "%d times, %d pages",
856799fb82aSSeongJae Park list[i].num, list[i].page_num);
857799fb82aSSeongJae Park if (cull & CULL_PID || filter & FILTER_PID)
858799fb82aSSeongJae Park fprintf(fout, ", PID %d", list[i].pid);
859799fb82aSSeongJae Park if (cull & CULL_TGID || filter & FILTER_TGID)
860*92357568SSteve Chou fprintf(fout, ", TGID %d", list[i].tgid);
861799fb82aSSeongJae Park if (cull & CULL_COMM || filter & FILTER_COMM)
862799fb82aSSeongJae Park fprintf(fout, ", task_comm_name: %s", list[i].comm);
863799fb82aSSeongJae Park if (cull & CULL_ALLOCATOR) {
864799fb82aSSeongJae Park fprintf(fout, ", ");
865799fb82aSSeongJae Park print_allocator(fout, list[i].allocator);
866799fb82aSSeongJae Park }
867799fb82aSSeongJae Park if (cull & CULL_UNRELEASE)
868799fb82aSSeongJae Park fprintf(fout, " (%s)",
869799fb82aSSeongJae Park list[i].free_ts_nsec ? "UNRELEASED" : "RELEASED");
870799fb82aSSeongJae Park if (cull & CULL_STACKTRACE)
871799fb82aSSeongJae Park fprintf(fout, ":\n%s", list[i].stacktrace);
872799fb82aSSeongJae Park fprintf(fout, "\n");
873799fb82aSSeongJae Park }
874799fb82aSSeongJae Park }
875799fb82aSSeongJae Park
876799fb82aSSeongJae Park out_free:
877799fb82aSSeongJae Park if (ext_buf)
878799fb82aSSeongJae Park free(ext_buf);
879799fb82aSSeongJae Park if (buf)
880799fb82aSSeongJae Park free(buf);
881799fb82aSSeongJae Park if (list)
882799fb82aSSeongJae Park free(list);
883799fb82aSSeongJae Park out_free_ts:
884799fb82aSSeongJae Park regfree(&free_ts_nsec_pattern);
885799fb82aSSeongJae Park out_ts:
886799fb82aSSeongJae Park regfree(&ts_nsec_pattern);
887799fb82aSSeongJae Park out_comm:
888799fb82aSSeongJae Park regfree(&comm_pattern);
889799fb82aSSeongJae Park out_tgid:
890799fb82aSSeongJae Park regfree(&tgid_pattern);
891799fb82aSSeongJae Park out_pid:
892799fb82aSSeongJae Park regfree(&pid_pattern);
893799fb82aSSeongJae Park out_order:
894799fb82aSSeongJae Park regfree(&order_pattern);
895799fb82aSSeongJae Park
896799fb82aSSeongJae Park return 0;
897799fb82aSSeongJae Park }
898