12

我们使用 System.Reflection.Emit 在运行时从源代码生成代码(是的 - 就像在编译器中一样)。我们使用 MarkSequencePoint 等向 ILGenerator 提供正确的符号信息,并启用 AssemblyBuilder 上的所有调试标志。程序集在编译它的同一进程中保存在内存中并直接执行。

当使用 Visual Studio 调试器单步调试动态生成代码的源代码时,它实际上工作得很好,而且 Visual Studio 似乎完全知道代码来自文件和行号的位置。

但是 - 当生成的代码抛出异常,System.Exception 对象包含完全错误的堆栈跟踪。它们指向其他(有效但错误的)文件和行号。它正确地获取了类和方法名,但指向的文件和行号与异常实际来自的代码路径无关。

指向的文件是如此无关,似乎无法链接到内联或优化。我能发现的唯一模式是它似乎被某些文件抵消了(在构建程序集的源文件的虚构字母顺序列表中)。然而,这种模式并不是 100% 一致的,这与问题的根源有关似乎是不合理的。

如果我从异常构造一个 System.Diagnostics.Debug 对象,它包含相同的错误信息。

我假设 .NET 运行时使用与调试器用于单步执行代码的元数据相同的元数据来构造异常堆栈跟踪,在这种情况下,这种行为真的很奇怪。

我试图找出在处理动态内存程序集时这是否是 .NET 中的一个已知错误,或者是否有人在其他领域看到过类似的问题。

4

1 回答 1

2

好的,我无法弄清楚导致问题的原因以及如何使 .NET 正常运行,但至少我能够找到一种可能也适用于遇到相同问题的其他人的解决方法。

  1. 在生成 CIL 字节码时,我正在构建一个单独的数据库,将方法名称(完整路径)和 IL 偏移映射回原始文件名和行号。

  2. 当异常被捕获时,我检查堆栈跟踪并仅使用堆栈帧对象中的GetMethod()GetILOffset()信息。GetFileName()来自 CLR 的信息恰好是正确的,即使GetFileLineNumber()是错误的。

  3. 然后,对于每个堆栈帧,我使用从异常中检索到的方法名称和 IL 偏移量来查找我生成的数据库,以确定我掌握的每个堆栈帧的实际文件名和行号。

  4. 我在数据库中找不到相关信息的帧通常是预编译的 .NET 模块中的堆栈帧,从 CLR 检索到的信息实际上是正确的。对于这些框架,我直接在堆栈框架上使用GetFileName()和。GetFileLineNumber()

于 2014-01-14T16:43:25.130 回答