【问题标题】:How can the "Optimize code" option in a C# project cause a crash in native code?C# 项目中的“优化代码”选项如何导致本机代码崩溃?
【发布时间】:2012-06-13 21:24:34
【问题描述】:

我有一个使用 SQLite 文件作为文件格式的 Windows 窗体应用程序。这些文件由通过 C++/CLI 包装器调用的本机代码创建,其中包括对托管代码的回调以进行进度/取消更新。这对于 32 位非常有效。我正试图通过切换到 64 位来消除我的内存上限,但遇到了一个重大障碍。

当在本机代码中分配或取消分配内存时,我会遇到伪确定性崩溃,这让我想到了内存损坏。但它只发生在发布构建期间,并且仅在没有附加调试器的情况下发生。以下是我噩梦的细分:

配置位 优化 互操作 Dbg-symbols 运行时 带调试器 不带调试器 Release 32 on no no / MD no crash no crash Release 64 on yes no /MD no crash no crash Release 32 on yes no /MD no crash no crash Release 64 on yes no /MD no crash crash Release 64 on yes yes /MD no crash crash 释放 64 关 是 否 /MD 不崩溃 不崩溃 释放 64 关 是 是 /MD 不崩溃 不崩溃 Debug 32 off no yes /MDd no crash no crash 调试 32 关闭 是 是 /MDd 没有崩溃 没有崩溃 调试 64 关闭 是 是 /MDd 没有崩溃 没有崩溃 调试 64 on yes yes /MD no crash no crash 调试 64 on yes no /MD no crash no crash 调试 64 关 是 是 /MD 不崩溃 不崩溃 调试 64 关闭 是 是 /MDd 没有崩溃 没有崩溃 Debug 64 on yes yes /MDd no crash no crash 调试 64 on yes no /MD no crash no crash

“互操作”意味着我正在使用 GUI 应用程序中的 C++/CLI 包装器来运行本机解析器代码。我有一个用本机 C++ 编写的命令行驱动程序,在任何配置中都不会崩溃。

基本上调试配置永远不会崩溃,即使本机代码完全像发布代码一样编译!我比较了响应文件,它们是相同的,除了我为调试配置添加了 _NO_DEBUG_HEAP=1 。我怀疑在使用 /MD 和定义 NDEBUG 时会产生任何影响。那么我该如何调试这个噩梦呢?感觉这不是我的代码的问题。请不要要求一个小的复制品,我不知道如何制作它。但是代码都是开源的,所以如果有人想尝试复制它,我会发布一个指向源代码的链接。

【问题讨论】:

  • 添加Trace::WriteLine 以将崩溃隔离到特定位置。一旦你知道了位置,你就会找出原因。
  • @RomanR.:我如何在本机代码中添加它?我使用日志记录将崩溃隔离到代码的某些部分;这就是我知道它可能与分配或释放内存有关的方式。清除 vector> 时,一个配置崩溃了,但这实际上不应该释放任何内存,它应该只是减少了所有 ptr 的引用计数(它们都保存在其他地方)。我希望这很容易。 :(
  • 更新:我发现可以通过关闭 C# 项目中的“优化代码”来避免崩溃(这就是为什么带有本机优化的调试配置可以正常工作的原因)。这到底怎么会导致本机代码崩溃?!
  • 这听起来像是原生端的未初始化堆栈变量。禁用优化时,由 .NET 端初始化的堆栈部分可能与启用时不同。
  • 另外,检查是否可能需要pinning

标签: c# visual-studio-2010 debugging c++-cli


【解决方案1】:

Heisenbugs 通常是由内存踩踏(在托管代码中不会发生)或将内存中的垃圾视为有效数据引起的。

我已经搜索了多个通常可重现的 heisenbug,这些问题归结为使用未初始化的变量,该变量会从另一个例程中获取堆栈中的任何内容。您的本机代码可能包含这样一个错误,当它获得一个值时会起作用,而当它获得另一个值时会崩溃。 Debug vs release 并不能保证幕后的东西是一样的(毕竟,如果是这两种配置,为什么会有这两种配置呢?)而且你的内存中可能有不同的垃圾。

优化器也可以产生这样的效果。

我会尝试一件事:在本地代码中的第一个例程的局部变量中添加一个实质性结构。对它做一些事情以防止链接器剥离它。

这将移动事物,如果它是内存中的垃圾问题,这应该会改变行为。

【讨论】:

  • 为什么在我的非互操作情况下调试堆不会捕获本机代码中的此类错误?
  • @MattChambers:另一个版本可能会在内存中留下可接受的内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-26
  • 1970-01-01
  • 1970-01-01
  • 2015-02-18
  • 1970-01-01
相关资源
最近更新 更多