Вопрос по .net, c# – Как я могу отладить внутреннюю ошибку в .NET Runtime?

65

Я пытаюсь отладить некоторую работу, которая обрабатывает большие файлы. Сам кодработает, но в самой .NET Runtime сообщается о спорадических ошибках. Для контекста, обработка здесь - это файл объемом 1,5 ГБ (загружаемый в память только один раз), который обрабатывается и освобождается в цикле, специально для того, чтобы попытаться воспроизвести эту иначе непредсказуемую ошибку.

Мой тестовый фрагмент в основном:

try {
    byte[] data =File.ReadAllBytes(path);
    for(int i = 0 ; i < 500 ; i++)
    {
        ProcessTheData(data); // deserialize and validate

        // force collection, for tidiness
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
    }
} catch(Exception ex) {
    Console.WriteLine(ex.Message);
    // some more logging; StackTrace, recursive InnerException, etc
}

(с некоторыми сроками и другими добавленными вещами)

Цикл будет нормально обрабатываться для недетерминированного числа итерацийполностью успешно - никаких проблем вообще; тогда процесс резко прекратится. Обработчик исключений не ударил. Тест включает в себя много использования памяти, но он очень хорошо работает во время каждой итерации (нет явной утечки памяти, и у меня много свободного места - 14 ГБ неиспользуемой первичной памяти внаихудший точка в пиле). Процесс 64-битный.

Журнал ошибок Windows содержит 3 новые записи, которые (через код выхода 80131506) указывают на ошибку механизма выполнения - неприятный маленький фактор.связанный ответ, предлагает ошибку GC, с «исправлением» для отключения одновременного GC; однако это «исправление» не предотвращает проблему.

Пояснение: эта ошибка низкого уровня не затрагиваетCurrentDomain.UnhandledException мероприятие.

Пояснение:GC.Collect есть только для того, чтобы контролировать пилообразную память, проверять утечки памяти и сохранять вещи предсказуемыми; его удаление не устраняет проблему: оно просто удерживает больше памяти между итерациями и увеличивает размер dmp-файлов; p

Добавив больше трассировки консоли, я заметил, что она дает сбой во время каждого из:

во время десериализации (много выделений и т. д.)во время GC (между «подходом» GC и «завершением» GC с использованием API уведомлений GC)во время проверки (простоforeach по некоторым данным) - любопытнотолько после GC "завершено" во время проверки

Так много разных сценариев.

Я могу получить файлы аварийного дампа (dmp); как я могу исследовать это дальше, чтобы увидеть, что делает система, когда она так эффектно выходит из строя?

Вам удалось выяснить, что было причиной этого? Dan Neely
@EricJ это не означает производственный код; сбор GC предназначен просто для того, чтобы привести вещи в известное состояние для каждой итерации, а не в случайном порядке в середине GC. Удаление его не исправляет ошибку: это только усложняет наблюдение за зубцом пилы; p Весь этот блок кода существуетчисто чтобы проверить это, чтобы воспроизвести сообщенную ошибку. Marc Gravell♦
Любопытно, почему вы явно звоните в GC, потому что очень мало ситуаций, когда это можно считать хорошей практикой. Учитывая вашу репутацию, я уверен, что у вас есть веская причина и любопытно, что это. Eric J.
Не уверен, если уместно, но в соответствии сMSDNсборщик мусора может выдать эту ошибку при большой нагрузке:In some cases, an application that targets the .NET Framework may throw an ExecutionEngineException exception during garbage collection when an application or the system on which it is running is under a heavy load. As a workaround, you can disable concurrent garbage collection by modifying the application's configuration file. For more information, see How to: Disable Concurrent Garbage Collection. Bridge
@Nahum, когда я спросил об этом, это было по электронной почте в поддержку одного из моих проектов с открытым исходным кодом, которые я мог воспроизвести на месте. Кажется маловероятным, что у нас будет точно такая же ошибка ОЗУ. Marc Gravell♦

Ваш Ответ

5   ответов
3

Если вы запускаете свои вещи в Windows, есть много хороших альтернатив, таких как verysleepy для callgrind, как предлагается здесь:
Есть ли хорошая замена Valgrind для Windows?

Если вы действительно хотите отлаживать внутренние ошибки среды выполнения .NET, у вас есть проблема, что нет источника ни для библиотек классов, ни для виртуальной машины.

Поскольку вы не можете отлаживать то, чего у вас нет, я предлагаю (кроме декомпиляции библиотек инфраструктуры .NET с помощью ILSpy и добавления их в ваш проект, который все еще не распространяется на vm), вы можете использовать моно время выполнения.
Там у вас есть как источник библиотек классов, так и виртуальной машины.
Возможно, ваша программа отлично работает с моно, тогда ваша проблема будет решена, по крайней мере, до тех пор, пока это всего лишь одноразовая задача обработки.

Если нет, то существует обширный FAQ по отладке, в том числеGDB служба поддержки
http://www.mono-project.com/Debugging

Мигель также имеет этот пост о поддержке Valgrind:
http://tirania.org/blog/archive/2007/Jun-29.html

В дополнение к этому, если вы позволите ему работать в Linux, вы также можете использоватьТрассирование, чтобы увидеть, что происходит в системных вызовах. Если у вас нет обширного использования winforms или вызовов WinAPI, программы .NET обычно работают нормально в Linux (для проблем, связанных с чувствительностью к регистру файловой системы, вы можете подключить файловую систему без учета регистра и / или использоватьMONO_IOMAP).

Если вы человек, ориентированный на Windows,эта почта говорит, что самая близкая вещь, которую имеет Windows, является Logger.exe WinDbg, но информация трассировки не столь обширна.

Mono исходный код доступен здесь:
http://download.mono-project.com/sources/

Вы, вероятно, заинтересованы в источниках последней моно версии
http://download.mono-project.com/sources/mono/mono-3.0.3.tar.bz2

Если вам нужна платформа 4.5, вам понадобится mono 3, вы можете найти предварительно скомпилированные пакеты здесь
https://www.meebey.net/posts/mono_3.0_preview_debian_ubuntu_packages/

Если вы хотите внести изменения в исходный код, вот как его скомпилировать:
http://ubuntuforums.org/showthread.php?t=1591370

1

которые не могут быть перехвачены. Проверять, выписываться:http://msdn.microsoft.com/en-us/magazine/dd419661.aspx.

3

есть ли необработанное исключение, убивающее ваше приложение.

    AppDomain currentDomain = AppDomain.CurrentDomain;
    currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);

static void MyExceptionHandler(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);
afaik исключение ExecutionEngineException приводит к немедленному завершению процесса начиная с .NET 4.0, поэтому, к сожалению, это не поможет. Carsten
Чтобы быть явным: да, я попробовал это; нет, это также не получает удар Marc Gravell♦
Я предположил, что его обработчик исключений - это блок catch, который он написал в цикле. Dhawalk
Я ожидаю, что он уже попробовал это. Он заявляет: «Обработчик исключений не ударил». в вопросе. ChrisF♦
Увы, это «исключение» более низкого уровня - 80131506 - это исключение ExecutionEngineException; после того,нет Управляемый код будет работать. Хорошая идея, но не работает. Marc Gravell♦
7

Tools->Debugging->General->Enable .Net Framework Debugging

+

Tools->IntelliTace-> IntelliTaceEbents And Call Information

+

Tools->IntelliTace-> Set StorIntelliTace Recordings in this directory

и выберите каталог

должен позволить вам перейти в код INTO .net и отследить каждый вызов функции. Я попробовал это на небольшом примере проекта, и он работает

после каждого сеанса отладки предполагается создать запись сеанса отладки. это установленный каталог, даже если CLR умирает, если я не ошибаюсь

это должно позволить вам добраться до вызова extact до того, как CLR рухнет.

выполнение работы, которая занимает 10 + ГБ памяти и занимает более минуты на итерацию, и может не произойти целую вечность, что может быть чрезмерное количество журналирования. Хорошая идея, хотя. Marc Gravell♦
21

я бы посоветовал использовать WinDbg для их просмотра, предполагая, что вы этого еще не сделали.

Попытка запустить комментарий!EEStack (смешанная собственная и управляемая трассировка стека), и посмотрите, есть ли что-нибудь, что может появиться в трассировке стека В моей тестовой программе я однажды обнаружил, что в моей трассировке стека происходит FEEE (я целенаправленно искажал кучу):

0:000> !EEStack
---------------------------------------------
Thread   0
Curren,t frame: ntdll!NtWaitForSingleObject+0xa
Child-SP         RetAddr          Caller, Callee
00000089879bd3d0 000007fc586610ea KERNELBASE!WaitForSingleObjectEx+0x92, calling ntdll!NtWaitForSingleObject
00000089879bd400 000007fc5869811c KERNELBASE!RaiseException+0x68, calling ntdll!RtlRaiseException
[...]
00000089879bec80 000007fc49109cf6 clr!WKS::gc_heap::gc1+0x96, calling clr!WKS::gc_heap::mark_phase
00000089879becd0 000007fc49109c21 clr!WKS::gc_heap::garbage_collect+0x222, calling clr!WKS::gc_heap::gc1
00000089879bed10 000007fc491092f1 clr!WKS::GCHeap::RestartEE+0xa2, calling clr!Thread::ResumeRuntime
00000089879bed60 000007fc4910998d clr!WKS::GCHeap::GarbageCollectGeneration+0xdd, calling clr!WKS::gc_heap::garbage_collect
00000089879bedb0 000007fc4910df9c clr!WKS::GCHeap::Alloc+0x31b, calling clr!WKS::GCHeap::GarbageCollectGeneration
00000089879bee00 000007fc48ff82e1 clr!JIT_NewArr1+0x481

Поскольку это может быть связано с повреждением кучи сборщиком мусора, я бы попробовал!VerifyHeap команда. По крайней мере, вы можете убедиться, что куча не повреждена (и ваша проблема лежит в другом месте), или обнаружить, что ваша проблема может быть связана с GC или с некоторыми подпрограммами P / Invoke, которые ее портят.

Если вы обнаружите, что куча повреждена, я мог бы попытаться выяснить, сколько повреждено кучи, что вы можете сделать через!HeapStat, Это может просто показать, что вся куча повреждена с определенной точки.

Трудно предложить какие-либо другие методы для анализа этого с помощью WinDbg, поскольку я не имею ни малейшего представления о том, что делает ваш код или как он структурирован.

Я полагаю, если вы обнаружите, что это проблема с кучей и, таким образом, означает, что это может быть странностью GC, я бы посмотрел наСобытия CLR GC в трассировке событий для Windows.

Если минидампы, которые вы получаете, не режута также вы используете Windows 7 / 2008R2 или более позднюю версию, вы можете использовать глобальные флаги (gflags.exe) для присоединения отладчика, когда процесс завершается без исключения, если вы не получаете уведомление WER.

вSilent Process Exit на вкладке введите имя исполняемого файла,не полный путь к нему (т.е.TestProgram.exe). Используйте следующие настройки:

Установите флажок Включить мониторинг выхода из тихого процессаПроверьте процесс запуска монитораДля процесса мониторинга используйте{path to debugging tools}\cdb.exe -server tcp:port=5005 -g -G -p %e.

И применить настройки.

Когда ваша тестовая программа дает сбой, cdb подключится и будет ждать, пока вы подключитесь к нему. Запустите WinDbg, введите Ctrl + R и используйте строку подключения:tcp:port=5005,server=localhost.

Вы можете пропустить удаленную отладку и использовать вместо этого{path to debugging tools}\windbg.exe %e, Тем не менее, причина, по которой я предложил вместо этого, была в том, чтоWerFault.exeЯ считаю, что это то, что читает реестр и запускает процесс мониторинга, запустит отладчик в сеансе 0.

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

Похожие вопросы