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

arb*_*ter 16 .net c# debugging stack-trace

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

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

问题: 在.NET中是否有任何lib,或技术或其他任何相同的操作?

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

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

然而,该解决方案需要重新分配两个额外的组件(大小约为1mb),并且小型转储占用一些空间,并且用户通过电子邮件发送它们是不舒服的.所以对于我的目的,现在,这是不可接受的.

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

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

我只是想知道为什么没有其他人没有实现这样的工具?我不相信这只对我有意义.

wei*_*ure 15

您可以使用System.Diagnostics.StackTrace从Exception获取最后一条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());
}
Run Code Online (Sandbox Code Playgroud)

输出:

位于 mscorlib.dll.DateTime的mscorlib.dll.DateTime..ctor(IL偏移量:0x9) 处的
ConsoleApplicationN.exe.Program.ThrowError(IL偏移量:0x1b)处的ConsoleApplicationN.exe.Program.Main(IL偏移量:0x7)处. DateToTicks(IL偏移量:0x61) 消息:年,月和日参数描述不可表示的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 
}
Run Code Online (Sandbox Code Playgroud)

你知道0x1b之前的指令引发了异常.很容易找到C#代码:

 myDateTime = new DateTime(2000, 5555555, 1);
Run Code Online (Sandbox Code Playgroud)

您现在可以将IL代码映射到C#代码,但我认为增益太少而且工作量太大(尽管可能有反射器插件).IL偏移应该没问题.


Sha*_*men 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.

  • 不适合我的需求:1.大多数异常都是可恢复的,因此app必须显示信息(可选择将其发送到邮件)并继续.这个方法会破坏app上任何未处理的异常.2.此方法需要用户进行大量操作才能向我发送异常信息.几乎没有人会这样做.您可以期望用户可以从错误对话框中复制过去的信息并通过邮件发送,不再需要. (3认同)