C# 如何在 .NET 中进行线程转储?(一个 JVM 线程转储)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/190236/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:22:08  来源:igfitidea点击:

How do I make a thread dump in .NET ? (a la JVM thread dumps)

c#.net

提问by

I have found no way of dumping the stack on all threads in .NET. Neither a signal to be send to the process nor programatic access to all the threads. I can only get access to the current thread via Thread.CurrentThread.

我发现无法在 .NET 中的所有线程上转储堆栈。既不是要发送到进程的信号,也不是对所有线程的编程访问。我只能通过 Thread.CurrentThread 访问当前线程。

Any tricks ?

有什么技巧吗?

回答by smaclell

There is a variety of handy classes in the System.Diagnostics that can help you with debugging and gathering various tracking information, i.e. StackTrace.

System.Diagnostics 中有多种方便的类可以帮助您调试和收集各种跟踪信息,即 StackTrace。

There is a wonky Process class that can be used to get the number of executing threads but very few details. Use the following Snippet:

有一个不稳定的 Process 类可用于获取执行线程的数量,但细节很少。使用以下代码段:

Using  System.Diagnostics;

var threads = Process.GetCurrentProcess().Threads;

Okay after looking a little bit more it appears the easiest way to capture all the current stacks is through a mini dump and a tool like SOSor if you are running vista this.

好吧,在查看更多内容后,似乎捕获所有当前堆栈的最简单方法是通过小型转储和SOS 之类的工具,或者如果您正在运行 vista this

Good luck.

祝你好运。

回答by mattlant

I wrote a dumper for a project i worked on in the past:

我为我过去从事的一个项目写了一个转储程序:

void CrashHandler::WriteThreadInfo(StringWriter* sw, ArrayList* threads, String* type)
{
    sw->WriteLine(type);

    IEnumerator* ie = threads->GetEnumerator();
    while(ie->MoveNext())
    {
        botNETThread* bnt = static_cast<botNETThread*>(ie->Current);
        if(!bnt->IsAlive) continue;
        sw->WriteLine(String::Concat(S"ORIGIN ASSEMBLY: ", bnt->Assembly->FullName));
        sw->WriteLine(String::Concat(S"THREAD NAME: ", (bnt->Name && bnt->Name->Length)?bnt->Name:S"Unnamed thread"));

        sw->Write(GetStackTrace(bnt->_thread));
        sw->WriteLine();
        sw->WriteLine();
    }
}

String* CrashHandler::GetStackTrace(Thread* t)
{

    System::Diagnostics::StackTrace __gc * trace1 = __gc new System::Diagnostics::StackTrace(t, true);

    System::String __gc * text1 = System::Environment::NewLine;
    System::Text::StringBuilder __gc * builder1 = __gc new System::Text::StringBuilder(255);
    for (System::Int32 num1 = 0; (num1 < trace1->FrameCount); num1++)
    {
            System::Diagnostics::StackFrame __gc * frame1 = trace1->GetFrame(num1);
            builder1->Append(S"   at ");
            System::Reflection::MethodBase __gc * base1 = frame1->GetMethod();
            System::Type __gc * type1 = base1->DeclaringType;
            if (type1 != 0)
            {
                System::String __gc * text2 = type1->Namespace;
                if (text2 != 0)
                {
                        builder1->Append(text2);
                        if (builder1 != 0)
                        {
                            builder1->Append(S".");
                        }
                }
                builder1->Append(type1->Name);
                builder1->Append(S".");
            }
            builder1->Append(base1->Name);
            builder1->Append(S"(");
            System::Reflection::ParameterInfo __gc * infoArray1 __gc [] = base1->GetParameters();
            for (System::Int32 num2 = 0; (num2 < infoArray1->Length); num2++)
            {
                System::String __gc * text3 = S"<UnknownType>";
                if (infoArray1[num2]->ParameterType != 0)
                {
                        text3 = infoArray1[num2]->ParameterType->Name;
                }
                builder1->Append(System::String::Concat(((num2 != 0) ? S", " : S""), text3, S" ", infoArray1[num2]->Name));
            }
            builder1->Append(S")");
            if (frame1->GetILOffset() != -1)
            {
                System::String __gc * text4 = 0;
                try
                {
                        text4 = frame1->GetFileName();
                }
                catch (System::Security::SecurityException*)
                {
                }
                if (text4 != 0)
                {
                        builder1->Append(System::String::Concat(S" in ", text4, S":line ", frame1->GetFileLineNumber().ToString()));
                }
            }
            if (num1 != (trace1->FrameCount - 1))
            {
                builder1->Append(text1);
            }
    }
    return builder1->ToString();



}

You can use Process.GetCurrentProcess().Threads to get threads

您可以使用 Process.GetCurrentProcess().Threads 来获取线程

And I know i spasted Managed C++ but its easy enough to follow. I take an arraylist of threads because for my purpose I had catagorized my threads. And yes i used previously written stack frame code as I was new to MC++ at the time :)

而且我知道我坚持使用托管 C++,但它很容易遵循。我采用了一个线程数组列表,因为出于我的目的,我已经对我的线程进行了分类。是的,我使用了以前编写的堆栈帧代码,因为当时我是 MC++ 的新手:)

The entire file is here. This was for a Diablo II botting engineI wrote some time ago.

整个文件在这里。这是我前段时间写的一个暗黑破坏神 II botting 引擎

回答by Squirrel

Just to save anyone else the bother here's the port of the above to c#:

只是为了省去其他人的麻烦,这里是上面对 c# 的端口:

    static void WriteThreadInfo(StringBuilder sw, IEnumerable<Thread> threads)
    {
        foreach(Thread thread in threads)
        {
            if(!thread.IsAlive) continue;
            sw.Append(String.Concat("THREAD NAME: ", thread.Name));

            sw.Append(GetStackTrace(thread));
            sw.AppendLine();
            sw.AppendLine();
        }
    }

    static String GetStackTrace(Thread t)
    {
        t.Suspend();
        var trace1 = new StackTrace(t, true);
        t.Resume();

        String  text1 = System.Environment.NewLine;
        var builder1 = new StringBuilder(255);
        for (Int32 num1 = 0; (num1 < trace1.FrameCount); num1++)
        {
            StackFrame  frame1 = trace1.GetFrame(num1);
            builder1.Append("   at ");
            System.Reflection.MethodBase  base1 = frame1.GetMethod();
            Type  type1 = base1.DeclaringType;
            if (type1 != null)
            {
                String  text2 = type1.Namespace;
                if (text2 != null)
                {
                    builder1.Append(text2);
                    builder1.Append(".");                                                
                }
                builder1.Append(type1.Name);
                builder1.Append(".");
            }
            builder1.Append(base1.Name);
            builder1.Append("(");
            System.Reflection.ParameterInfo [] infoArray1 = base1.GetParameters();
            for (Int32 num2 = 0; (num2 < infoArray1.Length); num2++)
            {
                String text3 = "<UnknownType>";
                if (infoArray1[num2].ParameterType != null)
                {
                                text3 = infoArray1[num2].ParameterType.Name;
                }
                builder1.Append(String.Concat(((num2 != 0) ? ", " : ""), text3, " ", infoArray1[num2].Name));
            }
            builder1.Append(")");
            if (frame1.GetILOffset() != -1)
            {
                String text4 = null;
                try
                {
                    text4 = frame1.GetFileName();
                }
                catch (System.Security.SecurityException)
                {
                }
                if (text4 != null)
                {
                    builder1.Append(String.Concat(" in ", text4, ":line ", frame1.GetFileLineNumber().ToString()));
                }
            }
            if (num1 != (trace1.FrameCount - 1))
            {
                builder1.Append(text1);
            }
        }
        return builder1.ToString();
    }

I've not found a way to get a list of all managed threads in C# (only ProcessThreads), so it does look like you need to maintain the list of threads your interested in yourself.

我还没有找到一种方法来获取 C# 中所有托管线程的列表(仅限 ProcessThreads),因此看起来您确实需要维护自己感兴趣的线程列表。

Also I found I couldn't call new Stacktrace(t,true) on a running thread, so have added pause and resumes. Obviously you'll need to consider whether this could cause problems were you to thread dump your production app.

我还发现我无法在正在运行的线程上调用 new Stacktrace(t,true),因此添加了暂停和恢复。显然,如果您线程转储生产应用程序,您需要考虑这是否会导致问题

btw, we've put this call on our apps wcf rest interface so it's easy to do.

顺便说一句,我们已将此调用放在我们的应用程序 wcf 休息界面上,因此很容易做到。

回答by Tomer Gabel

If you're trying to get a stack dump while the process is already running (a la jstack), there are two methods as described here:

如果您在进程已经运行时尝试获取堆栈转储(a la jstack),这里描述两种方法:

Using Managed Stack Explorer

使用托管堆栈资源管理器

There is a little-known but effective tool called the Managed Stack Explorer. Although it features a basic GUI, it can effectively be a .NET equivalent of jstack if you add to the path; then it's just a question of typing:

有一个鲜为人知但有效的工具,称为Managed Stack Explorer。尽管它具有基本的 GUI,但如果添加到路径中,它可以有效地成为 jstack 的 .NET 等价物;那么这只是一个打字的问题:

mse /s /p <pid>

Using windbg

使用windbg

  1. Download and install the appropriate Debugging Tools for Windows version for your architecture (x86/x64/Itanium)
  2. If you need information about Windows function calls (e.g. you want to trace into kernel calls), download and install the appropriate symbols. This isn't strictly necessary if you just want a thread dump of your own code.
  3. If you need line numbers or any other detailed information, make sure to place your assemblies' PDB files where the debugger can find them (normally you just put them next to your actual assemblies).
  4. Start->Programs->Debugging Tools for Windows [x64]->windbg
  5. Attach the debugger to your running process using the menu
  6. Load the SOS extension with ".loadby sos mscorwks" for .NET 2.0 (".load sos" for .NET 1.0/1.1)
  7. Take a thread dump using "!eestack"
  8. Detach using ".detach"
  1. 为您的架构 (x86/x64/Itanium) 下载并安装适用于 Windows 版本的适当调试工具
  2. 如果您需要有关 Windows 函数调用的信息(例如,您想跟踪内核调用),请下载并安装适当的符号。如果您只想要自己代码的线程转储,这不是绝对必要的。
  3. 如果您需要行号或任何其他详细信息,请确保将程序集的 PDB 文件放在调试器可以找到它们的位置(通常您只需将它们放在实际程序集旁边)。
  4. 开始->程序->Windows [x64] 调试工具->windbg
  5. 使用菜单将调试器附加到正在运行的进程
  6. 为 .NET 2.0 加载带有“.loadby sos mscorwks”的 SOS 扩展(对于 .NET 1.0/1.1 是“.load sos”)
  7. 使用“!eestack”进行线程转储
  8. 使用“.detach”分离

I just found it necessary to take a production thread dump and this worked for me. Hope it helps :-)

我只是发现有必要进行生产线程转储,这对我有用。希望能帮助到你 :-)

回答by P-H

The best tool I have seen at this point to generate thread dumps for the .NET CLR is DebugDiag. This tool will generate a very detailed report (using the Crash/Hang analyzer) of the active CLR threads along with recommendations.

目前我看到的为 .NET CLR 生成线程转储的最佳工具是 DebugDiag。此工具将生成活动 CLR 线程的非常详细的报告(使用崩溃/挂起分析器)以及建议。

I recommend to review the following .NET DebugDiag tutorialas it is showing the analysis process in action following a production problem. The steps are as per below:

我建议查看以下.NET DebugDiag 教程,因为它显示了生产问题后的分析过程。步骤如下:

  • Create a dump file of your affected w3wp process
  • Start the Debug Diagnostic Tool, select and launch the Crash/Hang analyzers.
  • Open and analyze the report analysis overview.
  • Finally, review the blocked Thread summary and perform a deeper dive analysis.
  • 创建受影响的 w3wp 进程的转储文件
  • 启动调试诊断工具,选择并启动崩溃/挂起分析器。
  • 打开并分析报告分析概览。
  • 最后,查看被阻止的线程摘要并进行更深入的潜水分析。

回答by S?ren Boisen

If you need to do this programmatically (maybe you want automatic dumps during your CI process), you can use the info from this answerto a different question.

如果您需要以编程方式执行此操作(也许您希望在 CI 过程中自动转储),您可以将此答案中的信息用于不同的问题。

Basically, attach to your own process using CLR MD:

基本上,使用CLR MD附加到您自己的进程:

using Microsoft.Diagnostics.Runtime;

using (DataTarget target = DataTarget.AttachToProcess(
    Process.GetCurrentProcess().Id, 5000, AttachFlag.Passive))
{
    ClrRuntime runtime = target.ClrVersions.First().CreateRuntime();
    foreach (ClrThread thread in runtime.Threads)
    {
        IList<ClrStackFrame> stackFrames = thread.StackTrace;
        PrintStackTrace(stackFrames);            
    }
}

Here PrintStackTrace is left as an exercise for the reader.

这里 PrintStackTrace 留给读者作为练习。