C/C++等底层语言在提供强大功能及性能的同时,其灵活的内存访问也带来了各种纠结的问题。但是,在这样灵活操作的后面,还隐藏着很危险的操作,那就是关于内存的问题。一看到内存的问题,大部分的初学者就开始傻眼了。怎么样快速的去找到内存中的问题并且解决它。

初学者常用的是逐步打印log信息但其效率不是太高,也比较的繁琐,尤其是在运行成本高或重现概率低的情况下。

另外,静态检查也是一类方法,有很多工具(lint, cppcheck, klockwork, splint, o, etc.)。但缺点是误报很多,不适合针对性问题。另外误报率低的一般还需要收费。

后,就是动态检查工具。下面介绍几个Linux平台下主要的运行时内存检查工具。绝大多数都是开源免费且支持x86和ARM平台的。

首先,比较常见的内存问题有下面几种:

• memory overrun:写内存越界

• double free:同一块内存释放两次

• use after free:内存释放后使用

• wild free:释放内存的参数为非法值

• access uninitialized memory:访问未初始化内存

• read invalid memory:读取非法内存,本质上也属于内存越界

• memory leak:内存泄露

• use after return:caller访问一个指针,该指针指向callee的栈内内存

• stack overflow:栈溢出

针对上面的问题,主要有以下几种方法:

1. 为了检测内存非法使用,需要hook内存分配和操作函数。hook的方法可以是用C-preprocessor,也可以是在链接库中直接定义(因为Glibc中的malloc/free等函数都是weak symbol),或是用LD_PRELOAD。另外,通过hook strcpy(),memmove()等函数可以检测它们是否引起buffer overflow。

2. 为了检查内存的非法访问,需要对程序的内存进行bookkeeping,然后截获每次访存操作并检测是否合法。bookkeeping的方法大同小异,主要思想是用shadow memory来验证某块内存的合法性。至于instrumentation的方法各种各样。有run-time的,比如通过把程序运行在虚拟机中或是通过binary translator来运行;或是compile-time的,在编译时就在访存指令时就加入检查操作。另外也可以通过在分配内存前后加设为不可访问的guard page,这样可以利用硬件(MMU)来触发SIGSEGV,从而提高速度。

3. 为了检测栈的问题,一般在stack上设置canary,即在函数调用时在栈上写magic number或是随机值,然后在函数返回时检查是否被改写。另外可以通过mprotect()在stack的顶端设置guard page,这样栈溢出会导致SIGSEGV而不至于破坏数据。

以上方法有些强于功能,有些胜在性能,有些则十分方便易用,总之各有千秋。以下是几种常用工具在Linux x86_64平台的实验结果,注意其它平台可能结果有差异。

另外也可能由于版本过老,编译环境差异,总之各种原因造成遗漏,如有请谅解

Tool\Problem memory overrun double free use after free wild free access uninited read invalid memory memory leak use after return stack overflow

Memory checking tools in Glibc Yes Yes Yes Yes(if use memcpy, strcpy, etc)

TCMalloc(Gperftools) Yes

Valgrind Yes Yes Yes Yes Yes Yes Yes Yes Yes

Address Sanitizer(ASan) Yes Yes Yes Yes (Memory Sanitizer) Yes Yes Yes Yes

Memwatch Yes Yes Yes

Dr.Memory Yes Yes Yes Yes Yes Yes Yes Yes

Electric Fence Yes Yes Yes Yes

Dmalloc Yes Yes Yes Yes Yes

下面简单介绍一下这些工具以及基本用法。更详细用法请参见各自manual。

Memory checking tools in Glibc

Glibc中自带了一些Heap consistency checking机制。

MALLOC_CHECK_

用mallopt()的M_CHECK_ACTION可以设置内存检测行为,设MALLOC_CHECK_环境变量效果也是一样的。从Glibc 2.3.4开始,默认为3。即打印出错信息,stack trace和memory mapping,再退出程序。设置LIBC_FATAL_STDERR_=1可以将这些信息输出到stderr。比如运行以下有double free的程序:

$ MALLOC_CHECK_=3 ./bug

会打印如下信息然后退出:

*** Error in `./bug': free(): invalid pointer: 0x00000000010d6010 ***

======= Backtrace: =========

/lib/x86_64-linux-gnu/libc.so.6(+0x7338f)[0x7f367073238f]

/lib/x86_64-linux-gnu/libc.so.6(+0x81fb6)[0x7f3670740fb6]

./bug[0x400845]

./bug[0x400c36]

/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7f36706e0ec5]

./bug[0x400729]

======= Memory map: ========

00400000-00402000 r-xp 00000000 08:01 2893041 /home/jzj/code/bug

00601000-00602000 r--p 00001000 08:01 2893041 /home/jzj/code/bug

00602000-00603000 rw-p 00002000 08:01 2893041 /home/jzj/code/bug

010d6000-010f7000 rw-p 00000000 00:00 0 [heap]

7f36704a8000-7f36704be000 r-xp 00000000 08:01 4203676 /lib/x86_64-linux-gnu/libgcc_s.so.1

7f36704be000-7f36706bd000 ---p 00016000 08:01 4203676 /lib/x86_64-linux-gnu/libgcc_s.so.1

7f36706bd000-7f36706be000 r--p 00015000 08:01 4203676 /lib/x86_64-linux-gnu/libgcc_s.so.1

7f36706be000-7f36706bf000 rw-p 00016000 08:01 4203676 /lib/x86_64-linux-gnu/libgcc_s.so.1

Aborted (core dumped)

mcheck

mcheck是Glibc中的堆内存一致性检查机制。使用时只要加上头文件:

#include <mcheck>

再在要开始检查的地方加上:

if (mcheck(NULL) != 0) {

fprintf(stderr, "mcheck() failed\n");

exit(EXIT_FAILURE);

}

编译时加-lmcheck然后运行即可:

$ g++ -Wall -g problem.cpp -o bug -lmcheck

_FORTIFY_SOURCE

宏_FORTIFY_SOURCE提供轻量级的buffer overflow检测。设置后会调用Glibc里带_chk后缀的函数,做一些运行时检查。主要检查各种字符串缓冲区溢出和内存操作。比如memmove, memcpy, memset, strcpy, strcat, vsprintf等。注意一些平台上编译时要加-O1或以上优化。这样就可以检查出因为那些内存操作函数导致的缓冲溢出问题:

$ g++ -Wall -g -O2 -D_FORTIFY_SOURCE=2 problem.cpp -o bug

*** buffer overflow detected ***: ./bug terminated

======= Backtrace: =========

/lib/x86_64-linux-gnu/libc.so.6(+0x7338f)[0x7f9976e1638f]

/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x5c)[0x7f9976eadc9c]

/lib/x86_64-linux-gnu/libc.so.6(+0x109b60)[0x7f9976eacb60]

mtrace

mtrace可以用于检查malloc/free是否正确配对。用时用mtrace()和muntrace()表示开始和结束内存分配trace(如果检测到结束结尾的话可以不用muntrace())。但这是简单地记录没有free对应的malloc,可能会有一些false alarm。

#include <mcheck.h>

mtrace(); // …

muntrace();

然后编译:

$ g++ -Wall -g problem.cpp -o bug

运行时先设输出的log文件:

$ export MALLOC_TRACE=output.log

用mtrace命令将输出文件变得可读:

$ mtrace ./bug $MALLOC_TRACE

就可以得到哪些地方的内存申请还没有被free掉。

Memory not freed:

Address Size Caller0x00000000008d4520 0x400 at /home/jzj/code/problem.cpp:73

Gperftools

Gperftools(Google Performance Tools)为一组工具集,包括了thread-caching malloc(TCMalloc)和CPU profiler等组件。TCMalloc和Glibc中的ptmalloc相比更快,并可以有效减少多线程之间的竞争,因为它会为每个线程单独分配线程本地的Cache。这里先只关注它的内存相关组件。通过tcmalloc可以做heap-checking和heap-profiling。

如果懒得build,Ubuntu可以如下安装:

$ sudo apt-get install libgoogle-perftool-dev google-perftools

然后编译时加-ltcmalloc,注意一定要放后链接,如:

$ g++ -Wall -g problem.cpp -g -o bug -ltcmalloc

编译时不链接的话就也可以用LD_PRELOAD:

$ export LD_PRELOAD=”/usr/lib/libtcmalloc.so”

运行的时候执行:

$ HEAPCHECK=normal ./bug

就可以报出内存泄露:

Have memory regions w/o callers: might report false leaks

Leak check _main_ detected leaks of 1024 bytes in 1 objects

The 1 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 1024 bytes in 1 objects allocated from:

@ 400ba3

@ 400de0

@ 7fe1be24bec5

@ 400899

@ 0

如果想只检查某部分,可以用HeapProfileLeakChecker生成内存快照,然后执行完要检查部分后调用assert(checker.NoLeaks())。具体用法见:http://goog-perftools.sourceforge.net/doc/heap_checker.html

更详细的信息可以用google-pprof获得,如:

$ google-pprof ./bug "/tmp/bug.1353._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --gv

关于Tcmalloc更多的配置信息可以参见:http://gperftools.googlecode.com/svn/trunk/doc/tcmalloc.html

Valgrind

Valgrind是Valgrind core和Valgrind工具插件的集合,除了用于检查内存错误,还可以用来分析函数调用,缓存使用,多线程竞争,堆栈使用等问题。这里只关注memcheck工具,因为太常用 ,它默认就是打开的。其原理是让程序跑在一个虚拟机上,因此速度会慢几十倍。好在现实中很多程序是IO bound的,所以很多时候没有慢到忍无可忍的地步。好处是它不需要重新编译目标程序。它会通过hash表记录每个heap block,同时通过shadow memory记录这些内存区域的信息。这样就可以在每次访存时检查其合法性。

运行时可根据需要加配置参数,如:

$ valgrind --tool=memcheck --error-limit=no --track-origins=yes --trace-children=yes --track-fds=yes ./bug

如memory overrun就会报以下错误:

==1735== Invalid write of size 1==1735== at 0x4008A7: overrun() (problem.cpp:26)==1735== by 0x400C2B: main (problem.cpp:127)==1735== Address 0x51fc460 is not stack'd, malloc'd or (recently) free'd==1735==

use after free检测结果:

==1739== Invalid write of size 1==1739== at 0x4C2E51C: __GI_strncpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==1739== by 0x40098B: use_after_free() (problem.cpp:46)==1739== by 0x400C3F: main (problem.cpp:133)==1739== Address 0x51fc040 is 0 bytes inside a block of size 1,024 free'd==1739== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==1739== by 0x400975: use_after_free() (problem.cpp:45)==1739== by 0x400C3F: main (problem.cpp:133)==1739== ==1739== Invalid write of size 1==1739== at 0x4C2E5AC: __GI_strncpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==1739== by 0x40098B: use_after_free() (problem.cpp:46)==1739== by 0x400C3F: main (problem.cpp:133)==1739== Address 0x51fc045 is 5 bytes inside a block of size 1,024 free'd==1739== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==1739== by 0x400975: use_after_free() (problem.cpp:45)==1739== by 0x400C3F: main (problem.cpp:133)

access uninitialized memory结果:

==1742== Conditional jump or move depends on uninitialised value(s)==1742== at 0x4EB17F1: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:867)==1742== by 0x4E819CF: vfprintf (vfprintf.c:1661)==1742== by 0x4E8B498: printf (printf.c:33)==1742== by 0x400AA6: access_uninit() (problem.cpp:72)==1742== by 0x400C5A: main (problem.cpp:142)==1742== Uninitialised value was created by a heap allocation==1742== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)==1742== by 0x400A87: access_uninit() (problem.cpp:71)==1742== by 0x400C5A: main (problem.cpp:142)

像memory overrun和use after free这类问题比较难搞是因为出错的时候往往不是第一现场。用Valgrind就比较容易抓到第一现场。如果一个对象A被释放后,同一块内存再次被申请为对象B,但程序中还是通过指向对象A的dangling pointer进行访问,会覆盖已有数据或者读出错误数据。但这种情况Valgrind检查不出来,因为Valgrind不会做语义上的分析。但是Valgrind可以配置内存分配策略,通过设置空闲内存队列大小和优先级让被释放的内存不马上被重用。从而增大抓到此类问题的概率。

对于栈中内存,Memcheck只会做未初始化数据访问的检测,而不会做栈或全局数组中的越界检测。这是由SGCheck来完成的,它与memcheck功能互补。使用SGCheck只需在valgrind后加上–tool=exp-sgcheck参数即可。

另外memcheck还提供一系列参数可以调整检测策略,具体可参见Valgrind User Manual或者http://valgrind.org/docs/manual/mc-manual.html

Address sanitizer (ASan)

早先是LLVM中的特性,后被加入GCC 4.8。在GCC 4.9后加入对ARM平台的支持。因此用时不需要第三方库,通过在编译时指定flag即可打开开关。它是 Mudflap的替代品(Mudflap从GCC 4.9开始不再支持,指定了也不做事)。ASan在编译时在访存操作中插入额外指令,同时通过Shadow memory来记录和检测内存的有效性。slowdown官方称为2x左右。

使用时只要在CFLAGS中加上如下flag。注意如果链接so,只有可执行文件需要加flag。

$ g++ -Wall -g problem.cpp -o bug -fsanitize=address -fno-omit-frame-pointer

直接运行,检测出错误时会报出类似以下错误:

==22543==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61900000fea0 at pc 0x400f22 bp 0x7ffe3c21be90 sp 0x7ffe3c21be88

WRITE of size 1 at 0x61900000fea0 thread T0

#0 0x400f21 in overrun() /home/jzj/code/problem.cpp:26

#1 0x401731 in main /home/jzj/code/problem.cpp:127

#2 0x7fb2a46b8ec4 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)

#3 0x400d08 (/home/jzj/code/bug+0x400d08)

==26753==ERROR: AddressSanitizer: attempting double-free on 0x61900000fa80 in thread T0:

#0 0x7f591b4ba5c7 in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.1+0x545c7)

#1 0x400e46 in double_free() /home/jzj/code/problem.cpp:17

#2 0x40173b in main /home/jzj/code/problem.cpp:130

#3 0x7f591b0c2ec4 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)

#4 0x400d08 (/home/jzj/code/bug+0x400d08)

检测一些特定问题需要加上专门的选项,比如要检查访问指向已被释放的栈空间需要加上:

ASAN_OPTIONS=detect_stack_use_after_return=1

如果要检测memory leak需要加上:

ASAN_OPTIONS=detect_leaks=1

各种参数配置请参见:https://code.google.com/p/address-sanitizer/wiki/Flags

Address-sanitizer是Sanitizer系工具中的一员。有一部分功能是在其余工具里,比如memory leak检测在LeakSanitizer中,uninitialized memory read检测在MemorySanitizer中。data race检测在ThreadSanitizer中。它们初都是LLVM中的特性,后被移植到GCC,所以用GCC的话好用4.9,至少也是4.8以后版本。

AddressSanitizer不能检测读未初始化内存,而这MemorySanitizer(MSan)能做到。它包含compiler instrumentation模块和run-time的库。目前只支持Linux x86_64平台。使用时需在编译选项加-fsanitize=memory -fPIE -pie,为了得到更详细的信息,好加上-fno-omit-frame-pointer和-fsanitize-memory-track-origins。它实现了Valgrind的部分功能,但由于使用了compile-time instrumentation,所以速度更快。可惜目前只在LLVM上有,在GCC上还没有,暂且略过。

Memwatch

Memwatch是一个轻量级的内存问题检测工具。主要用于检测内存分配释放相关问题及内存越界访问问题。通过C preprocessor,Memwatch替换所有 ANSI C的内存分配 函数,从而记录分配行为。注意它不保证是线程安全的。效率上,大块分配不受影响,小块分配会受影响,因此它没法使用原分配函数中的memory pool。坏情况下会有3-5x的slowdown。它可以比较方便地模拟内存受限情况。对于未初始化内存访问,和已释放内存访问,Memwatch会poison相应内存(分配出来写0xFE,释放内存写0xFD)从而在出错时方便调试。

使用时需要修改源码。该库需要单独下载:

http://www.linkdata.se/sourcecode/memwatch/

然后在要检查的代码中包含头文件:

#include "memwatch.h"

然后加下面宏编译:

$ gcc -DMEMWATCH -DMW_STDIO test.c memwatch.c -o test

默认结果输出在memwatch.log。比如程序如果有double free的话会输出:

Modes: __STDC__ 64-bit mwDWORD==(unsigned int)

mwROUNDALLOC==8 sizeof(mwData)==56 mwDataSize==56

double-free: <3> test.c(17), 0x25745e0 was freed from test.c(16)

Stopped at Sun Jun 14 10:57:15 2015

Memory usage statistics (global):

N)umber of allocations made: 1

L)argest memory usage : 1024

T)otal of all alloc() calls: 1024

U)nfreed bytes totals : 0

Memory leak的输出:

Modes: __STDC__ 64-bit mwDWORD==(unsigned int)

mwROUNDALLOC==8 sizeof(mwData)==56 mwDataSize==56

Stopped at Sun Jun 14 10:56:22 2015

unfreed: <1> test.c(63), 1024 bytes at 0x195f5e0 {FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE ................}

Memory usage statistics (global):

N)umber of allocations made: 1

L)argest memory usage : 1024

T)otal of all alloc() calls: 1024

U)nfreed bytes totals : 1024

Electric Fence

Electric Fence主要用于追踪buffer overflow的读和写。它利用硬件来抓住越界访问的指令。其原理是为每一次内存申请额外申请一个page或一组page,然后把这些buffer范围外的page设为不可读写。这样,如果程序访问这些区域,由于页表中这个额外page的权限是不可读写,会产生段错误。那些被free()释放的内存也会被设为不可访问,因此访问也会产生段错误。因为读写权限以页为单位,所以如果多的页放在申请内存区域后,可防止overflow。如果要防止underflow,就得用环境变量EF_PROTECT_BELOW在区域前加保护页。因为Electric Fence至少需要丙个页来满足内存分配申请,因此内存使用会非常大,好处是它利用了硬件来捕获非法访问,因此速度快。也算是空间换时间吧。

目前支持Window, Linux平台,语言支持C/C++。限制包括无法检测使用未初始化内存,memory leak等。同时它不是线程安全的。Ubuntu上懒得编译可以安装现成的:

$ sudo apt-get install electric-fence

它是以库的方式需要被链接到程序中:

$ g++ -Wall -g problem.cpp -o bug -lefence

或者用LD_PRELOAD,不过记得不要同时链接其它的malloc debugger库。

$ export LD_PRELOAD=libefence.so.0.0

另外,EF_PROTECT_BELOW,EF_PROTECT_FREE,EF_ALLOW_MALLOC_0和EF_FILL这些环境变量都是用来控制其行为的。可以参见manual:http://linux.die.net/man/3/efence

比如memory overrun和double free就可以得到如下结果:

Electric Fence 2.2 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>

Segmentation fault (core dumped)

Electric Fence 2.2 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>

ElectricFence Aborting: free(7fc1c17c8c00): address not from malloc().

Illegal instruction (core dumped)

它无法在log中打出详细信息,但如果运行前打开了coredump:

$ ulimit -c unlimited

就可以gdb打开coredump来分析了:

$ gdb ./bug -c core

注意因为多数平台在分配时遇到block size不是word size整数倍时会通过加padding byte进行word alignment。如果是在padded area中出现overrun则无法检测。这里可以通过在程序中设置EN_ALIGNMENT=1来防止byte padding,从而更容易检测off by one的问题。

DUMA(http://duma.sourceforge.net/)从Electric Fence中fork出来并加入一些其它特性,比如leak detection,Windows支持等。

Dmalloc

比较经典的内存检测工具,虽然N年没更新了。dmalloc通过在分配区域增加padding magic number的做法来检测非法访问,因此它能够检测到问题但不能检测出哪条指令出的错。Dmalloc只能检测越界写,但不能检测越界读。另外,Dmalloc只检测堆上用malloc系函数(而不是sbrk()或mmap())分配的内存,而无法对栈内存和静态内存进行检测。 本质上它也是通过hook malloc(), realloc(), calloc(),free()等内存管理函数,还有strcat(), strcpy()等内存操作函数,来检测内存问题。它支持x86, ARM平台,语言上支持C/C++,并且支持多线程。

使用时可以先从官网下载源码包(http://dmalloc.com/releases/),然后编译安装:

$ tar zxvf dmalloc-5.5.2.tgz

$ cd dmalloc-5.5.2

$ ./configure

$ make && make install

少量修改源代码。只需要加上下面的头文件:

#ifdef DMALLOC#include "dmalloc.h"#endif

然后编译时CFLAGS加上 -DDMALLOC -DDMALLOC_FUNC_CHECK,如:

$ g++ -Wall -g -DDMALLOC -DDMALLOC_FUNC_CHECK problem.cpp -o bug -ldmalloc

dmalloc的配置选项可以通过设置环境变量DMALLOC_OPTIONS来实现,例如:

$ export DMALLOC_OPTIONS=log=logfile,check-fence,check-blank,check-shutdown,check-heap,check-funcs,log-stats,log-non-free,print-messages,log-nonfree-space

这些用法可参见:

http://dmalloc.com/docs/latest/online/dmalloc_26.html

http://dmalloc.com/docs/latest/online/dmalloc_27.html

也可以用dmalloc这个命令来设置。直接dmalloc -v可用于查看当前设置。

发生错误时会给出类似以下输出:

1434270937: 2: error details: checking user pointer1434270937: 2: pointer '0x7fc235336808' from 'unknown' prev access 'problem.cpp:35'1434270937: 2: ERROR: _dmalloc_chunk_heap_check: free space has been overwritten (err 67)1434270937: 2: error details: checking pointer admin1434270937: 2: pointer '0x7fc235336808' from 'problem.cpp:37' prev access 'problem.cpp:35'1434270937: 2: ERROR: free: free space has been overwritten (err 67)

1434271030: 3: error details: finding address in heap1434271030: 3: pointer '0x7f0a7e29d808' from 'problem.cpp:27' prev access 'unknown'1434271030: 3: ERROR: free: tried to free previously freed pointer (err 61)

另外Dmalloc还提供一些函数,如dmalloc_mark(),dmalloc_log_changed()和dmalloc_log_unfreed()等来打印内存信息和分析内存变化:

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来做内存检测。

Dr. Memory提供各平台的包下载。

https://github.com/DynamoRIO/drmemory/wiki/Downloads

下载后即可直接使用。首先编译要检测的测试程序:

$ 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

更多用法参见 drmemory -help或http://drmemory.org/docs/page_options.html。

像遇到double-free和heap overflow问题的话就会给出类似下面结果:

~~Dr.M~~

~~Dr.M~~ Error #1: INVALID HEAP ARGUMENT to free 0x08ceb0e8

~~Dr.M~~ # 0 replace_free [/work/drmemory_package/common/alloc_replace.c:2503]

~~Dr.M~~ # 1 double_free [/home/jzj/code/problem.cpp:23]

~~Dr.M~~ # 2 main [/home/jzj/code/problem.cpp:157]

~~Dr.M~~ Note: @0:00:00.127 in thread 26159

~~Dr.M~~ Note: memory was previously freed here:

~~Dr.M~~ Note: # 0 replace_free [/work/drmemory_package/common/alloc_replace.c:2503]

~~Dr.M~~ Note: # 1 double_free [/home/jzj/code/problem.cpp:22]

~~Dr.M~~ Note: # 2 main [/home/jzj/code/problem.cpp:157]

~~Dr.M~~

~~Dr.M~~ Error #1: UNADDRESSABLE ACCESS beyond heap bounds: writing 0x0988f508-0x0988f509 1 byte(s)

~~Dr.M~~ # 0 overrun [/home/jzj/code/problem.cpp:32]

~~Dr.M~~ # 1 main [/home/jzj/code/problem.cpp:154]

~~Dr.M~~ Note: @0:00:00.099 in thread 26191

~~Dr.M~~ Note: prev lower malloc: 0x0988f0e8-0x0988f4e8

~~Dr.M~~ Note: instruction: mov $0x6a -> (%eax)

Stack protection

前面的工具大多用于堆内存检错,对于栈内存GCC本身提供了一些检错机制。加上-fstack-protector后,GCC会多加指令来检查buffer/stack overflow。原理是为函数加guard variable。在函数进入时初始化,函数退出时检查。相关的flag有-fstack-protector-strong -fstack-protector -fstack-protector-all等。使用例子:

$ g++ -Wall -O2 -U_FORTIFY_SOURCE -fstack-protector-all problem.cpp -o bug

运行时会检测到stack overflow:

*** stack smashing detected ***: ./bug terminated

Aborted (core dumped)

对于线程的栈可以参考pthread_attr_setguardsize()。

Rational purify & Insure++

Rational purity是IBM的商业化产品,要收费,所以木有用过,精神上支持。和Valgrind很像,也基于binary instrumentation,适用于不同平台。另一个工具Insure++基于compile-time和binary instrumentation,可以检测use-after-free,out-of-bounds,wild free和memory leak等内存问题。但也是要收费的,也精神上支持。。。。。。

大体来说,遇到诡异的内存问题,先可以试下Glibc和GCC里自带的检测机制,因为enable起来方便。如果检测不出来,那如果toolchain版本较新且有编译环境,可以先尝试ASan,因为其功能强大,且效率高。接下来,如果程序是I/O bound或slowdown可以接受,可以用Valgrind和Dr.Memory。它们功能强大且无需重新编译,但速度较慢,且后者不支持64位程序和ARM平台。然后可以根据实际情况和具体需要考虑Memwatch,Dmalloc和Electric Fence等工具。

在实际的工作和项目开发的过程中,内存泄漏问题是个很危险的隐患,所以我们一定要养成很好的编程习惯,要学会用这些工具来检测自己的代码,从而快速的去解决关于内存的问题,提高自己的工作效率。

嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!

分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!(点击找小助理领取)

内存问题检测工具的介绍相关推荐

  1. Ubuntu下内存泄露检测工具Valgrind的使用

    在VS中可以用VLD检测是否有内存泄露,可以参考http://blog.csdn.net/fengbingchun/article/details/44195959,下面介绍下Ubuntu中内存泄露检 ...

  2. C/C++的内存泄漏检测工具Valgrind memcheck的使用经历

    Linux下的Valgrind真是利器啊(不知道Valgrind的请自觉查看参考文献(1)(2)),帮我找出了不少C++中的内存管理错误,前一阵子还在纠结为什么VS 2013下运行良好的程序到了Lin ...

  3. 精准 iOS 内存泄露检测工具

    MLeaksFinder:精准 iOS 内存泄露检测工具 发表于 2016-02-22   |   zepo   |   23 Comments 背景 平常我们都会用 Instrument 的 Lea ...

  4. 内存错误检测工具——kfence工作原理分析

    一.功能介绍 Linux 5.13引入一个新的内存错误检测工具:KFENCE(Kernel Electric-Fence,内核电子栅栏).KFENCE是一个低开销的.基于采样的内存错误检测工具.KFE ...

  5. Linux中的常用内存问题检测工具

    原文地址:http://blog.csdn.net/jinzhuojun/article/details/46659155 C/C++等底层语言在提供强大功能及性能的同时,其灵活的内存访问也带来了各种 ...

  6. java内存泄漏怎么检测_JAVA内存泄漏原因和内存泄漏检测工具

    JAVA内存泄漏原因和内存泄漏检测工具 摘要 虽然Java 虚拟机(JVM)及其垃圾收 集器(garbage collector,GC)负责管理大多数的内存任务,Java 软件程序中还是有可能出现内 ...

  7. MLeaksFinder :腾讯开源的 iOS 内存泄漏检测工具

    一.工具简介 MLeaksFinder :腾讯开源的 iOS 内存泄漏检测工具 工具优势:在日常开发调试或测试业务逻辑过程中,可以自动发现并警告内存泄漏.暂时没有发现误报:基本上报了leak的  进去 ...

  8. C++内存泄漏检测工具

    C++内存泄漏检测工具 1.VC自带的CRT:_CrtCheckMemory   调试器和 CRT 调试堆函数 1.1用法: /************************************ ...

  9. 内存错误检测工具AddressSanitizer原理

    原论文:AddressSanitizer: A Fast Address Sanity Checker 谷歌官方文档:AddressSanitizerAlgorithm 参考博客:Introducti ...

最新文章

  1. 面试官:为什么MySQL的索引要使用B+树,而不是其它树?比如B树?
  2. excel行转列_excel统计函数:应用广泛的动态统计之王OFFSET(上)
  3. rs485调试软件_5种RS485切换方向的方法及优劣势分析
  4. Hibernate3.x,hibernate3.x,Hibernate3.x整合Spring3.x不能实现自动创建表结构的解决办法:...
  5. Windows Phone 7开发一月谈(3)
  6. 我最喜欢的Bash骇客
  7. C#中的复制在unity中使用
  8. 手把手教你提取WOW中的所有声音文件
  9. Hadoop文章收集汇总 - 如禁止转载,请及时联系本人 收集学习互联网各位前辈分享的文章
  10. 浙大玉泉校区路由器L2TP设置(极路由1s)
  11. 用c语言莲花,用荷花为寓意的网名-网名搜索
  12. oracle 同义词表结构,Oracle 数据库的同义词+视图
  13. c++二进制转化成十进制
  14. java prase xml error
  15. python3版本升级和系统更新_如何更新mac系统自带的python版本到最新3.3
  16. windows中常见后门持久化方法总结
  17. torch.squeeze和torch.unsqueeze
  18. python验证码识别如何连接第三方平台-------cjy
  19. php中跨页面id的获取,excel跨表格提取数据?phpexcel 读取excel里的数据并在页面显示出来...
  20. MES与ERP的集成

热门文章

  1. 【破解手记】普利尼,破解手记[3]
  2. Presto RBO 之 算子列裁剪
  3. centos 一键安装lamp
  4. vue项目 无法下载导出的execel文件
  5. canvas实战之酷炫背景动画(六)
  6. 限流式保护器在建筑地下车库防火中的应用与研究
  7. XDOJ 消除类游戏
  8. 生成banner的网页工具
  9. 错误:403 forbidden
  10. Bulk Photo Watermark for Mac(图片水印批量制作工具)