Подтвердить что ты не робот

Как создать дамп потока в .NET? (a la JVM thread dumps)

Я не нашел способа сброса стека для всех потоков в .NET. Ни сигнал, который нужно отправить процессу, ни программный доступ ко всем потокам. Я могу получить доступ к текущему потоку через Thread.CurrentThread.

Любые трюки?

4b9b3361

Ответ 1

Если вы пытаетесь получить дамп стека, пока процесс уже запущен (a la jstack), существует два метода, описанных здесь:

Использование управляемого Stack Explorer

Существует малоизвестный, но эффективный инструмент, называемый Managed Stack Explorer. Хотя он имеет базовый графический интерфейс, он может эффективно быть эквивалентом .NET jstack, если вы добавите его в путь; то это просто вопрос ввода:

mse /s /p <pid>

Использование windbg

  • Загрузите и установите соответствующие средства для отладки для Windows для вашей архитектуры (x86/x64/Itanium)
  • Если вам нужна информация о вызовах функций Windows (например, вы хотите отслеживать вызовы ядра), загрузите и установите соответствующие символы. Это не является абсолютно необходимым, если вам просто нужен дамп потока вашего собственного кода.
  • Если вам нужны номера строк или какая-либо другая подробная информация, обязательно разместите файлы PDB своих сборок, где отладчик может их найти (обычно вы просто ставите их рядом с вашими фактическими сборками).
  • Пуск- > Программы- > Инструменты отладки для Windows [x64] → windbg
  • Прикрепите отладчик к выполняемому процессу с помощью меню
  • Загрузите расширение SOS с помощью ".loadby sos mscorwks" для .NET 2.0 ( ".load sos" для .NET 1.0/1.1)
  • Возьмите дамп потока с помощью "! eestack"
  • Отсоединить, используя ".detach"

Я просто счел необходимым взять дамп производственного потока, и это сработало для меня. Надеюсь, что это поможет: -)

Ответ 2

Просто, чтобы сохранить кого-нибудь еще, напишите здесь порт выше, чтобы С#:

    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();
    }

Я не нашел способ получить список всех управляемых потоков в С# (только ProcessThreads), поэтому похоже, что вам нужно поддерживать список тем, заинтересованных в себе.

Также я обнаружил, что не могу вызвать новую Stacktrace (t, true) в текущем потоке, поэтому добавил паузу и резюме. Очевидно, вам нужно будет подумать, может ли это вызвать проблемы, если бы вы потокостроили ваше производственное приложение.

btw, мы поместили этот вызов в наш интерфейс wcf rest, так что это легко сделать.

Ответ 3

Лучший инструмент, который я видел на этом этапе для создания дампов потоков для .NET CLR, - DebugDiag. Этот инструмент будет генерировать очень подробный отчет (используя анализатор Crash/Hang) активных потоков CLR вместе с рекомендациями.

Я рекомендую рассмотреть следующий .NET DebugDiag учебник, поскольку он показывает процесс анализа в действии после производственной проблемы. Шаги выполняются следующим образом:

  • Создайте файл дампа вашего затронутого процесса w3wp
  • Запустите инструмент диагностики отладки, выберите и запустите анализаторы Crash/Hang.
  • Откройте и проанализируйте обзор анализа отчета.
  • Наконец, просмотрите заблокированное резюме потока и выполните более глубокий анализ погружений.

Ответ 4

Я написал самосвал для проекта, над которым я работал в прошлом:

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();



}

Вы можете использовать Process.GetCurrentProcess(). Темы для получения тем

И я знаю, что я искал управляемый С++, но его достаточно легко следовать. Я беру arraylist из нитей, потому что для своей цели я катафорировал свои потоки. И да, я использовал ранее написанный код кадра кадра, поскольку я был новым для MС++ в то время:)

Весь файл здесь. Это было для Diablo II botting engine Я написал некоторое время назад.

Ответ 5

В System.Diagnostics есть множество удобных классов, которые могут помочь вам в отладке и сборе различной информации отслеживания, т.е. StackTrace.

Существует класс winky Process, который может использоваться для получения количества исполняемых потоков, но очень мало деталей. Используйте следующий фрагмент:

Using  System.Diagnostics;

var threads = Process.GetCurrentProcess().Threads;

Хорошо, посмотрев немного больше, кажется, самый простой способ захватить все текущие стеки - через мини-дамп и инструмент вроде SOS или если вы используете vista this.

Удачи.

Ответ 6

Если вам нужно сделать это программно (возможно, вам нужны автоматические дампы во время процесса CI), вы можете использовать информацию из этого ответа для другого вопроса.

В принципе, присоединитесь к своему собственному процессу, используя 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);            
    }
}

Здесь PrintStackTrace остается в качестве упражнения для читателя.