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

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

清单 1. 内存样本(test1.c) #include 
#include 
#include "memwatch.h"
int main(void)
{
char *ptr1;
char *ptr2;
ptr1 = malloc(512);
ptr2 = malloc(512);
ptr2 = ptr1;
free(ptr2);
free(ptr1);
}

清单 1 中的代码将分配两个 512 字节的内存块,然后指向第一个内存块的指针被设定为指向第二个内存块。结果,第二个内存块的地址丢失,从而产生了内存泄漏。
现在我们编译清单 1 的 memwatch.c。下面是一个 makefile 示例:
test1 gcc -DMEMWATCH -DMW_STDIO test1.c memwatch
c -o test1

当您运行 test1 程序后,它会生成一个关于泄漏的内存的报告。清单 2 展示了示例 memwatch.log 输出文件。

清单 2. test1 memwatch.log 文件   MEMWATCH 2.67 Copyright (C) 1992-1999 Johan Lindh
...
double-free:  test1.c(15), 0x80517b4 was freed from test1.c(14)
...
unfreed:  test1.c(11), 512 bytes at 0x80519e4
{FE FE FE FE FE FE FE FE FE FE FE FE ..............}
Memory usage statistics (global):
N)umber of allocations made:  2
L)argest memory usage :  1024
T)otal of all alloc() calls:  1024
U)nfreed bytes totals :  512

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

linux下的测试工具真是少之又少,还不好用,最近试用了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捕获的错误记录在日志文件中的输出格式如下:
message: <sequence-number> filename(linenumber), information

2.4 注意事项
mwInit()和mwTerm()是对应的.所以使用了多少次mwInit(),就需要调用多少次
mwTerm()用于终止MemWatch.
如果在流程中捕获了程序的异常中断,那么需要调用mwAbort()而不是
mwTerm()。即使有显示的调用mwTerm(),mwAbort()也将终止MemWatch。
MemWatch不能确保是线程安全的。如果你碰巧使用Wind32或者你使用了线程,作为2.66,是初步支持线程的。定义WIN32或者MW_PTHREADS以明确支持线程。这会导致一个全局互斥变量产生,同时当访问全局内存链时,MemWatch会锁定互斥变量,但这远不能证明是线程安全的。

3 结论
从MemWatch的使用可以得知,无法用于内核模块。因为MemWatch自身就使用了应用层的接口,而不是内核接口。但是,对于普通的应用层程序,我认为还是比较有用,并且是开源的,可以自己修改代码实现;它能方便地查找内存泄漏,特别是提供的接口函数简单易懂,学习掌握很容易,对应用层程序的单元测试会较适用。

内存调试 - MEMWATCH相关推荐

  1. 【Android 逆向】修改运行中的 Android 进程的内存数据 ( Android 系统中调试器进程内存流程 | 编译内存调试动态库以及调试程序 )

    文章目录 一.Android 系统中调试器进程内存流程 二.编译内存调试动态库以及调试程序 三.博客资源 一.Android 系统中调试器进程内存流程 修改游戏运行中的内存 , 游戏运行之后 , 游戏 ...

  2. Valgrind ---内存调试,内存泄漏检测以及性能分析的软件开发工具

    Valgrind是一款用于内存调试.内存泄漏检测以及性能分析的软件开发工具.Valgrind这个名字取自北欧神话中英灵殿的入口. 一般使用方式  valgrind --leak-check=full ...

  3. 检验内存泄漏memwatch的使用

    项目中检索内存泄漏,memwatch的使用 首先下载源码,可以在网上down一份源码,如果找不到可以在我的资源那下载,我已经上传. 下载后将其加入到项目中,在c盘下新建一个文件夹命名为itcast. ...

  4. #内存泄露# #memwatch# memwatch

    MemWatch是一个开放源代码 C 语言内存错误检测工具.MemWatch支持 ANSI C,它提供结果日志纪录. MemWatch 主页: https://www.linkdata.se/sour ...

  5. Windows程序调试----第三部分 调试技术----第9章 内存调试

    第9章内存调试 能够方便高效地进行动态内存分配,是C++编程语言的重要优点之一:而调试时容易错误使用动态分配的内存也是其最大的缺点之一.Windows程序也可能同样存在与系统资源泄漏或者堆栈相关的内存 ...

  6. 使用memwatch进行内存调试

    1.memwatch介绍 memwatch可以跟踪程序中的内存泄漏和错误,提供结果日志记录,能检测双重释放.错误释放.没有释放的内存.上溢和下溢等. 2.使用memwatch步骤 1)在代码中加入头文 ...

  7. STM32 使用片外外扩内存调试

    紧接上一篇,如果程序改来改去还是太大对不进STM32的内存怎么办?只能用下载到flash然后调试了吗?其实还可以下载到外扩的SRAM里,这样就几乎能搞定所有的普通程序了.唯一的缺点是在这种情况下,程序 ...

  8. 内存调试: GC_CONCURRENT freed

    GC_CONCURRENT表示触发垃圾收集的原因,有以下几种: GC_MALLOC, 内存分配失败时触发 GC_CONCURRENT,当分配的对象大小超过384K时触发 GC_EXPLICIT,对垃圾 ...

  9. GDB内存调试初探八

    Linux/amd64的调用规则 为了方便调试,笔者在PC机上直接调试简单的内存相关的应用:这需要了解x86_64的ABI,该文档对函数调用制定了一些限定规则,其中重要的有两点,第一点是参数的传参(非 ...

最新文章

  1. 68-95-99规则–以普通英语解释正态分布
  2. 暑期集训1:C++STL 练习题B:HDU-1004
  3. ​【Python基础】一文看懂 Pandas 中的透视表
  4. 在C++中使用Protocol Buffers
  5. 牛客网--字符串合并处理(Java)
  6. 深度学习在搜索业务中的探索与实践 1
  7. ANSI C标准预定义宏
  8. javascript用window open的子窗口关闭自己并且刷新父窗口
  9. 改变你一生命运的话语 不得不信
  10. find命令_用find命令查找文件目录
  11. Tensorflow自编码器及多层感知机
  12. linux下libreoffice增加字体,自由办公说:LibreOffice添加中文标点扩展
  13. 耗时5小时用纯HTML和CSS写成的博学谷
  14. 推荐一款跨平台视频播放器:ZY-Player
  15. 笔记本电脑硬盘不见了_笔记本电脑找不到硬盘原因及解决方法
  16. 安全狗再次入选中国数字安全百强报告
  17. 验证码识别之w3cschool字符图片验证码(easy级别)
  18. 关键路径例题图表_详解关键路径法,这可能是你能找到的最详尽的了
  19. HashMap 中那些精妙绝伦的设计
  20. Xiaojie雷达之路再回首---TI文档总结

热门文章

  1. mysql重启时报错 /etc/my.cnf is ignored
  2. ansible中的角色使用--nginx+持续交付和滚动升级+时间同步角色+selinux+自动添加磁盘
  3. 顾城 - 我是一个任性的孩子
  4. mysql数据库练习第二章----二表查询
  5. ARM 内核SP,LR,PC寄存器的理解
  6. 安卓QQ分享网络图片
  7. [量化-017]防雷
  8. 小型中文版聊天机器人
  9. 【HTML】笔记2-表格、列表、表单标签
  10. Code:Blocks 中文乱码问题原因分析和解决方法!