内存管理四 内存泄漏检测kmemleak

一、简介:

    Kmemleak工作于内核态,Kmemleak 提供了一种可选的内核泄漏检测,其方法类似于跟踪内存收集器。当独立的对象没有被释放时,其报告记录在 /sys/kernel/debug/kmemleak中,Kmemcheck能够帮助定位大多数内存错误的上下文。

二、使用方法:

(1)打开方法:

    在kernel的defconfig的文件中,使能如下宏控

    CONFIG_SLUB_DEBUG=y
    CONFIG_DEBUG_KMEMLEAK=y
    CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=n
    CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=40000

    其中kmemleak缓冲区的大小通过配CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE设置

CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=40000  // 可以把这个值稍微改大一点,其中Kmemleak相关的代码在如下路径的文件中: kernel-4.9/drivers/misc/mediatek/mem/mtk_memcfg.c

    当发现一些SLUB泄漏(通过slub机制分配的内存,但未释放)的案例时,需要同时添加slabtrace的节点,以便获取到通过SLUB机制释放的内存的堆栈信息(/proc/mtk_memcfg/slabtrace)。下面会通过案例解释。

(2)检测方法(假设有泄漏):

a、一次scan: echo scan > sys/kernel/debug/kmemleak//开始扫描
     然后 cat sys/kernel/debug/kmemleak 会得到很多backtrace,但是这其中有些是误抓的(kmemleak存在误报情况)
b、然后echo clear > sys/kernel/debug/kmemleak 清除log
    第二次scan:echo scan > sys/kernel/debug/kmemleak//开始扫描
    过段时间等待leak的积累, 然后 cat sys/kernel/debug/kmemleak
    很多第一次误报的backtrace没有了,会得到很多重复的backtrace,假设这样的backtrace称为A;
c、kmemleak的特征是A backtrace会越来越多,不断增长,而且这里就是泄漏的点;

 

二、实验:

    通过修改linux_DEVICE_ATTR创建设备节点程序一文中的show函数如下:

static ssize_t hello_test_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    size_t kmalloc_size = 26;
    
    char *hello_test = kmalloc(kmalloc_size,GFP_KERNEL);
    
    hello_test[kmalloc_size - 1] = 'x';
    
    return 0;
}

    以上函数可以看出每次cat对应的节点都会通过SLUB申请26字节的大小的内存,但并没有去释放(free)这一块内存,所以可以通过如下方式,不停的去cat此节点,就可以看出对应的kmemleak中频繁出现此函数:

    cd  /sys/bus/platform/drivers/hello_test/odm:hello_test     //进入到对应的路径

    while true;do cat hello_test;done;                                      //不停的去cat对应的节点

    此时去进行二中的检测方法:

           echo clear > sys/kernel/debug/kmemleak
           echo scan > sys/kernel/debug/kmemleak
           cat sys/kernel/debug/kmemleak

    可以发现如下堆栈信息:

unreferenced object 0xffffffc7e779d800 (size 64):
  comm "cat", pid 17097, jiffies 4295075509 (age 564.640s)
  hex dump (first 32 bytes):
    6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
    6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
  backtrace:
    [<ffffff94f34a439c>] create_object+0x1cc/0x478
    [<ffffff94f43d3fc8>] kmemleak_alloc+0x60/0xd8
    [<ffffff94f349a5f4>] kmem_cache_alloc_trace+0x274/0x2d8
    [<ffffff94f3dc435c>] hello_test_show+0x2c/0x38
    [<ffffff94f382c2a0>] dev_attr_show+0x40/0x88
    [<ffffff94f3565230>] sysfs_kf_seq_show+0x128/0x1c8
    [<ffffff94f3562f98>] kernfs_seq_show+0x80/0x98
    [<ffffff94f34e7184>] seq_read+0x14c/0x720
    [<ffffff94f3563fec>] kernfs_fop_read+0x1e4/0x280
    [<ffffff94f34b070c>] __vfs_read+0x74/0x1d0
    [<ffffff94f34b1a80>] vfs_read+0x98/0x188
    [<ffffff94f34b3570>] SyS_read+0x68/0xe0
    [<ffffff94f3282f70>] el0_svc_naked+0x24/0x28
    [<ffffffffffffffff>] 0xffffffffffffffff

    另外,kmalloc函数是基于SLUB机制释放的内存,可以通过上述的的slabtrace的节点看到对应的的内存大小再不断的增加:

======= kmem_cache: kmalloc-64 alloc_calls =======
  19537 selinux_cred_prepare+0x34/0x68 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349e048
  50318 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
  13326 load_elf_binary+0x30c/0x1760 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349aa20

   测试方法是:

         在一个cmd终端不停的执行:/sys/bus/platform/drivers/hello_test/odm:hello_test # while true;do cat hello_test;done;

         在另外一个终端不停的执行:/proc/mtk_memcfg # while true;do cat slabtrace|grep -nr hello;done;

   可以看到如下结果:

(standard input):45:  53439 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53401 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53351 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53370 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53368 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
......
(standard input):45:  53461 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630
(standard input):45:  53491 hello_test_show+0x2c/0x38 ffffff88f3498c04 ffffff88f349a078 ffffff88f349a0ac ffffff88f349a630

//会发现53439 - 53491 的数值会不断的增加,上面只截取了一部分

二、总结:

    kmemleak的问题比较难以排查,需要复现找到规律,看具体那一块,或者哪一个函数存在内存的泄漏,这样才能具体的去定位解决问题。

作者:frank_zyp 
您的支持是对博主最大的鼓励,感谢您的认真阅读。 
本文无所谓版权,欢迎转载。

猜你喜欢

转载自blog.csdn.net/frank_zyp/article/details/82588638