Как я могу отладить внутреннюю ошибку в .NET Runtime?

Я пытаюсь отладить некоторую работу, которая обрабатывает большие файлы. Сам код работает , но есть спорадические ошибки, сообщаемые из самого .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) указывают на ошибку Engine Engine - неприятный маленький тредтер. A связанный ответ , предлагает ошибку GC с «исправлением», чтобы отключить параллельный GC; однако это «исправление» не предотвращает проблему.

Уточнение: эта ошибка низкого уровня не попадает в событие CurrentDomain.UnhandledException .

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

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

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

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

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

64
Попробуйте подключиться к процессу в основном режиме. Диалоговое окно Attach позволяет выбрать режим. Затем установите отладчик на все исключения; Во всяком случае, это, очевидно, ошибка CLR, так как ваше использование небезопасного кода явно безопасно. Я думаю, что лучшее, что вы можете сделать, это деактивировать код, запускающий эту ошибку и работающую вокруг нее.
добавлено автор usr, источник
Можете ли вы определить, где в вашем коде приложение, когда оно завершается?
добавлено автор Rawling, источник
Любопытно, почему вы прямо называете GC, так как существует очень мало ситуаций, когда это можно считать хорошей практикой. Учитывая вашу репутацию, я уверен, что у вас есть веская причина и любопытно, что это такое.
добавлено автор Eric J., источник
хм, может быть, это простая ошибка бара? Несколько месяцев назад у меня была проблема с моей дроблением системы вокруг выталкивания 12GIG бара, это просто поразило плохой сектор памяти. и после случайного количества времени дробления ..
добавлено автор Nahum, источник
что, если вы используете .net framework source stepping + disable «justmycode» + использовать события intellitrace И информация о вызове + установить разрыв, когда исключение - это перехват всех исключений?
добавлено автор Nahum, источник
Вам удалось выяснить, что вызвало это?
добавлено автор Dan Neely, источник
Видели ли вы, что компиляция и запуск в режиме исполнения Mono делает то же самое? Просто может быть интересно посмотреть, какие контрасты в результатах появляются.
добавлено автор Jesse C. Slicer, источник
Почти два года спустя вам удалось это исправить?
добавлено автор Breeze, источник
Каков диапазон успешных итераций? Обычно ли он терпит неудачу после примерно того же числа, или он повсюду?
добавлено автор Jon B, источник
Не уверен, если это уместно, но согласно MSDN , мусор сборщик может произвести эту ошибку при большой нагрузке: В некоторых случаях приложение, предназначенное для .NET Framework, может вызывать исключение ExecutionEngineException во время сбора мусора, когда приложение или система, на которых он работает, находятся под большой нагрузкой. В качестве обходного пути вы можете отключить одновременную сборку мусора, изменив файл конфигурации приложения. Дополнительные сведения см. В разделе Как отключить параллельную сборку мусора.
добавлено автор Bridge, источник
Странный вопрос - возникают ли у вас те же проблемы, если вы щелкните правой кнопкой мыши -> запустите как администратор? Это было предложено в блоге, который я нашел, написанном кем-то, пытающимся отладить ту же проблему ( ссылка , если вам интересно).
добавлено автор Bridge, источник
@Nahum, когда я спросил об этом, это было через электронную почту поддержки одному из моих проектов с открытым исходным кодом, которые я мог бы воспроизвести на местном уровне. Кажется маловероятным, что у нас будет такая же ошибка RAM.
добавлено автор Marc Gravell, источник
@Hans MoveNext был немного другим сценарием: я видел только один раз , и это было AccessViolationException; единственное время (из многих), что я видел фактическое исключение .NET - в основном, повреждение памяти, предположительно, из-за безумных вещей, происходящих во время GC.
добавлено автор Marc Gravell, источник
@Hossein nope, я так и не сделал
добавлено автор Marc Gravell, источник
@ChristopherCurrens нет, я признаюсь, что нет. Из-за того, что на месте, это, к сожалению, боль ...
добавлено автор Marc Gravell, источник
@ChristopherCurrens Я не помню этого имени; один нацелен на .NET 3; Зачем?
добавлено автор Marc Gravell, источник
Проблема @usr заключается в следующем: это происходит в непредсказуемых и несвязанных областях: ни одна причина, кроме большого количества использования памяти в то время
добавлено автор Marc Gravell, источник
@Hans Я посмотрю, что коллега-Ник может сделать из одного моего более недавнего dmp-файла, тогда я попробую выполнить регистрацию через MS
добавлено автор Marc Gravell, источник
@mathieu re memory: это я изучаю то, что изначально сообщалось на совершенно отдельной машине. Я недавно проверил память, хотя и не сегодня, но я думаю, что это маловероятно
добавлено автор Marc Gravell, источник
@EricJ это не означает производственный код; этот сбор GC предназначен только для того, чтобы получить информацию в известное состояние для каждой итерации, а не GC случайно в середине. Удаление этого исправления не устраняет: это просто затрудняет просмотр пилы; p Весь этот блок кода существует чисто , чтобы подчеркнуть это, чтобы воспроизвести сообщенную ошибку.
добавлено автор Marc Gravell, источник
@Rawling Я могу добавить немного больше записей, но: только немного
добавлено автор Marc Gravell, источник
@EricJ. как ни странно, другой ответ по связанным вопросам предполагает, что это связано с GC; Я добавил некоторые записи, и я надеюсь , это будет происходить во время шага GC. Это было бы хорошо и убедительно.
добавлено автор Marc Gravell, источник
@NahumLitvin там is не исключение; сам механизм выполнения завершается
добавлено автор Marc Gravell, источник
@JonB повсюду; иногда он разбивается менее чем на 5 итераций; иногда он будет работать для «разочаровывающего долго» счастливо
добавлено автор Marc Gravell, источник
@Bridge да, это теория (найденная после публикации вопроса), которую я сейчас изучаю
добавлено автор Marc Gravell, источник
@ Черт побери: «Потому что я воспроизвел его с установленным 4.5 (нацеленным на 4.0 и 4.5)
добавлено автор Marc Gravell, источник
@ Это просто так: он был передан мне от кого-то другого, так что он определенно воспроизводимый. Тем не менее, я запустил новый memtest.
добавлено автор Marc Gravell, источник
@HansPassant ничего из вышеперечисленного; он использует небезопасный только , чтобы ударить между Int32 <===> Single, локального параметра by-val; ничего страшно. И ошибка может возникнуть вне десериализации - как раз на шаге проверки (MoveNext коллекции, как ни странно, но в другое время в EnsureCapacity коллекции). Либо CLI является вялым, либо SortedList <,> делает что-то злое!
добавлено автор Marc Gravell, источник
Это одно. Исправлено в 4.5
добавлено автор Hans Passant, источник
Черт побери. Учитывая случайность и отсутствие доказательств того, что у 4.5 есть багги-коллекционер, вы действительно должны рассмотреть проблему с оборудованием. Убедитесь, что вы можете воспроизвести это на другой машине.
добавлено автор Hans Passant, источник
Хорошо, это имеет больше смысла. Сильные намеки на то, что GC неправильно обновляет ссылку на объект после того, как он уплотняет кучу. Создает FEEE, когда эта плохая ссылка встречается во время GC, AV, если вы разыскиваете ссылку на объект в своем коде после сбора. Уверенные звуки, как у GC все еще есть cooties.
добавлено автор Hans Passant, источник
Ну, это довольно убедительно. Можете ли вы охарактеризовать код немного лучше? Сколько там хакромама? Я знаю, что вам нравится выжимать последнюю унцию перфорации из сериализации. Выполнение чего-либо с классом маршала, фиксированных буферов или stackalloc?
добавлено автор Hans Passant, источник
Код должен работать в недрах CLR для создания FEEE. EnsureCapacity() является хорошим, очень вероятным для запуска GC. MoveNext() не работает, может произойти только при включенном фоновом GC, и вы уже устранили это. У вас есть сценарий воспроизведения, я просто возьму это в поддержку Microsoft.
добавлено автор Hans Passant, источник
Что делает метод ProcessTheData (data)/выглядит так? вам также не нужно будет звонить GC вообще, где этот код работает ..? это на вашей локальной или удаленной машине ..? начните с некоторых базовых 1. проверьте, чтобы убедиться, что у вас есть правильная проверка {} catch error. 2. Убедитесь, что такие вещи, как настройки региона, согласованы. 3. Вставьте записи в те места, где вы думаете, что может произойти ошибка.
добавлено автор MethodMan, источник
У меня были прерывистые сбои программы, даже не запускающие WER, с этим именем события раньше. Случилось на нескольких компьютерах довольно случайным образом, когда есть интенсивное использование ЦП. Мой вопрос о версии 4.0 vs 2.0 можно игнорировать. Я думал о чем-то, что влияет только на сборки смешанного режима. Вы упомянули ранее о воспроизведении с установленным 4.5. Вы тестировали его с помощью .NET 4.0 only , который нацелен на 4.0 и не имеет вообще 4.5 установленного (поскольку это обновление на месте)?
добавлено автор Christopher Currens, источник
Случайно ли в журнале событий есть RADAR_PRE_LEAK_64 как имя события? Кроме того, все ваши сборки .NET 4.0 или некоторые из них предназначены для среды выполнения 2.0?
добавлено автор Christopher Currens, источник
Вы взяли дамп памяти с чем-то вроде sysinternals procdump до сбоя, а затем посмотрели w/windbg? Похоже, что LOH может быть чрезвычайно фрагментирован из всех этих больших распределений массивов, и мне интересно, может ли это привести к некоему повреждению памяти. Код исключения 0xc0000005 также указывает на проблему с доступом к памяти ... всего лишь следующий следующий шаг.
добавлено автор Chris, источник
Чтобы исключить проблему с оборудованием, попробовали ли вы запустить инструмент проверки памяти, например memtest86: memtest.org/# downiso и стресс-тест ЦП ( superuser.com/questions/396501/… )?
добавлено автор mathieu, источник

5 ответы

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

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

0:000> !EEStack
---------------------------------------------
Thread   0
Current 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 или новее, вы можете использовать Global Flags (gflags.exe) для присоединения отладчика, когда процесс завершается без исключение, если вы не получаете уведомление WER.

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

  • Отметьте «Включить мониторинг выхода без звука»
  • Проверить процесс запуска запуска
  • Для процесса мониторинга используйте {путь к средствам отладки} \ cdb.exe -server tcp: port = 5005 -g -G -p% e .

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

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

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

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

21
добавлено

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

+

Tools->IntelliTace-> IntelliTaceEbents And Call Information

+

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

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

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

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

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

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

Обычно я вызываю проблемы с памятью с Valgrind и gdb.

If you run your things on Windows, there are plenty of good alternatives such as verysleepy for callgrind as suggested here:
Is there a good Valgrind substitute for Windows?

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

Since you can't debug what you don't have, I suggest that (apart from decompiling the .NET framework libraries in question with ILSpy, and adding them to your project, which still doesn't cover the vm) you could use the mono runtime.
There you have both the source of the class libraries as well as of the VM.
Maybe your program works fine with mono, then your problem would be solved, at least as long as it's only a one-time-processing task.

If not, there is an extensive FAQ on debugging, including GDB support
http://www.mono-project.com/Debugging

Miguel also has this post regarding valgrind support:
http://tirania.org/blog/archive/2007/Jun-29.html

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

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

Mono sourcecode is available here:
http://download.mono-project.com/sources/

You are probably interested in the sources of the latest mono version
http://download.mono-project.com/sources/mono/mono-3.0.3.tar.bz2

If you need framework 4.5, you'll need mono 3, you can find precompiled packages here
https://www.meebey.net/posts/mono_3.0_preview_debian_ubuntu_packages/

If you want to make changes to the sourcecode, this is how to compile it:
http://ubuntuforums.org/showthread.php?t=1591370

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);
3
добавлено
@MarcGravell: Ха-ха, да ... Я никогда не делал этого сам, поэтому я не знаю, как сильно это lol. Просто упомянул об этом, так как он может помочь исключить исключения из собственного кода. :)
добавлено автор Mehrdad, источник
@MarcGravell: Вы пробовали разместить CLR с помощью собственного кода, а затем обрабатывать исключение?
добавлено автор Mehrdad, источник
Увы, это «исключение» более низкого уровня - 80131506 является ExecutionEngineException; после этого будет выполняться управляемый код no . Хорошая идея, но не работает.
добавлено автор Marc Gravell, источник
Чтобы быть явным: да, я пробовал это; нет, что тоже не попадает
добавлено автор Marc Gravell, источник
@Mehrdad действительно нет; это звучит довольно много работы
добавлено автор Marc Gravell, источник
Я ожидаю, что он уже пробовал это. Он утверждает: «Обработчик исключений не попал». в вопросе.
добавлено автор ChrisF, источник
afaik ExecutionEngineException приводит к немедленному завершению процесса с .NET 4.0, поэтому это, к сожалению, не будет полезным.
добавлено автор Carsten, источник
Я предположил, что его обработчик исключений - это блок catch, который он написал вокруг цикла.
добавлено автор Dhawalk, источник

Есть исключения .NET, которые невозможно поймать. Проверьте: http://msdn.microsoft.com/en-us/magazine/ dd419661.aspx .

1
добавлено
DotNetRuChat
DotNetRuChat
2 992 участник(ов)

Чат русскоязычного .NET сообщества http://dotnet.ru/ Вам могут быть интересны: @dotnetchat, @cilchat, @fsharp_chat, @pro_net, @xamarin_russia, @microsoftstackjobs, @uwp_ru Флуд в @dotnettalks

Microsoft Stack Jobs
Microsoft Stack Jobs
1 788 участник(ов)

Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks

pro.net
pro.net
710 участник(ов)

Обсуждение .NET Framework и всего, что с ним связано. Правила: не флудить не по теме, уважать ваших коллег и никакой рекламы (объявления о вакансиях можно согласовать с @AlexFails). Флудилка: @dotnettalks Участник @proDOT

Microsoft Developer Community Chat
Microsoft Developer Community Chat
584 участник(ов)

Чат для разработчиков и системных администраторов Microsoft Developer Community. __________ Новостной канал: @msdevru __________ Баним за: оскорбления, мат, рекламу, флуд, флейм, спам, NSFW контент, а также большое количество оффтоп тем. @banofbot

.NET Talks: Force Push Masters
.NET Talks: Force Push Masters
490 участник(ов)

Свободный чат .NET разработчиков. Правила: t.me/dotnettalks/56823 Вам могут быть интересны: @dotnetruchat, @dotnetchat, @cilchat, @fsharp_chat, @pro_net, @dotnetgroup, @xamarin_russia, @microsoftstackjobs, @uwp_ru http://combot.org/chat/-1001128250813

.NET Chat Убежище
.NET Chat Убежище
246 участник(ов)

Чат .NET разработчиков под эгидой MSK/SPB .NET Community Group Вам могут быть интересны: @fsharp_chat, @dotnetruchat, @cilchat, @xamarin_russia, @microsoftstackjobs, @dotnetgroup Флуд в @dotnettalks

.NET CIL Chat
.NET CIL Chat
54 участник(ов)

.NET CIL (aka IL aka MSIL)