嵌入式Linux/Android疑难杂症工作笔记

嵌入式Linux/Android疑难杂症工作笔记

Android系统Cortex-A57 内核压力测试连续震荡性内存泄漏导致OOM Killer

  • 硬件平台
    公司自研 ARM Cortex-A57 4核 SOC 产品板

  • 软件环境
    系统: Android-P
    Linux内核版本: 4.9
    运行公司内部的kernel_submit内核压力测试脚本,通过内存工具抓取物理内存使用率的情况

  • 现象描述
    系统内存使用率的情况如图,该图是连续24小时Linux 内核压力测试的物理内存使用率的情况,蓝色的点代表不运行压力测试的时候正常的内存使用率,红色的点代表运行内核压力测试时物理内存使用率上下震荡,缓慢上升,在16小时左右发生了low memory killer (oom killer) 导致系统很多后台服务被杀死,内存使用率断崖式下滑
    compare.png

  • Root Cause
    Linux内核某个模块的驱动代码是这样写的,

int bug_driver(struct bug_dev *dev)
{
  struct foo *bar = NULL;
  bar = kzalloc(sizeof(struct foo)) ;
  if (bar) {
    kfree(dev->spec);
    dev->spec = bar;
    bar = NULL;
    return 0;
   } 

    return -1;
}

这段代码看起来没有导致内存泄漏,因为旧的dev->spec指针先kfree()之后,重新保存了kzalloc()分配的内存,下次运行到这里的时候,会先释放旧的,再获取新的内存,不会泄漏。
但是大公司,分工比较细,一个驱动模块可能很多个人进来修改过,另外一个工程师在这个函数上层的某个地方写了一段这样的代码:

struct bug_dev dev;

bug_driver(&dev);

....................一大段代码省略...................................

memset(&dev, 0x00, sizeof(struct bug_dev));
dev.spec = kmalloc(sizeof(struct bug_dev))

因为这段代码直接把通过bug_driver()函数获取的bug_dev->spec指针意外得memset()再重新分配了,因而下次再调用bug_driver(&dev) 函数时,在里面kfree(dev->spec)的指针并不是上一次的kzalloc()的bar指针,上一次kzalloc()的bar指针彻底变成孤魂野鬼未引用对象存在内核里面,久而久之内存使用率就上去了,造成了内存泄漏
该内存泄漏导致oom killer需要16小时才复现,每次泄漏2KB其实很少,而且该内核压力测试脚本本身运行时就有内存使用上下震荡波动,因而几分钟之内的实验是看不出有内存泄漏的

  • 解决过程总结
  1. 刚开始跑压力测试的工程师反馈回来的是oom killer的问题,需要16小时复现, 我只能一边分析log一边拿着脚本继续跑复现。
  2. 我看着log有一大段内存使用率的打印,于是突发奇想用最近学的python matplotlib画图功能把内存使用率的数据都抓出来,画成散点图, 看看能不能拟合出什么,画出来之后就看到了内存使用率缓慢震荡上升的趋势
  3. 在内核中打开了kmemleak工具,改了一下压力测试脚本,每跑一次压力测试之后,就scan一次kememleak的情况,记录log, 通过kmemleak工具打印出来的stack找到了内存泄漏的函数
  4. 一开始看泄漏的函数,发现第一段代码挺正常,有记得free之前的指针,看不出内存泄漏的情况,直到后面加了一点打印,打出每次kzalloc()分配的指针的具体地址和kfree()释放的具体指针地址,发现与差异,然后全局检查代码其它地方的调用,终于发现这套函数调用逻辑的bug所在,不同的工程师写的代码,互相之间不知道对方做了哪些细节调用,遂导致这个内存泄漏。

Android系统Cortex-A57 Android Framework 反复stop/start 导致内存碎片,内存占用率连续震荡性升高最终引发oom killer

  • 硬件平台
    公司自研 ARM Cortex-A57 4核 SOC 产品板

  • 软件环境
    系统: Android-P
    Linux内核版本: 4.9
    运行公司内部的Linux内核压力测试脚本,通过内存工具抓取物理内存使用率的情况

  • 现象描述
    系统内存使用率的情况如图,物理内存使用率上下震荡,缓慢上升,该图是连续24小时Linux 内核压力测试的物理内存使用率的情况,通过缩小范围,修复内存泄露问题之后,发现压力测试反复调用Android Framework的stop / start 命令引发内存使用率升高,导致oom killer。从该图得到的另外的信息是,stop / start 压力测试停止之后(停止震荡上升的后面蓝色直线),内存使用率就维持一个很高的比例,不会降低
    oom_killer.png

  • Root Cause
    跑Android Framework stop /start 反复kill 和启动FW的核心进程,会导致物理内存的碎片化,而Android核心进程启动又需要大块的内存页,从 cat /proc/buddyinfo 可以看出刚开始跑测试,系统还要很多较大块的内存页,结果到后来,大块内存页越来越少,碎片页得不到释放合并,FW 的server 重新启动又不得不去分配超过需求的内存大页,引发内存使用率不断上升,最后oom killer

  • 解决过程总结

  1. 一开始处理这个问题,大家的焦点都在内存泄露上,而且内核驱动确实有内存泄露的情况,所以大家花很多精力去debug内存泄露问题,而忽略内存碎片对内存使用率升高的影响。
  2. 在花费很多精力,解决了一个非常隐蔽的重要内存泄露问题时,内存使用率还是会在压力测试中攀升。
  3. 经过缩小debug范围,发现是反复执行stop / start命令导致的,而且在缩小模拟测试的时候,并没有严重的内核内存泄露发生,并且用户态的旧进程也在不断被kill, 再重新启动新进程,用top -s 10 排序观察用户态进程的状态,似乎也没有看出哪个进程内存使用率
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值