一、问题: 我有几个免费项目,和任何软件一样,它们包含错误。遇到错误时,一些其他用户会向我发送带有堆栈跟踪的错误报告。为了简化查找错误位置,我想在此堆栈跟踪中查看行号。如果应用程序在没有 .pdb 文件的情况下交付,那么所有行信息都会丢失,因此目前我所有使用 .pdb 文件部署的项目,因此生成的堆栈跟踪都有这个数字。 但!但我不想在分发中看到这些文件,并希望删除所有 .pdb。它们会使用户感到困惑,占用安装程序空间等。
德尔福解决方案: 很久以前,当我还是一名 Delphi 程序员时,我使用了以下技术:出现异常时,我的应用程序在堆栈上行走并收集地址。然后,当我收到错误报告时,我使用了一个工具,根据收集到的地址和位于我的机器上的相应符号文件,使用函数名和行号重建有效的堆栈跟踪。
问题: 是否有任何库、技术或其他任何东西可以在 .NET 中做同样的事情?
状态更新:非常有趣,经常提出问题是开始自己调查的最佳方式。例如,我考虑这个问题有一段时间了,但几天前才开始寻找答案。
选项 1:小型转储。经过大量谷歌搜索后,我找到了一种从代码创建小型转储的方法,以及如何从托管小型转储重新创建堆栈的方法。
- 用于创建小型转储表单代码的可再发行程序集 - clrdump
- 关于使用以前的程序集的博文 - Creating and analyzing minidumps in .NET production applications
然而,此解决方案需要重新分发两个额外的程序集(大小约为 1mb),并且迷你转储会占用一些空间,并且用户通过电子邮件发送它们会很不舒服。所以就我的目的而言,现在这是 Not Acceptable 。
选项 2:感谢 weiqure 提供线索。可以为每个堆栈帧提取托管 IL 偏移量。现在的问题是如何根据此偏移量从 .pdb 获取行号。我发现了什么:
- PDB File Internals ,仅供引用,因为:
- ISymbolReader - 读取程序数据库文件的托管接口(interface)
- 最后是 tool to convert .pdb files to structured xml便于 xpath 处理
使用此工具,可以为每个发布版本创建 xml 文件并将它们放入存储库中。当用户机器上发生异常时,可以创建带有 IL 偏移量的格式化错误消息。然后用户通过邮件发送此消息(非常小)。最后,可以创建一个简单的工具,从格式化的错误消息中重新创建结果堆栈。
我只是想知道为什么没有其他人不实现这样的工具?我不相信这对我来说很有趣。
最佳答案
您可以使用 System.Diagnostics.StackTrace 从异常中获取最后一个 MSIL 指令的偏移量:
// Using System.Diagnostics
static void Main(string[] args)
{
try { ThrowError(); }
catch (Exception e)
{
StackTrace st = new System.Diagnostics.StackTrace(e);
string stackTrace = "";
foreach (StackFrame frame in st.GetFrames())
{
stackTrace = "at " + frame.GetMethod().Module.Name + "." +
frame.GetMethod().ReflectedType.Name + "."
+ frame.GetMethod().Name
+ " (IL offset: 0x" + frame.GetILOffset().ToString("x") + ")\n" + stackTrace;
}
Console.Write(stackTrace);
Console.WriteLine("Message: " + e.Message);
}
Console.ReadLine();
}
static void ThrowError()
{
DateTime myDateTime = new DateTime();
myDateTime = new DateTime(2000, 5555555, 1); // won't work
Console.WriteLine(myDateTime.ToString());
}
输出:
at ConsoleApplicationN.exe.Program.Main (IL offset: 0x7)
at ConsoleApplicationN.exe.Program.ThrowError (IL offset: 0x1b)
at mscorlib.dll.DateTime..ctor (IL offset: 0x9)
at mscorlib.dll.DateTime.DateToTicks (IL offset: 0x61)
Message: Year, Month, and Day parameters describe an un-representable DateTime.
然后您可以使用 Reflector或 ILSpy解释偏移量:
.method private hidebysig static void ThrowError() cil managed
{
.maxstack 4
.locals init (
[0] valuetype [mscorlib]System.DateTime myDateTime)
L_0000: nop
L_0001: ldloca.s myDateTime
L_0003: initobj [mscorlib]System.DateTime
L_0009: ldloca.s myDateTime
L_000b: ldc.i4 0x7d0
L_0010: ldc.i4 0x54c563
L_0015: ldc.i4.1
L_0016: call instance void [mscorlib]System.DateTime::.ctor(int32, int32, int32)
L_001b: nop
L_001c: ldloca.s myDateTime
L_001e: constrained [mscorlib]System.DateTime
L_0024: callvirt instance string [mscorlib]System.Object::ToString()
L_0029: call void [mscorlib]System.Console::WriteLine(string)
L_002e: nop
L_002f: ret
}
你知道0x1b之前的指令抛出了异常。很容易找到它的 C# 代码:
myDateTime = new DateTime(2000, 5555555, 1);
您现在可以将 IL 代码映射到您的 C# 代码,但我认为 yield 太小,工作量太大(尽管可能有反射器插件)。 IL 偏移量应该没问题。
关于c# - 使用 .net 中用户错误报告的行号重新创建堆栈跟踪?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1037925/