Linux内核内存问题定位
¶定位linux内存异常问题思路
¶内存访问异常
- 空指针访问:常见,易解决
- “踩内存”:对不属于你的内存进行读写,难点在于踩内存发生的时间与程序崩溃的时间没有任何关系
¶被踩内存/踩内存的关系分类
- 越界踩内存
定义或申请的内存小了 - 使用已释放的内存
释放掉的内存又被使用 - 随机踩内存(最难排查)
踩坏的位置与使用的内存没有直接关系,每次崩溃的情况可能都不一样
¶被踩坏内存的位置分类
- 堆踩坏
malloc/new/kmalloc/vmalloc
申请的内存 - 栈踩坏
- 全局变量踩坏
¶被踩坏内存的内容分类
- 字符串
- 指针
- 随机值
¶测试手法分类
- 测试某些模块
- 测试某些场景
¶多次复现观察的结果分类
- 踩坏位置固定
用MMU保护,产生段错误,抓到发生问题时的调用栈 - 踩坏位置的内存类型固定
位置在变,但总是堆或栈内存被踩坏
¶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则只降低一半。
¶工作原理
-
运行时库
不是踩内存发生后检查内存被谁踩了,而是通过运行时库libasan.so
接管malloc
和free
函数。
这两个函数执行完后,ASAN会在操作内存区域的前后多分配一块内存,称为“红区”(RedZone),标记为“中毒”状态,这些区域发生访存时立刻就会被检测出来。 -
编译器插桩模块
加入了ASAN相关的编译选项后,代码中的每一次内存访问操作都会被编译器改动。 -
对虚拟内存的划分
- 主应用内存区(Mem)
给普通APP代码使用的内存 - 影子内存区(Shadow)
仅ASAN可感知的内存区域,将主应用内存区的每8个byte映射成1个byte,记录这一段中可用的byte数,全部可用记作0
,全部不可用记作-1
。
- 主应用内存区(Mem)
¶优缺点
- 不存在误报可能,只可能漏报
- 目前只有博通平台满足ASAN的运行环境
- 需要额外消耗内存和CPU,也会增加代码大小
- ASAN只能检测出踩到RedZone的情况,如果越界过分离谱,还是会被漏掉
- 使用已释放的内存时,由于ASAN的隔离区具有大小限制(FIFO),有些释放较早的内存被踩时不会被检测到
¶如何使用
GCC4.8之后的版本包含ASAN。
编译选项:
-fsanitizer-address
-fno-omit-frame-pointer
-O1
,或者更高的优化级别编译
链接时需要加-lasan
。
还需要把运行时库libasan.so拷贝进设备文件系统。