1. slab debug定位kernelspace内存泄露的方法¶
以下为辅助定位内存泄漏的手段,主要是针对内存kmalloc。
-
在kernel中打开如下两个config
-
查看meminfo
如下图,若SUnreclaim一直增加即可初步判断为内核中kmalloc之后没有释放,导致内存泄漏。
-
slabinfo对比
开机启动时使用
cat /proc/slabinfo
备份,出现泄露后再使用cat/proc/slabinfo
,将两者进行对比。 -
分析上图数据
图中kmalloc-8192的次数增加较多,在
/sys/kernel/slab/kmalloc-8192/alloc_calls
中查看调用kmalloc-8192的symbol和他调用的次数,定位到出现泄露的function。
2. kmemleak--Kernel space内存泄露分析工具¶
2.1. kernel mode的memory申请¶
SDK里申请kernel mode的memory,主要有两种方式:
-
vmalloc
分配大块内存,走budy system;通过
cat/proc/vmallocinfo
可以统计;注:
Cat vmallocinfo
,如果有buffer块数一直在增加,则是内存泄露。 -
kmalloc/kmem_cache_create
分配小于pagesize,走slab机制;通过
cat /proc/meminfo
里的slab字段可以统计。注:如果怀疑有kernel mode内存泄露,运行应用过程中每间隔一段时间
cat/proc/meminfo
留意slab。如果一直在增加,大概率有内存泄露的可能。具体模块的函数泄露可以用kmemleak debug。
2.2. 检查内存泄漏的方法 -- Kernel Space¶
Linux kernel 2.6.31后的版本,提供了 KMEMLEAK 的选项,可以拿来测试 kernel modules是否有 memory leakage,用法整理如下。
-
修改
.config
设定 KMEMLEAK,重新编译 kernel,重烧 image。摘录
.config
如下:# # Memory Debugging # CONFIG_HAVE_DEBUG_KMEMLEAK=y CONFIG_DEBUG_KMEMLEAK=y CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4096
-
确认板子上的 kernel 已经提供 kmemleak的功能
# mount -t debugfs nodev /sys/kernel/debug/ # cat /sys/kernel/debug/kmemleak
注:若存在 kmemleak,表示 kernel 已经支援 kmemleak
-
用法举例
cid:image003.png\\@01D2DB8B.2E89DE10
注:若沒有手动执行 scan,系统每10分钟自动扫描一次 memory leakage.
-
实际测试 Driver memory leakage 的方法
# mount -t debugfs nodev /sys/kernel/debug/ # echo clear > /sys/kernel/debug/kmemleak # run_your_driver_test_here # echo scan > /sys/kernel/debug/kmemleak # cat /sys/kernel/debug/kmemleak
3. dmalloc--查usr space内存泄露¶
3.1. 内存泄漏排查¶
程序运行过程meminfo的Memfree不断减少,echo 3 >/proc/sys/vm/drop_cache
也不返回内存,则可能存在内存泄露。
排除kernel space内存泄露后,隔段时间再观察cat /proc/PID/smap
每个so的堆,可以找到内存泄漏的so。
准确定位内存泄露需使用dmalloc工具。
3.2. 交叉编译¶
-
http://dmalloc.com/可下载dmalloc-5.5.2.gz
解压,修改configure文件,ac_cv_page_size=0 改为ac_cv_page_size=12
-
./configure --prefix=/home/cbm/workplace/dmalloc/output/ --exec-prefix=/home/cbm/workplace/dmalloc/output/CC=arm-buildroot-linux-uclibcgnueabihf-gcc --host=arm-linux CXX=arm-buildroot-linux-uclibcgnueabihf-g++ AR=arm-buildroot-linux-uclibcgnueabihf-ar LD=arm-buildroot-linux-uclibcgnueabihf-ld --enable-threads --enable-shlib
-
auto脚本对交叉编译支持不够完善,生成的Makefile有少数命令需替换
如
ld-G,ar cr
需改为$(LD) -G
和$(AR) cr
-
make CC=arm-buildroot-linux-uclibcgnueabihf-gcc CXX=arm-buildroot-linux-uclibcgnueabihf-g++ AR=arm-buildroot-linux-uclibcgnueabihf-ar LD=arm-buildroot-linux-uclibcgnueabihf-ld
-
make install
,即可在prefix目录生成bin include和lib。
3.3. 环境变量设置¶
dmalloc工具进行环境变量设置,如:dmalloc -l /tmp/dmalloc.log all
,即输出所有信息并存入dmalloc.log
文件;
# dmalloc -l /tmp/dm.log all DMALLOC_OPTIONS=debug=0xcf4ed2b,log=/tmp/dm.log ` export DMALLOC_OPTIONS `
3.4. 库的应用¶
-
在主代码中添加
#include <dmalloc.h>
-
更改Makefile,添加
CFLAGS += -Iinclude LDFLAGS += -L$(TOP)/dmalloc -ldmalloc
-
重新build需要detect的应用
3.5. Leak检测¶
-
通过dmalloc所生产的变量DMALLOC_OPTIONS,手动或自动export到系统中
-
执行需detect的应用
-
查看变量DMALLOC_OPTIONS中所定义的导出文件内容
-
退出需detect的应用
-
查看变量DMALLOC_OPTIONS中所定义的导出文件内容
-
上述过程所查看的导出文件存在
not freed:
字串,则为leak点
4. 程序运行内存占用¶
以公板运行mixer为例,详细说明内存如何分配。
4.1. 实验设备¶
335+imx307 sensor
4.2. 实验场景¶
(典型案例)mixer路4路流:JPEG + 主码流 + 子码流 + MD算法
4.3. 实验基础¶
SDK里kernel space 下ko有两种内存申请方式:
-
vmalloc
分配大块内存,走budysystem;通过
cat/proc/vmallocinfo
可以统计; -
kmalloc/kmem_cache_create
分配小于pagesize,走slab机制;通过
cat/proc/meminfo
里的slab字段可以统计。内核中常见的还有如alloc_pages/__get_free_page,只能通过free统计,但目前看起来只有emmc会调用。
-
Ioremap
I/O内存资源的物理地址映射到核心虚地址空间,不占用物理空间;
-
PageTable
用于将内存的虚拟地址翻译成物理地址,/proc/meminfo中的PageTables统计了Page Table所占用的内存大小。
-
KernelStack
每一个用户线程都会分配一个kernel stack(内核栈),它是kernel消耗的内存。统计值是/proc/meminfo的KernelStack。
Userspace中常见malloc,小于128K走c标准库的brk,大于128K走mmap,但对于内核来说,一旦发生缺页中断,都是走pageallocater。可以通过procmem工具统计实际占用的物理空间(实际上就是把cat/proc/PID/smap里的RSS/PSS之和)。
4.4. 实验步骤¶
-
开机加载完ko后,drop cache再看
cat proc/meminfo
和cat /proc/vmallocinfo
-
运行mixer后,drop cache再看
cat proc/meminfo
、cat /proc/vmallocinfo
、cat/proc/’PID’/status
和./procmem PID
-
对比工具分析运行前后的vmalloc增加部分
4.5. 总结¶
free减少 = 16836 – 8888 = 7948K
Vmalloc增加:547page = 2188K
lab增加:6328 – 4792 = 1536K
PageTable+KernelStack增加:536+116 – (352+52) = 248K
应用代码段+ so 代码段 + 栈 + ro段+堆 = 3732K (Pss)
2188 + 1536 + 248 + 3732 = 7704K,Linux kernel并没有统计所有的内存分配,kernel动态分配的内存中有一部分没有计入/proc/meminfo
。我们只能大概的统计出来mixer的内存占用。
5. procmem procrank--usr space内存分析工具¶
源码下载:GitHub - sunao2002002/mem_proc
-
解压后用下文内容替换
CMakeLists.txt
.SET(CMAKE_C_COMPILER "arm-buildroot-linux-uclibcgnueabihf-gcc") SET(CMAKE_CXX_COMPILER "arm-buildroot-linux-uclibcgnueabihf-g++") cmake_minimum_required(VERSION 2.8) PROJECT (mem_proc) file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/libpagemap/*.c") add_definitions ( -D_LARGEFILE64_SOURCE ) include_directories (${PROJECT_SOURCE_DIR}/libpagemap/include) add_library(pagemap ${SOURCES}) add_executable(procmem ${PROJECT_SOURCE_DIR}/procmem/procmem.c) target_link_libraries(procmem pagemap) add_executable(procrank ${PROJECT_SOURCE_DIR}/procrank/procrank.c) target_link_libraries(procrank pagemap)
-
cd mem_proc
-
mkdir out
-
cd out
-
cmake ..
-
make 即可生成procmem和procrank
VSS:Virtual Set Size,虚拟内存耗用内存,包括共享库的内存
RSS:Resident Set Size,实际使用物理内存,包括共享库
PSS:Proportional Set Size,实际使用的物理内存,共享库按比例分配
USS:Unique Set Size,进程独占的物理内存,不计算共享库,也可以理解为将进程杀死能释
https://wx.comake.online/doc/doc/Version_Tiramisu_DLS00V011-20220308/customer/faq/m6-ipc/memory.html
另外可参考
l https://blog.arstercz.com/what-to-do-when-linux-slab-memory-leak/