用KMEMLEAK检测内核内存泄漏【转】
转自:http://linuxperf.com/?p=188
所谓内存泄漏(memory leak),是指分配出去的内存在用完之后忘了释放,造成内存浪费,可用的内存越来越少。内存泄漏是程序设计的错误导致的,既可能发生在用户程序里,也可能发生在内核中。
诊断内存泄漏问题的目标是定位为什么内存用完之后会忘了释放,最终都是需要阅读源代码,理解内在的逻辑,找出其中的错误。作为最基本的分析线索,我们至少需要观察内存的分配与释放操作,还有一些更高级的工具可以帮助我们找出无人引用的内存块以及最初分配它的backtrace,这就更有针对性了。诊断用户态程序内存泄漏最流行的工具是Valgrind,对于内核,类似的工具是kmemleak。
kmemleak的原理
kmemleak通过追踪kmalloc(), vmalloc(), kmem_cache_alloc()等函数,把分配内存的指针和大小、时间、stack trace等信息记录在一个rbtree中,等到调用free释放内存时就把相应的记录从rbtree中删除,也就是说rbtree中的记录就是已经分配出去但尚未释放的内存,其中有些内存尚未释放是因为还在被使用,这属于正常情况,而不正常的情况即内存尚未释放但又不会再被使用,就是“泄漏”的内存,那么如何找出泄漏的内存呢?kmemleak缺省每10分钟对内存做一次扫描,在内存中寻找rbtree中记录的地址,如果某个rbtree记录的地址在内存中找不到,就认为这个地址是无人引用的,以后也不可能再被用到,是“泄漏”的内存,然后,把这些泄漏的内存地址以及rbtree中记录的时间、大小、strack trace等相关信息通过 /sys/kernel/debug/kmemleak 这个接口展现给我们。
注:
kmemleak的扫描算法存在误报的可能,比如内存中碰巧有一个数据与rbtree中的某个地址相同,但它只是数据而非指针,kmemleak是无法分辨的,会把它当作访问内存的指针;再比如rbtree中的某个地址在内存中找不到,但程序可能还在用它,只是因为程序并没有直接保存访问地址,而是通过某种方式临时计算访问地址,这种情况kmemleak也无法分辨,会认为是泄漏。但是请注意,kmemleak 这个工具的目的是为了给进一步分析提供线索,并不需要绝对精确,小概率的误报并不影响这个工具的实用性。
怎样启用kmemleak
要启用kmemleak,前提是内核编译时在“Kernel hacking”中开启了 CONFIG_DEBUG_KMEMLEAK 选项。怎样知道一个运行系统的内核是否支持kmemleak呢?可以查看 /boot/config-$(uname -r) 配置文件中 CONFIG_DEBUG_KMEMLEAK 是否等于”y”。以RHEL6为例,它的debug kernel是支持kmemleak的,我们看它的config文件:
1
2
3
4
|
$ grep CONFIG_DEBUG_KMEMLEAK /boot/config-2.6.32-642.4.2.el6.x86_64.debug
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=20000
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
|
- CONFIG_DEBUG_KMEMLEAK=y
表示内核支持kmemleak; - CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
表示kmemleak默认是关闭的,需要显式启用,启用的方式是在kernel command line中添加以下选项:
kmemleak=on
怎样使用kmemleak
kmemleak的用户接口是:
/sys/kernel/debug/kmemleak
发送指令和输出信息都是通过以上文件进行的。要访问这个文件,必须先挂载以下文件系统:
# mount -t debugfs nodev /sys/kernel/debug/
kmemleak缺省每10分钟扫描内存一次,找到可疑的内存泄漏会在syslog中写一条记录并提示通过/sys/kernel/debug/kmemleak可以看到更详细的信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
# tail /var/log/messages
Aug 30 14:10:32 bj71s060-vm1 kernel: kmemleak: 1 new suspected memory leaks (see /sys/kernel/debug/kmemleak)
# cat /sys/kernel/debug/kmemleak
unreferenced object 0xffff88003cff1260 (size 32):
comm "softirq", pid 0, jiffies 4297625839
hex dump (first 32 bytes):
f0 a1 9b 35 00 00 00 00 0c 00 00 00 01 00 01 00 ...5............
da c0 70 3a 00 00 00 00 2a 00 00 00 00 00 00 00 ..p:....*.......
backtrace:
[<ffffffff8156585e>] kmemleak_alloc+0x5e/0xe0
[<ffffffff8119c622>] __kmalloc+0x1f2/0x330
[<ffffffffa0056800>] virtqueue_add_buf+0x2c0/0x5e0 [virtio_ring]
[<ffffffffa0140ee7>] start_xmit+0x1a7/0x460 [virtio_net]
[<ffffffff814ac0dc>] dev_hard_start_xmit+0x22c/0x4a0
[<ffffffff814cbce6>] sch_direct_xmit+0x166/0x1d0
[<ffffffff814ac8a8>] dev_queue_xmit+0x268/0x380
[<ffffffff815123b8>] arp_xmit+0x58/0x60
[<ffffffff81512903>] arp_send+0x43/0x50
[<ffffffff81513316>] arp_solicit+0x236/0x2b0
[<ffffffff814b7aa1>] neigh_timer_handler+0xf1/0x370
[<ffffffff81094a5e>] run_timer_softirq+0x20e/0x3e0
[<ffffffff81089b6f>] __do_softirq+0xff/0x260
[<ffffffff8100c48c>] call_softirq+0x1c/0x30
[<ffffffff8100fe6d>] do_softirq+0xad/0xe0
[<ffffffff81089885>] irq_exit+0x95/0xa0
|
也可以手工发起kmemleak的内存扫描:
# echo scan > /sys/kernel/debug/kmemleak
还可以调整kmemleak内存扫描的频率等参数,更多的操作详见手册:
https://www.kernel.org/doc/html/latest/dev-tools/kmemleak.html
参考资料:
http://tekkamanninja.blog.chinaunix.net/uid-26859697-id-5758036.html