关键词:sqllite、meminfo、slabinfo、alloc_calls、nand、SUnreclaim等等。

下面记录一个由于驱动导致的内存泄漏问题分析过程。

首先介绍问题背景,在一款嵌入式设备上,新使用sqllite库进行数据库操作,在操作数据(大量读写操作)一段时间之后,发生OOM现象。

然后OOM会选择进程kill,即使系统中不剩什么进程,仍然内存紧张。

下面就介绍从上往下查找问题,然后在底层掐住RootCause,进而解决问题的分析过程。

1. 问题初步分析

首先怀疑的是sqllite库问题,在PC进行同样的测试未发现内存泄漏。在另一款参考设备上,进行同样的测试,未发现内存泄漏。

以上测试确保了测试程序、sqllite库等一致,仅交叉工具链和平台不一致。

结论:可以基本肯定sqllite库以及测试程序没有问题,可能的问题包括交叉工具链、平台问题。平台问题更大,所以问题集中到具体平台上进行分析。

疑问点:

1. 为何进程退出后,泄漏的内存没有释放?参见分析,是因为SUnreclaim的slab内存不在进程内存统计范围之内。

2. 是否由于工具链不同导致库函数表现不一致?参见分析1,内存泄漏点在内核驱动中。 参见分析2,经过在RAM上运行sqllite测试;单独测试NAND文件系统,得出泄漏和sqllite无关。

3. 是平台内核导致的泄漏吗?参见分析,确定泄漏在kmalloc-4096这个slab中。

2. 具体平台查找内存泄漏方向

定位内存泄漏按照从大到小的思路,即首先看系统内存哪里泄漏,然后再看进程内存哪里泄漏,最后看哪种内存泄漏。

2.1 分析系统内存

首先通过cat /proc/meminfo,然后分析泄漏点。

从MemFree和MemAvailable看,内存将低了235M和228M。

然后看一下下面内存消耗在哪里?可以看出slab消耗了228M,再细节可以看出SUreclaim消耗了228M。基本确定

结论:确定由于Unreclaim类型的slab泄漏导致的内存泄漏。

疑问点:找出具体哪个slab泄漏了?参见分析,在kmalloc-4096这个slab中。 哪个调用的slab申请?参见分析,通过kmalloc-4096的alloc_calls可以知道调用点。

2.2 分析进程内存

通过pmap -X -p `pidof xxx`来获取进程的地址映射空间,可以分析进程内存细节。

结论:通过下面的对比,可以看出进程本身没有导致内存泄漏。所以内存泄漏虽然有此进程导致,但是泄漏点不在进程中。

2.3 分析slab内存泄漏点

既然确定slab导致的泄漏,那么就需要使用slabtop、/proc/slabinfo以及/sys/kernel/slab来分析。

从下面可以看出泄漏点在kmalloc-4096这个slab,这个slab消耗的内存为250M左右。

然后就是去找slab的调用记录,幸运的是系统在/sys/kernel/slab/*/中提供了alloc_calls和free_calls。

alloc_calls:
pidmap_init+0x4e/0x10c age= pid=
con_init+0xf6/0x21c age= pid=
pcpu_mem_zalloc+0x36/0x74 age= pid=
seq_buf_alloc+0x26/0x54 age= pid=
register_leaf_sysctl_tables+0x74/0x1b0 age= pid=
ubifs_mount+0x68/0x15e8 age= pid=
ubifs_mount+0xdaa/0x15e8 age= pid=
nand_scan_tail+0xa2/0x6a8 age= pid=
ubi_attach_mtd_dev+0x9a/0xc38 age= pid=
sourcesink_bind+0x382/0x4c0 age= pid=
vid_dev_probe+0x32/0x1a0 age= pid=
hantrodec_probe+0x56/0x944 age=// pid=
55898 spinand_cmdfunc+0x236/0x52c age=8997/225988/416514 pid=1-202
flow_cache_cpu_prepare.isra.+0x3c/0x74 age= pid=

free_calls:
<not-available> age= pid=
kvfree+0x2a/0x60 age=// pid=-
ubifs_read_superblock+0x690/0xe14 age= pid=
kobject_uevent_env+0xda/0x580 age=// pid=
uevent_show+0x5e/0xf4 age=// pid=-
skb_free_head+0x2c/0x6c age= pid=

结论:从alloc_calls可以看出spinand_cmdfunc中申请了55898次slab,和系统内存泄漏量基本一致。

2.4 分析驱动内存泄漏

通过objdump -S -l -D vmlinux > vmlinux.txt,然后结合反汇编代码,找到spinand_cmdfunc+0x236可以找到具体点。

805829e2:       e3f26433        bsr             0x803cf248      // 803cf248 <_end+0xff997448>
805829e6: c4004820 lsli r0, r0,
spinand_cmdfunc():
/home/al/deepeye1000/linux/drivers/staging/mt29f_spinand/mt29f_spinand.c:
spinand_program_page(info->spi, state->row, state->col,

所以问题最终指向了mt29f_spinand.c的806行,spinand_program_page()这个函数里面。

分析此驱动代码,可以看出通过devm_kzalloc()申请的内存没有被释放的点。虽然此内存在模块卸载的时候会被自动释放,但是NAND驱动一般不会被卸载。

结论:确定泄漏点在spinand_program_page()中。

3. 旁证测试

基本上找到的问题点,为了验证上述分析做了几个简单的测试。

3.1 在ramfs中进行sqllite数据库操作

既然泄漏点在NAND驱动中,那么避开在NAND中进行读写操作即可。在/tmp目录下,进行数据库操作,作为对比测试。

同样的软件和平台下,运行同样的业务。

结论:在ramfs中进行操作,没有发生内存泄漏。说明泄漏sqllite操作无关。。

3.2 在NAND上进行文件cp、rm等操作

既然泄漏点在NAND,那么不使用数据库读写,纯文件系统读写如何呢?

经过测试,同样发现SUreclaim内存增加导致的泄漏。

结论:内存泄漏跟NAND上文件操作有关。

为什么之前没有发现内存泄漏问题呢?原来主要业务是运行应用,很少对NAND进行写,即使有写也是偶尔的,发现不了内存泄漏。这种情况在频繁的读写、删除操作下比较容易复现。

4. 解决问题

解决的思路就是确保在函数退出的时候,保证wbuf的内存能够得到释放。

diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c
index 2474d88..
--- a/drivers/staging/mt29f_spinand/mt29f_spinand.c
+++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c
@@ -, +, @@ static int spinand_program_page(struct spi_device *spi_nand,
#ifdef CONFIG_MTD_SPINAND_ONDIEECC
unsigned int i, j; - wbuf = devm_kzalloc(&spi_nand->dev, CACHE_BUF, GFP_KERNEL);
+ wbuf = kzalloc(CACHE_BUF, GFP_KERNEL);
if (!wbuf)
return -ENOMEM; @@ -, +, @@ static int spinand_program_page(struct spi_device *spi_nand,
retval = spinand_enable_ecc(spi_nand);
if (retval < ) {
dev_err(&spi_nand->dev, "enable ecc failed!!\n");
- return retval;
+ goto exit;
}
}
#else...
-
- return 0;
+exit:
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ kfree(wbuf);
+#endif
+ return retval;
}

5. 验证测试

基于以上的分析过程,构建测试用例:

1. 进行同样的NAND上sqllite数据操作

2. 同样重复cp、rm操作NAND上文件系统

一个驱动导致的内存泄漏问题的分析过程(meminfo->pmap->slabtop->alloc_calls)的更多相关文章

  1. 一个跨平台的 C++ 内存泄漏检测器

    2004 年 3 月 01 日 内存泄漏对于C/C++程序员来说也可以算作是个永恒的话题了吧.在Windows下,MFC的一个很有用的功能就是能在程序运行结束时报告是否发生了内存泄漏.在Linux下, ...

  2. 使用block的时候,导致的内存泄漏

    明确,只要在block里边用到我们自己的东西,成员变量,self之类的,我们都需要将其拿出来,把它做成弱指针以便之后进行释放. 在ZPShareViewController这个控制器中,由如下代码: ...

  3. 精华阅读第 13 期 |常见的八种导致 APP 内存泄漏的问题

    本期是移动开发精英俱乐部的第13期文章,都是以技术为主,所以这里就不过多的进行赘述了,我们直接看干货内容吧!本文系ITOM管理平台OneAPM整理. 实际项目中的MVVM(积木)模式–序章 导读:开篇 ...

  4. 在Activity中使用Thread导致的内存泄漏

    https://github.com/bboyfeiyu/android-tech-frontier/tree/master/issue-7/%E5%9C%A8Activity%E4%B8%AD%E4 ...

  5. static关键字所导致的内存泄漏问题

    大家都知道内存泄漏和内存溢出是不一样的,内存泄漏所导致的越来越多的内存得不到回收的失手,最终就有可能导致内存溢出,下面说一下使用staitc属性所导致的内存泄漏的问题. 在dalvik虚拟机中,sta ...

  6. vue自定义指令导致的内存泄漏问题解决

    vue的自定义指令是一个比较容易引起内存泄漏的地方,原因就在于指令通常给元素绑定了事件,但是如果忘记了解绑,就会产生内存泄漏的问题. 看下面代码: directives: { scroll: { in ...

  7. 使用HandyJSON导致的内存泄漏问题相关解决方法

    在移动开发中,与服务器打交道是不可避免的,从服务器拿到的接口数据最终都会被我们解析成模型,现在比较常见的数据传输格式是json格式,对json格式的解析可以使用原生的解析方式,也可以使用第三方的,我们 ...

  8. android Context 持有导致的内存泄漏

    Context使用场景 为了防止Activity,Service等这样的Context泄漏于一些生命周期更长的对象,可以使用生命周期更长的ApplicationContext,但是不是所有的Conte ...

  9. 利用RunTime解决由NSTimer导致的内存泄漏

    NSTimer使用场景 用NSTimer来实现每隔一定时间执行制定的任务,例如最常见的广告轮播图,使用NSTimer实现这个功能很简单代码如下 NSTimer *_timer; _timer = [N ...

随机推荐

  1. 收到一个神盾局的offer,怎么样?

    漫威十一年系列总结性的电影<复联4>正在热映,而衍生出的一部和漫威宇宙关联的美剧<神盾局特工>,今年我也在陆陆续续地看.一开始预期的是一部特工加一些科幻或魔幻元素的剧集,就图看 ...

  2. ConcurrentHashMap源码走读

    目录 ConcurrentHashMap源码走读 简介 放入数据 容器元素总数更新 容器扩容 协助扩容 遍历 ConcurrentHashMap源码走读 简介 在从JDK8开始,为了提高并发度,Con ...

  3. TCP 连接与 HTTP 请求的相关问题

    1.现代浏览器在与服务器建立了一个 TCP 连接后是否会在一个 HTTP 请求完成后断开?什么情况下会断开? 默认情况下建立 TCP 连接不会断开,只有在请求报头中声明 Connection: clo ...

  4. Azure 上通过Automation 实现定时开关虚拟机

    更多内容,请关注公众号: Azure Automation 可以提供一些自动化的功能,比如我们可以指定在每天早上6点开启虚拟机,每天晚上8点关闭虚拟机.同时还提供一些基于监控参数的自动化配置.今天的主 ...

  5. Excel映射到实体-easyexcel工具

    来源 项目需要把Excel进行解析,并映射到对象属性,实现类似Mybatis的ORM的效果.使用的方式是自定义注解+POI,这种方式代码复杂而且不易于维护. easyexcel是阿里巴巴开源的一个框架 ...

  6. Git多个远程仓库不同步时的补救办法

    git本地仓库是可以与多个远程仓库关联的,如果想知道怎么配置,请参考Git如何使用多个托管平台管理代码 . 当git remote关联了多个远程仓库时,总会遇到一些问题.今天就遇到了两个远程仓库不一致 ...

  7. mysql 排它锁之行锁、间隙锁、后码锁

    MySQL InnoDB支持三种行锁定 行锁(Record Lock):锁直接加在索引记录上面,锁住的是key. 间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变.间隙锁是针对事务 ...

  8. IDEA的Maven设置阿里镜像

    作为一名.net开发学习java,虽然学习起来没啥难度,但是各种配置实在让人头大. 下面就简单记录下IDEA的Maven设置阿里镜像 进入开发软件的这个设置 点击进入 如图所示 第一个是你安装mave ...

  9. RU/RUR的安装

    RU/RUR的安装方法是仍然使用现有的Opatch技术来安装RU/RUR. 更多常见问题,请参考文档: Release Update and Release Update Revisions for ...

  10. django 做 migrate 时 表已存在的处理

    在开发web的时候,如果是以前已存在的项目,项目下载下来后,为了使用测试库的数据,会直接将整个测试库(如sqlite3)拿到本机来.这种情况下,如果执行的顺序不对,很容易在执行migrate的时候出现 ...