Статьи

Как отлаживать тихие сбои в .Net

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

Я отлаживал именно такую ​​проблему сегодня. Клиент дважды щелкнет по приложению, и ничего не произойдет. Наше приложение настроено на запись любых необработанных исключений в файл журнала. Но файл журнала не создавался. Однако в журнал событий приложения была записана запись, в которой сообщалось, что было выдано исключение TypeInitializationException , и даже была дана трассировка стека. Но он ничего не сказал мне о том, почему тип не был инициализирован.

Чтобы усложнить ситуацию, это был один из тех heisenbugs, где, хотя я мог бы воспроизвести его при нормальных обстоятельствах, он исчез бы, если бы я подключил к процессу отладчик. Что мне было нужно, так это аварийный дамп — снимок всего состояния приложения на момент возникновения исключения.

DebugDiag был моим первым портом захода. Это отличный инструмент от Microsoft, который можно настроить для создания дампов из вашего приложения при определенных обстоятельствах, в том числе при его сбое. Удобно, он также проанализирует файл дампа и поможет вам понять, почему произошло сбой приложения. Необъяснимо, что DebugDiag не смог перехватить какие-либо дампы для моего приложения.

Поэтому я обратился к Windows Error Reporting. Начиная с Windows Vista с пакетом обновления 1 (SP1), вы можете настраивать некоторые флаги в реестре , и Windows автоматически захватывает дампы — при условии, что вы используете .Net 4.0.

Захват файлов дампа с помощью отчетов об ошибках Windows

Все, что вам нужно сделать, это установить ключ создания в HKEY_LOCAL_MACHINE \ Software \ Microsoft \ Windows \ Отчеты об ошибках Windows \ LocalDumps \ [Exe Name вашего приложения]. В этом ключе создайте строковое значение с именем DumpFolder и установите его в папку, в которую вы хотите записать дампы. Затем создайте значение DWORD с именем DumpType со значением 2.

образ

Теперь заставьте этих ниндзя разбить ваше приложение.

Вы должны увидеть файл * .dmp, который появится в выбранной вами папке. Дважды щелкните по нему — и вы увидите некоторую магию Visual Studio, представленную в VS 2010. Вы можете отлаживать файл дампа почти так же, как если бы это было живое приложение. Когда вы видите экран сводки файла Minidump, вам просто нужно нажать Debug with Mixed.

образ

Разоблачение ниндзя

Так что же это за исключение ниндзя, которое уклонялось от моих процедур регистрации? Это было исключение, вызванное моим кодом обработки исключений!

Первоначальным исключением было исключение ConfigurationErrorsException , создаваемое API конфигурации при загрузке пользовательских настроек моего приложения из поврежденного файла user.config. Мой код обработки исключений выглядел так:

private void ReportUnhandledException(Exception exception)
{
    try
    {
        Tracing.TraceUnhandledError(exception);
 
        ReportExceptionToUser(exception);
    }
    catch (Exception fatalException)
    {
        Tracing.TraceUnhandledError(fatalException);
    }
}

Проблема была вызвана статическим конструктором в классе Tracing. Мы используем API-интерфейс System.Diagnostics TraceSource для регистрации наших исключений, и конструктор все это настраивал. Оказывается, API-интерфейс TraceSource также пытается загрузить конфигурацию приложения, поэтому возникла исключительная ситуация.

Решение? Используйте Environment.FailFast вместо попытки регистрации вторичного исключения. Это просто записывает сообщение в журнал событий, затем выручает, без шансов вызвать хаос, вызывая дальнейшие исключения.

private void ReportUnhandledException(Exception exception)
{
    try
    {
        Tracing.TraceUnhandledError(exception);
 
        ReportExceptionToUser(exception);
    }
    catch (Exception fatalException)
    {
        Environment.FailFast("An error occured whilst reporting an error.", exception);
    }
}