16

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

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

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

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

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

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

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

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

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

4

4 回答 4

16

您可以使用 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 offset: 0x61)
消息:Year、Month 和 Day 参数描述了一个不可表示的 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# 代码,但我认为收益太少而且工作量太大(尽管可能有反射器插件)。你应该对 IL 偏移没问题。

于 2009-06-24T13:25:56.650 回答
5

您应该使用Environment.FailFast,在Application.UnhandledException中调用 FailFast并且将为您创建一个转储文件。

来自 MSDN:

FailFast 方法使用 message 参数将日志条目写入 Windows 应用程序事件日志,创建应用程序的转储,然后终止当前进程。

如果应用程序的状态已损坏无法修复,则使用 FailFast 方法而不是 Exit 方法来终止应用程序,并且执行应用程序的 try-finally 块和终结器将损坏程序资源。FailFast 方法终止当前进程并执行任何 CriticalFinalizerObject 对象,但不执行任何活动的 try-finally 块或终结器。

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

现在,打开转储文件有点棘手,Visual Studio 无法处理托管转储文件(在 .NET 4.0 中已修复)您可以使用WinDBG但您需要使用SOS

于 2009-06-24T12:45:22.970 回答
2

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

于 2009-06-24T12:38:49.007 回答
1

您完全正确地需要 PDB 文件来准确获取源和线路信息。我会质疑任何与运送 PDB 文件有关的问题实际上是一个有效的问题,但是,假设有一个正当的理由,你可以这样做,但它需要你付出更多的努力来创建适当的软件构建环境。

Microsoft 符号服务器将索引调试符号并存储它们以供以后使用,例如,当您有故障转储并需要可用符号时。您可以将 Visual Studio 或 Windbg 指向您自己的符号服务器实例,就像Microsoft 的一样,它会拉下调试该版本应用程序所需的符号(假设您在发布之前使用符号服务器对符号进行了索引)。

一旦您有了构建的适当符号,您需要确保您拥有属于该构建的适当源文件。

这就是Microsoft Source Server的用武之地。就像 Symbol Server 索引符号的地方一样,源服务器将索引source以确保您拥有属于软件构建的源代码的适当版本。

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

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

约翰·罗宾斯谈PDB 文件

源服务器上的 John Robbins

查看有关启动和运行符号服务器的WinDbg 文档。

于 2009-08-10T13:23:26.213 回答