内存相关


1. slab debug定位kernelspace内存泄露的方法

以下为辅助定位内存泄漏的手段,主要是针对内存kmalloc。

  1. 在kernel中打开如下两个config

  2. 查看meminfo

    如下图,若SUnreclaim一直增加即可初步判断为内核中kmalloc之后没有释放,导致内存泄漏。

  3. slabinfo对比

    开机启动时使用cat /proc/slabinfo备份,出现泄露后再使用cat/proc/slabinfo,将两者进行对比。

  4. 分析上图数据

    图中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,主要有两种方式:

  1. vmalloc

    分配大块内存,走budy system;通过cat/proc/vmallocinfo可以统计;

    注:Cat vmallocinfo,如果有buffer块数一直在增加,则是内存泄露。

  2. 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,用法整理如下。

  1. 修改 .config 设定 KMEMLEAK,重新编译 kernel,重烧 image。

    摘录 .config 如下:

    #
    # Memory Debugging
    #
    CONFIG_HAVE_DEBUG_KMEMLEAK=y
    CONFIG_DEBUG_KMEMLEAK=y
    CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4096
    
  2. 确认板子上的 kernel 已经提供 kmemleak的功能

    # mount -t debugfs nodev /sys/kernel/debug/
    
    # cat /sys/kernel/debug/kmemleak
    

    注:若存在 kmemleak,表示 kernel 已经支援 kmemleak

  3. 用法举例

    cid:image003.png\\@01D2DB8B.2E89DE10

    注:若沒有手动执行 scan,系统每10分钟自动扫描一次 memory leakage.

  4. 实际测试 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. 交叉编译

  1. http://dmalloc.com/可下载dmalloc-5.5.2.gz

    解压,修改configure文件,ac_cv_page_size=0 改为ac_cv_page_size=12

  2. ./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

  3. auto脚本对交叉编译支持不够完善,生成的Makefile有少数命令需替换

    ld-G,ar cr需改为$(LD) -G$(AR) cr

  4. 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

  5. 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. 库的应用

  1. 在主代码中添加#include <dmalloc.h>

  2. 更改Makefile,添加CFLAGS += -Iinclude LDFLAGS += -L$(TOP)/dmalloc -ldmalloc

  3. 重新build需要detect的应用


3.5. Leak检测

  1. 通过dmalloc所生产的变量DMALLOC_OPTIONS,手动或自动export到系统中

  2. 执行需detect的应用

  3. 查看变量DMALLOC_OPTIONS中所定义的导出文件内容

  4. 退出需detect的应用

  5. 查看变量DMALLOC_OPTIONS中所定义的导出文件内容

  6. 上述过程所查看的导出文件存在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. 实验步骤

  1. 开机加载完ko后,drop cache再看cat proc/meminfocat /proc/vmallocinfo

  2. 运行mixer后,drop cache再看cat proc/meminfocat /proc/vmallocinfocat/proc/’PID’/status./procmem PID

  3. 对比工具分析运行前后的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内存分析工具

源码下载:https://github.com/sunao2002002/mem_proc.git

  1. 解压后用下文内容替换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)
    
  2. cd mem_proc

  3. mkdir out

  4. cd out

  5. cmake ..

  6. make 即可生成procmem和procrank

VSS:Virtual Set Size,虚拟内存耗用内存,包括共享库的内存

RSS:Resident Set Size,实际使用物理内存,包括共享库

PSS:Proportional Set Size,实际使用的物理内存,共享库按比例分配

USS:Unique Set Size,进程独占的物理内存,不计算共享库,也可以理解为将进程杀死能释