зависание отладки с помощью DebugDiag - загадочные исключения - PullRequest
0 голосов
/ 19 июня 2020

Я недавно работаю над отладкой "зависания" одного из наших приложений. Я новичок в этом инструменте, но у меня есть конкретный вопрос о том, что я вижу в анализе DebugDiag. По другим вопросам выложу отдельно. В нем есть раздел под названием «Предыдущие. NET Исключения (Исключения во всех. NET Кучи)», в нем перечислены 5 исключений как таковых:

Exception Type                          Count    Message     Stack Trace
System.Exception                        1        <none>      ;
System.OutOfMemoryException             1        <none>      ;
System.StackOverflowException           1        <none>      ;
System.ExecutionEngineException         1        <none>      ;
System.Threading.ThreadAbortException   2        <none>      ; 

Может кто-нибудь помочь мне понять несколько вещей:

  • что означает заголовок раздела? «Предыдущий. NET Исключения»?
  • если возникло исключение, почему оно не отображается в журнале и почему приложение не прерывается, а не зависает? Я проверил наш код на предмет "съедания" любого возможного исключения.
  • Почему нет связанного сообщения или трассировки стека? Я получаю отладочную информацию для всего остального в файле .dmp, включая трассировку стека CLR для всех потоков. Есть ли лучший способ / инструмент для определения их происхождения?

Заранее спасибо!

1 Ответ

1 голос
/ 26 июня 2020

что означает название раздела? «Предыдущий. NET Исключения»?

Когда возникает исключение, оно может быть обработано кодом с использованием конструкции try{} catch{}. Во многих случаях (по крайней мере, что я вижу в коде) исключения преобразуются в другие исключения. Причиной часто является какой-то API, который должен выдавать только «собственные» исключения, т.е. исключения типа, определенного библиотекой:

try 
{
    DoSomething();
}
catch (ArgumentException)
{
    throw new MyWhateverLibraryException();
}

Также часто люди не устанавливают InnerException , хотя это было бы несложно сделать:

try 
{
    DoSomething();
}
catch (ArgumentException argex)
{
    var myex = new MyWhateverLibraryException();
    myex.InnerException = argex;
    throw myex;
}

В любом случае, если это не сделано должным образом, исходное исключение исчезает, и отладка этой ситуации становится сложнее.

Однако , исключение не займет много времени, чтобы всплыть вверх и вызвать ошибку sh, что приведет к отказу sh. Во многих случаях сборщику мусора не хватит времени для сбора исключения, на которое больше нет ссылок.

DebugDiag пытается сделать это потерянное исключение доступным для вас. В основном он перечисляет все объекты, в названии которых есть Exception , при условии, что они являются исключениями. Это эквивалентно команде SOS !dumpheap -type Exception.

DebugDiag finding an Exception that isn't an exception

In some cases, you may find an exception of interest there, so it may be helpful. But in many cases it will just list the 6 default Exceptions, which is more confusing than helpful.

if there was an exception thrown, why wouldn't it show up in logging and why wouldn't the app abort instead of hang?

The 6 exceptions you see are "default" exceptions, which exists in every .NET project, even in a simple Hello World project. These exceptions are created once per AppDomain , и у вас всегда есть AppDomain по умолчанию.

Они создаются для особых случаев. Некоторые из них можно легко объяснить:

  • OutOfMemoryException: когда память заполнена, new объект может быть невозможен. Создание OutOfMemoryException снова вызовет OutOfMemoryException.
  • StackOverflowException: если стек заполнен, вы не можете вызвать другой метод. Для. NET может потребоваться вызвать метод, чтобы вызвать конструктор. Это снова приведет к исключению StackOverflowException.
  • ExecutionEngineException: если среда выполнения. NET повреждена, возможно, она больше не сможет создать это исключение. (Это исключение устарело в. NET Core, поэтому его может не быть в. NET Core проектах)

ThreadAbortExceptions, вероятно, имеют аналогичные причины. Я не могу точно сказать, для чего подходит обычное исключение.

Почему нет связанного сообщения или трассировки стека?

Это потому, что они еще не были созданы . Некоторые из них могут даже не получить стек вызовов при их вызове, например, не остается памяти для построения трассировки стека в случае OutOfMemoryException. То же самое и для StackOverflowException.

Есть ли лучший способ / инструмент для определения их происхождения?

Нет. Это просто вопрос знания. Теперь вы получили это знание: -)

Награда была связана с

поиском ответа из авторитетного источника

Я не точно знаю, что квалифицируется как авторитетный источник. Я могу предложить заархивированный пост в блоге Тесс Феррандес . Тесс Феррандес была инженером по эскалации Microsoft ASP. NET. Вы обнаружите, что в 2009 году было не так много исключений по умолчанию, как сейчас. * книга. У меня есть книга, но в настоящее время она недоступна, иначе я бы поискал ее.

В исходном коде . NET CLR вы можете найти appdomain. cpp , у которого есть метод void SystemDomain::CreatePreallocatedExceptions(). Даже я не знал, что есть одно нормальное исключение ThreadAbortException и одно «грубое» исключение ThreadAbortException. Вау!

EXCEPTIONREF pRudeAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass);
...
EXCEPTIONREF pAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass);

Исходный код для самостоятельного тестирования некоторых утверждений:

using System;

namespace DefaultExceptions
{
    class ThisDoesNotDeriveFromException
    { }
    class Program
    {
        static void Main()
        {

            var noexception = new ThisDoesNotDeriveFromException();
            Console.WriteLine("This is just an example to demonstrate the presence of some default exceptions.");
            Console.WriteLine("Create a crash dump now, so you can analyze it with WinDbg and SOS or DebugDiag.");
            Console.WriteLine("Commands:");
            Console.WriteLine(".loadby sos clr");
            Console.WriteLine("!dumpheap -type Exception");
            Console.ReadLine();
            Console.WriteLine(noexception.ToString()); // avoid noexception being GC'd
        }
    }
}
...