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

Mor*_*hai 6 c++ mfc memory-leaks memory-management

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

但是,我有一个讨厌的小内存损坏错误,我非常想跟踪它.我从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有任何积极意义.

如何正确替换全局new和delete运算符
是否可以在MFC应用程序的调试版本中替换内存分配器?

我已经知道了:

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

好吧,它提供了它提供的功能 - 比如让我首先沿着这条路走下去的信息.我现在知道腐败正在发生,但这非常糟糕!

我想提供的是保护分配(或者甚至只是保护重新分配).这显然可以通过使用大量虚拟地址空间并将每个分配隔离开来,这极大地浪费了内存.好吧,是的,当这是一个仅用于调试的代码时,无法看到它的缺点,就像现在这样的特殊用途时刻一样.

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

  1. 尽管提供了CRT/MFC,但强制编译器与我的全局运算符new/delete保持共存.
  2. 找到另一种方法将MFC/CRT _heap_alloc_dbg链连接到底部,使用我自己的代码代替他们,用于笔的最终分配(即我将通过操作系统的VirtualAlloc/VirtualFree分配为新的和/或提供内存malloc的).

有没有人知道答案,或者阅读的好文章可能会说明如何实现这些目标?

其他想法:

  1. 使用thunk技术在运行时替换CRT的new/delete.
  2. 还有其他一些方法吗?!

进一步的调查:

  • 这篇文章很酷......它为我提供了一种在运行时修补全局新/删除操作符的方法.但是,正如文章所指出的那样,它有点hackish(但是,因为我只需要这个用于调试版本,这不是什么大问题)http://zeuxcg.blogspot.com/2009/03/fighting-against-crt-堆和- 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日更新:

Ben*_*Ben 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 个块上中断!分配,因此,如果您可以获得相当一致的重现,因此每次都会损坏相同的数字块,那么您应该能够使用“第 n 个中断”功能来获取分配时的完整调用堆栈。

您可能还会发现_CrtCheckMemory了解腐败是否更早发生的情况很有用。只需定期调用它,一旦您将错误括起来(错误没有发生在一个错误中,确实发生在另一个错误中),请将它们移得越来越近。