内存调试 - MEMWATCH

MEMWATCH 由 Johan Lindh 编写,是一个开放源代码 C 语言内存错误检测工具,您可以自己下载它(请参阅本文后面部分的 参考资料 )。只要在代码中添加一个头文件并在 gcc 语句中定义了 MEMWATCH 之后,您就可以跟踪程序中的内存泄漏和错误了。 MEMWATCH 支持 ,它提供结果日志纪录,能检测双重释放( double-free )、错误释放( erroneous free )、没有释放的内存( unfreed memory )、溢出和下溢等等。 ANSI C

1.   下载 Memwatch, http://www.linkdata.se/ ;

2.   解压后可以看到 memwatch.c 、 memwatch.h 、 test.c 等文件;

2 . 1 首先运行源代码中的事例程序,检测是否捕捉到内存错误,具体步骤如下:

Linux and other *nixes with gcc:

                 gcc -o test -DMEMWATCH -DMEMWATCH_STDIO test.c memwatch.c

         Windows 95, Windows NT with MS Visual C++:

                 cl -DMEMWATCH -DMEMWATCH_STDIO test.c memwatch.c

         Then simply run the test program.

                ./test

         成功运行后,会看到一个名为 "memwatch.log" 的文件,里面记录了内存泄漏的具体情况。

3.     上面事例运行成功后,就可以将测试自己的代码了。

3.1 首先将 memwatch.c 、 memwatch.h 复制到被测代码中。

3.2 将源码中每个文件都包含 memwatch.h ;如果源码中的所有文件都用到了一个文件,也可以将其添加到该文件中。

3.3 重新编译源代码,并在 gcc 选项中使用 –DMEMWATCH ,如果需要对出错时进行控制,可以添加 -DMEMWATCH_STDIO 选项,此时调试出错时可以在标准输出上看到 "Abort, Retry,Ignore?”

3.4 编译成功后,运行程序!如果操作无误,可以 memwatch.log ,里面记录了内存调试的具体情况。

4.   容易出现的问题

4.1 在 memwatch.h 之后包含 string.h 时,编译时提示 strdup() 出错!

    解决办法:可以将 string.h 放置在 memwatch.h 之前;也可以修改 memwatch.h ,使其包含

string.h.

4.2 运行程序后,没有出现 memwatch.log 文件

解决办法:在编译时可能没有定义 MEMWATCH ;也可能是有些文件没有包含 memwatch.h ;查看后解决


总结: MEMWATCH 为您显示真正导致问题的行。如果您释放一个已经释放过的指针,它会告诉您。对于没有释放的内存也一样。日志结尾部分显示统计信息,包括泄漏了多少内存,使用了多少内存,以及总共分配了多少内存。

最近试用了memwatch,感觉网上的介绍不太好,所以放在这里跟大家分享 。其实大部分都是看的帮助,很多地方翻译得不好还有错

1 介绍
MemWatch 由 Johan Lindh 编写,是一个开放源代码 C 语言内存错误检测工具。MemWatch 支持 ANSI C ,它提供结果日志纪录,能检测双重释放(double-free )、错误释放(erroneous free )、内存泄漏(unfreed memory )、溢出(Overflow) 、下溢(Underflow) 等等。

1.1 MemWatch 的内存处理
MemWatch 将所有分配的内存用0xFE 填充,所以,如果你看到错误的数据是用0xFE 填充的,那就是你没有初始化数据。例外是calloc() ,它会直接把分配的内存用0 填充。
MemWatch 将所有已释放的内存用0xFD 填充(zapped with 0xFD). 如果你发现你使用的数据是用0xFD 填充的,那你就使用的是已释放的内存。在这种情况,注意MemWatch 会立即把一个" 释放了的块信息" 填在释放了的数据前。这个块包括关于内存在哪儿释放的信息,以可读的文本形式存放,格式为"FBI<counter>filename(line)" 。如:"FBI<267>test.c(12)". 使用FBI 会降低free() 的速度,所以默认是关闭的。使用mwFreeBufferInfo(1) 开启。
为了帮助跟踪野指针的写情况,MemWatch 能提供no-mans-land (NML )内存填充。no-mans-land 将使用0xFC 填充. 当no-mans-land 开启时,MemWatch 转变释放的内存为NML 填充状态。

1.2 初始化和结束处理
一般来说,在程序中使用MemWatch 的功能,需要手动添加mwInit() 进行初始化,并用对应的mwTerm () 进行结束处理。
当然,如果没有手动调用mwInit() ,MemWatch 能自动初始化. 如果是这种情形,memwatch 会使用atext() 注册mwTerm() 用于atexit-queue. ; 如果你手动调用atexit() 以进行清理工作,memwatch 可能在你的程序结束前就终止。为了安全起见,请显式使用mwInit() 和 mwTerm().
涉及的函数主要有:
mwInit()mwTerm() mwAbort()

1.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()

1.4 MemWatch 对C++ 的支持
可以将MemWatch 用于C++, 但是不推荐这么做。请详细阅读memwatch.h 中关于对C++ 的支持。

2 使用
2.1 为自己的程序提供MemWatch 功能
在要使用MemWatch 的.c 文件中包含头文件 "memwatch.h"
使用GCC 编译(注意:不是链接)自己的程序时,加入 -DMEMWATCH -DMW_STDIO
如:gcc -DMEMWATCH -DMW_STDIO –o test.o – c    test1.c

2.2 使用MemWatch 提供的功能
1 )在程序中常用的MemWatch 功能有:
mwTRACE ( const char* format_string, ... );
或 TRACE ( const char* format_string, ... );
mwASSERT ( int, const char*, const char*, int )
或 ASSERT ( int, const char*, const char*, int )
mwVERIFY ( int, const char*, const char*, int )
或 VERIFY ( int, const char*, const char*, int )
mwPuts ( const char* text )
ARI 机制( mwSetAriFunc(int (*func)(const char *)) ,
mwSetAriAction(int action) ,
mwAriHandler ( const char* cause ) )
mwSetOutFunc (void (*func)(int))
mwIsReadAddr(const void *p, unsigned len )
mwIsSafeAddr(void *p, unsigned len )
mwStatistics ( int level )
mwBreakOut ( const char* cause)

2 )mwTRACE ,mwASSERT ,mwVERIFY 和mwPuts 顾名思义,就不再赘述。仅需要注意的是,Memwatch 定义了宏TRACE, ASSERT VERIFY. 如果你已使用同名的宏,memwatch2.61 及更高版本的memwatch 不会覆盖你的定义。MemWatch2.61 及以后,定义了mwTRACE, mwASSERT 和 mwVERIFY 宏,这样,你就能确定使用的是memwatch 的宏定义。2.61 版本前的memwatch 会覆盖已存在的同名的TRACE, ASSERT 和 VERIFY 定义。
当然,如果你不想使用MemWatch 的这几个宏定义,可以定义MW_NOTRACE, MW_NOASSERT 和 MW_NOVERIFY 宏,这样MemWatch 的宏定义就不起作用了。所有版本的memwatch 都遵照这个规则。
3 )ARI 机制即程序设置的“Abort, Retry, Ignore 选择陷阱。
mwSetAriFunc :
设置“Abort, Retry, Ignore ”发生时的MemWatch 调用的函数. 当这样设置调用的函数地址时,实际的错误消息不会打印出来,但会作为一个参数进行传递。
如果参数传递NULL ,ARI 处理函数会被再次关闭。当ARI 处理函数关闭后, meewatch 会自动调用有mwSetAriAction() 指定的操作。
正常情况下,失败的ASSERT() or VERIFY() 会中断你的程序。但这可以通过mwSetAriFunc() 改变,即通过将函数"int myAriFunc(const char *)" 传给它实现。你的程序必须询问用户是否中断,重试或者忽略这个陷阱。返回2 用于Abort , 1 用于Retry ,或者0 对于Ignore 。注意retry .
MemWatch 有个默认的ARI 处理器。默认是关闭的,但你能通过调用mwDefaultAri() 开启。注意这仍然会中止你的程序除非你定义MEMWATCH_STDIO 允许MemWatch 使用标准C 的I/O 流。
同时,设置ARI 函数也会导致MemWatch 不将ARI 的错误信息写向标准错误输出,错误字符串而是作为'const char *' 参数传递到ARI 函数 .
mwSetAriAction :
如果没有ARI 处理器被指定,设置默认的ARI 返回值。默认是 MW_ARI_ABORT
mwAriHandler :
这是个标准的ARI 处理器,如果你喜欢就尽管用。它将错误输出到标准错误输出,并从标准输入获得输入。
mwSetOutFunc :
将输出转向调用者给出的函数( 参数即函数地址) 。参数为NULL ,表示把输出写入日志文件 memwatch.log.
mwIsReadAddr:
检查内存是否有读取的权限
mwIsSafeAddr:
检查内存是否有读、写的权限
mwStatistics:
设置状态搜集器的行为。对应的参数采用宏定义。
#define MW_STAT_GLOBAL 0 /* 仅搜集全局状态信息 */
#define MW_STAT_MODULE 1      /* 搜集模块级的状态信息 */
#define MW_STAT_LINE 2 /* 搜集代码行级的状态信息 */
#define MW_STAT_DEFAULT 0 /* 默认状态设置 */
mwBreakOut:
当某些情况MemWatch 觉得中断(break into) 编译器更好时,就调用这个函数. 如果你喜欢使用MemWatch, 那么可以在这个函数上设置执行断点。
其他功能的使用,请参考源代码的说明。

2.3 分析日志文件
日志文件memwatch.log 中包含的信息主要有以下几点:
测试日期
状态搜集器的信息
使用MemWatch 的输出函数或宏(如TRACE 等)的信息。
MemWatch 捕获的错误信息
内存使用的全局信息统计,包括四点:1 )分配了多少次内存 2 )最大内存使用量3 )分配的内存总量 4 )为释放的内存总数
MemWatch 捕获的错误记录在日志文件中的输出格式如下:

2.4 注意事项
mwInit() 和mwTerm() 是对应的. 所以使用了多少次mwInit() ,就需要调用多少次
mwTerm() 用于终止 MemWatch.
如果在流程中捕获了程序的异常中断,那么需要调用mwAbort() 而不是
mwTerm() 。即使有显示的调用mwTerm() ,mwAbort() 也将终止MemWatch 。
size="3"
MemWatch 不能确保是线程安全的。如果你碰巧使用Wind32 或者你使用了线程,作为2.66 ,是初步支持线程的。定义WIN32 或者MW_PTHREADS 以明确支持线程。这会导致一个全局互斥变量产生,同时当访问全局内存链时,MemWatch 会锁定互斥变量,但这远不能证明是线程安全的。
3 结论
从MemWatch 的使用可以得知,无法用于内核模块。因为MemWatch 自身就使用了应用层的接口,而不是内核接口。但是,对于普通的应用层程序,还是比较有用,并且是开源的,可以自己修改代码实现;它能方便地查找内存泄漏,特别是提供的接口函数简单易懂,学习掌握很容易,对应用层程序的单元测试会较适用。
对于使用自动初始化技术有一个告诫 和 时,会导致表达式重新求值

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值