【问题标题】:Generate / create mdump files for in my app在我的应用程序中生成/创建 mdump 文件
【发布时间】:2011-04-14 22:29:41
【问题描述】:

我正在寻找一种在我的应用程序中生成 minidump 文件的方法,类似于 ProcDump 所做的工作,但最好是使用代码,而不必提取 3dparty 工具来做到这一点。

不想使用ProcDump的主要原因是:
1)二进制文件的大小会大大增加(这是一个问题,因为我的应用程序是免费软件,而带宽不是免费的)。
2) 感觉很脏。
3) 我无法将该应用移植到运行 inn windows mobile。

我的要求是:
1) 能够在致命崩溃中生成 mdump 文件。
2) 能够“暂停”应用程序进行转储,并且继续将是一个奖励

如果这不是一个选项,有没有办法在当前上下文中动态获取局部变量的值?

旁注: 我did find 这篇文章,但它已经很老了,所以我很犹豫要不要以此为基础。
似乎 IE 9 或网站有问题,所以我的标签有问题。

【问题讨论】:

  • 关于这个问题的任何最终好的解决方案?在 .NET 中具有良好的示例应用程序源代码?

标签: c# exception crash-dumps


【解决方案1】:

因此,想到了一种解决方案并满足以下目标:

  • 二进制文件的大小将增加大约 300k
  • 能够在致命崩溃中生成 mdump 文件。
  • 能够“暂停”应用程序进行转储,并且继续将是一个奖励

我会给这个要求一个完全未知的:

  • 我无法将该应用移植到运行 inn windows mobile。

那么解决办法是什么?

从 Microsoft Sample for MDbg.exe 中集成您所需的部分,为您提供一个“即时”调试器,用于附加、转储和分离崩溃进程。

第 1 步 - 首先从此处将源代码下载到 mdbg:http://www.microsoft.com/downloads/en/details.aspx?FamilyID=38449a42-6b7a-4e28-80ce-c55645ab1310&DisplayLang=en

第 2 步 - 创建一个“崩溃”处理程序,该处理程序会生成一个调试器进程并等待完成。我使用以下几行代码重新启动了带有一些额外参数的同一个 exe,以调用调试器并将 xml 文件输出到 std::out。

string tempFile = Path.GetTempFileName();
Mutex handle = new Mutex(true, typeof(Program).Namespace + "-self-debugging");
try
{
    Process pDebug = Process.Start(typeof(Program).Assembly.Location,
        "debug-dump " + Process.GetCurrentProcess().Id + " " + tempFile);
    if (pDebug != null)
        pDebug.WaitForExit();
}
catch { }
finally
{
    handle.ReleaseMutex();
}

Console.WriteLine(File.ReadAllText(tempFile));

第 3 步 - 编写调试转储例程,这可以在同一个 exe 或不同的 exe 中。您需要引用示例中的“raw”、“corapi”和“mdbgeng”模块(或包括源代码)。然后在 Main() 中添加几行:

public static void Main(string[] args)
{
    if (args.Length > 0 && args[0] == "debug-dump")
    {   //debug-dump process by id = args[1], output = args[2]
        using (XmlTextWriter wtr = new XmlTextWriter(args[2], Encoding.ASCII))
        {
            wtr.Formatting = Formatting.Indented;
            PerformDebugDump(Int32.Parse(args[1]), wtr);
        }
        return;
    }
    //... continue normal program execution
}

static void PerformDebugDump(int process, XmlWriter x)
{
    x.WriteStartElement("process");
    x.WriteAttributeString("id", process.ToString());
    x.WriteAttributeString("time", XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.RoundtripKind));

    MDbgEngine e = new MDbgEngine();
    MDbgProcess me = e.Attach(process);
    me.Go().WaitOne();

    try
    {
        x.WriteStartElement("modules");
        foreach (MDbgModule mod in me.Modules)
            x.WriteElementString("module", mod.CorModule.Name);
        x.WriteEndElement();

        foreach (MDbgThread thread in me.Threads)
        {
            x.WriteStartElement("thread");
            x.WriteAttributeString("id", thread.Id.ToString());
            x.WriteAttributeString("number", thread.Number.ToString());
            int ixstack = -1;

            foreach (MDbgFrame frame in thread.Frames)
            {
                x.WriteStartElement("frame");
                x.WriteAttributeString("ix", (++ixstack).ToString());
                x.WriteAttributeString("loc", frame.ToString(String.Empty));
                string valueText = null;

                x.WriteStartElement("args");
                try
                {
                    foreach (MDbgValue value in frame.Function.GetArguments(frame))
                    {
                        x.WriteStartElement(value.Name);
                        x.WriteAttributeString("type", value.TypeName);
                        try { x.WriteAttributeString("value", value.GetStringValue(1, false)); }
                        finally { x.WriteEndElement(); }
                    }
                }
                catch { }
                x.WriteEndElement();

                x.WriteStartElement("locals");
                try
                {
                    foreach (MDbgValue value in frame.Function.GetActiveLocalVars(frame))
                    {
                        x.WriteStartElement(value.Name);
                        x.WriteAttributeString("type", value.TypeName);
                        try { x.WriteAttributeString("value", value.GetStringValue(1, false)); }
                        finally { x.WriteEndElement(); }
                    }
                }
                catch { }
                x.WriteEndElement();
                x.WriteEndElement();
            }
            x.WriteEndElement();
        }
    }
    finally
    {
        me.Detach().WaitOne();
    }

    x.WriteEndElement();
}

示例输出

<process id="8276" time="2010-10-18T16:03:59.3781465-05:00">
<modules>
<module>C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll</module>
...etc
</modules>
<thread id="17208" number="0">
<frame ix="0" loc="System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop (source line information unavailable)">
    <args>
        <this type="System.Windows.Forms.Application.ComponentManager" value="System.Windows.Forms.Application.ComponentManager&#xA;    oleComponents=System.Collections.Hashtable&#xA; cookieCounter=1&#xA;    activeComponent=System.Windows.Forms.Application.ThreadContext&#xA; trackingComponent=&lt;null&gt;&#xA; currentState=0" />
        <dwComponentID type="N/A" value="&lt;N/A&gt;" />
        <reason type="System.Int32" value="-1" />
        <pvLoopData type="System.Int32" value="0" />
    </args>
    <locals>
        <local_0 type="System.Int32" value="0" />
        <local_1 type="System.Boolean" value="True" />
        <local_2 type="System.Windows.Forms.UnsafeNativeMethods.IMsoComponent" value="&lt;null&gt;" />
        <local_3 type="N/A" value="&lt;N/A&gt;" />
        <local_4 type="N/A" value="&lt;N/A&gt;" />
        <local_5 type="N/A" value="&lt;N/A&gt;" />
        <local_6 type="System.Windows.Forms.Application.ThreadContext" value="System.Windows.Forms.Application.ThreadContext&#xA;   contextHash=System.Collections.Hashtable&#xA;   tcInternalSyncObject=System.Object&#xA; totalMessageLoopCount=1&#xA;    baseLoopReason=-1&#xA;  currentThreadContext=System.Windows.Forms.Application.ThreadContext&#xA;    threadExceptionHandler=System.Threading.ThreadExceptionEventHandler&#xA;    idleHandler=&lt;null&gt;&#xA;   enterModalHandler=&lt;null&gt;&#xA; leaveModalHandler=&lt;null&gt;&#xA; applicationContext=System.Windows.Forms.ApplicationContext&#xA; parkingWindow=&lt;null&gt;&#xA; marshalingControl=System.Windows.Forms.Application.MarshalingControl&#xA;   culture=&lt;null&gt;&#xA;   messageFilters=&lt;null&gt;&#xA;    messageFilterSnapshot=&lt;null&gt;&#xA; handle=912&#xA; id=17208&#xA;   messageLoopCount=1&#xA; threadState=1&#xA;  modalCount=0&#xA;   activatingControlRef=&lt;null&gt;&#xA;  componentManager=System.Windows.Forms.Application.ComponentManager&#xA; externalComponentManager=False&#xA; fetchingComponentManager=False&#xA; componentID=1&#xA;  currentForm=Program.MainForm&#xA;   threadWindows=&lt;null&gt;&#xA; tempMsg=System.Windows.Forms.NativeMethods.MSG&#xA; disposeCount=0&#xA; ourModalLoop=False&#xA; messageLoopCallback=&lt;null&gt;&#xA;   __identity=&lt;null&gt;" />
        <local_7 type="N/A" value="&lt;N/A&gt;" />
        <local_8 type="N/A" value="&lt;N/A&gt;" />
        <local_9 type="N/A" value="&lt;N/A&gt;" />
        <local_10 type="N/A" value="&lt;N/A&gt;" />
        <local_11 type="N/A" value="&lt;N/A&gt;" />
        <local_12 type="N/A" value="&lt;N/A&gt;" />
        <local_13 type="System.Boolean" value="False" />
        <local_14 type="System.Windows.Forms.NativeMethods.MSG[]" value="array [1]&#xA; [0] = System.Windows.Forms.NativeMethods.MSG" />
    </locals>
</frame>
<frame ix="1" loc="System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner (source line information unavailable)">
    <args>
        <this type="System.Windows.Forms.Application.ThreadContext" value="System.Windows.Forms.Application.ThreadContext&#xA;  contextHash=System.Collections.Hashtable&#xA;   tcInternalSyncObject=System.Object&#xA; totalMessageLoopCount=1&#xA;    baseLoopReason=-1&#xA;  currentThreadContext=System.Windows.Forms.Application.ThreadContext&#xA;    threadExceptionHandler=System.Threading.ThreadExceptionEventHandler&#xA;    idleHandler=&lt;null&gt;&#xA;   enterModalHandler=&lt;null&gt;&#xA; leaveModalHandler=&lt;null&gt;&#xA; applicationContext=System.Windows.Forms.ApplicationContext&#xA; parkingWindow=&lt;null&gt;&#xA; marshalingControl=System.Windows.Forms.Application.MarshalingControl&#xA;   culture=&lt;null&gt;&#xA;   messageFilters=&lt;null&gt;&#xA;    messageFilterSnapshot=&lt;null&gt;&#xA; handle=912&#xA; id=17208&#xA;   messageLoopCount=1&#xA; threadState=1&#xA;  modalCount=0&#xA;   activatingControlRef=&lt;null&gt;&#xA;  componentManager=System.Windows.Forms.Application.ComponentManager&#xA; externalComponentManager=False&#xA; fetchingComponentManager=False&#xA; componentID=1&#xA;  currentForm=Program.MainForm&#xA;   threadWindows=&lt;null&gt;&#xA; tempMsg=System.Windows.Forms.NativeMethods.MSG&#xA; disposeCount=0&#xA; ourModalLoop=False&#xA; messageLoopCallback=&lt;null&gt;&#xA;   __identity=&lt;null&gt;" />
        <reason type="System.Int32" value="-1" />
        <context type="System.Windows.Forms.ApplicationContext" value="System.Windows.Forms.ApplicationContext&#xA; mainForm=Program.MainForm&#xA;  userData=&lt;null&gt;&#xA;  ThreadExit=System.EventHandler" />
    </args>
    <locals>
        <local_0 type="System.Windows.Forms.Form" value="&lt;null&gt;" />
        <local_1 type="System.Boolean" value="False" />
        <local_2 type="N/A" value="&lt;N/A&gt;" />
        <local_3 type="N/A" value="&lt;N/A&gt;" />
        <local_4 type="N/A" value="&lt;N/A&gt;" />
    </locals>
</frame>
... etc
</thread>
</process>

由于您拥有调试器的全部功能,因此没有什么能阻止您随心所欲地编写代码,但上面的示例应该可以帮助您入门。

更新

这适用于 .Net 2.0 和/或 3.5 运行时,无需任何进一步的依赖。

这可以调试在.Net 4.0进程中运行的.Net 2.0/3.5代码;但是,它不适用于 4.0(还)。

对于 4.0 CLR,请参阅此帖子: http://blogs.msdn.com/b/rmbyers/archive/2008/10/27/icordebug-re-architecture-in-clr-4-0.aspx

【讨论】:

  • @csharptest.net - 根据您链接的页面,它需要安装 .Net 2.0 SDK,这可能在客户端计算机上不可用。另外,它是否支持 .Net 4 程序?
  • 这是一件值得考虑的事情——也许有人知道最终用户部署的最低要求?
  • 虽然我还没有验证过,但我认为除了 .net 运行时之外没有任何要求。至于 .net 4.0,您应该只需要该版本 SDK 中的示例(假设 MS 已更新它?IDK,它甚至可以工作,因为它都是基于 COM 的。我会在没有安装 sdk 的情况下尝试它,当我开始工作时.
  • @csharptest.net - 我一直在玩它,局部变量的值大部分时间都是 N/A (就像你的例子一样)。知道为什么会发生这种情况以及如何避免吗?
  • @Giorgi,您可能希望将此作为一个单独的问题。在此处发布后续问题的链接,我会同时看看我能想出什么。
【解决方案2】:

您可以从AppDomain.UnhandledExceptionApplication.ThreadException 事件处理程序调用MiniDumpWriteDump 来创建小型转储。这篇文章非常详细地解释了这个功能:Effective minidumps

您也可以使用这个具有其他功能的库:Catch All Bugs with BugTrap!

编辑

看起来获得一个有用的小型转储并不容易。首先当转储未满时 sos.dll 会抱怨(完全转储大约 100-150MB)。其次,不建议在catch块中写dump:Getting good dumps when an exception is thrown.

如果你有一个 winforms 应用程序,这个问题有一些有用的信息:How does SetUnhandledExceptionFilter work in .NET WinForms applications?

【讨论】:

  • 我已经捕获了未处理的异常,但我需要获取局部变量值。我基本上是在寻找一种动态地做到这一点的方法。而且我不认为我可以创建完整的转储,如果那是我会这样做的唯一选择,但更喜欢 minidump 是的。
  • @EKS - 你需要转储。
  • 我想通了,明天去测试你发布的内容。在局域网派对自动取款机上:)
  • 你能看一下这个吗:stackoverflow.com/questions/3963542/…我有所有的异常句柄,但它们无论如何都没有被捕获......有什么经验吗?
猜你喜欢
  • 2021-11-04
  • 2015-09-21
  • 1970-01-01
  • 2021-06-09
  • 1970-01-01
  • 2013-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多