67 votes

Comment puis-je déboguer une erreur interne dans le runtime .NET?

Je suis en train de debug de travail qui traite de gros fichiers. Le code fonctionne, mais il y a des erreurs sporadiques sont signalés dans les .NET Runtime lui-même. Pour le contexte, le traitement ici est de 1,5 GO de fichier (chargé en mémoire qu'une seule fois) d'être traitée et diffusée en boucle, délibérément, pour essayer de reproduire ce imprévisible d'erreur.

Mon test fragment est en gros:

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
}

(avec un peu de timing et d'autres trucs jetés dans)

La boucle processus d'amende pour non-déterministe nombre d'itérations pleinement avec succès - aucun problème; ensuite, le processus va s'arrêter brutalement. Le gestionnaire d'exception n'est pas atteint. Le test n'impliquent beaucoup de l'utilisation de la mémoire, mais il ne voyait-dents très très bien lors de chaque itération (il n'est pas évident de fuite de mémoire, et j'ai beaucoup de dégagement à la tête - 14GB inutilisés mémoire primaire au pire point en dents de scie). Le processus est en 64 bits.

L'erreur windows-journal contient 3 nouvelles entrées, ce qui (par l'intermédiaire de code de sortie 80131506) suggèrent un Moteur d'Exécution d'erreur - une vilaine petite bête. Une réponse similaire, suggère un GC erreur, avec un "fix" pour désactiver simultanées GC; cependant cette "solution" n'empêchent pas la question.

Clarification: ce faible niveau de l'erreur de ne pas frapper l' CurrentDomain.UnhandledException événement.

Clarification: l' GC.Collect est là que pour surveiller la scie à denture de mémoire, à vérifier pour les fuites de mémoire et de garder les choses prévisibles, l'enlever n'est pas le problème: elle permet simplement de garder plus de mémoire entre les itérations, et rend le dmp des fichiers plus ;p

En ajoutant de la console de traçage, j'ai observé qu'il défaillant au cours de chacune de:

  • lors de la désérialisation (beaucoup de dotations,...)
  • au cours de GC (entre une table "approche" et une table "complète", à l'aide de la GC API de notification)
  • au cours de validation (juste foreach - dessus de certaines des données) - curieusement , juste après un GC "complet" lors de la validation

Beaucoup de scénarios différents.

Je peux obtenir un crash dump (dmp) des fichiers; comment puis-je étudier cette question plus loin, pour voir ce que le système est en train de faire lorsqu'il échoue spectaculairement?

22voto

Si vous avez des vidages de mémoire, je suggérerais à l'aide de WinDbg à les regarder, en supposant que vous ne faites pas déjà.

En essayant de course le commentaire !EEStack (mixte maternelle et géré trace de la pile), et voir si il ya quelque chose qui peut sauter dans la trace de la pile. Dans mon programme de test, j'ai trouvé celui de la fois que mon trace de la pile d'où un FEEE qui s'est passé (j'ai été à dessein de corrompre le segment de mémoire):

0:000> !EEStack
---------------------------------------------
Thread 0
Image active: ntdll!NtWaitForSingleObject+0xa
Enfant-SP RetAddr de l'Appelant, le Destinataire de l'appel
00000089879bd3d0 000007fc586610ea KERNELBASE!WaitForSingleObjectEx+0x92, l'appel de ntdll!NtWaitForSingleObject
00000089879bd400 000007fc5869811c KERNELBASE!RaiseException+0x68, l'appel de ntdll!RtlRaiseException
[...]
00000089879bec80 000007fc49109cf6 clr!SEM::gc_heap::gc1+0x96, appelant clr!SEM::gc_heap::mark_phase
00000089879becd0 000007fc49109c21 clr!SEM::gc_heap::garbage_collect+0x222, appelant clr!SEM::gc_heap::gc1
00000089879bed10 000007fc491092f1 clr!SEM::GCHeap::RestartEE+0xa2, appelant clr!Thread::ResumeRuntime
00000089879bed60 000007fc4910998d clr!SEM::GCHeap::GarbageCollectGeneration+0xdd, appelant clr!SEM::gc_heap::garbage_collect
00000089879bedb0 000007fc4910df9c clr!SEM::GCHeap::Alloc+0x31b, appelant clr!SEM::GCHeap::GarbageCollectGeneration
00000089879bee00 000007fc48ff82e1 clr!JIT_NewArr1+0x481

Depuis ce pourrait être lié à la corruption de segment du garbage collector, je voudrais essayer de l' !VerifyHeap commande. Au moins vous pouvez assurez-vous que le segment de mémoire est intacte (et votre problème est ailleurs) ou de découvrir que votre problème pourrait être avec le GC ou certains P/Invoke routines de corruption.

Si vous trouvez que le tas est corrompu, je pourrais essayer et découvrir combien de segment est endommagé, vous pourriez être en mesure de le faire via !HeapStat. Qui aurait pu montrer l'ensemble du tas corrompus à partir d'un certain point, cependant.

Il est difficile de proposer d'autres méthodes pour analyser ce par WinDbg, puisque je n'ai pas de réelle idée de ce que votre code est fait ou comment il est structuré.

Je suppose que si vous trouvez un problème avec le tas et donc le sens qu'il pourrait être GC quelque chose d'étrange, je regarde le CLR GC événements dans le suivi d'Événements pour Windows.


Si la les minidumps que vous obtenez ne sont pas le couper et que vous utilisez Windows 7/2008R2 ou version ultérieure, vous pouvez utiliser les indicateurs Globaux (gflags.exe) pour attacher un débogueur lorsque le processus se termine sans exception, si vous n'obtenez pas un WER de notification.

Dans l' Silent Process Exit onglet, entrez le nom de l'exécutable, pas le chemin d'accès complet (ie. TestProgram.exe). Utilisez les paramètres suivants:

  • Cochez La Case Activer Silencieux De Sortie Du Processus De Surveillance
  • Vérifiez Le Lancement Du Processus Du Moniteur
  • Pour le Processus du Moniteur, utilisez {path to debugging tools}\cdb.exe -server tcp:port=5005 -g -G -p %e.

Et appliquer les paramètres.

Lors de votre test de programme se bloque, cdb, vous fixez et attendre pour vous pour vous y connecter. Début WinDbg, tapez Ctrl+R, et d'utiliser la chaîne de connexion: tcp:port=5005,server=localhost.

Vous pourriez être en mesure de sauter en utilisant le débogage à distance et au lieu d'utiliser {path to debugging tools}\windbg.exe %e. Cependant, la raison pour laquelle j'ai suggéré à distance au lieu de cela, c'est parce que WerFault.exe, je crois que c'est ce que lit le registre et lance le processus de moniteur, va démarrer le débogueur dans la Session 0.

Vous pouvez faire la session 0 interactive et se connecter à la station de fenêtre, mais je ne me souviens pas comment c'est fait. C'est aussi gênant, parce que vous en auriez pour alterner entre les sessions, si vous avez besoin d'accéder à un de vos fenêtres que vous avez ouvertes.

7voto

Nahum Litvin Points 2593

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

+

Tools->IntelliTace-> IntelliTaceEbents And Call Information

+

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

et choisissez un répertoire

devrait vous permettre d’intervenir dans le code .net et de suivre chaque appel de fonction. Je l'ai essayé sur un petit exemple de projet et ça marche

après chaque session de débogage, il est supposé créer un enregistrement de la session de débogage. il le répertoire défini, même si CLR meurt si im ne me trompe pas

Cela devrait vous permettre d'obtenir l'appel extact avant que le CLR ne soit réduit.

3voto

Dhawalk Points 831

Essayez d'écrire un gestionnaire d'exception générique et voyez s'il existe une exception non gérée qui tue votre application.

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

3voto

Quandary Points 12867

J'ai l'habitude de invesitgate liée à la mémoire de problèmes avec Valgrind et gdb.

Si vous exécutez votre choses sur Windows, il y a beaucoup de solutions de rechange telles que verysleepy pour callgrind comme suggéré ici:
Est-il un bon Valgrind substitut pour Windows?

Si vous voulez vraiment pour déboguer les erreurs internes de l' .NET, vous avez le problème qu'il n'existe aucune source, ni pour les bibliothèques de classes, ni de la VM.

Puisque vous ne pouvez pas déboguer ce que vous n'avez pas, je vous suggère (à l'exception de décompilation du .NET framework bibliothèques en question avec ILSpy, et de les ajouter à votre projet, qui n'a pas de couvercle de la machine virtuelle), vous pouvez utiliser le mono de l'exécution.
Là, vous avez à la fois la source de la classe des bibliothèques ainsi que de la VM.
Peut-être que votre programme fonctionne très bien avec mono, alors votre problème serait résolu, au moins aussi longtemps que c'est seulement une fois la tâche de traitement.

Si non, il ya une vaste FAQ sur le débogage, y compris GDB soutien
http://www.mono-project.com/Debugging

Miguel a aussi ce post concernant valgrind de soutien:
http://tirania.org/blog/archive/2007/Jun-29.html

En plus de cela, si vous le laisser fonctionner sur Linux, vous pouvez également utiliser strace, pour voir ce qui se passe sur les syscalls. Si vous n'avez pas de vastes winforms de l'utilisation ou de la WinAPI appels .NET programmes fonctionnent très bien sur Linux (pour les problèmes concernant le système de fichiers de la casse, vous pouvez loopmount la casse du système de fichiers et/ou de l'utilisation MONO_IOMAP).

Si vous êtes Windows centrée sur la personne, ce post dit la chose la plus proche de Windows a est WinDbg est Logger.exe mais ltrace de l'information n'est pas aussi vaste.

Mono code source est disponible ici:
http://download.mono-project.com/sources/

Vous êtes probablement intéressé par les sources de la dernière version de mono
http://download.mono-project.com/sources/mono/mono-3.0.3.tar.bz2

Si vous avez besoin de framework 4.5, vous aurez besoin de mono 3, vous pouvez trouver des paquets précompilés ici
https://www.meebey.net/posts/mono_3.0_preview_debian_ubuntu_packages/

Si vous voulez apporter des modifications au code source, c'est la façon de le compiler:
http://ubuntuforums.org/showthread.php?t=1591370

1voto

dcarapic Points 128

Il existe des exceptions .NET qui ne peuvent pas être interceptées. Découvrez: http://msdn.microsoft.com/en-us/magazine/dd419661.aspx .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X