代码之家  ›  专栏  ›  技术社区  ›  arbiter

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

  •  15
  • arbiter  · 技术社区  · 15 年前

    首先,问题是: 我有几个免费的项目,作为任何软件,他们包含的错误。一些用户在遇到bug时会给我发送一个带有堆栈跟踪的bug报告。为了简化查找故障点的过程,我想查看这个堆栈跟踪中的行号。如果应用程序没有.pdb文件,那么所有行信息都会丢失,因此当前所有部署了.pdb文件的项目,以及生成的堆栈跟踪都有这个数字。 但是!但我不想在分发版中看到这些文件,我想删除所有.pdb文件。它们会迷惑用户,消耗安装程序中的空间等。

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

    问题: 在.NET中是否有任何库、技术或其他类似的功能?

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

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

    不过,此解决方案需要重新分发另外两个程序集(大小约为1MB),而小型转储会占用一些空间,用户不愿意通过电子邮件发送它们。所以就我而言,现在,这是不可接受的。

    选择2:多亏了魏奎的提示。可以为每个堆栈帧提取托管IL偏移量。现在的问题是如何根据这个偏移量从.pdb中获取行号。我发现:

    使用这个工具,可以为每个版本构建创建xml文件并将它们放入repository中。当用户计算机上发生异常时,可以创建具有IL偏移量的格式化错误消息。然后用户通过邮件发送此消息(非常小)。最后,可以创建一个简单的工具,从格式化的错误消息中重新创建结果堆栈。

    我只想知道为什么没有人不实现这样的工具?我不相信这只对我有意思。

    4 回复  |  直到 9 年前
        1
  •  14
  •   Răzvan Flavius Panda    9 年前

    您可以使用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());
    }
    

    输出:

    在consoleapplicationn.exe.program.main(IL偏移量:0x7)
    在consoleapplicationn.exe.program.throwerror(IL偏移量:0x1B)
    位于mscorlib.dll.datetime..ctor(IL偏移量:0x9)
    位于mscorlib.dll.datetime.datetoticks(IL偏移量:0x61)
    消息:年、月和日参数描述不可表示的日期时间。

    然后你可以使用 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代码,但我认为收益太小,工作量太大(尽管可能有一个反射插件)。你应该对IL偏移没问题。

        2
  •  5
  •   Shay Erlichmen    15 年前

    你应该用 Environment.FailFast ,呼叫FailFast Application.UnhandledException 会为您创建一个转储文件。

    从MSDN:

    failfast方法写入日志项 到Windows应用程序事件日志 使用消息参数,创建 转储应用程序,然后 终止当前进程。

    使用FailFast方法而不是 终止您的 如果您的 应用程序损坏无法修复, 执行应用程序的 try finally块和finalizers将 程序资源损坏。这个 FailFast方法终止当前 处理并执行任何 CriticalFinalizerObject对象,但是 不执行任何活动 尝试finally块或终结器。

    你可以编写一个简单的应用程序来收集日志文件并发送给你。

    现在,打开转储文件有点棘手,VisualStudio无法处理托管转储文件(固定在.NET 4)中,您可以使用 WinDBG 但是你需要使用 SOS

        3
  •  2
  •   Canopus    15 年前

    您可以在出现错误时创建应用程序小型转储,并将其用于脱机分析。这并不要求您在客户机上部署pdb。 This 链接可以作为学习的好起点。

        4
  •  1
  •   Zach Bonham    15 年前

    您绝对是在正确的轨道上需要pdb文件,以准确地获得源和行信息。我会质疑航运PDB文件的任何问题,实际上是一个有效的关注,但假设有一个有效的理由,你可以做到这一点,但它需要更多的努力,你的一部分,以创建适当的软件构建环境。

    符号文件服务器 将为调试符号编制索引,并将它们存储起来,以便在以后有崩溃转储(例如,需要可用的符号)时使用。您可以将VisualStudio或WrnBG指向自己的符号服务器实例,就像 Microsoft's ,它将拉下调试该版本应用程序所需的符号(假设您在装运之前使用symbol server对这些符号编制了索引)。

    一旦有了用于生成的适当符号,就需要确保有属于该生成的适当源文件。

    这里就是 Microsoft源服务器 进来。就像符号服务器索引的位置一样 符号 ,源服务器将索引 来源 以确保您拥有属于软件生成的源代码的适当版本。

    工作版本 版本控制 , 符号服务器 源服务器 应该是软件配置管理策略的一部分。

    有一些商业的第三方工具将为您提供一个api来生成应用程序快照,但是正如您已经了解到的,您需要一种机制以某种方式将这些快照上载到您的环境中。

    约翰·罗宾斯 PDB Files

    约翰·罗宾斯 Source Server

    退房 WinDbg documentation 启动并运行符号服务器。