Linux内核内存问题定位

定位linux内存异常问题思路

内存访问异常

  • 空指针访问:常见,易解决
  • “踩内存”:对不属于你的内存进行读写,难点在于踩内存发生的时间与程序崩溃的时间没有任何关系

被踩内存/踩内存的关系分类

  1. 越界踩内存
    定义或申请的内存小了
  2. 使用已释放的内存
    释放掉的内存又被使用
  3. 随机踩内存(最难排查)
    踩坏的位置与使用的内存没有直接关系,每次崩溃的情况可能都不一样

被踩坏内存的位置分类

  1. 堆踩坏
    malloc/new/kmalloc/vmalloc申请的内存
  2. 栈踩坏
  3. 全局变量踩坏

被踩坏内存的内容分类

  1. 字符串
  2. 指针
  3. 随机值

测试手法分类

  1. 测试某些模块
  2. 测试某些场景

多次复现观察的结果分类

  1. 踩坏位置固定
    用MMU保护,产生段错误,抓到发生问题时的调用栈
  2. 踩坏位置的内存类型固定
    位置在变,但总是堆或栈内存被踩坏

Backtrace介绍及使用

程序异常退出时,列出当前函数调用关系

博通使用的glibc支持backtrace;MTK/RTL的MIPS平台使用uclibc,占用内存小但没有backtrace。

ARM平台,进程要接管SIGSEGV信号处理。

需要一些编译选项:

  • -rdynamic
  • -funwind-tables
  • -ffunction-sections

coredump介绍及使用

程序异常终止/崩溃时,操作系统会生成程序的内存快照,保存在一个文件中。

coredump的相关设置:

ulimit -c 查看可生成的core文件的大小,默认为0

ulimit -c {filesize} 设置core文件大小,可以设为unlimited

生成的core文件可以用gdb来调试

ASAN介绍及使用

内存错误探测工具AdressSanitizer,与其它几种Sanitizer一并为LLVM的一部分,也包含在GCC编译器中。

以前常用的valgrind会导致程序性能下降到十几分之一,ASAN则只降低一半。

工作原理

  1. 运行时库
    不是踩内存发生后检查内存被谁踩了,而是通过运行时库libasan.so接管mallocfree函数。
    这两个函数执行完后,ASAN会在操作内存区域的前后多分配一块内存,称为“红区”(RedZone),标记为“中毒”状态,这些区域发生访存时立刻就会被检测出来。

  2. 编译器插桩模块
    加入了ASAN相关的编译选项后,代码中的每一次内存访问操作都会被编译器改动。

  3. 对虚拟内存的划分

    • 主应用内存区(Mem)
      给普通APP代码使用的内存
    • 影子内存区(Shadow)
      仅ASAN可感知的内存区域,将主应用内存区的每8个byte映射成1个byte,记录这一段中可用的byte数,全部可用记作0,全部不可用记作-1

优缺点

  1. 不存在误报可能,只可能漏报
  2. 目前只有博通平台满足ASAN的运行环境
  3. 需要额外消耗内存和CPU,也会增加代码大小
  4. ASAN只能检测出踩到RedZone的情况,如果越界过分离谱,还是会被漏掉
  5. 使用已释放的内存时,由于ASAN的隔离区具有大小限制(FIFO),有些释放较早的内存被踩时不会被检测到

如何使用

GCC4.8之后的版本包含ASAN。

编译选项:

  • -fsanitizer-address
  • -fno-omit-frame-pointer
  • -O1,或者更高的优化级别编译

链接时需要加-lasan

还需要把运行时库libasan.so拷贝进设备文件系统。