c# - 使用 .net 中用户错误报告的行号重新创建堆栈跟踪?

标签 c# .net debugging stack-trace

一、问题: 我有几个免费项目,和任何软件一样,它们包含错误。遇到错误时,一些其他用户会向我发送带有堆栈跟踪的错误报告。为了简化查找​​错误位置,我想在此堆栈跟踪中查看行号。如果应用程序在没有 .pdb 文件的情况下交付,那么所有行信息都会丢失,因此目前我所有使用 .pdb 文件部署的项目,因此生成的堆栈跟踪都有这个数字。 但!但我不想在分发中看到这些文件,并希望删除所有 .pdb。它们会使用户感到困惑,占用安装程序空间等。

德尔福解决方案: 很久以前,当我还是一名 Delphi 程序员时,我使用了以下技术:出现异常时,我的应用程序在堆栈上行走并收集地址。然后,当我收到错误报告时,我使用了一个工具,根据收集到的地址和位于我的机器上的相应符号文件,使用函数名和行号重建有效的堆栈跟踪。

问题: 是否有任何库、技术或其他任何东西可以在 .NET 中做同样的事情?

状态更新:非常有趣,经常提出问题是开始自己调查的最佳方式。例如,我考虑这个问题有一段时间了,但几天前才开始寻找答案。

选项 1:小型转储。经过大量谷歌搜索后,我找到了一种从代码创建小型转储的方法,以及如何从托管小型转储重新创建堆栈的方法。

然而,此解决方案需要重新分发两个额外的程序集(大小约为 1mb),并且迷你转储会占用一些空间,并且用户通过电子邮件发送它们会很不舒服。所以就我的目的而言,现在这是 Not Acceptable 。

选项 2:感谢 weiqure 提供线索。可以为每个堆栈帧提取托管 IL 偏移量。现在的问题是如何根据此偏移量从 .pdb 获取行号。我发现了什么:

使用此工具,可以为每个发布版本创建 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.

然后您可以使用 ReflectorILSpy解释偏移量:

.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/

相关文章:

c# - 无法找到 HttpContextBase 即使我正在使用 System.Web

android - 为 Android 开发时,如何在 AsyncTask 中使用 Eclipse 调试器?

sql-server - 如何在没有CLR的情况下调试SQL Server 2008中的存储过程?

c# - NHibernate Criteria QueryByExample 卡在中间的 SQL

c# - 描述.net 的速记符号的含义? ("+=","=>")

c# - SpecialFolder.MyDocuments 在控制台应用程序中提供正确的值,但在 Windows 服务中不提供

c# - 为什么非托管内存可能占控制台应用程序使用的内存的 60% 以上?

.net - 是否有任何 .NET 测试工具可以更轻松地发现线程问题?

c# - C# 数据库访问的建议

c# - 编码UI : Why is searching for a cell so slow?