Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools/vm/page_owner_sort.c: count and sort by mem

When viewing page owner information, we may be more concerned about the
total memory rather than the times of stack appears. Therefore, the
following adjustments are made:

1. Added the statistics on the total number of pages.

2. Added the optional parameter "-m" to configure the program to sort by
memory (total pages).

The general output of page_owner is as follows:

Page allocated via order XXX, ...
PFN XXX ...
// Detailed stack

Page allocated via order XXX, ...
PFN XXX ...
// Detailed stack

The original page_owner_sort ignores PFN rows, puts the remaining rows
in buf, counts the times of buf, and finally sorts them according to the
times. General output:

XXX times:
Page allocated via order XXX, ...
// Detailed stack

Now, we use regexp to extract the page order value from the buf, and
count the total pages for the buf. General output:

XXX times, XXX pages:
Page allocated via order XXX, ...
// Detailed stack

By default, it is still sorted by the times of buf; If you want to sort
by the pages nums of buf, use the new -m parameter.

Link: https://lkml.kernel.org/r/1631678242-41033-1-git-send-email-weizhenliang@huawei.com
Signed-off-by: Zhenliang Wei <weizhenliang@huawei.com>
Cc: Tang Bin <tangbin@cmss.chinamobile.com>
Cc: Zhang Shengju <zhangshengju@cmss.chinamobile.com>
Cc: Zhenliang Wei <weizhenliang@huawei.com>
Cc: Xiaoming Ni <nixiaoming@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Zhenliang Wei and committed by
Linus Torvalds
f7df2b1c 7e6ec49c

+108 -11
+22 -1
Documentation/vm/page_owner.rst
··· 85 85 cat /sys/kernel/debug/page_owner > page_owner_full.txt 86 86 ./page_owner_sort page_owner_full.txt sorted_page_owner.txt 87 87 88 + The general output of ``page_owner_full.txt`` is as follows: 89 + 90 + Page allocated via order XXX, ... 91 + PFN XXX ... 92 + // Detailed stack 93 + 94 + Page allocated via order XXX, ... 95 + PFN XXX ... 96 + // Detailed stack 97 + 98 + The ``page_owner_sort`` tool ignores ``PFN`` rows, puts the remaining rows 99 + in buf, uses regexp to extract the page order value, counts the times 100 + and pages of buf, and finally sorts them according to the times. 101 + 88 102 See the result about who allocated each page 89 - in the ``sorted_page_owner.txt``. 103 + in the ``sorted_page_owner.txt``. General output: 104 + 105 + XXX times, XXX pages: 106 + Page allocated via order XXX, ... 107 + // Detailed stack 108 + 109 + By default, ``page_owner_sort`` is sorted according to the times of buf. 110 + If you want to sort by the pages nums of buf, use the ``-m`` parameter.
+86 -10
tools/vm/page_owner_sort.c
··· 5 5 * Example use: 6 6 * cat /sys/kernel/debug/page_owner > page_owner_full.txt 7 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 8 10 * 9 11 * See Documentation/vm/page_owner.rst 10 12 */ ··· 18 16 #include <fcntl.h> 19 17 #include <unistd.h> 20 18 #include <string.h> 19 + #include <regex.h> 20 + #include <errno.h> 21 21 22 22 struct block_list { 23 23 char *txt; 24 24 int len; 25 25 int num; 26 + int page_num; 26 27 }; 27 28 28 - 29 + static int sort_by_memory; 30 + static regex_t order_pattern; 29 31 static struct block_list *list; 30 32 static int list_size; 31 33 static int max_size; ··· 65 59 return l2->num - l1->num; 66 60 } 67 61 62 + static int compare_page_num(const void *p1, const void *p2) 63 + { 64 + const struct block_list *l1 = p1, *l2 = p2; 65 + 66 + return l2->page_num - l1->page_num; 67 + } 68 + 69 + static int get_page_num(char *buf) 70 + { 71 + int err, val_len, order_val; 72 + char order_str[4] = {0}; 73 + char *endptr; 74 + regmatch_t pmatch[2]; 75 + 76 + err = regexec(&order_pattern, buf, 2, pmatch, REG_NOTBOL); 77 + if (err != 0 || pmatch[1].rm_so == -1) { 78 + printf("no order pattern in %s\n", buf); 79 + return 0; 80 + } 81 + val_len = pmatch[1].rm_eo - pmatch[1].rm_so; 82 + if (val_len > 2) /* max_order should not exceed 2 digits */ 83 + goto wrong_order; 84 + 85 + memcpy(order_str, buf + pmatch[1].rm_so, val_len); 86 + 87 + errno = 0; 88 + order_val = strtol(order_str, &endptr, 10); 89 + if (errno != 0 || endptr == order_str || *endptr != '\0') 90 + goto wrong_order; 91 + 92 + return 1 << order_val; 93 + 94 + wrong_order: 95 + printf("wrong order in follow buf:\n%s\n", buf); 96 + return 0; 97 + } 98 + 68 99 static void add_list(char *buf, int len) 69 100 { 70 101 if (list_size != 0 && 71 102 len == list[list_size-1].len && 72 103 memcmp(buf, list[list_size-1].txt, len) == 0) { 73 104 list[list_size-1].num++; 105 + list[list_size-1].page_num += get_page_num(buf); 74 106 return; 75 107 } 76 108 if (list_size == max_size) { ··· 118 74 list[list_size].txt = malloc(len+1); 119 75 list[list_size].len = len; 120 76 list[list_size].num = 1; 77 + list[list_size].page_num = get_page_num(buf); 121 78 memcpy(list[list_size].txt, buf, len); 122 79 list[list_size].txt[len] = 0; 123 80 list_size++; ··· 130 85 131 86 #define BUF_SIZE (128 * 1024) 132 87 88 + static void usage(void) 89 + { 90 + printf("Usage: ./page_owner_sort [-m] <input> <output>\n" 91 + "-m Sort by total memory. If this option is unset, sort by times\n" 92 + ); 93 + } 94 + 133 95 int main(int argc, char **argv) 134 96 { 135 97 FILE *fin, *fout; ··· 144 92 int ret, i, count; 145 93 struct block_list *list2; 146 94 struct stat st; 95 + int err; 96 + int opt; 147 97 148 - if (argc < 3) { 149 - printf("Usage: ./program <input> <output>\n"); 98 + while ((opt = getopt(argc, argv, "m")) != -1) 99 + switch (opt) { 100 + case 'm': 101 + sort_by_memory = 1; 102 + break; 103 + default: 104 + usage(); 105 + exit(1); 106 + } 107 + 108 + if (optind >= (argc - 1)) { 109 + usage(); 110 + exit(1); 111 + } 112 + 113 + fin = fopen(argv[optind], "r"); 114 + fout = fopen(argv[optind + 1], "w"); 115 + if (!fin || !fout) { 116 + usage(); 150 117 perror("open: "); 151 118 exit(1); 152 119 } 153 120 154 - fin = fopen(argv[1], "r"); 155 - fout = fopen(argv[2], "w"); 156 - if (!fin || !fout) { 157 - printf("Usage: ./program <input> <output>\n"); 158 - perror("open: "); 121 + err = regcomp(&order_pattern, "order\\s*([0-9]*),", REG_EXTENDED|REG_NEWLINE); 122 + if (err != 0 || order_pattern.re_nsub != 1) { 123 + printf("%s: Invalid pattern 'order\\s*([0-9]*),' code %d\n", 124 + argv[0], err); 159 125 exit(1); 160 126 } 161 127 ··· 215 145 list2[count++] = list[i]; 216 146 } else { 217 147 list2[count-1].num += list[i].num; 148 + list2[count-1].page_num += list[i].page_num; 218 149 } 219 150 } 220 151 221 - qsort(list2, count, sizeof(list[0]), compare_num); 152 + if (sort_by_memory) 153 + qsort(list2, count, sizeof(list[0]), compare_page_num); 154 + else 155 + qsort(list2, count, sizeof(list[0]), compare_num); 222 156 223 157 for (i = 0; i < count; i++) 224 - fprintf(fout, "%d times:\n%s\n", list2[i].num, list2[i].txt); 158 + fprintf(fout, "%d times, %d pages:\n%s\n", 159 + list2[i].num, list2[i].page_num, list2[i].txt); 225 160 161 + regfree(&order_pattern); 226 162 return 0; 227 163 }