The contents of page_owner have changed to include more information than
the stack trace. On a modern kernel, the blocks look like
Page allocated via order 0, mask 0x0(), pid 1, ts 165564237 ns, free_ts 0 ns
register_early_stack+0x4b/0x90
init_page_owner+0x39/0x250
kernel_init_freeable+0x11e/0x242
kernel_init+0x16/0x130
Sorting by the contents of .txt will result in almost no repeated pages, as
the pid, ts, and free_ts will almost never be the same. Instead, sort by
the contents of the stack trace, which we assume to be whatever is after
the first line.
Signed-off-by: Sean Anderson <[email protected]>
---
(no changes since v1)
tools/vm/page_owner_sort.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/tools/vm/page_owner_sort.c b/tools/vm/page_owner_sort.c
index 9ebb84a9c731..9ad3772a294d 100644
--- a/tools/vm/page_owner_sort.c
+++ b/tools/vm/page_owner_sort.c
@@ -23,6 +23,7 @@
struct block_list {
char *txt;
+ char *stacktrace;
int len;
int num;
int page_num;
@@ -51,11 +52,11 @@ int read_block(char *buf, int buf_size, FILE *fin)
return -1; /* EOF or no space left in buf. */
}
-static int compare_txt(const void *p1, const void *p2)
+static int compare_stacktrace(const void *p1, const void *p2)
{
const struct block_list *l1 = p1, *l2 = p2;
- return strcmp(l1->txt, l2->txt);
+ return strcmp(l1->stacktrace ?: "", l2->stacktrace ?: "");
}
static int compare_num(const void *p1, const void *p2)
@@ -121,6 +122,7 @@ static void add_list(char *buf, int len)
list[list_size].page_num = get_page_num(buf);
memcpy(list[list_size].txt, buf, len);
list[list_size].txt[len] = 0;
+ list[list_size].stacktrace = strchr(list[list_size].txt, '\n');
list_size++;
if (list_size % 1000 == 0) {
printf("loaded %d\r", list_size);
@@ -199,7 +201,7 @@ int main(int argc, char **argv)
printf("sorting ....\n");
- qsort(list, list_size, sizeof(list[0]), compare_txt);
+ qsort(list, list_size, sizeof(list[0]), compare_stacktrace);
list2 = malloc(sizeof(*list) * list_size);
if (!list2) {
@@ -211,7 +213,7 @@ int main(int argc, char **argv)
for (i = count = 0; i < list_size; i++) {
if (count == 0 ||
- strcmp(list2[count-1].txt, list[i].txt) != 0) {
+ strcmp(list2[count-1].stacktrace, list[i].stacktrace) != 0) {
list2[count++] = list[i];
} else {
list2[count-1].num += list[i].num;
--
2.33.0
This adds the ability to sort by stacktraces. This is helpful when
comparing multiple dumps of page_owner taken at different times, since
blocks will not be reordered if they were allocated/free'd.
Signed-off-by: Sean Anderson <[email protected]>
---
Changes in v2:
- Fix truncated commit message (oops)
tools/vm/page_owner_sort.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/tools/vm/page_owner_sort.c b/tools/vm/page_owner_sort.c
index 9ad3772a294d..b91d3381300c 100644
--- a/tools/vm/page_owner_sort.c
+++ b/tools/vm/page_owner_sort.c
@@ -29,7 +29,6 @@ struct block_list {
int page_num;
};
-static int sort_by_memory;
static regex_t order_pattern;
static struct block_list *list;
static int list_size;
@@ -134,13 +133,16 @@ static void add_list(char *buf, int len)
static void usage(void)
{
- printf("Usage: ./page_owner_sort [-m] <input> <output>\n"
- "-m Sort by total memory. If this option is unset, sort by times\n"
+ printf("Usage: ./page_owner_sort [OPTIONS] <input> <output>\n"
+ "-m Sort by total memory.\n"
+ "-s Sort by the stack trace.\n"
+ "-t Sort by times (default).\n"
);
}
int main(int argc, char **argv)
{
+ int (*cmp)(const void *, const void *) = compare_num;
FILE *fin, *fout;
char *buf;
int ret, i, count;
@@ -149,10 +151,16 @@ int main(int argc, char **argv)
int err;
int opt;
- while ((opt = getopt(argc, argv, "m")) != -1)
+ while ((opt = getopt(argc, argv, "mst")) != -1)
switch (opt) {
case 'm':
- sort_by_memory = 1;
+ cmp = compare_page_num;
+ break;
+ case 's':
+ cmp = compare_stacktrace;
+ break;
+ case 't':
+ cmp = compare_num;
break;
default:
usage();
@@ -221,10 +229,7 @@ int main(int argc, char **argv)
}
}
- if (sort_by_memory)
- qsort(list2, count, sizeof(list[0]), compare_page_num);
- else
- qsort(list2, count, sizeof(list[0]), compare_num);
+ qsort(list2, count, sizeof(list[0]), cmp);
for (i = 0; i < count; i++)
fprintf(fout, "%d times, %d pages:\n%s\n",
--
2.33.0
On 2021/11/25 3:37, Sean Anderson <[email protected]> wrote:
> static int compare_num(const void *p1, const void *p2) @@ -121,6
>+122,7 @@ static void add_list(char *buf, int len)
>list[list_size].page_num = get_page_num(buf);
>memcpy(list[list_size].txt, buf, len); list[list_size].txt[len] = 0;
>+ list[list_size].stacktrace = strchr(list[list_size].txt, '\n');
When read_block gets an empty line, buf is "\n", then the stacktrace is NULL
> list_size++;
> if (list_size % 1000 == 0) {
> printf("loaded %d\r", list_size);
>@@ -199,7 +201,7 @@ int main(int argc, char **argv)
>
> printf("sorting ....\n");
>
>- qsort(list, list_size, sizeof(list[0]), compare_txt);
>+ qsort(list, list_size, sizeof(list[0]), compare_stacktrace);
>
> list2 = malloc(sizeof(*list) * list_size); if (!list2) { @@ -211,7
>+213,7 @@ int main(int argc, char **argv)
>
> for (i = count = 0; i < list_size; i++) { if (count == 0 ||
>- strcmp(list2[count-1].txt, list[i].txt) != 0) {
>+ strcmp(list2[count-1].stacktrace, list[i].stacktrace) != 0) {
And when stacktrace is NULL, a segmentation fault will be triggered here.
> list2[count++] = list[i];
> } else {
> list2[count-1].num += list[i].num;
1. Maybe you can check whether the ret of read_block is 0 before add_list,
or whether the len of buf is 0 in add_list
2. Is it necessary to check stacktrace==NULL before use ?
Wei.
On 11/25/21 4:46 AM, weizhenliang wrote:
> On 2021/11/25 3:37, Sean Anderson <[email protected]> wrote:
>
>> static int compare_num(const void *p1, const void *p2) @@ -121,6
>> +122,7 @@ static void add_list(char *buf, int len)
>> list[list_size].page_num = get_page_num(buf);
>> memcpy(list[list_size].txt, buf, len); list[list_size].txt[len] = 0;
>> + list[list_size].stacktrace = strchr(list[list_size].txt, '\n');
>
> When read_block gets an empty line, buf is "\n", then the stacktrace is NULL
>
>> list_size++;
>> if (list_size % 1000 == 0) {
>> printf("loaded %d\r", list_size);
>> @@ -199,7 +201,7 @@ int main(int argc, char **argv)
>>
>> printf("sorting ....\n");
>>
>> - qsort(list, list_size, sizeof(list[0]), compare_txt);
>> + qsort(list, list_size, sizeof(list[0]), compare_stacktrace);
>>
>> list2 = malloc(sizeof(*list) * list_size); if (!list2) { @@ -211,7
>> +213,7 @@ int main(int argc, char **argv)
>>
>> for (i = count = 0; i < list_size; i++) { if (count == 0 ||
>> - strcmp(list2[count-1].txt, list[i].txt) != 0) {
>> + strcmp(list2[count-1].stacktrace, list[i].stacktrace) != 0) {
>
> And when stacktrace is NULL, a segmentation fault will be triggered here.
Ah, whoops. Looks like I check for this in compare_stacktrace but not here.
>> list2[count++] = list[i];
>> } else {
>> list2[count-1].num += list[i].num;
>
> 1. Maybe you can check whether the ret of read_block is 0 before add_list,
> or whether the len of buf is 0 in add_list
I think this is the best route.
Since this seems to have already been applied I've sent a follow-up patch.
--Sean