tools/vm/page_owner_sort.c: support sorting pid and time

When viewing the page owner information, we expect that the information
can be sorted by PID, so that we can quickly combine PID with the program
to check the information together.

We also expect that the information can be sorted by time.  Time sorting
helps to view the running status of the program according to the time
interval when the program hangs up.

Finally, we hope to pass the page_ owner_ Sort.  C can reduce part of the
output and only output the plate information whose memory has not been
released, which can make us locate the problem of the program faster.
Therefore, the following adjustments have been made:

1. Add the static functions search_pattern and check_regcomp to
   improve the cleanliness.

2. Add member attributes and their corresponding sorting methods.  In
   terms of comparison time, int will overflow because the data of ull is
   too large, so the ternary operator is used

3. Add the -f parameter to filter out the information of blocks whose
   memory has not been released

Link: https://lkml.kernel.org/r/20211206165653.5093-1-zhaochongxi2019@email.szu.edu.cn
Signed-off-by: Chongxi Zhao <zhaochongxi2019@email.szu.edu.cn>
Reviewed-by: Sean Anderson <seanga2@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Chongxi Zhao 2022-03-24 18:08:47 -07:00 committed by Linus Torvalds
parent cd75ea0e32
commit 8f9c447e2e
1 changed files with 148 additions and 29 deletions

View File

@ -20,6 +20,7 @@
#include <string.h>
#include <regex.h>
#include <errno.h>
#include <linux/types.h>
struct block_list {
char *txt;
@ -27,9 +28,15 @@ struct block_list {
int len;
int num;
int page_num;
pid_t pid;
__u64 ts_nsec;
__u64 free_ts_nsec;
};
static regex_t order_pattern;
static regex_t pid_pattern;
static regex_t ts_nsec_pattern;
static regex_t free_ts_nsec_pattern;
static struct block_list *list;
static int list_size;
static int max_size;
@ -79,36 +86,126 @@ static int compare_page_num(const void *p1, const void *p2)
return l2->page_num - l1->page_num;
}
static int get_page_num(char *buf)
static int compare_pid(const void *p1, const void *p2)
{
int err, val_len, order_val;
char order_str[4] = {0};
char *endptr;
const struct block_list *l1 = p1, *l2 = p2;
return l1->pid - l2->pid;
}
static int compare_ts(const void *p1, const void *p2)
{
const struct block_list *l1 = p1, *l2 = p2;
return l1->ts_nsec < l2->ts_nsec ? -1 : 1;
}
static int compare_free_ts(const void *p1, const void *p2)
{
const struct block_list *l1 = p1, *l2 = p2;
return l1->free_ts_nsec < l2->free_ts_nsec ? -1 : 1;
}
static int search_pattern(regex_t *pattern, char *pattern_str, char *buf)
{
int err, val_len;
regmatch_t pmatch[2];
err = regexec(&order_pattern, buf, 2, pmatch, REG_NOTBOL);
err = regexec(pattern, buf, 2, pmatch, REG_NOTBOL);
if (err != 0 || pmatch[1].rm_so == -1) {
printf("no order pattern in %s\n", buf);
return 0;
printf("no matching pattern in %s\n", buf);
return -1;
}
val_len = pmatch[1].rm_eo - pmatch[1].rm_so;
if (val_len > 2) /* max_order should not exceed 2 digits */
goto wrong_order;
memcpy(order_str, buf + pmatch[1].rm_so, val_len);
memcpy(pattern_str, buf + pmatch[1].rm_so, val_len);
return 0;
}
static void check_regcomp(regex_t *pattern, const char *regex)
{
int err;
err = regcomp(pattern, regex, REG_EXTENDED | REG_NEWLINE);
if (err != 0 || pattern->re_nsub != 1) {
printf("Invalid pattern %s code %d\n", regex, err);
exit(1);
}
}
# define FIELD_BUFF 25
static int get_page_num(char *buf)
{
int order_val;
char order_str[FIELD_BUFF] = {0};
char *endptr;
search_pattern(&order_pattern, order_str, buf);
errno = 0;
order_val = strtol(order_str, &endptr, 10);
if (errno != 0 || endptr == order_str || *endptr != '\0')
goto wrong_order;
return 1 << order_val;
wrong_order:
if (order_val > 64 || errno != 0 || endptr == order_str || *endptr != '\0') {
printf("wrong order in follow buf:\n%s\n", buf);
return 0;
}
return 1 << order_val;
}
static pid_t get_pid(char *buf)
{
pid_t pid;
char pid_str[FIELD_BUFF] = {0};
char *endptr;
search_pattern(&pid_pattern, pid_str, buf);
errno = 0;
pid = strtol(pid_str, &endptr, 10);
if (errno != 0 || endptr == pid_str || *endptr != '\0') {
printf("wrong/invalid pid in follow buf:\n%s\n", buf);
return -1;
}
return pid;
}
static __u64 get_ts_nsec(char *buf)
{
__u64 ts_nsec;
char ts_nsec_str[FIELD_BUFF] = {0};
char *endptr;
search_pattern(&ts_nsec_pattern, ts_nsec_str, buf);
errno = 0;
ts_nsec = strtoull(ts_nsec_str, &endptr, 10);
if (errno != 0 || endptr == ts_nsec_str || *endptr != '\0') {
printf("wrong ts_nsec in follow buf:\n%s\n", buf);
return -1;
}
return ts_nsec;
}
static __u64 get_free_ts_nsec(char *buf)
{
__u64 free_ts_nsec;
char free_ts_nsec_str[FIELD_BUFF] = {0};
char *endptr;
search_pattern(&free_ts_nsec_pattern, free_ts_nsec_str, buf);
errno = 0;
free_ts_nsec = strtoull(free_ts_nsec_str, &endptr, 10);
if (errno != 0 || endptr == free_ts_nsec_str || *endptr != '\0') {
printf("wrong free_ts_nsec in follow buf:\n%s\n", buf);
return -1;
}
return free_ts_nsec;
}
static void add_list(char *buf, int len)
{
if (list_size != 0 &&
@ -129,6 +226,11 @@ static void add_list(char *buf, int len)
memcpy(list[list_size].txt, buf, len);
list[list_size].txt[len] = 0;
list[list_size].stacktrace = strchr(list[list_size].txt, '\n') ?: "";
list[list_size].pid = get_pid(buf);
list[list_size].ts_nsec = get_ts_nsec(buf);
list[list_size].free_ts_nsec = get_free_ts_nsec(buf);
memcpy(list[list_size].txt, buf, len);
list[list_size].txt[len] = 0;
list_size++;
if (list_size % 1000 == 0) {
printf("loaded %d\r", list_size);
@ -144,6 +246,9 @@ static void usage(void)
"-m Sort by total memory.\n"
"-s Sort by the stack trace.\n"
"-t Sort by times (default).\n"
"-p Sort by pid.\n"
"-a Sort by memory allocate time.\n"
"-r Sort by memory release time.\n"
"-c cull by comparing stacktrace instead of total block.\n"
);
}
@ -152,28 +257,40 @@ int main(int argc, char **argv)
{
int (*cmp)(const void *, const void *) = compare_num;
int cull_st = 0;
int filter = 0;
FILE *fin, *fout;
char *buf;
int ret, i, count;
struct block_list *list2;
struct stat st;
int err;
int opt;
while ((opt = getopt(argc, argv, "mstc")) != -1)
while ((opt = getopt(argc, argv, "acfmprst")) != -1)
switch (opt) {
case 'a':
cmp = compare_ts;
break;
case 'c':
cull_st = 1;
break;
case 'f':
filter = 1;
break;
case 'm':
cmp = compare_page_num;
break;
case 'p':
cmp = compare_pid;
break;
case 'r':
cmp = compare_free_ts;
break;
case 's':
cmp = compare_stacktrace;
break;
case 't':
cmp = compare_num;
break;
case 'c':
cull_st = 1;
break;
default:
usage();
exit(1);
@ -192,13 +309,10 @@ int main(int argc, char **argv)
exit(1);
}
err = regcomp(&order_pattern, "order\\s*([0-9]*),", REG_EXTENDED|REG_NEWLINE);
if (err != 0 || order_pattern.re_nsub != 1) {
printf("%s: Invalid pattern 'order\\s*([0-9]*),' code %d\n",
argv[0], err);
exit(1);
}
check_regcomp(&order_pattern, "order\\s*([0-9]*),");
check_regcomp(&pid_pattern, "pid\\s*([0-9]*),");
check_regcomp(&ts_nsec_pattern, "ts\\s*([0-9]*)\\s*ns,");
check_regcomp(&free_ts_nsec_pattern, "free_ts\\s*([0-9]*)\\s*ns");
fstat(fileno(fin), &st);
max_size = st.st_size / 100; /* hack ... */
@ -248,10 +362,15 @@ int main(int argc, char **argv)
qsort(list2, count, sizeof(list[0]), cmp);
for (i = 0; i < count; i++)
for (i = 0; i < count; i++) {
if (filter == 1 && list2[i].free_ts_nsec != 0)
continue;
fprintf(fout, "%d times, %d pages:\n%s\n",
list2[i].num, list2[i].page_num, list2[i].txt);
}
regfree(&order_pattern);
regfree(&pid_pattern);
regfree(&ts_nsec_pattern);
regfree(&free_ts_nsec_pattern);
return 0;
}