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