由使用LeakDialog时遇到的问题而引出的一些分析

标签: leakdialog 问题 分析 | 发表时间:2014-02-12 04:12 | 作者:ithzhang
出处:http://blog.csdn.net

                            由使用LeakDialog时遇到的问题而引出的一些分析

 

     前段时间在使用leakDialog检测调用malloc和new所分配的内存泄露时,发现其根本不起作用!这让我百思不得其解!周末有时间研究了一下终于弄清了原因所在。本着分享的精神,将其写成博文,希望对大家有用。

 

       LeakDialog是用于内存泄露检测的常用工具。使用LeakDialog不需要添加任何代码,就可以捕获各种形式的内存泄露。同时它还能显示执行内存分配的栈回溯以及内存分配的统计信息。

其使用方法很简单,在此不再介绍它的使用方法,有不懂的可以查找其他资料。

      LeakDialog支持6种不同的分配器:

        虚拟内存分配器

        堆分配器

          MPHeap分配器

         COM的AllocatorCoTaskmem分配器

         COM的私有分配器

         C运行时分配器

         我们在使用拦截new和malloc分配的内存泄露时就是使用了C运行时(CRT C Run Time)分配器。

 

                                            LeakDialog原理

            LeakDialog通过微软的Detours库来拦截对内存分配和释放操作的调用,如malloc或free函数。Microsoft Detours库是在二进制级别对现有代码进行修改。其主要原理为:在程序执行过程中将所要拦截的函数的起始位置的若干指令替换为一条无条件跳转指令。该修改是动态修改的,不会修改二进制文件。

            LeakDialog通过上述技术拦截每一次的内存分配和释放操作。点击log按钮时LeakDialog会找出已经被分配但还未被释放的内存操作。

          今天我们仅仅讨论LeakDialog的C运行时分配器对调用malloc和new分配的内存泄露的检测。将通过以下几个测试用例一步步深入挖掘。

 

实验一:测试Leakdialog对内存泄露的检测。

使用vc6.0编写的控制台程序。使用默认配置,主要代码如下:

 

#include <iostream>
void func2()
{
       int *p = new int[200];
       if(p)
        {

             std::cout<<"func2 sucecss!"<<std::endl;
       }
}

void func3()

{
       char *p = (char*)malloc(sizeof(char) * 100);
       if(p)
       {
              std::cout<<"func3 success!"<<std::endl;
       }
}

void func1()

{
       func2();
       func3();
}

int main()

{

       std::cout<<"by ithzhang---------blog.csdn.net/ithzhang"<<std::endl;

       getchar();

       func1();

       getchar();

       return 0;

}


 

      编译运行后,使用LeakDialog进行监视,在执行完func1后点击log按钮,发现没有产生日志文件。这说明LeakDialog没有检测到内存泄露。通过代码我们可以发现程序明明出现了两处内存泄露。这说明LeakDialog没有起作用。相信很多童鞋在刚接触LeakDialog时都会进行类似的测试。但原因究竟为何?

       这里不卖关子了。由于控制台程序默认配置使用静态链接CRT(C运行时)库,而LeakDialog的C运行时分配器仅仅拦截位于msvcrt.dll中的指定的内存分配操作。这是导致该问题的罪魁祸首!

       选择工程->设置->C++ ->Code generation 我们看到了工程的默认配置: Userun-time library选项的值为Debugsingle-Thread表示程序在链接时使用c运行库的静态版本。如下图:

                              

 

        要使程序在运行时动态的加载CRT库,只需将Use run-time library修改为Multithreaded DLL或Debug Multithreaded DLL。Debug表示用于调试的dll。如下图所示:

                

 

 

       修改配置后再次执行刚才的操作,这次我们看到log文件产生了,使用ie打开后,如下图:

                            

    上图显示出了两次内存泄露操作,分别位于func2和func3中。由此我们很容易的找出内存泄露的位置。

注意在LeakDialog中配置调试符号所在目录,否则function和filename将会显示为空,同时不能选中Use DebugHelp Stack walk API to Walk stacks。

 

实验二:验证LeakDialog原理

       1.唤出windbg,选择open Executable 选择实验一产生的控制台程序。由于在本地生成,因此不要配置调试符号和源代码路径。

       2.程序暂停在了在了ntdll.dll中的初始断点处。输入bp msvcrt!malloc命令并回车,在位于msvcrt.dll中的malloc函数的入口处设置软件断点。

       3.按F5继续执行,并让程序执行func1.

       4.可以看到程序停在了msvcrt.dll的malloc函数入口处。

      如下图,我们看到了malloc的汇编代码。

                    

       再次执行上述1-4,只是在2和3之间打开LeakDialog开启CRT allocator监视该进程。

       这一次程序仍然执行到malloc入口处,如下图:

                     

      比较上述两图,我们可以发现第二张截图malloc的第一条指令被替换成了jmp指令。

上述被替换的指令就是LeakDialog为了监视位于msvcrt.dll中的malloc函数而插入的。感兴趣的童鞋可以继续分析jmp指令后的一些操作。

     通过同样的方法我们同样可以得到对new的调用:

                     

       开启LeakDialog后,operator new函数入口的指令被替换,如下图:

                    

实验三:vc2005编译上述代码,重复做实验二(由于vc2005默认配置为动态链接CRT库,因此不需要修改配置)。

发现并没有生成log,说明leakdialog并没有检测到内存泄露。实验继续进行。

      使用windbg打开生成的exe。在调用func2之前打开leakdialog对该进程进行监视,并在msvcr80D.dll的malloc处设置断点。

      执行func2,发现程序并未停到断点处,输入u msvcrt80D!malloc 对位于msvcrt80D.dll中的malloc进行反汇编,发现其第一条指令并未被替换,如下图:

                            

        而接下来的情况更让人难以置信:输入u msvcrt!malloc 对位于msvcrt.dll中的malloc进行反汇编,如下图:

                   

     可以发现位于msvcrt.dll中的malloc的第一条指令被Leakdialog替换了无条件跳转指令。

查看operator new也发现了类似的情况:

                              

 

       msvcrt.dll中的operator new的替换,如下图:

                  

 

      这究竟是为何?

        参考帮助文档,有下面一句话:

  The C Runtime Allocator tracks the following calls from MSVCRT.DLL:

  •  malloc,
  • calloc,
  • realloc,
  • free,
  • new,
  • new[],
  • delete and
  • delete[]

 

       这次终于真相大白,原来Leakdialog仅仅拦截位于msvcrt.dll中的内存分配函数。

      由于msvcrt.dll是vc6.0使用的CRT库,因此leakdialog仅仅对使用vc6.0编写的程序有效。因此对于由vc2005编译的使用msvcr80.dll的程序的内存泄露,leakdialog无法检测。

      原因或许就是leakdialog版本太老所致,那时候vc2005还未出现。

      本次实验结束,你看懂了吗?

                                                                           2014.2.11于浙江杭州

作者:ithzhang 发表于2014-2-11 20:12:56 原文链接
阅读:8 评论:0 查看评论

相关 [leakdialog 问题 分析] 推荐:

由使用LeakDialog时遇到的问题而引出的一些分析

- - CSDN博客推荐文章
                            由使用LeakDialog时遇到的问题而引出的一些分析.      前段时间在使用leakDialog检测调用malloc和new所分配的内存泄露时,发现其根本不起作用!这让我百思不得其解. 周末有时间研究了一下终于弄清了原因所在. 本着分享的精神,将其写成博文,希望对大家有用.

redis超时问题分析

- - 搜索技术博客-淘宝
Redis在分布式应用中占据着越来越重要的地位,短短的几万行代码,实现了一个高性能的数据存储服务. 最近dump中心的cm8集群出现过几次redis超时的情况,但是查看redis机器的相关内存都没有发现内存不够,或者内存发生交换的情况,查看redis源码之后,发现在某些情况下redis会出现超时的状况,相关细节如下.

Redis时延问题分析及应对

- - 博客 - 伯乐在线
Redis的事件循环在一个线程中处理,作为一个单线程程序,重要的是要保证事件处理的时延短,这样,事件循环中的后续任务才不会阻塞;. 当redis的数据量达到一定级别后(比如20G),阻塞操作对性能的影响尤为严重;. 下面我们总结下在redis中有哪些耗时的场景及应对方法;. keys、sort等命令.

机房流量问题总结分析

- - 企业架构 - ITeye博客
凌晨 3:00 点某公司(网站业务)的一个 IDC 机房带宽流量突然从平时高峰期 150M 猛增至 1000M ,如下图:. 该故障的影响:直接导致数百台服务器无法连接,该机房全部业务中断. 某年某月某日夜老男 1 孩接到学生紧急求助,公司网站( web 游戏业务)平时几十 M 带宽,结果突然跑满 100M ,持续 100M 已经很久.

缓存击穿问题分析

- - 数据库 - ITeye博客
缓存一般作为RDS的前置组件,将常用的资源缓存,用来减少RDS的读取压力,也是诸多系统常用的一种方案,如果允许访问缓存失败直接访问数据库,然后再将数据回写到缓存中,那么就会存在缓存击穿的问题,. 缓存击穿:缓存中的数据未被命中,进而请求直接对数据库进行查询,当大量的类似查询瞬间出现,就会出现数据库的压力爆炸甚至引起数据库的雪崩,本质就是一种缓存失效引发的极端问题.

深入分析 Java 中的中文编码问题

- Alex - IBM developerWorks 中国 : 文档库
编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切换较多. 本文将向你详细介绍 Java 中编码问题出现的根本原因,你将了解到:Java 中经常遇到的几种编码格式的区别;Java 中经常需要编码的场景;出现中文问题的原因分析;在开发 Java web 程序时可能会存在编码的几个地方,一个 HTTP 请求怎么控制编码格式.

再谈问题分析和解决思路

- Typhoon - 人月神话的BLOG
如果从问题层面来将工作中的能力,那么一个是独立解决问题的能力,一个是自己提出假设创造问题自己解决的能力. 前者足以应对复杂多变的内外环境,后者足以提出有价值的创新. 而独立解决问题的本身又包括两块,一个是针对问题现象提出应急解决方法,一个是针对问题根源提出的风险管理和预警机制. 问题分析和解决本身就应该是一种通过迭代不断收敛的过程,因此根源分析和机制建立就显得更加重要.

MySQL查询大小写是否敏感问题分析

- - OurMySQL
mysql数据库在做查询时候,有时候是英文字母大小写敏感的,有时候又不是的,主要是由mysql的字符校验规则的设置决定的,通常默认是不支持的大小写字母敏感的. 校对规则是在字符集内用于比较字符的一套规则. 任何一个给定的字符集至少有一个校对规则,它可能有几个校对规则. 要想列出一个字符集的校对规则,使用SHOW COLLATION语句.

java程序cpu占用过高问题分析

- - Web前端 - ITeye博客
针对某个java程序cpu占用过高问题分析,要想找到问题的真正原因,首先要明确cpu过高的进程,通过对进程下线程的分析,定位到具体的应用代码,从而定位问题的原因所在.     在jdk自带的分析工具中,通过jconsole只能分析到应用程序的相关系统资源使用情况,但无法定位应用程序,故通过此工具了解到应用程序存在问题,但要具体定位到哪块程序不合理造成的是很困难的.

Hive中跑MapReduce Job出现OOM问题分析及解决

- - CSDN博客云计算推荐文章
今天在跑一段很复杂而且涉及数据量10多年的N个表join的长SQL时,发生了OOM的异常. 由于一个map通常配置只有64MB或者128MB,则在Map阶段出现OOM的情况很少见. 所以一般发生在reduce阶段. 但是今天这个异常详细的看后,会发现既不是map阶段,也不是reduce阶段,发现不是执行过程,而是driver提交job阶段就OOM了.