C ++ - 在调试期间无法使用“新”运算符
Posted
技术标签:
【中文标题】C ++ - 在调试期间无法使用“新”运算符【英文标题】:C++ - using 'new' operator cannot be stepped into during debug 【发布时间】:2012-03-07 15:49:13 【问题描述】:COM+ 应用程序,在 Windows XP SP 3 上使用 MS Visual Studio 6、SP 6 构建,以及远程调试。
我的主要问题是这个;如果我可以进入“删除”,为什么我无法进入“新”?我主要是在寻找关于我应该研究什么的想法。
我正在从事一个相当大的项目,我只是刚刚开始熟悉。当前的问题是堆损坏问题,其中发布版本最终会耗尽其工作集并崩溃。这个问题非常普遍,以至于下面的代码会破坏堆:
int * iArray = new int [100];
delete [] iArray;
我说“损坏堆”是因为调试输出在“删除”时显示“heap[dllhost.exe]: invalid address specified to rtlvalidateheap”。
我可以正常地进入“删除”调用,它似乎调用了正确的调用(位于 ...\Microsoft Visual Studio\VC98\CRT\SRC 中的 DELOP.cpp 中)但是,无论出于何种原因,我无法介入对“新”的任何调用。我在这里抓住了稻草,但我感觉代码库中的某个地方有人覆盖了“新”运算符,而我正在查看的代码无意中使用了它。发布版本的症状似乎是内存被分配给一个堆并试图从另一个堆中删除,至少这是我的预感。
编辑: 确认!对不起大家,我发的太早了,应该提供更多信息。
我搜索了代码库并找到了一些覆盖,但它们都在类中,而不是全局定义的。唯一不在一个类中的是:
struct _new_selector
;
inline void* operator new(size_t, void *ptr, _new_selector)
return (ptr);
但这是一个新的展示位置,我很确定在这种情况下它不计算在内。我应该为最初的“新”进入哪个库?我猜它与“删除”相同,但如果不是,也许我只是没有调试信息?
编辑 2: 解决这个问题我发现在调试版本中这个问题不存在。我已经查看了运行时库,它使用 /MD 和 /MDd 进行调试。不仅如此,我还使用 /MDd jut 构建了发布版本,以确保仍然没有任何变化。查看调试构建和发布构建的映射,新操作符(带有它的修饰)如下:
发布:
0001:00061306 ??2@YAPAXI@Z 10062306 f MSVCRTD:MSVCRTD.dll
0002:00000298 _imp??2@YAPAXI@Z 1006e298 MSVCRTD:MSVCRTD.dll
调试:
0001:00077d06 ??2@YAPAXI@Z 10078d06 f MSVCRTD:MSVCRTD.dll
0004:00000ad4 _imp??2@YAPAXI@Z 100b5ad4 MSVCRTD:MSVCRTD.dll
我还检查了删除运算符:
发布:
0001:000611f0 ??3@YAXPAX@Z 100621f0 f msvcprtd:delop_s.obj
调试:
0001:00077bf0 ??3@YAXPAX@Z 10078bf0 f msvcprtd:delop_s.obj
另外,我没有打印出来,但如果有帮助,我可以得到它,新运算符的反汇编在发布和调试中看起来相同。所以我想这不是覆盖?运算符的内联覆盖会使这不正确吗?
另外,作为一个产生多个 dllhost.exe 进程的 COM+ 应用程序,是否可以调用 new 运算符转到另一个 DLL 或 exe,而调用 delete 转到相反的位置?
【问题讨论】:
您是否先检查指针是否为空?我知道今天删除一个 NULL 指针应该是无害的,但是 VC6 太老了我不知道当时是否还可以...... 你可以在所有代码中搜索operator new
,看看它是否在某个类中被重载了。
你能搜索 operator new override 吗?
至少暂时切换到反汇编视图应该可以准确地向您展示它在做什么。
【参考方案1】:
根据你的预感,代码中可能有一个重载的 new,你可以检查的东西很少
-
反汇编代码并找出该文件中的库名称,通常汇编中有一些东西会给你一个提示
如果您无法找到库名称,请检查您正在输入的程序集中的地址。然后在调试输出窗口中,检查各种库的加载地址 - 这可以为您提供关于要检查哪个库的线索
如果上述方法没有帮助,请检查是否可以为整个项目生成地图文件。如果可以,那么您可以在地图文件中查找地址,这可能会有所帮助
尝试使用运行时库的调试版本。不记得打开 debug_malloc 的选项是什么。这可以帮助你弄清楚堆上发生了什么
社区可以添加更多我可能错过的内容。最后,如果您确实解决了问题,请分享您是如何解决的。在这里或作为您博客的链接。处理大型项目的堆问题通常并不容易,我们都可以学习一两个新技巧。
【讨论】:
【参考方案2】:好的,这就是它最终的样子。
我正在从事的项目有大约三十个左右的项目。有些创建库,有些创建 dll,还有一些创建 exe。无论如何,这一切都是交织在一起的。再加上我们使用 ATL 和 COM 的事实,它很快就开始变得疯狂。最终结果是,部分(并非全部)项目是使用 _ATL_MIN_CRT 编译器定义构建的,即使这是一个桌面应用程序,而不是客户端需要下载一些模块的基于 Web 的应用程序。
以下是关于 _ATL_MIN_CRT 的一些信息:
•http://support.microsoft.com/default.aspx?scid=kb;EN-US;q166480
•http://msdn.microsoft.com/en-us/library/y3s1z4aw%28v=vs.80%29.aspx
请注意,从第一个链接开始,这也将消除内存分配例程的使用。我不确定使用它的最初动机是什么,或者它是否真的是故意的,但它肯定会导致分配内存的问题。此外,这只会影响发布版本,因此很难找到它。
基本上,模块 A 是使用 _ATL_MIN_CRT 构建的,而模块 B 是在没有它的情况下构建的,但依赖于模块 A。由于这也使用 COM 并且所有内容都在 dllhost.exe 中运行,因此当模块 B 尝试使用 new 运算符时它似乎已经脱离了它的 dll 来尝试在堆上分配内存。然后,当调用 delete 时,它试图在其 dll 中删除它。因此,我们有一个疯狂的内存泄漏。
请注意,删除 _ATL_MIN_CRT 可以解决此问题,但我上面提到的只是我对它的理解。它可能更复杂/更简单,但无论如何,这就是问题所在。
感谢大家的建议,他们确实帮我找到了这个东西!
【讨论】:
以上是关于C ++ - 在调试期间无法使用“新”运算符的主要内容,如果未能解决你的问题,请参考以下文章
当 AutoLayout 在调试控制台中记录无法满足的约束时,如何使我的应用程序崩溃?
调试期间无法在 Eclipse 的表达式视图中使用 org.apache.common.lang3.builder.toStringBuilder