-_-b 有些高看我了, 咱又不是搞 linux 内存方向的, 这么大的课题, 能普及些啥 ?!
先从内存泄漏入手吧, 翻了翻之前使用过的工具 mtrace/ memwatch/ valgrind ...
又搜了些, 比如 <Memory Leak Detection in Embedded Systems> http://www.linuxjournal.com/article/6059 // MAGIC2. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/ 好吧, 读读英文, 写读后感给组内也算有个交代, 这些工具的使用其实到处都有文章描述, 这里走一遍加深些体会吧;
1) mtrace -- 定义为 builtin part of glibc which allows detection of memory leaks caused by unbalanced malloc/free calls. 优点是因为是 glibc 自带, 所以不用另外安装啥或编译啥, include 头文件 mcheck.h 就行了 缺点也是因为 glibc 自带, android bionic 上没有, 因此 android 上没法用;
举例 // MAGIC3. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/
#include <iostream> #include <mcheck.h> #include <stdlib.h> int main() { setenv("MALLOC_TRACE","output",1); mtrace(); int *p1=new int; int *p2=new int; int *p3=(int*)malloc(sizeof(int)); int *p4=(int*)malloc(sizeof(int)); delete p1; free(p3); return 0; } g++ -g main.cpp -o test 编完之后 mtrace test output 就能看到泄漏的行号 xuchengliang@comip-ldd22:~/mycodes/mtrace$ mtrace test output - 0x00000000015cb010 Free 8 was never alloc'd 0x7f2a0f0ddfaa - 0x00000000015cb200 Free 9 was never alloc'd 0x7f2a0f198a6d - 0x00000000015cb220 Free 10 was never alloc'd 0x7f2a0f20700c Memory not freed: ----------------- Address Size Caller 0x00000000015cb6c0 0x4 at 0x7f2a0f4c5dad 0x00000000015cb700 0x4 at /home/xuchengliang/mycodes/mtrace/main.cpp:12 再看对动态库的泄漏检测: // MAGIC4. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/ // 由于 glibc 内置, 因此 mstrace 检测动态库内存时, 动态库不用做代码修改, 挺好的, 无源码的第三方库有办法了 #_#
// func.c
#include <stdlib.h> int func() { int *p3=(int*)malloc(sizeof(int)); int *p4=(int*)malloc(sizeof(int)); free(p3); return 0; } gcc -g func.c -fPIC -shared -o libfunc.so// main.c
#include <mcheck.h>
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include "func.h" int main() { setenv("MALLOC_TRACE","output",1); mtrace(); int *p3=(int*)malloc(sizeof(int)); int *p4=(int*)malloc(sizeof(int)); free(p3); func(); sleep(30); return 0; } gcc -g main.c -L. -lfunc -o test 动态库的泄漏行号检测就曲折了一些, 要 addr2line : // MAGIC5. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/ xuchengliang@comip-ldd22:~/mycodes/mtrace$ ./test & [1] 64867 xuchengliang@comip-ldd22:~/mycodes/mtrace$ cat /proc/64867/maps 7f4b816ea000-7f4b816eb000 r-xp 00000000 fc:00 240718549 /home/xuchengliang/mycodes/mtrace/libfunc.so 7f4b816eb000-7f4b818ea000 ---p 00001000 fc:00 240718549 /home/xuchengliang/mycodes/mtrace/libfunc.so 7f4b818ea000-7f4b818eb000 r--p 00000000 fc:00 240718549 /home/xuchengliang/mycodes/mtrace/libfunc.so 7f4b818eb000-7f4b818ec000 rw-p 00001000 fc:00 240718549 /home/xuchengliang/mycodes/mtrace/libfunc.so xuchengliang@comip-ldd22:~/mycodes/mtrace$ mtrace test output Memory not freed: ----------------- Address Size Caller 0x0000000001cee6c0 0x4 at /home/xuchengliang/mycodes/mtrace/main.c:12 0x0000000001cee6e0 0x4 at 0x7f4b816ea725 xuchengliang@comip-ldd22:~/mycodes/mtrace$ addr2line -e libfunc.so 0x725 /home/xuchengliang/mycodes/mtrace/func.c:6 另外动态库还有个限制: 动态库检测时, 行号解析只对C代码有效,CPP代码能检测出泄漏, 但行号解析异常2) memwatch -- 定义 Memwatch is primarily a memory leak detector for C. // MAGIC6. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/
就是说 C++ 不建议使用... android 上又没指望了;
另外这个工具的 readme 中还写明了: Make sure that memwatch.h is included in all of the source code files. If you have an include file that all of the source code uses, you might be able to include memwatch.h from there. 就是说, 进程所依赖的静态库和动态库都必须包含头文件 ... 限制挺多的, 不过 memwatch 的检测原理是通过数据填充实现, 这个思路可以的, 以后搞别的检测可以参考; // MAGIC7. DO NOT TOUCH. BY 冗戈微言 #include <stdlib.h> #include "memwatch.h" int main() { int *p3=(int*)malloc(sizeof(int)); int *p4=(int*)malloc(sizeof(int)); free(p3); return 0; } gcc -o test -DMEMWATCH -DMEMWATCH_STDIO main.c memwatch.c ./test 即生成 memwatch.log ============= MEMWATCH 2.71 Copyright (C) 1992-1999 Johan Lindh ============= Started at Wed Dec 28 16:04:59 2016 Modes: __STDC__ 64-bit mwDWORD==(unsigned int) mwROUNDALLOC==8 sizeof(mwData)==56 mwDataSize==56 Stopped at Wed Dec 28 16:04:59 2016 unfreed: <2> main.c(10), 4 bytes at 0x6e5370 {FE FE FE FE .. .. .. .. .. .. .. .. .. .. .. .. ....} Memory usage statistics (global): N)umber of allocations made: 2 L)argest memory usage : 8 T)otal of all alloc() calls: 8 U)nfreed bytes totals : 4 另外据说 mwLimit() 配置 memory 分配门限挺有用, 可以验证内存分配失败, memwatch.log 会打印出分配失败的行号, 这里不演示了; 3) dmalloc -- // MAGIC8. DO NOT TOUCH. BY 冗戈微言 定义: The debug memory allocation or dmalloc library has been designed as a drop in replacement for the system's malloc, realloc, calloc, free and other memory management routines while providing powerful debugging facilities configurable at runtime. These facilities include such things as memory-leak tracking, fence-post write detection, file/line number reporting, and general logging of statistics. 所以它不是 C库原生的功能, 也不是通过数据填充做分析, 它就是替换 malloc 符号; 所以第三方的动态库没法查了; 虽然它支持 C++, 但是 dmalloc 的使用方式是链接它发布的静态库, 我去 -_-b android 貌似又没法用了; export DMALLOC_OPTIONS=log=dmalloc.log,debug=0x3 // MAGIC9. DO NOT TOUCH. BY 冗戈微言 #include <iostream> #include <stdlib.h> #include "dmalloc.h" int main() { int *p3=(int*)malloc(sizeof(int)); int *p4=(int*)malloc(sizeof(int)); free(p3); return 0; } gcc main.c ../build/lib/libdmalloc.a -o test 环境变量配置 log 名为 dmalloc.log, 里面会写泄漏的行号: // MAGIC10. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/ 1482979835: 3: Dmalloc version '5.5.2' from 'http://dmalloc.com/' 1482979835: 3: flags = 0x3, logfile 'dmalloc.log' 1482979835: 3: interval = 0, addr = 0, seen # = 0, limit = 0 1482979835: 3: starting time = 1482979835 1482979835: 3: process pid = 43505 1482979835: 3: Dumping Chunk Statistics: 1482979835: 3: basic-block 4096 bytes, alignment 8 bytes 1482979835: 3: heap address range: 0x7fe44fabc000 to 0x7fe44fac7000, 45056 bytes 1482979835: 3: user blocks: 1 blocks, 4084 bytes (9%) 1482979835: 3: admin blocks: 10 blocks, 40960 bytes (91%) 1482979835: 3: total blocks: 11 blocks, 45056 bytes 1482979835: 3: heap checked 0 1482979835: 3: alloc calls: malloc 2, calloc 0, realloc 0, free 1 1482979835: 3: alloc calls: recalloc 0, memalign 0, valloc 0 1482979835: 3: alloc calls: new 0, delete 0 1482979835: 3: current memory in use: 4 bytes (1 pnts) 1482979835: 3: total memory allocated: 8 bytes (2 pnts) 1482979835: 3: max in use at one time: 8 bytes (2 pnts) 1482979835: 3: max alloced with 1 call: 4 bytes 1482979835: 3: max unused memory space: 24 bytes (75%) 1482979835: 3: top 10 allocations: 1482979835: 3: total-size count in-use-size count source 1482979835: 3: 4 1 4 1 main.c:7 1482979835: 3: 4 1 0 0 main.c:6 1482979835: 3: 8 2 4 1 Total of 2 1482979835: 3: Dumping Not-Freed Pointers Changed Since Start: 1482979835: 3: not freed: '0x7fe44fac6fe0|s1' (4 bytes) from 'main.c:7' 1482979835: 3: total-size count source 1482979835: 3: 4 1 main.c:7 1482979835: 3: 4 1 Total of 1 1482979835: 3: ending time = 1482979835, elapsed since start = 0:00:00 4) valgrind -- 定义 Valgrind is an instrumentation framework for building dynamic analysis tools. There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. You can also use Valgrind to build new tools. 有个网页: http://www.oschina.net/translate/valgrind-is-not-a-leak-checker 注意看网址, valgrind 有很大的怨气啊, 它不想让大家把它当作泄漏检测工具 ! 也确实, 貌似 memcheck 只是 valgrind 的很小一部分功能; // MAGIC11. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/ 它的工作原理 NB 了 ---- 模拟 CPU ! 1,当要读写内存中的某个字节时,首先检查这个字节对应的A bit。如果该A bit显示该位置是无效位置,memcheck则会报告读写错误 2,内核(core)类似于一个虚拟的CPU环境,这样当内存中的某个字节被加载到真实的CPU时,该字节对应的V bit也会被加载到虚拟的CPU环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则memcheck会检查对应的V bits,如果该值尚未初始化,则会报告使用未初始化内存错误。 既然模拟CPU了, 那所有代码就都不用修改了, 第三方库也是如此; Android 上应该也能用! 确实, android 在 external 下有 valgrind 模块; // MAGIC12. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/ 先在 linux 下测: #include <iostream> #include <stdlib.h> int func() { int *p1=new int; int *p2=new int; int *p3=(int*)malloc(sizeof(int)); int *p4=(int*)malloc(sizeof(int)); delete p1; free(p3); return 0; } g++ -g func.cpp -fPIC -shared -o libfunc.so
#include <iostream>
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include "func.h" int main() { int *p1=new int; int *p2=new int; int *p3=(int*)malloc(sizeof(int)); int *p4=(int*)malloc(sizeof(int)); delete p1; free(p3); func(); return 0; } g++ -g main.cpp -L. -lfunc -o test 编出来后用 valgrind 模拟, 能看到行号 // MAGIC13. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/ valgrind --leak-check=full ./test ==34803== Memcheck, a memory error detector ==34803== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==34803== Using Valgrind-3.11.0.SVN.aosp and LibVEX; rerun with -h for copyright info ==34803== Command: ./test ==34803== HEAP SUMMARY: ==34803== in use at exit: 16 bytes in 4 blocks ==34803== total heap usage: 8 allocs, 4 frees, 32 bytes allocated ==34803== 4 bytes in 1 blocks are definitely lost in loss record 1 of 4 ==34803== by 0x40091C: main (main.cpp:10) ==34803== 4 bytes in 1 blocks are definitely lost in loss record 2 of 4 ==34803== by 0x400938: main (main.cpp:12) ==34803== 4 bytes in 1 blocks are definitely lost in loss record 3 of 4 ==34803== by 0x4A25904: func() (func.cpp:7) ==34803== 4 bytes in 1 blocks are definitely lost in loss record 4 of 4 ==34803== by 0x4A25920: func() (func.cpp:9) ==34803== ==34803== LEAK SUMMARY: ==34803== definitely lost: 16 bytes in 4 blocks ==34803== indirectly lost: 0 bytes in 0 blocks ==34803== possibly lost: 0 bytes in 0 blocks ==34803== still reachable: 0 bytes in 0 blocks ==34803== suppressed: 0 bytes in 0 blocks 但在 Android 上, 同样的 test 程序, 同样使用 valgrind 的 android 版本来模拟, 就无法定到行号层面: // MAGIC14. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/ ==4316== Memcheck, a memory error detector ==4316== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==4316== Using Valgrind-3.11.0.SVN.aosp and LibVEX; rerun with -h for copyright info ==4316== Command: test_valgrind ==4316== ==4316== HEAP SUMMARY: ==4316== in use at exit: 480 bytes in 8 blocks ==4316== total heap usage: 15 allocs, 7 frees, 1,659 bytes allocated ==4316== ==4316== 4 bytes in 1 blocks are definitely lost in loss record 1 of 8 ==4316== at 0x4888408: malloc (in /data/valgrind_lib/vgpreload_memcheck-arm64-linux.so) ==4316== by 0x4A73F4B: operator new(unsigned long) (in /system/lib64/libc++.so) ==4316== by 0x108917: main (in /system/bin/test_valgrind) ==4316== ==4316== LEAK SUMMARY: ==4316== definitely lost: 8 bytes in 2 blocks ==4316== indirectly lost: 0 bytes in 0 blocks ==4316== possibly lost: 104 bytes in 2 blocks ==4316== still reachable: 368 bytes in 4 blocks ==4316== suppressed: 0 bytes in 0 blocks ==4316== Reachable blocks (those to which a pointer was found) are not shown. ==4316== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==4316== ==4316== For counts of detected and suppressed errors, rerun with: -v ==4316== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0) 所以, valgrind 强大归强大, 都模拟 CPU了那当然什么都逃不过它的侦测, 但是在 Android 上还是有使用局限: // MAGIC15. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/ 1, 无法定位到行号 2, 模拟 CPU 的方式, 跑复杂程序会影响性能 3, 不能 attach 到已有进程, 因此不能直接侦测 mediaserver 4, valgrind 需要被侦测进程正常终止, 而 mediaserver 是需要常驻系统的 5, 即使mediaserver 做成比如根据 property 值去正常终止, 测下来在stop media 之后执行 valgrind mediaserver 会有异常 // MAGIC16. DO NOT TOUCH. BY 冗戈微言 http://blog.csdn.net/leonxu_sjtu/
未完待续...