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实现中发生了一个内存泄露,我们接下来看看系统是如何检测到它的。
- 触发内存泄露
/ # echo 1 > /proc/workqueue
mem leak,ptr = 9e3c0000
mem leak,ptr =9e3c8000
- 等待系统的memleak检测线程调度(最长10min),或者你可以执行如下的命令强制系统去检测内存泄露
echo scan > /sys/kernel/debug/kmemleak
注:当你强制触发检测的时候,需要留意的是第一次触发检测的时候,会先sleep出去1min,以保证系统完全的bring up。
- 随后系统将检测到内存泄露,并通知你去查看
/ # kmemleak: 2 new suspected memory leaks (see /sys/kernel/debug/kmemleak)
- 查看可能的内存泄露点
/ # 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