kmemleak的使用---内存泄露检测工具【转】

转自:http://blog.csdn.net/lishenglong666/article/details/8287783

版权声明:本文为博主原创文章,未经博主允许不得转载。

 

目录(?)[-]

  1. 内核泄露检测kmemleak
    1. 介绍
    2. 用法
    3. 功能实现的基本方法原理
    4. Kmemleak API
    5. 处理假阳性阴性
    6. 限制和缺点
    7. 测试的特定部分kmemleak

 

内核泄露检测(kmemleak)

介绍:

Kmemleak 提供了一种可选的内核泄漏检测,其方法类似于跟踪内存收集器。(http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29#Tracing_garbage_collectors)当独立的对象没有被释放时,其报告记录在 /sys/kernel/debug/kmemleak中。

用法:

CONFIG_DEBUG_KMEMLEAK 在Kernel hacking中被使能,一个内核线程每10分钟(默认值)扫描内存,并打印发现新的未引用的对象的数量。

查看内核打印信息详细过程如下:

1、挂载debugfs文件系统

   mount -t debugfs nodev /sys/kernel/debug/

2、开启内核自动检测线程

   echo scan > /sys/kernel/debug/kmemleak

3、查看打印信息

   cat /sys/kernel/debug/kmemleak

4、清除内核检测报告,新的内存泄露报告将重新写入/sys/kernel/debug/kmemleak

   echo clear > /sys/kernel/debug/kmemleak

 

内存扫描参数可以进行修改通过向/sys/kernel/debug/kmemleak 文件写入。 参数使用如下:

  off 禁用kmemleak(不可逆)

  stack=on 启用任务堆栈扫描(default)

  stack=off 禁用任务堆栈扫描

  scan=on 启动自动记忆扫描线程(default)

  scan=off 停止自动记忆扫描线程

  scan=<secs> 设置n秒内自动记忆扫描,默认600s

  scan 开启内核扫描

  clear 清除内存泄露报告

  dump=<addr> 转存信息对象在<addr>

 

通过“kmemleak = OFF”,也可以在启动时禁用Kmemleak在内核命令行。在初始化kmemleak之前,内存的分配或释放这些动作被存储在一个前期日志缓冲区。这个缓冲区的大小通过配CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE设置。

 

功能实现的基本方法原理

通过的kmalloc、vmalloc、kmem_cache_alloc等内存分配会跟踪其指针,连同其他

的分配大小和堆栈跟踪信息,存储在PRIO搜索树。

相应的释放函数调用跟踪和指针就会从kmemleak数据结构中移除。

 

分配的内存块,被认为是独立的,如果没有指针指向它起始地址或块的内部的任何位置,可以发现扫描内存(包括已保存的寄存器)。这意味着,有可能没有办法为内核通过所分配的地址传递块到一个释放函数,因此,该块被认为是一个内存泄漏。

 

扫描算法步骤:

 

  1。标记的所有分配对象为白色(稍后将剩余的白色物体

     考虑独立的)

  2。扫描存储器与所述数据片段和栈开始,检查对地址的值存储在PRIO搜索树。如果

     一个白色的对象的指针被发现,该对象将被添加到黑名单

  3。扫描的灰色对象匹配的地址(一些白色物体可以变成黑色,并添加结束时的黑名单),直到黑色集结束

  4。剩下的白色物体被认为是独立儿,并报告写入/sys/kernel/debug/kmemleak。

 

一些分配的内存块的指针在内核的内部数据结构和它们不能被检测为孤儿。对

避免这种情况,kmemleak也可以存储的数量的值,指向一个

内的块的地址范围内的地址,需要找到使

块不被认为是泄漏。其中一个例子是使用vmalloc()函数。

Kmemleak API

------------

见include / linux / kmemleak.h中的函数原型的头。

kmemleak_init  - 初始化kmemleak

kmemleak_alloc  - 一个内存块分配的通知

kmemleak_alloc_percpu  - 通知的一个percpu的内存块分配

kmemleak_free  - 通知的内存块释放

kmemleak_free_part  - 通知释放部分内存块

kmemleak_free_percpu  - 一个percpu内存块释放的通知

kmemleak_not_leak  - 当不是泄露时,标记对象

kmemleak_ignore  - 当泄漏时不扫描或报告对象

kmemleak_scan_area  - 添加扫描区域内的内存块

kmemleak_no_scan  - 不扫描的内存块

kmemleak_erase  - 删除一个指针变量的旧值

kmemleak_alloc_recursive  - 为kmemleak_alloc,只检查递归

kmemleak_free_recursive  - 为kmemleak_free,只检查递归

 

处理假阳性/阴性

--------------------------------------

 

 

对于假性的内存泄漏,但不需要报告的,由于值的内存扫描过程中发现kmemleak是指向这样的对象。为了减少假性报告的数目,kmemleak提供kmemleak_

ignore,kmemleak_scan_area,kmemleak_no_scan,kmemleak_erase的功能,可以指定指针扫描方式,他们的扫描默认情况下不启用。

对于不能确定是否是内存泄露的,kmemleak提供kmemleak_not_leak。kmemleak_ignore的功能可以指定固定类型的数据是否需要扫描或打印,以上具体函数分析详见3.3详细处理处理过程及功能函数分析。

有的泄露只是瞬间的,尤其是在SMP系统,因为指针暂时存储在CPU的寄存器或栈。当内存泄漏时Kmemleak定义MSECS_MIN_AGE(默认为1000)一个对象的最低时间。

 

限制和缺点

-------------------------

 

主要缺点是减少了内存分配和性能释放。为了避免其他开销,只进行内存扫描,当在/ sys /kernel/debug/ kmemleak文件被读取。不管怎样,这个工具是用于调试目的,其表现的性能不是重要的。为了保持算法简单,kmemleak的值指向任何扫描一个块的地址范围内的地址。这可能会导致增加假阴性的报告。然而,它包括真正的内存泄漏,最终内存泄露将变得可见。

假阴性的另一个来源是数据存储在非指针值。

在未来的版本中,kmemleak只能扫描指针成员中分配的结构。此功能解决了许多上述假阴性的情况下。

该工具可能存在误报。这些个案的分配块可能不需要被释放(如一些在init_call功能的情况下),这样的指针通过其他方法计算,与通常的container_of宏或指针被存储在一个位置相比不会被kmemleak扫描。页分配和ioremap不被跟踪

测试的特定部分kmemleak

---------------------------------------

在初始启动时,/sys/kernel/debug/kmemleak输出页面比较多。这样的情况下,当检测指定已经开发的代码错误时,可以通过清除/sys/kerner/debug/kmemleak的输出。通过启动kmemleak的扫描后,你可以找到新的未引用的对象,这应该与测试特定的代码段。

详细步骤如下:

要测试的关键部分之前需要清除kmemleak报告:

echo clear > /sys/kernel/debug/kmemleak

测试你的内核或模块...

echo scan =5> /sys/kernel/debug/kmemleak

然后像往常一样查看报告:

cat /sys/kernel/debug/kmemleak

已经测试的实例详见内核文档kmenleak_test.txt文档

 

1:检测内核内存泄漏的功能

2:Documentation/kmemleak.txt

3:内核demo:mm/kmemleak-test.c

 

 

对于kmemleak,需要理解下面三点就可以了

1:我们需要知道它能检测哪几种内存泄漏(即用什么方法分配的内存可以检测)

2:内核存在特殊情况,即分配内存但没有引用。使用什么方法可以防止kmemleak report

3:检测的机理是什么,如何知道分配的内存被引用,或者没有引用。

 

  • 关注点1

kmalloc/kzalloc

vmalloc

kmem_cache_alloc

per_cpu

[Page allocations and ioremap are not tracked]

  • 关注点2

kmemleak_not_leak、kmemleak_ignore、kmemleak_no_scan

这几个函数在内核中被使用,是为了不被kmemleak 打印出来。但是深层次的区别是什么?

 

kmemleak_not_leak

/**
* kmemleak_not_leak - mark an allocated object as false positive
* @ptr:        pointer to beginning of the object
*
* Calling this function on an object will cause the memory block to no longer
* be reported as leak and always be scanned.
*/

不打印;但是要扫描这个指针所分配的内存的内容。分配数据结构那么该结构本身不打印,但是会扫描结构内部的成员变量,是否引用其他指针。

这个函数往往用在:分配内存的内存永远不会被释放(与内核是一体,vmlinux或者不可移除的模块一类)。

kmemleak_ignore

/**
* kmemleak_ignore - ignore an allocated object
* @ptr:        pointer to beginning of the object
*
* Calling this function on an object will cause the memory block to be
* ignored (not scanned and not reported as a leak). This is usually done when
* it is known that the corresponding block is not a leak and does not contain
* any references to other allocated memory blocks.
*/

既不打印,也不扫描指针所指的数据结构的成员变量。如果知道分配的数据结构内部不包含其他引用(不含指针)。

kmemleak_no_scan

/**
* kmemleak_no_scan - do not scan an allocated object
* @ptr:        pointer to beginning of the object
*
* This function notifies kmemleak not to scan the given memory block. Useful
* in situations where it is known that the given object does not contain any
* references to other objects. Kmemleak will not scan such objects reducing
* the number of false negatives.
*/

该指针本身被扫描,但是内容不会扫描。

  • 关注点3

所谓reference即所分配的内存有指针引用。如果没有任何指针引用那么肯定就是memleak。

所以要查找所有的指针的内容,来寻找其内容是否包含我们已经记录的分配内存的地址(包括在其实地址+size之间)。

 

 

那么这些指针变量的

1:函数的局部变量

     这些变量本身在栈中,所以需要检测进程的内核栈

2:全局变量(整个系统/模块内)静态变量

     这些变量是存在:ELF的bss/data     

     这些变量可以通过查看vmlinux或者*.ko查看这类指针变量的区段。

     可以通过objdump -x file   

---指针是静态分配

3:指针本身是动态分配的,即动态分配内存块(struct).成员变量是指针

     所以必须要搜索这类动态分配的内存块的内容。

 

 

通过objdump -x vmlinux

.data

      where global tables, variables, etc. stand. objdump -s -j .data .process.o will hexdump it. 

.bss

      don't look for bits of .bss in your file: there's none. That's where your uninitialized arrays and variable are, and the loader 'knows' they should be filled with zeroes ... there's no point storing more zeroes on your disk than there already are, is it ? 

 .rodata

       that's where your strings go, usually the things you forgot when linking and that cause your kernel not to work. objdump -s -j .rodata .process.o will hexdump it. Note that depending on the compiler, you may have more sections like this.

 

.data..percpu

 

  • kmemleak_scan()

data/bss 段扫描

    /* data/bss scanning */

     scan_block(_sdata, _edata, NULL, 1);
     scan_block(__bss_start, __bss_stop, NULL, 1);

 

data..percpu

#ifdef CONFIG_SMP
     /* per-cpu sections scanning */
     for_each_possible_cpu(i)
          scan_block(__per_cpu_start + per_cpu_offset(i),
                  __per_cpu_end + per_cpu_offset(i), NULL, 1);
#endif

 

-->>>>以上都是全局指针变量、per_cpu变量

 

struct pagep[]数组

        /*  
         * Struct page scanning for each node.
         */
        lock_memory_hotplug();
        for_each_online_node(i) {
                pg_data_t *pgdat = NODE_DATA(i);
                unsigned long start_pfn = pgdat->node_start_pfn;
                unsigned long end_pfn = start_pfn + pgdat->node_spanned_pages;
                unsigned long pfn;

                for (pfn = start_pfn; pfn < end_pfn; pfn++) {
                        struct page *page;

                        if (!pfn_valid(pfn))
                                continue;
                        page = pfn_to_page(pfn);
                        /* only scan if page is in use */
                        if (page_count(page) == 0)
                                continue;
                        scan_block(page, page + 1, NULL, 1);
                }   
        }   
        unlock_memory_hotplug();

 

内核struct page数组是动态分配的,所以也要单独的进行检测。

 

内核进程栈

if (kmemleak_stack_scan) {
          struct task_struct *p, *g;

          read_lock(&tasklist_lock);
          do_each_thread(g, p) {
               scan_block(task_stack_page(p), task_stack_page(p) +
                       THREAD_SIZE, NULL, 0);
          } while_each_thread(g, p);
          read_unlock(&tasklist_lock);
     

一般遍历内核所有的进程用的是:for_each_process();

但是这里却使用:do_each_thread(){};while_each_thread()

 

>>>for_each_process:只打印进程;而不打印进程内的线程

>>>do_each_thread(){};while_each_thread():打印进程以及进程内的线程信息。这是因为线程有自己单独的内核栈信息。

 

分配的内存块的内部

 

分配一块内存(一般是分配数据结构),内部的成员变量是指针,所以这部分也需要检测。

 

>>>     scan_gray_list();---->scan_object():

扫描分配内存的全部内容或者部分内容,是否引用其他指针。

    pointer+size

 

 

 

  • 问题

1:读代码理解下面的扫描

struct A*a ---> struct A {

                              struct B * b------>struct B {

                                                                       struct C *c ------->struct C

 

如果struct A *a = NULL

时间: 2024-10-26 00:29:26

kmemleak的使用---内存泄露检测工具【转】的相关文章

Visual C++内存泄露检测工具(VLD)

简述 C/C++ 程序越复杂,内存的管理显得越重要,稍有不慎就会出现泄漏.如果内存泄漏不是很严重,在短时间内对程序不会有太大影响,这也使得内存泄漏问题有很强的隐蔽性,不易被发现.然而不管内存泄漏多么轻微,当程序长时间运行时,其破坏力是惊人的 - 从性能下降到内存耗尽,甚至会影响其他程序的正常运行. 简述 VLD 下载安装 使用 Visual Studio Qt 更多参考 VLD VLD(Visual Leak Detector)是一款用于 Visual C++ 的免费内存泄露检测工具.相比较其它

Android 和 Java 内存泄露检测工具——LeakCanary

LeakCanary Android 和 Java 内存泄露检测. "A small leak will sink a great ship." - Benjamin Franklin 千里之堤, 毁于蚁穴. -- <韩非子·喻老> demo 一个非常简单的 LeakCanary demo: https://github.com/liaohuqiu/leakcanary-demo 开始使用 在 build.gradle 中加入引用,不同的编译使用不同的引用: depende

Linux C 编程内存泄露检测工具(一):mtrace

前言 所有使用动态内存分配(dynamic memory allocation)的程序都有机会遇上内存泄露(memory leakage)问题,在Linux里有三种常用工具来检测内存泄露的情況,包括: mtrace dmalloc memwatch 1. mtrace mtrace是三款工具之中是最简单易用的,mtrace是一个C函數,在<mcheck.h>里声明及定义,函数原型为:     void mtrace(void);   其实mtrace是类似malloc_hook的malloc

Visual C++内存泄露检测—VLD工具使用说明 .

Visual C++内存泄露检测-VLD工具使用说明 一.        VLD工具概述 Visual Leak Detector(VLD)是一款用于Visual C++的免费的内存泄露检测工具.他的特点有:可以得到内存泄漏点的调用堆栈,如果可以的话,还可以得到其所在文件及行号: 可以得到泄露内存的完整数据: 可以设置内存泄露报告的级别:并且是开源免费的.   二.        VLD下载 http://www.codeproject.com/tools/visualleakdetector.

C语言内存泄露检测--Memwatch

项目:迅雷下载库内存泄露检测 作者:曾金龙 供职:深圳迅雷网络技术股份有限公司 领域:迅雷下载库 时间:2014-07-26 迅雷的移动下载库是用C语言编写的,为了能够横跨欧亚非拉(ios,android,还有诺基亚的什么系统来着,对了symban)和性能,我们厂的下载库就选择了C,然后在C代码里面使用各种宏,预编译等来达到一 个下载库,多平台使用.不过平台相关的都已经约束在了一个叫common的库里面的一个子模块里面了.这极大的解放了我厂的码农朋友. 但是C毕竟是C,在聊起C的时候,我们组最喜

VC内存泄露检查工具:VisualLeakDetector

初识VisualLeakDetector灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题.内存泄漏是最常见的内存问题之一.内存泄漏如果不是很严重,在短时间内对程序不会有太大的影响,这也使得内存泄漏问题有很强的隐蔽性,不容易被发现.然而不管内存泄漏多么轻微,当程序长时间运行时,其破坏力是惊人的,从性能下降到内存耗尽,甚至会影响到其他程序的正常运行.另外内存问题的一个共同特点是,内存问题本身并不会有很明

C/C++的内存泄漏检测工具Valgrind memcheck的使用经历

Linux下的Valgrind真是利器啊(不知道Valgrind的请自觉查看参考文献(1)(2)),帮我找出了不少C++中的内存管理错误,前一阵子还在纠结为什么VS 2013下运行良好的程序到了Linux下用g++编译运行却崩溃了,给出一堆汇编代码也看不懂.久久不得解过后,想想肯定是内存方面的错误,VS在这方面一般都不检查的,就算你的程序千疮百孔,各种内存泄露.内存管理错误,只要不影响运行,没有读到不该读的东西VS就不会告诉你(应该是VS内部没实现这个内存检测功能),因此用VS写出的程序可能不是

Cocos开发中性能优化工具介绍之Visual Studio内存泄漏检测工具——Visual Leak Detector

那么在Windows下有什么好的内存泄漏检测工具呢微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检测功能我们可以使用第三方工具Visual Leak Detector以下简称vld. vld工具是VC++环境下一款小巧易用.免费开源的内存泄漏检测工具vld可以显示导致内存泄漏的完整内存分配调用堆栈.vld的检测报告能够对每个内存泄漏点提供完整的堆栈跟踪并且包含其源文件及行号信息. 安装过程是先在到地址http://vld.codeplex.com/下载vld安装文件然后进行

android内存泄露分析工具MAT详解

一.准备 1.什么是MAT Eclipse提供的一个内存分析工具.它是一个功能丰富的 JAVA 堆转储文件分析工具,可以帮助你发现内存漏洞和减少内存消耗. android studio未集成该插件 需要你下载独立版 android studio的DDMS可以生成hprof是什么文件,不过需要进行一下格式转化(.hprof文件从Dalvik格式转换成J2SE HPROF格式),才可以导入MAT独立版软件. 2.hprof是什么文件 heap dumps,中文翻译,堆转储,快照.即堆内存某个时刻的情