简体   繁体   中英

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

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.

Any tricks?

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 :

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:

mse /s /p <pid>

Using 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 (eg 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"

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

Just to save anyone else the bother here's the port of the above to 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.

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 .

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

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.

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

  • 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.

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

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 :)

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

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

Basically, attach to your own process using 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.

There is a variety of handy classes in the System.Diagnostics that can help you with debugging and gathering various tracking information, ie 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:

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 SOS or if you are running vista this .

Good luck.

I am using https://github.com/odinserj/stdump , a tool for explore stack trace of a running managed process.

To display a stack trace of a running program, use its PID or process name.

stdump 12832

stdump w3wp.exe

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM