Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4855b811 authored by Alexander Nyberg's avatar Alexander Nyberg Committed by Laura Abbott
Browse files

debugging: keep track of page owners

akpm: Alex's ancient page-owner tracking code, resurrected yet
      again.  Someone(tm) should mainline this.  Please see Ingo's
      thoughts at https://lkml.org/lkml/2009/4/1/137

.

PAGE_OWNER tracks free pages by setting page->order to -1.  However, it is
set during __free_pages() which is not the only free path as
__pagevec_free() and free_compound_page() do not go through __free_pages().
 This leads to a situation where free pages are visible in page_owner
which is confusing and might be interpreted as a memory leak.

This patch sets page->owner when PageBuddy is set.  It also prints a
warning to the kernel log if a free page is found that does not appear free
to PAGE_OWNER.  This should be considered a fix to
page-owner-tracking-leak-detector.patch.

This only applies to -mm as PAGE_OWNER is not in mainline.

[mel@csn.ul.ie: print out PAGE_OWNER statistics in relation to fragmentation avoidance]
[mel.ul.ie: allow PAGE_OWNER to be set on any architecture]
Signed-off-by: default avatarMel Gorman <mel@csn.ul.ie>
Acked-by: default avatarAndy Whitcroft <apw@shadowen.org>
Signed-off-by: default avatarMel Gorman <mel@csn.ul.ie>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Laura Abbott <lauraa@codeaurora.org>
From: Dave Hansen <dave@linux.vnet.ibm.com>
Subject: debugging-keep-track-of-page-owners-fix

Updated 12/4/2012 - should apply to 3.7 kernels.  I did a quick
sniff-test to make sure that this boots and produces some sane
output, but it's not been exhaustively tested.

 * Moved file over to debugfs (no reason to keep polluting /proc)
 * Now using generic stack tracking infrastructure
 * Added check for MIGRATE_CMA pages to explicitly count them
   as movable.

The new snprint_stack_trace() probably belongs in its own patch
if this were to get merged, but it won't kill anyone as it stands.

Signed-off-by: default avatarDave Hansen <dave@linux.vnet.ibm.com>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Andy Whitcroft <apw@shadowen.org>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Laura Abbott <lauraa@codeaurora.org>
From: Minchan Kim <minchan@kernel.org>
Subject: Fix wrong EOF compare

The C standards allows the character type char to be singed or unsinged,
depending on the platform and compiler. Most of systems uses signed char,
but those based on PowerPC and ARM processors typically use unsigned char.
This can lead to unexpected results when the variable is used to compare
with EOF(-1). It happens my ARM system and this patch fixes it.

Signed-off-by: default avatarMinchan Kim <minchan@kernel.org>
Cc: Dave Hansen <dave@linux.vnet.ibm.com>
Cc: Michal Nazarewicz <mina86@mina86.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
From: Andrew Morton <akpm@linux-foundation.org>
Subject: debugging-keep-track-of-page-owners-fix-2-fix

Reduce scope of `val', fix coding style

Cc: Minchan Kim <minchan@kernel.org>
From: Minchan Kim <minchan@kernel.org>
Subject: Enhance read_block of page_owner.c

The read_block reads char one by one until meeting two newline.
It's not good for the performance and current code isn't good shape
for readability.

This patch enhances speed and clean up.

Signed-off-by: default avatarMinchan Kim <minchan@kernel.org>
Signed-off-by: default avatarMichal Nazarewicz <mina86@mina86.com>
Cc: Dave Hansen <dave@linux.vnet.ibm.com>
From: Andrew Morton <akpm@linux-foundation.org>
Subject: debugging-keep-track-of-page-owner-now-depends-on-stacktrace_support-fix

stomp sparse gfp_t warnings

Cc: Dave Hansen <dave@linux.vnet.ibm.com>
Cc: Fengguang Wu <fengguang.wu@intel.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
From: Dave Hansen <dave@linux.vnet.ibm.com>
Subject: PAGE_OWNER now depends on STACKTRACE_SUPPORT

One of the enhancements I made to the PAGE_OWNER code was to make
it use the generic stack trace support.  However, there are some
architectures that do not support it, like m68k.  So, make
PAGE_OWNER also depend on having STACKTRACE_SUPPORT.

This isn't ideal since it restricts the number of places
PAGE_OWNER runs now, but it at least hits all the major
architectures.

tree:   git://git.cmpxchg.org/linux-mmotm.git

 master
head:   83b324c5ff5cca85bbeb2ba913d465f108afe472
commit: 2a561c9d47c295ed91984c2b916a4dd450ee0279 [484/499] debugging-keep-track-of-page-owners-fix
config: make ARCH=m68k allmodconfig

All warnings:

warning: (PAGE_OWNER && STACK_TRACER && BLK_DEV_IO_TRACE && KMEMCHECK) selects STACKTRACE which has unmet direct dependencies (STACKTRACE_SUPPORT)

Change-Id: I8d9370733ead1c6a45bb034acc7aaf96e0901fea
Signed-off-by: default avatarDave Hansen <dave@linux.vnet.ibm.com>
Reported-by: default avatarFengguang Wu <fengguang.wu@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Git-commit: c6ca98b4acab6ae45cf0f9d93de9c717186e62cb
Git-repo: http://git.cmpxchg.org/cgit/linux-mmotm.git/


Signed-off-by: default avatarLaura Abbott <lauraa@codeaurora.org>
parent 563010bc
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
/*
 * User-space helper to sort the output of /sys/kernel/debug/page_owner
 *
 * Example use:
 * cat /sys/kernel/debug/page_owner > page_owner_full.txt
 * grep -v ^PFN page_owner_full.txt > page_owner.txt
 * ./sort page_owner.txt sorted_page_owner.txt
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

struct block_list {
	char *txt;
	int len;
	int num;
};


static struct block_list *list;
static int list_size;
static int max_size;

struct block_list *block_head;

int read_block(char *buf, int buf_size, FILE *fin)
{
	char *curr = buf, *const buf_end = buf + buf_size;

	while (buf_end - curr > 1 && fgets(curr, buf_end - curr, fin)) {
		if (*curr == '\n') /* empty line */
			return curr - buf;
		curr += strlen(curr);
	}

	return -1; /* EOF or no space left in buf. */
}

static int compare_txt(struct block_list *l1, struct block_list *l2)
{
	return strcmp(l1->txt, l2->txt);
}

static int compare_num(struct block_list *l1, struct block_list *l2)
{
	return l2->num - l1->num;
}

static void add_list(char *buf, int len)
{
	if (list_size != 0 &&
	    len == list[list_size-1].len &&
	    memcmp(buf, list[list_size-1].txt, len) == 0) {
		list[list_size-1].num++;
		return;
	}
	if (list_size == max_size) {
		printf("max_size too small??\n");
		exit(1);
	}
	list[list_size].txt = malloc(len+1);
	list[list_size].len = len;
	list[list_size].num = 1;
	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);
		fflush(stdout);
	}
}

#define BUF_SIZE	1024

int main(int argc, char **argv)
{
	FILE *fin, *fout;
	char buf[BUF_SIZE];
	int ret, i, count;
	struct block_list *list2;
	struct stat st;

	fin = fopen(argv[1], "r");
	fout = fopen(argv[2], "w");
	if (!fin || !fout) {
		printf("Usage: ./program <input> <output>\n");
		perror("open: ");
		exit(2);
	}

	fstat(fileno(fin), &st);
	max_size = st.st_size / 100; /* hack ... */

	list = malloc(max_size * sizeof(*list));

	for(;;) {
		ret = read_block(buf, BUF_SIZE, fin);
		if (ret < 0)
			break;

		add_list(buf, ret);
	}

	printf("loaded %d\n", list_size);

	printf("sorting ....\n");

	qsort(list, list_size, sizeof(list[0]), compare_txt);

	list2 = malloc(sizeof(*list) * list_size);

	printf("culling\n");

	for (i=count=0;i<list_size;i++) {
		if (count == 0 ||
		    strcmp(list2[count-1].txt, list[i].txt) != 0) {
			list2[count++] = list[i];
		} else {
			list2[count-1].num += list[i].num;
		}
	}

	qsort(list2, count, sizeof(list[0]), compare_num);

	for (i=0;i<count;i++) {
		fprintf(fout, "%d times:\n%s\n", list2[i].num, list2[i].txt);
	}
	return 0;
}
+7 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/spinlock.h>
#include <linux/rbtree.h>
#include <linux/rwsem.h>
#include <linux/stacktrace.h>
#include <linux/completion.h>
#include <linux/cpumask.h>
#include <linux/page-debug-flags.h>
@@ -177,6 +178,12 @@ struct page {
#ifdef LAST_NID_NOT_IN_PAGE_FLAGS
	int _last_nid;
#endif
#ifdef CONFIG_PAGE_OWNER
	int order;
	gfp_t gfp_mask;
	struct stack_trace trace;
	unsigned long trace_entries[8];
#endif
}
/*
 * The struct page can be forced to be double word aligned so that atomic ops
+3 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ extern void save_stack_trace_tsk(struct task_struct *tsk,
				struct stack_trace *trace);

extern void print_stack_trace(struct stack_trace *trace, int spaces);
extern int  snprint_stack_trace(char *buf, int buf_len,
				struct stack_trace *trace, int spaces);

#ifdef CONFIG_USER_STACKTRACE_SUPPORT
extern void save_stack_trace_user(struct stack_trace *trace);
@@ -32,6 +34,7 @@ extern void save_stack_trace_user(struct stack_trace *trace);
# define save_stack_trace_tsk(tsk, trace)		do { } while (0)
# define save_stack_trace_user(trace)			do { } while (0)
# define print_stack_trace(trace, spaces)		do { } while (0)
# define snprint_stack_trace(buf, len, trace, spaces)	do { } while (0)
#endif

#endif
+23 −0
Original line number Diff line number Diff line
@@ -11,6 +11,29 @@
#include <linux/kallsyms.h>
#include <linux/stacktrace.h>

int snprint_stack_trace(char *buf, int buf_len, struct stack_trace *trace,
			int spaces)
{
	int ret = 0;
	int i;

	if (WARN_ON(!trace->entries))
		return 0;

	for (i = 0; i < trace->nr_entries; i++) {
		unsigned long ip = trace->entries[i];
		int printed = snprintf(buf, buf_len, "%*c[<%p>] %pS\n",
				1 + spaces, ' ',
				(void *) ip, (void *) ip);
		buf_len -= printed;
		ret += printed;
		buf += printed;
	}

	return ret;
}
EXPORT_SYMBOL_GPL(snprint_stack_trace);

void print_stack_trace(struct stack_trace *trace, int spaces)
{
	int i;
+12 −0
Original line number Diff line number Diff line
@@ -99,6 +99,18 @@ config UNUSED_SYMBOLS
	  you really need it, and what the merge plan to the mainline kernel for
	  your module is.

config PAGE_OWNER
	bool "Track page owner"
	depends on DEBUG_KERNEL && STACKTRACE_SUPPORT
	select DEBUG_FS
	select STACKTRACE
	help
	  This keeps track of what call chain is the owner of a page, may
	  help to find bare alloc_page(s) leaks. Eats a fair amount of memory.
	  See Documentation/page_owner.c for user-space helper.

	  If unsure, say N.

config DEBUG_FS
	bool "Debug Filesystem"
	help
Loading