如何在MFC应用程序中替换全局运算符new和delete(仅限调试)

时间:2012-05-02 00:53:51

标签: c++ memory-management memory-leaks mfc

由于我觉得它在Windows上是一个泥潭(特别是使用MFC),因此我多年来一直避免尝试使用operator new做任何事情。更不用说,除非有一个非常令人信服的理由混淆全局(甚至是类)新的和删除,否则不应该。

然而,我有一个讨厌的小内存损坏错误,我非常想跟踪它。我从CRT调试分配器获取消息,指示先前释放的内存被覆盖。此消息仅在稍后的分配调用期间显示,当它尝试重用块时(无论如何,我相信这是它的工作方式)。

由于有问题的代码部分,错误消息和损坏点非常无关。我所知道的只是"某些东西覆盖了一些以前用一个空字节释放的内存。" (我通过使用调试器并在几次不同的运行中观察调试堆引用的内存来确定这一点。)

对于罪魁祸首可能出现的明显想法已经筋疲力尽,我离开了尝试做更严谨的事情。在我看来,如果我可以使每个释放的块成为无法访问的内存页面,那么编写器将立即被CPU的MMC捕获,这将是理想的!稍后再进行一些搜索,我发现有人在这些方面实施了一些东西:

http://www.codeproject.com/Articles/38340/Immediate-memory-corruption-detection

他的代码被埋没在大量的重新编码代码中,但提取核心概念非常简单,我已经这样做了。

我现在遇到的问题是MFC将新的redfines重新定义为DEBUG_NEW,然后进一步定义了一系列调试接口,直到CRT。此外,它确实定义了全局运算符new和delete。因此,就C ++而言,"用户"正在尝试将全局运算符new和delete两次替换,因此我在已定义的效果符号中出现了链接器错误。'

环顾互联网,以及SO,我看到了一些有前途的文章,但最终没有任何关于用MFC替换全局运算符new / delete的说法。

How to properly replace global new & delete operators
Is it possible to replace the memory allocator in a debug build of an MFC application?

我已经知道了:

  • MFC / CRT已经为内存分配提供了丰富的调试工具。

嗯,它提供了它提供的内容 - 例如让我首先沿着这条路径滚动的消息。我现在知道腐败现象正在发生,但这种情况非常糟糕!

我想提供的是保护分配(甚至只是保护解除分配)。这显然可以通过使用大量虚拟地址空间并将每个分配隔离开来,这极大地浪费了内存。好的,是的,当这是一个仅用于调试的代码时,不能看到这样的特殊用途,就像现在一样。

所以,我正在拼命寻求以下解决方案

  1. 尽管提供了CRT / MFC,但强制编译器与我的全局运营商new / delete保持共存。
  2. 找到另一种方法将MFC / CRT _heap_alloc_dbg链连接到底部,使用我自己的代码代替他们的代码,用于笔的最终分配(即我将通过操作系统的VirtualAlloc /分配) VirtualFree为new和/或malloc提供内存。)
  3. 是否有人知道答案或阅读的好文章可能会说明如何实现这些目标?

    其他想法:

    1. 使用thunk技术在运行时替换CRT的新/删除。
    2. 完全是其他方法吗?!
    3. 进一步调查:

      • 这篇文章非常酷......它为我提供了一种在运行时修补全局新/删除操作符的方法。但是,正如文章所指出的那样,它有点hackish(但是,因为我只需要这个用于调试版本,这不是什么大问题)http://zeuxcg.blogspot.com/2009/03/fighting-against-crt-heap-and-winning.html
        • 因此虽然这是我想要的(一种替换CRT内存分配功能的机制),但这种实现已经过时了,到目前为止,我尝试使其工作已经遇到了无数问题。我认为它只是对它最初创建的版本太过黑了,而且仅用于相对简单的控制台使用(即C,甚至不是C ++,并且放弃了微软CRT提供的大部分调试功能)。因此,尽管这是一个非常酷的想法,最终会花费很多时间来与当前的VS2010开发工作室合作,因此不值得(对我而言)。
      • 显然有一个众所周知的这个想法的版本:http://en.wikipedia.org/wiki/Electric_Fence遗憾的是,即使我找到的Windows端口http://code.google.com/p/electric-fence-win32/也无法正确覆盖CRT,但要求您修改所有源代码访问电栅栏堆分配代码。 :(

      2012年5月3日更新:

      • 现在我发现Windows已经提供了Electric Fence的实现,可以通过GFLAGS调试工具http://support.microsoft.com/kb/286470访问。这可以在被测试的应用程序外部打开和关闭。它与我感兴趣的技术基本相同,并且具有DUMA项目中的功能(电围栏的一个分支 - http://duma.sourceforge.net/

1 个答案:

答案 0 :(得分:4)

MSVCRT调试堆实际上非常好,并且有一些有用的功能,例如第n次分配的断点等。

http://msdn.microsoft.com/en-us/library/974tc9t1(v=VS.80).aspx

除此之外,你可以插入一个分配钩子,它输出调试信息等,你可以用来调试这类问题。

http://msdn.microsoft.com/en-us/library/z2zscsc2(v=vs.80).aspx

在您的情况下,您真正​​需要做的就是输出每个分配的地址,文件和行。然后,当您遇到损坏的块时,找到其地址紧接在其之前的块,这几乎肯定是覆盖的块。您可以使用Visual Studio调试器中的内存视图查看已损坏的内存地址,并查看前面的块。这应该告诉你需要知道的所有内容,以找出分配的时间。

调试堆在每个分配的块上也有一个数字分配ID,并且可以在第n个中断!分配,所以如果你可以获得一个相当一致的repro,那么每次都会损坏相同的数字块,那么你应该能够使用“break on nth”功能在分配时获得一个完整的调用堆栈。 / p>

您可能还会发现_CrtCheckMemory有助于了解腐败是否发生得更早。只需定期调用它,一旦你把bug放在括号内(错误没有发生在一个中,确实发生在另一个中)将它们移得越来越近。