Linux内核的Kmemleak实现内存泄露检测

1. Kmemleak的使用方法

    a. 在uboot的bootarg中加入"kmemleak=on"
    b. 在.config中使能如下配置

CONFIG_HAVE_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400

    c. mount -t debugfs nodev /sys/kernel/debug/
如果一切顺利的话,你将能够在"/sys/kernel/debug/"下面看到kmemleak的文件

 /sys/kernel/debug # ls
			 ...
kmemleak            suspend_stats
/sys/kernel/debug # 

我们用一个例子来说明其如何使用:

 static ssize_t workqueue_proc_store(struct file *file, const char __user *buffer,
			      size_t count, loff_t *ppos)
{
#ifdef MM_LEAK_DEBUG
				char *mm =	kmalloc(32*1024, GFP_KERNEL);
				printk("mem leak,ptr = %p\n",mm);
				if(mm)
				{
					memset(mm,0x0,32*1024);
					mm = kmalloc(32*1024, GFP_KERNEL);
					kfree(mm);
					printk("mem leak,ptr = %p\n",mm);
			
				}
#endif
	
	return count;
}
...
static const struct file_operations workqueue_proc_fops = {
	.open		= workqueue_proc_open,
	.read		= seq_read,
	.write      = workqueue_proc_store,
	.llseek		= seq_lseek,
	.release	= single_release,
};
...
proc_create("workqueue", 0, NULL, &workqueue_proc_fops);
...

如上我在proc节点的write实现中发生了一个内存泄露,我们接下来看看系统是如何检测到它的。

  1. 触发内存泄露

/ # echo 1 > /proc/workqueue
mem leak,ptr = 9e3c0000
mem leak,ptr =9e3c8000

  1. 等待系统的memleak检测线程调度(最长10min),或者你可以执行如下的命令强制系统去检测内存泄露

echo scan > /sys/kernel/debug/kmemleak

注:当你强制触发检测的时候,需要留意的是第一次触发检测的时候,会先sleep出去1min,以保证系统完全的bring up。

  1. 随后系统将检测到内存泄露,并通知你去查看

/ # kmemleak: 2 new suspected memory leaks (see /sys/kernel/debug/kmemleak)

  1. 查看可能的内存泄露点

/ # cat /sys/kernel/debug/kmemleak
unreferenced object 0x9e3c0000
(size 32768): comm “sh”, pid 778, jiffies 4294939511 (age 57.630s)
hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 … backtrace:
[<80138228>] proc_reg_write+0x5c/0x84
[<800ebdd4>] __vfs_write+0x1c/0xd8
[<800ec618>] vfs_write+0x90/0x170
[<800ece18>] SyS_write+0x3c/0x90
[<8000f3c0>] ret_fast_syscall+0x0/0x3c
[] 0xffffffff / #

注:如果你觉得memleak的检测线程间隔时间太长,那么你可以手动的修改,如下:

Kmemleak.c (mm)

#define SECS_SCAN_WAIT		600	/* subsequent auto scanning delay */

2. 深入的理解Kmemleak

为了更加深入的理解Kmemleak的检测原理,我们不妨做如下实验:
我们在最开始的位置定义一个全局的指针变量,让tmp_ptr 指向泄露的那块内存,如下:

char * tmp_ptr = NULL;
...
#ifdef MM_LEAK_DEBUG
				char *mm =	kmalloc(32*1024, GFP_KERNEL);
				printk("mem leak,ptr = %p\n",mm);
				tmp_ptr = mm;
				if(mm)
				{
					memset(mm,0x0,32*1024);
					mm = kmalloc(32*1024, GFP_KERNEL);
					printk("mem leak,ptr = %p\n",mm);
					kfree(mm);
				}
#endif
...

我们再次运行kmemleak:

/ # echo 1 > /proc/workqueue
mem leak,ptr = 9e3c0000
mem leak,ptr = 9e3c8000
/ #
/ # echo scan > /sys/kernel/debug/kmemleak
/ # echo scan > /sys/kernel/debug/kmemleak
/ # echo scan >/sys/kernel/debug/kmemleak
/ #

我们发现很遗憾,kmemleak并不能为你检测到这块内存泄露,因为它检测到tmp_ptr这个指针还指向它。所以系统错误的判定该处没有内存泄露。

我们不妨再做一次实验,再次调用“echo 1 > /proc/workqueue ”,再触发一次内存泄露

/ # echo 1 > /proc/workqueue
mem leak,ptr = 9e3c0000
mem leak,ptr =9e3c8000
/ # echo scan > /sys/kernel/debug/kmemleak
/ # echo scan >/sys/kernel/debug/kmemleak
/ # echo scan > /sys/kernel/debug/kmemleak
/ # echo 1 > /proc/workqueue
mem leak,ptr = 9e3c8000
mem leak,ptr =9e3d0000
/ #
/ # echo scan > /sys/kernel/debug/kmemleak
kmemleak: 1 new suspected memory leaks (see /sys/kernel/debug/kmemleak)

然后我们看下该处内存泄露

/ # cat /sys/kernel/debug/kmemleak
unreferenced object 0x9e3c0000 (size32768):
comm “sh”, pid 776, jiffies 4294938930 (age 326.730s)
hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 … backtrace:
[<80138228>] proc_reg_write+0x5c/0x84
[<800ebdd4>] __vfs_write+0x1c/0xd8
[<800ec618>] vfs_write+0x90/0x170
[<800ece18>] SyS_write+0x3c/0x90
[<8000f3c0>] ret_fast_syscall+0x0/0x3c
[] 0xffffffff

哦哦,系统帮我们检测到了第一次的内存泄露,原因是第一次泄露的内存已经没有人在引用了。

3. 将泄露的内存还给系统

当我们发现内存泄露后,是可以将泄露的内存释放还给系统的,方法如下:

# echo scan=off > /sys/kernel/debug/kmemleak
# echo off > /sys/kernel/debug/kmemleak

需要特别注意的是kmemleak scan thread运行的情况下,无法释放kmemleak objects,因此我们首先要将kmemleak的检测线程停止。

kmemleak_write
	if (strncmp(buf, "off", 3) == 0)
		kmemleak_disable();
			->schedule_work(&cleanup_work);
				->kmemleak_do_cleanup
					->__kmemleak_do_cleanup
						delete_object_full

猜你喜欢

转载自blog.csdn.net/zhuyong006/article/details/83089407