基于下面网站内容分析
https://blog.csdn.net/lqxandroid2012/article/details/79799844
一、libumem内存检查
https://www.codenong.com/8287649/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
环境变量
UMEM_DEBUG
此变量包含逗号分隔的选项列表。忽略无法识别的选项。可能的选项包括:
审计【=帧】
此选项启用记录审计信息,包括线程ID、高分辨率时间戳和每次分配上最后一个操作(分配或空闲)的堆栈跟踪。如果启用了事务日志记录(请参阅UMEM_LOGGING),则也会记录此审计信息。
frames参数设置审计结构中记录的堆栈帧数。帧的上限由实现定义。如果请求较大的值,则使用上界。
如果不指定frames或者frames不是整数,则使用默认值15。
此选项还启用“guards”选项。
内容【=计数】
如果启用了审计和内容记录(请参见UMEM_LOGGING),则每个缓冲区的第一个计数字节在释放时将被记录。如果缓冲区小于计数字节,则将其全部记录。
如果不指定count或者count不是整数,则默认为256。
违约
此选项等同于audit、contents、guards。
守卫
此选项允许用特殊模式填充已分配和释放的缓冲区,以帮助检测未初始化数据和以前释放的缓冲区的使用。它还在每个包含0xfeedfacefeedfaceULL的缓冲区之后启用一个8字节的红区。
当对象被释放时,它被填充为0xdeadbeef。分配对象时,校验0xdeadbeef模式,替换为0xbadcafe。每次分配或释放缓冲区时,都会检查红区。
对于具有构造函数或析构函数或析构函数的缓存,umem_cache_alloc(3MALLOC)和umem_cache_free(3MALLOC)分别应用缓存的构造函数和析构函数。而不是缓存构造对象。析构函数中是否存在断言( 3C )来验证缓冲区是否处于构造状态,可以用来检测任何在错误状态下返回的对象。详情请参见umem_cache_create(3MALLOC)。
冗长的
库在中止之前将错误描述写入标准错误。这些消息没有本地化。
UMEM_LOGGING
要启用,应将此变量设置为以逗号分隔的内存日志列表。可用的日志包括:
事务[=size]
如果设置了审计调试选项(请参阅UMEM_DEBUG),则先前事务的审计结构将输入到此日志中。
内容【=大小】
如果设置了审计调试选项,则对象的内容将在释放时记录到此日志中。
如果未设置“contents”调试选项,则每个释放的缓冲区将保存256字节。
失败【=大小】
每次失败的分配都会记录在此日志中。
对于这些选项中的任何一种,如果未指定size,则使用默认值64k。size参数必须是整数,可以与K、M、G或T限定,分别指定千字节、兆字节、千兆字节或TB。
未列出的日志或大小为0或无效的日志将被禁用。
如果在初始化期间无法分配请求的存储量,则禁用该日志。
二、glibc内存检测
1
2
3
reference:
https://www.cnblogs.com/arnoldlu/p/10827884.html
https://murphypei.github.io/blog/2019/01/linux-heap glibc内存分配
Glibc中自带了一些Heap consistency checking机制。
** (1) MALLOC_CHECK_环境变量(double free)**
1
检测内容:重复释放、头覆盖、尾覆盖。
-
MALLOC_CHECK_=3 ./test
不能检测内存未释放1 2 3 4
0 - 不产生错误信息,也不中止这个程序 1 - 产生错误信息,但是不中止这个程序 2 - 不产生错误信息,但是中止这个程序 3 - 产生错误信息,并中止这个程序
验证:double free场景
1 2 3 4 5 6 7 8 9 10 11
#include <stdio.h> #include <stdlib.h> int main() { int *p = (int *)malloc(10); int a; free(p); a = 0; free(p); return 0; }
结果:
1 2 3 4 5 6
[root@localhost mem_leak_way]# LIBC_FATAL_STDERR_=1 MALLOC_CHECK_=3 ./test free(): invalid pointer Aborted [root@localhost mem_leak_way]# LIBC_FATAL_STDERR_=1 MALLOC_CHECK_=4 ./test free(): double free detected in tcache 2 Aborted
(2) mcheck
与MALLOC_CHECK环境量功能差不多,但是需要重新编译代码。
1
检测内容:重复释放、头覆盖、尾覆盖。
-
mcheck是Glibc中的堆内存一致性检查机制.
不能检测内存未释放验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#include <stdio.h> #include <stdlib.h> #include <mcheck.h> ///// 头文件 int main() { //int *p = (int *)malloc(10); int *p1 = (int *)malloc(10); ///////再在要开始检查的地方加上//////////// if (mcheck(NULL) != 0) { fprintf(stderr, "mcheck() failed\n"); exit(EXIT_FAILURE); } int a; //free(p); a = 0; //free(p); return 0; }
结果:
double free场景:
1 2 3
[root@localhost mem_leak_way]# ./bug memory clobbered before allocated block Aborted
(3)mprobe
1
检查:double free、 头覆盖、尾覆盖;与mcheck一致;
1
2
#include <mcheck.h> //函数模型
enum mcheck_status mprobe(void *ptr);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <malloc.h>
#include <mcheck.h>
#include <errno.h>
#include <string.h>
void abortfun(enum mcheck_status mstatus)
{
switch(mstatus) {
case MCHECK_FREE: fprintf(stderr, "Block freed twice.\n"); break;
case MCHECK_HEAD: fprintf(stderr, "Memory before the block was clobbered.\n"); break;
case MCHECK_TAIL: fprintf(stderr, "Memory after the block was clobbered.\n"); break;
default: fprintf(stderr, "Block is fine.\n");
}
}
void main(void)
{
char *s = NULL;
if(mcheck(abortfun) != 0) //使用时,注册处理函数
{
fprintf(stderr, "mcheck:%s\n", strerror(errno));
return;
}
s = malloc(32);
mprobe(s);------------------------------正确
mprobe(s-1);----------------------------错误,返回MCHECK_HEAD错误类型。
mprobe(s+32);---------------------------错误,返回MCHECK_HEAD错误类型。
free(s);
}
(4)-lmcheck编译选项
在编译的时候加上-lmcheck,不需要修改代码就可以对malloc()/free()进行检查; 但是需要重新编译代码!!!!
1
检查:double free、 头覆盖、尾覆盖。 与mcheck一致;
(5)mtrace (mtrace、muntrace、MALLOC_TRACE)
1
检查: 重复释放、泄漏
mtrace()和muntrace()函数分别在程序中打开和关闭对内存分配调用进行跟踪的功能。
这两个函数要与环境变量MALLOC_TRACE搭配使用,该变量定义了写入跟踪信息的文件名。
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdlib.h>
#include <stdio.h>
#include <mcheck.h> //需要的头文件
int main(int argc, char **argv)
{
mtrace(); //开始trace
char * p = malloc(100);
p = malloc(1000);
free(p);
muntrace(); //结束trace
return 0;
}
1
2
gcc -g test-mtrace.c -o test-mtrace
export MALLOC_TRACE=/home/test/mtrace.log
mtrace记录的申请释放流程:
1
2
3
4
5
= Start
@ ./test-mtrace:[0x400684] + 0xc076a0 0x64
@ ./test-mtrace:[0x400692] + 0xc07710 0x3e8
@ ./test-mtrace:[0x4006a2] - 0xc07710
= End
配套mtrace命令可以分析具体的泄漏位置。
1
2
3
4
5
6
[root@localhost test]# mtrace test-mtrace mtrace.log
Memory not freed:
-----------------
Address Size Caller
0x0000000000c076a0 0x64 at /home/test/test-mtrace.c:8
三、gperftools(tcmalloc)内存检查机制
1
2
3
4
reference:
http://goog-perftools.sourceforge.net/doc/tcmalloc.html
https://blog.csdn.net/breaksoftware/article/details/81234967
http://www.cppblog.com/markqian86/archive/2018/08/24/215870.aspx
1
2
检查内容: 内存泄漏 heap-check
增强部分: 可以找到程序的内存使用热点。 heap-porfiler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@localhost test]# gcc test.c -ltcmalloc -g -o test-tcmalloc
[root@localhost test]# HEAPCHECK=normal ./test-tcmalloc &
[1] 42880
[root@localhost test]# No live heap object at 0x7f8366695168 to ignore
WARNING: Perftools heap leak checker is active -- Performance may suffer
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 100 bytes in 2 objects
The 2 largest leaks:
*** WARNING: Cannot convert addresses to symbols in output below.
*** Reason: Cannot find 'pprof' (is PPROF_PATH set correctly?)
*** If you cannot fix this, try running pprof directly.
Leak of 50 bytes in 1 objects allocated from:
@ 400657
@ 7f83662a96a3
@ 40052e
Leak of 50 bytes in 1 objects allocated from:
@ 4005f8
@ 400660
@ 7f83662a96a3
@ 40052e
If the preceding stack traces are not enough to find the leaks, try running THIS shell command:
pprof ./test-tcmalloc "/tmp/test-tcmalloc.42880._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --gv
If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1
If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find lea
Exiting with error code (instead of crashing) because of whole-program memory leaks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
TCMALLOC_DEBUG=<level> 调试级别,取值为1-2
MALLOCSTATS=<level> 设置显示内存使用状态级别,取值为1-2
HEAPPROFILE=<pre> 指定内存泄露检查的数据导出文件
HEAPCHECK=<type> 堆检查类型,type=normal/strict/draconian
TcMalloc库还可以进行内存泄露的检查,使用这个功能有两种方法:
1)将tcmalloc库链接到程序中,注意应该将tcmalloc库最后链接到程序中;
2)设置LD_PRELOAD=”libtcmalloc.so”/HEAPCHECK=normal,这样就不需重新编译程序
打开检查功能,有两种方式可以开启泄露检查功能:
1) 使用环境变量,对整个程序进行检查: HEAPCHECK=normal /bin/ls
2) 在源代码中插入检查点,这样可以控制只检查程序的某些部分,代码如下:
HeapProfileLeakCheckerchecker("foo"); // 开始检查
Foo(); // 需要检查的部分
assert(checker.NoLeaks()); // 结束检查
调用checker建立一个内存快照,在调用checker.NoLeaks建立另一个快照,然后进行比较,如果内存有增长或者任意变化,NoLeaks函数返回false,并输出一个信息告诉你如何使用pprof工具来分析具体的内存泄露。
执行内存检查:
#LD_PRELOAD=libtcmalloc.so HEAPCHECK=strict HEAPPROFILE=memtm ./a.out
执行完成后会输出检查的结果,如果有泄露,pprof会输出泄露多少个字节,有多少次分配,也会输出详细的列表指出在什么地方分配和分配多少次。
比较两个快照:
#pprof --base=profile.0001.heap 程序名 profile.0002.heap
已知内存泄漏时,关闭内存泄露检查的代码:
void *mark =HeapLeakChecker::GetDisableChecksStart();
<leaky code> //不做泄漏检查的部分
HeapLeakChecker::DisableChecksToHereFrom(mark);
注:在某些libc中程序可能要关闭检查才能正常工作。
注:不能检查数组删除的内存泄露,比如:char *str = new char[100]; delete str;。
四、jemalloc内存检查机制
1
2
3
4
reference:
https://zhuanlan.zhihu.com/p/48957114
https://blog.intzero.net/tools/jemalloc.html
https://github.com/jemalloc/jemalloc/wiki/Use-Case:-Leak-Checking
1
2
MALLOC_CONF=prof_leak:true,lg_prof_sample:0,prof_final:true \
LD_PRELOAD=${JEMALLOC_PATH}/lib/libjemalloc.so.2 w
1
2
<jemalloc>: Leak summary: 267184 bytes, 473 objects, 20 contexts
<jemalloc>: Run jeprof on "jeprof.19678.0.f.heap" for leak detail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jeprof --show_bytes `which w` jeprof.19678.0.f.heap
Using local file /usr/bin/w.
Using local file jeprof.19678.0.f.heap.
Welcome to jeprof! For help, type 'help'.
(jeprof) top
Total: 267184 B
258032 96.6% 96.6% 258032 96.6% _3_2_5
3616 1.4% 97.9% 3616 1.4% _nl_intern_locale_data
2048 0.8% 98.7% 2208 0.8% __tzfile_read
1024 0.4% 99.1% 1024 0.4% getpwnam
1024 0.4% 99.5% 1072 0.4% getpwuid
448 0.2% 99.6% 448 0.2% __gconv_lookup_cache
384 0.1% 99.8% 384 0.1% getutent
224 0.1% 99.9% 224 0.1% strdup
160 0.1% 99.9% 160 0.1% __tzstring
128 0.0% 100.0% 3760 1.4% _nl_load_locale_from_archive
48 0.0% 100.0% 48 0.0% get_mapping
1
jeprof --show_bytes --pdf `which w` jeprof.19678.0.f.heap > w.pdf
五、valgrind内存检查机制
https://blog.csdn.net/CODINGCS/article/details/50357263
六、ASan内存检查机制
1
2
reference:
https://blog.csdn.net/lyj22/article/details/109459428?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
export ASAN_OPTIONS=halt_on_error=0 //使进程检测出内存错误的时候别退出
export ASAN_OPTIONS=alloc_dealloc_mismatch=0 //不检测内存不匹配的情况,例如 new [] 与delete point 不匹配
asan的选项很多,可以根据需要设置ASAN_OPTIONS的选项,其他选项可以自行百度
export LD_PRELOAD=/usr/lib64/libasan.so.5.0.0 //预加载运行库,替换系统库libc中内存分配函数
LD_PRELOAD是Linux系统的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库,这个环境变量是必须的,因为libasan.so.5.0.0会替换掉libc中malloc和free函数的实现,所以需要将该库进行预加载。
至此,所有工作完成,直接运行所需要测试的程序即可,程序会打印出检测出来的内存问题。
七、Memwatch内存检查机制
1
2
3
reference:
https://blog.csdn.net/shangguanyunlan/article/details/68951525
https://blog.csdn.net/embrace_forest/article/details/52048694?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control
memwatch根本是不需要安装的,因为它只是一组C程序代码。需要做的是:
1、在代码中加入 memwatch.c 和 memwatch.h,一起编译、链接
2、编译时定义宏 DMEMWATCH、DMW_STDIO,即在编译程序时加上选项-DMEMWATCH -DMW_STDIO
5.1 MemWatch的内存处理
memWatch将全部分配的内存用0xFE填充,所以,假设你看到错误的数据是用0xFE填充的,那就是你没有初始化数据。例外是calloc(),它会直接把分配的内存用0*填充。
MemWatch将全部已释放的内存用0xFD填充(zapped with 0xFD).假设你发现你使用的数据是用0xFD填充的,那你就使用的是已释放的内存。在这样的情况,注意MemWatch会马上把一个“释放了的块信息“填在释放了的数据前。这个块包含关于内存在哪儿释放的信息,以可读的文本形式存放,格式为*“FBI
为了帮助跟踪野指针的写情况,MemWatch能提供no-mans-land(NML)内存填充。no-mans-land将使用0xFC填充.当no-mans-land开启时,MemWatch转变释放的内存为NML填充状态。
5.2 初始化和结束处理
一般来说,在程序中使用MemWatch的功能,须要手动加入mwInit()进行初始化,并用相应的mwTerm ()进行结束处理。
当然,假设没有手动调用mwInit(),MemWatch能自己主动初始化.假设是这样的情形,memwatch会使用atext()注冊mwTerm()用于atexit-queue.对于使用自己主动初始化技术有一个告诫;假设你手动调用atexit()以进行清理工作,memwatch可能在你的程序结束前就终止。为了安全起见,请显式使用mwInit()和mwTerm().
涉及的函数主要有:
mwInit() mwTerm() mwAbort()
5.3 MemWatch的I/O*操作
对于一般的操作,MemWatch创建memwatch.log文件。有时,该文件不能被创建;MemWatch会试图创建memwatNN.log文件,NN在01~99之间。
假设你不能使用日志,或者不想使用,也没有问题。仅仅要使用类型为“void func(int c)”的參数调用mwSetOutFunc(),然后全部的输出都会按字节定向到该函数.
当ASSERT或者VERIFY失败时,MemWatch也有Abort/Retry/Ignore处理机制。默认的处理机制没有I/O操作,可是会自己主动中断程序。你能够使用不论什么其它Abort/Retry/Ignore的处理机制,仅仅要以參数“void func(int c)”调用mwSetAriFunc()。后面在1.2使用一节会具体解说。
涉及的函数主要有:
mwTrace() mwPuts() mwSetOutFunc() mwSetAriFunc()
mwSetAriAction() mwAriHandler() mwBreakOut()
八、Dr.Memory内存检查机制
重量级内存监测工具之一,用于检测如未初始化内存访问,越界访问,已释放内存访问,double free,memory leak以及Windows上的handle leak, GDI API usage error等。它支持Windows, Linux和Mac操作系统, IA-32和AMD64平台,和其它基于binary instrumentation的工具一样,它不需要改目标程序的binary。有个缺点是目前只针对x86上的32位程序。貌似目前正在往ARM上port。其优点是对程序的正常执行影响小,和Valgrind相比,性能更好。官网为http://www.drmemory.org/。Dr. Memory基于DynamioRIO Binary Translator。原始代码不会直接运行,而是会经过translation后生成code cache,这些code cache会调用shared instrumentation来做内存检测。
1
2
3
4
5
6
7
下载后即可直接使用。首先编译要检测的测试程序:
$ g++ -m32 -g -Wall problem.cpp -o bug -fno-inline -fno-omit-frame-pointer
(在64位host上编译32位程序需要安装libc6-dev-i386和g++-multilib)
然后把Dr.Memory的bin加入PATH,如:
$ export PATH=/home/jzj/tools/DrMemory-Linux-1.8.0-8/bin:$PATH
之后就可以使用Dr.Memory启动目标程序:
\$ drmemory – ./bug
九、Electric Fence内存检查机制
Electric Fence主要用于追踪buffer overflow的读和写。它利用硬件来抓住越界访问的指令。其原理是为每一次内存申请额外申请一个page或一组page,然后把这些buffer范围外的page设为不可读写。这样,如果程序访问这些区域,由于页表中这个额外page的权限是不可读写,会产生段错误。那些被free()释放的内存也会被设为不可访问,因此访问也会产生段错误。因为读写权限以页为单位,所以如果多的页放在申请内存区域后,可防止overflow。如果要防止underflow,就得用环境变量EF_PROTECT_BELOW在区域前加保护页。因为Electric Fence至少需要丙个页来满足内存分配申请,因此内存使用会非常大,好处是它利用了硬件来捕获非法访问,因此速度快。也算是空间换时间吧。
十、Dmalloc内存检查机制
比较经典的内存检测工具,虽然N年没更新了。dmalloc通过在分配区域增加padding magic number的做法来检测非法访问,因此它能够检测到问题但不能检测出哪条指令出的错。Dmalloc只能检测越界写,但不能检测越界读。另外,Dmalloc只检测堆上用malloc系函数(而不是sbrk()或mmap())分配的内存,而无法对栈内存和静态内存进行检测。 本质上它也是通过hook malloc(), realloc(), calloc(),free()等内存管理函数,还有strcat(), strcpy()等内存操作函数,来检测内存问题。它支持x86, ARM平台,语言上支持C/C++,并且支持多线程。