tags:

views:

1589

answers:

4

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 ?

+1  A: 

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.

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.

smaclell
+1  A: 

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.

mattlant
+1  A: 

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.

Squirrel
+3  A: 

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

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

Tomer Gabel
+1 for mse. That's a very useful little app. Combined with pstools, you can remotely snapshot a process. That's very useful for me as my current users are traders who can't free up their machines during the trading day.
Drew Noakes