【问题标题】:Generating .NET crash dumps automatically自动生成 .NET 故障转储
【发布时间】:2009-07-15 21:00:11
【问题描述】:

我知道如何使用 ADPlus 或 DebugDiag 生成故障转储文件,但我想知道是否有办法在客户的计算机上执行此操作而无需安装这些工具...具体来说,我希望能够配置我的应用程序(例如使用注册表值)在发生严重故障时生成故障转储。更具体地说,我需要能够从 C# 应用程序中执行此操作,但如有必要,我不介意 P/Invoke'ing。谢谢!

【问题讨论】:

    标签: c# .net crash-dumps


    【解决方案1】:

    请注意,从“失败”进程(甚至线程)内部创建小型转储本身并非易事或可能不准确(还有MiniDumpWriteDump 函数的备注)。

    此外,如果您的进程非常愤怒以至于您可能需要编写故障转储,那么整个情况通常会非常紧张,即使尝试创建故障转储也可能导致另一次崩溃(诸如挂起之类的情况 - 但那些可能更难从当前流程中“捕获”)。

    如果您无法在客户端系统上安装单独的应用程序,您可以做的“最好”的事情是启动一个外部进程(在紧急情况下也可能失败!)并让它从您当前的进程创建一个故障转储(见Superassert.NET from John Robbins)。您甚至可以将外部二进制文件放入您的应用程序资源,在启动时将其从那里提取(以尽量减少关键情况下的故障)到磁盘(如果您敢的话)。

    【讨论】:

    • 虽然在应用程序崩溃期间小心您的操作是明智之举,但考虑一下您的应用程序正在完成的工作也是一件好事。如果您在生产中,请非常小心——但是,如果您正在编写批处理应用程序,您几乎肯定最好稍后再创建 minidump 以进行调试,而不必进行耗时的重现。一般建议:在做出设计决定之前始终考虑您的情况,并警惕过于宽泛的建议。
    • @jhclark 我完全反对编写迷你/崩溃转储;事实上,适当的转储是人们可以要求的最好的事后分析工具。但重点不是试图从受害进程内部编写它。因为这通常是不可靠的。拥有某种外部看门狗进程是可行的方法(甚至像 Sysinternals procdump 这样的东西也可能适用于内部情况)。恕我直言,无论应用程序类型或试图完成什么,都是如此。
    【解决方案2】:

    您可以配置 Windows 错误报告 (WER) 以使用以下注册表脚本在特定目录中创建故障转储:

    Windows 注册表编辑器版本 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows 错误报告\LocalDumps] "DumpFolder"="C:\\Dumps" “转储计数”=dword:00000064 “转储类型”=dword:00000002 "CustomDumpFlags"=dword:00000000

    转储将进入 C:\Dumps,其名称反映了崩溃进程的名称。 DumpType=2 给出完整的内存转储。 DumpType=1 给出一个小型转储。在 64 位机器上,您不需要将它们放在 Wow32 节点下。 WER 仅使用上面指定的非 WOW 注册表项。

    根据崩溃的类型,此方法可能不起作用。我还没有弄清楚为什么或哪些崩溃类型它没有捕捉到。有人吗?

    【讨论】:

    • 正如这里提到的 (msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx) 我不认为这适用于托管代码。
    • “此功能不支持执行自己的自定义崩溃报告的应用程序,包括 .NET 应用程序”
    • 我已经尝试过了,它确实适用于托管代码 - 在我的测试用例中,它是框架 4.5。但我必须在我的 .net 应用程序中使用以下语句:Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
    • MSDN 文章不正确:这也适用于托管应用程序。在 Windows 10 RS4 上进行了测试,但我似乎记得它也可以在 Windows 8 上运行。
    • 也适用于非 GUI .net 应用程序(即 Application.SetUnhandledExceptionMode() 不可能) - 在 Windows 服务(通过 TopShelf)、.net 4.6、Windows 10 + Server 2008 R2 上测试跨度>
    【解决方案3】:

    我认为,如果您的应用程序崩溃了,那么您不妨尝试创建一个小型转储文件,最糟糕的情况是什么,您的应用程序会崩溃?反正它是这样做的,所以你不妨试试。
    MSDN forum mentioned by VoiDed 中的代码看起来很可靠。我需要一个 VB.Net 版本,所以这里有一个 VB 版本供任何需要它的人使用:

    Friend Class MiniDump
        'Code converted from C# code found here: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/6c8d3529-a493-49b9-93d7-07a3a2d715dc
    
        Private Enum MINIDUMP_TYPE
            MiniDumpNormal = 0 
            MiniDumpWithDataSegs = 1
            MiniDumpWithFullMemory = 2
            MiniDumpWithHandleData = 4
            MiniDumpFilterMemory = 8
            MiniDumpScanMemory = 10
            MiniDumpWithUnloadedModules = 20
            MiniDumpWithIndirectlyReferencedMemory = 40
            MiniDumpFilterModulePaths = 80
            MiniDumpWithProcessThreadData = 100
            MiniDumpWithPrivateReadWriteMemory = 200
            MiniDumpWithoutOptionalData = 400
            MiniDumpWithFullMemoryInfo = 800
            MiniDumpWithThreadInfo = 1000
            MiniDumpWithCodeSegs = 2000
        End Enum
    
        <Runtime.InteropServices.DllImport("dbghelp.dll")> _
        Private Shared Function MiniDumpWriteDump( _
             ByVal hProcess As IntPtr, _
             ByVal ProcessId As Int32, _
            ByVal hFile As IntPtr, _
             ByVal DumpType As MINIDUMP_TYPE, _
            ByVal ExceptionParam As IntPtr, _
             ByVal UserStreamParam As IntPtr, _
            ByVal CallackParam As IntPtr) As Boolean
        End Function
    
        Friend Shared Sub MiniDumpToFile(ByVal fileToDump As String)
            Dim fsToDump As IO.FileStream = Nothing
    
            If (IO.File.Exists(fileToDump)) Then
                fsToDump = IO.File.Open(fileToDump, IO.FileMode.Append)
            Else
                fsToDump = IO.File.Create(fileToDump)
            End If
    
            Dim thisProcess As Process = Process.GetCurrentProcess()
            MiniDumpWriteDump(thisProcess.Handle, _
                              thisProcess.Id, _
                              fsToDump.SafeFileHandle.DangerousGetHandle(), _
                              MINIDUMP_TYPE.MiniDumpNormal, _
                              IntPtr.Zero, _
                              IntPtr.Zero, _
                              IntPtr.Zero)
            fsToDump.Close()
        End Sub
    End Class
    

    只要确保你可靠地处理对它的调用,你就应该相对安全。

    【讨论】:

    • “最坏的情况是什么,你的应用会崩溃?” - 这还不是最坏的情况。更糟糕的情况是调用MiniDumpWriteDump 会使您的进程死锁,并阻止它终止。 MiniDumpWriteDump 做的第一件事就是暂停进程中的所有线程。如果你不走运,其中一个线程正在分配堆内存。而当MiniDumpWriteDump尝试分配堆内存时,它会无限期地等待锁被释放,但它被一个挂起的线程持有。您不能安全地在进程中执行此操作。
    • 你能杀死这个状态下的进程吗?如果这适用于 99% 的崩溃和 1% 的死锁,而用户所要做的就是强制杀死,我认为这是一个公平的权衡,具体取决于具体情况。从崩溃的进程中启动一个新进程会起作用吗?
    • 这很好用,但我注意到MINIDUMP_TYPE 中的所有值都是十六进制的,所以VB 应该有一个&amp;H 前缀。
    【解决方案4】:

    您可以在 AppDomain.UnhandledException 事件中 P/Invoke dbghelp.dllMiniDumpWriteDump 函数。

    在这种情况下,您可以转储 .NET 异常数据的日志并将 minidump 写入文件。

    还有一个thread on the MSDN forums 描述了 P/Invoke 签名和正确用法。

    【讨论】:

    【解决方案5】:

    您可以拨打Environment.FailFast,电话会:

    FailFast 方法将消息字符串写入 Windows 应用程序事件日志,创建应用程序的转储,然后终止当前进程。消息字符串也包含在向 Microsoft 报告的错误中。

    除其他外。

    【讨论】:

      【解决方案6】:

      根据您需要的信息类型,您可以为AppDomain.UnhandledException 事件添加处理程序吗? (我知道这不是您正在寻找的东西,但它绝对可以在客户端计算机上使用。)

      【讨论】:

        【解决方案7】:

        你使用像 log4net 这样的日志框架吗?通常,您关闭发布的调试级别消息。但是,您可以考虑编写一个特殊的附加程序,仅在某些情况下(如崩溃)记录到文件。这个附加程序首先写入一个仅内存的环形缓冲区,该缓冲区可以写入文件,稍后 - 例如由 280Z28 建议的异常处理程序触发。

        【讨论】:

        • 迷你转储方式比日志文件更有帮助。它捕获应用程序的完整状态,此时会引发未捕获的异常。您可以在调试器中加载它,并获得完整的符号信息、方便地链接到源代码、所有线程的完整堆栈跟踪、精确的故障位置、加载模块的信息、内存以及您想添加的任意数量的自定义信息.
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多