1

我们在 Windows CE 和 Windows Mobile 上运行一个相当复杂的软件,用于在不同设备类型上进行移动数据采集。在唯一安装了 Windows CE 6.0 的设备类型上,我们的客户端随机冻结操作系统(因此需要热启动)。客户端可能会在冻结之前运行良好的一两天,但也可能是五分钟(已经检查了句柄和内存泄漏)。在设备制造商的日志文件中,设备冻结时会出现此类条目:

异常“数据中止”(4):线程 ID = 070a003e(pth = 89ca07e0),过程 ID = 0709003e(pprc = 8a01d3d0)'OurClient.exe',VM-active = 0709003e(pprc = 8a01d3d0)'OurClient.exe ' PC=41a66b28(mscoree3_5.dll+0x00056b28) RA=41a64ab4(mscoree3_5.dll+0x00054ab4) SP=0003e28c, BVA=​​00000132

这些消息有时会有所不同(我想说到目前为止我统计了 20 条不同的消息,但 kernel.dll、k.core.dll 或 nk.exe 中的消息除外)。

所以我的问题基本上是,如何调试在.NET框架和内核深处发生的这种错误?例如,如何将程序计数器转换为 mscorlib 中的方法(返回地址相同)?我们的程序是否可能不适用于 CE 6,或者这也可能是驱动程序问题?

更新:事实证明,其中一个设备驱动程序干扰了我们的键盘挂钩实现。

4

2 回答 2

2

老实说,我不相信你所得到的(在 MS 之外)有可能调试这样的本机异常并找出当时在 mscoree3_5 中被调用的内容。自从我考虑这样做已经有一段时间了,但我确实记得发现有一个原因无法做到这一点。也许是因为我没有调试符号,或者是 .NET 运行时的其他性质,我不记得了。

但是,我已经成功翻译了数据中止消息以理解异常的含义,这有助于...

在你的例外:

RA:返回地址 BVA:基本虚拟地址 PC:程序计数器 SP:堆栈指针 FSR:故障状态寄存器

我有点惊讶你的例外不包括 FSR,你截断它了吗?这有助于找到错误对齐的读取等。我有一个很好的链接,可以帮助描述如何调试这些消息:

打印的异常字符串 - 所有这些标志是什么意思?

于 2013-01-30T19:14:56.953 回答
2

正如 Alan 指出的那样,如果你没有符号作为事情发生的来源(并且mscoree3_5.dll你没有),那么中止信息就毫无用处了。即使有源代码,如果没有编译器符号输出,您也无法将其退回。

在这一点上,您只能进行有根据的猜测。异常信息看起来都有效(即 RA 或 SP 非零)这一事实向我表明这不是堆栈问题,更可能是数据问题(可能是对齐,可能是错误的读取或写入指针)。

我的猜测是它来自不正确的 P/Invoke。它“移动”的事实表明,由于收集或压缩,传递给 P/Invoke 的对象引用或地址可能会变得无效。

想象以下场景。

您有一个本机 API,它接收指向某些数据 blob 的指针,该 API 不仅会立即使用,而且会定期使用。也许它会从中读取或写入,但关键是 API 不仅需要在调用时同步数据。API 必须存储该指针以供以后使用。

您创建一些通过 P/Invoke 调用此 API 的托管代码。要传递数据指针,您需要定义一个表示数据的类,创建该类的实例并将其传递。假设为了这个例子,地址是 0x500。

您运行您的应用程序,调用 API,一切正常。API 从 0x500 读取并开始其业务。

直到应用触发 GC。现在 GC 说“嘿,我在堆中有一些空白空间,我会移动一些东西来解决这个问题”。它移动托管对象,使其现在位于 0x200 并释放 0x500 处的内存。在那之后的某个时刻,API 转到它的指针,仍然在 0x500 并进行读取。操作系统说“嘿,那个未分配的空间,你不能那样做!” 它中止了。

这种情况的解决方法是使用固定 GCHandle。您无需将类传递给 API,而是固定类并传入 GCHandle 的地址,GC 在收集或压缩期间无法移动该地址。这确保了 GCHandle 的地址保持不变,并且可以安全地通过本机边界。

请注意,这种情况发生时根本没有使用不安全代码,尽管您可以使用不安全代码做同样的事情。事实上,我认为对于不安全的代码,您可能会更清楚它可能发生的位置,并且可能比未标记为不安全的代码“更安全”。避免使用unsafe关键字并不能防止不安全的代码。

于 2013-01-31T15:17:20.400 回答