Question

Je suis en train de déboguer une application C ++ multi-thread (native) sous Visual Studio & 2008. À des occasions apparemment aléatoires, je reçois un message "Windows a déclenché un point d'arrêt ...". erreur avec une note que cela pourrait être dû à une corruption dans le tas. Ces erreurs ne planteront pas toujours immédiatement l'application, même si elle risque de tomber en panne après.

Le gros problème de ces erreurs est qu’elles ne surviennent qu’après la corruption, ce qui les rend très difficiles à suivre et à déboguer, en particulier pour les applications multithreads.

  • Quel genre de choses peut provoquer ces erreurs?

  • Comment puis-je les déboguer?

Des astuces, des outils, des méthodes, des éclaircissements ... sont les bienvenus.

Était-ce utile?

La solution

vérificateur d'applications associé à Les outils de débogage pour Windows sont une configuration étonnante. Vous pouvez obtenir les deux éléments du kit de pilote Windows ou du kit de développement logiciel plus léger de Windows . (Découverte du vérificateur d'applications lors d'une recherche sur un question précédente sur un problème de corruption de tas .) J'ai déjà utilisé BoundsChecker et Insure ++ (mentionné dans d'autres réponses), bien que je sois surpris de la quantité de fonctionnalités de Application Verifier.

Electric Fence (ou "efence"), dmalloc , valgrind , etc., méritent d’être mentionnés, mais la plupart d’entre eux sont beaucoup plus faciles à exécuter sous * nix que Windows. Valgrind est ridiculement flexible: j'ai débogué un logiciel de serveur volumineux avec de nombreux problèmes de tas qui l'utilisent.

Lorsque tout le reste échoue, vous pouvez fournir à votre propre opérateur global les surcharges new / delete et malloc / calloc / realloc - la procédure varie légèrement selon le compilateur et la plate-forme - et ce sera un peu un investissement - mais cela peut porter ses fruits à long terme. La liste des fonctionnalités souhaitables devrait sembler familière chez dmalloc et electricfence, et le livre étonnamment excellent Écrire un code solide :

  • valeurs sentinelles : prévoyez un peu plus d'espace avant et après chaque allocation, dans le respect des exigences d'alignement maximales; remplir avec des nombres magiques (aide à attraper les débordements et les débordements de tampon, et le pointeur "sauvage" occasionnel)
  • allouer le remplissage : remplissez les nouvelles allocations avec une valeur magique non nulle - Visual C ++ le fait déjà pour vous dans les versions Debug (aide à détecter l'utilisation de vars non initialisés)
  • remplissage libre : remplissez la mémoire libérée avec une valeur magique non-0, conçue pour déclencher une erreur de segmentation si elle est déréférencée dans la plupart des cas (aide à détecter les pointeurs pendants)
  • libération retardée : ne restez pas la mémoire libérée pendant un certain temps, gardez-la libre, mais elle n'est pas disponible (aide à attirer davantage de pointeurs pendants, à proximité de la double libération)
  • suivi : il peut parfois être utile d’enregistrer où une allocation a été faite.

Notez que dans notre système homebrew local (pour une cible intégrée), le suivi est séparé de la plupart des autres éléments, car le temps système d'exécution est beaucoup plus élevé.

Si vous souhaitez davantage de raisons de surcharger ces fonctions / opérateurs d'allocation, consultez ma réponse à " Toute raison pour surcharger l’opérateur global new and delete? " ; En dehors de son auto-promotion sans vergogne, il répertorie d’autres techniques utiles pour le suivi des erreurs de corruption de tas, ainsi que d’autres outils applicables.

Comme je ne cesse de trouver ici ma propre réponse lors de la recherche des valeurs alloc / free / fence utilisées par MS, voici une autre réponse couvrant Microsoft dbgheap Remplir les valeurs .

Autres conseils

Vous pouvez détecter un grand nombre de problèmes de corruption de segment en activant Page Heap pour votre application. Pour ce faire, vous devez utiliser le fichier gflags.exe fourni avec Outils de débogage pour Windows

Exécutez Gflags.exe et dans les options de fichier image de votre exécutable, cochez la case "Activer le tas de page". option.

Maintenant, redémarrez votre fichier EXE et attachez-le à un débogueur. Avec Page Heap activé, l’application entrera dans le débogueur chaque fois qu’une corruption de tas se produit.

Pour vraiment ralentir le travail et effectuer beaucoup de vérifications à l'exécution, essayez d'ajouter ce qui suit en haut de votre main () ou son équivalent dans Microsoft Visual Studio C ++

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
  

Quel genre de choses peut provoquer ces erreurs?

Faire de vilaines choses avec de la mémoire, par exemple écrire après la fin d'un tampon, ou écrire dans un tampon après sa libération dans le tas.

  

Comment puis-je les déboguer?

Utilisez un instrument qui ajoute une vérification automatisée des limites à votre exécutable: par exemple, valgrind sous Unix, ou un outil tel que BoundsChecker (Wikipedia suggère également de purifier et d’assurer ++) sous Windows.

Attention, ils ralentiront votre application. Ils risquent donc d'être inutilisables si votre application est une application temps réel.

Un autre outil / outil de débogage possible pourrait être HeapAgent de MicroQuill.

Un conseil rapide que j'ai reçu de Détecter l'accès à la mémoire libérée est la suivante:

  

Si vous voulez localiser l'erreur   rapidement, sans vérifier tous les   déclaration qui accède à la mémoire   bloquer, vous pouvez définir le pointeur de la mémoire   à une valeur invalide après avoir libéré le   bloc:

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif

Le meilleur outil que j'ai trouvé utile et qui a fonctionné à chaque fois est la révision du code (avec de bons relecteurs de code).

Outre l'examen du code, je voudrais d'abord essayer Page Tas . Le tas de pages prend quelques secondes à mettre en place et avec un peu de chance, il pourrait localiser votre problème.

Si vous n’avez pas de chance avec Page Heap, téléchargez Outils de débogage pour Windows . de Microsoft et apprendre à utiliser WinDbg. Désolé, je ne pourrais pas vous aider de manière plus spécifique, mais le débogage de la corruption de segments multithreads est plus un art que la science. Google pour " WinDbg heap corruption " et vous devriez trouver de nombreux articles sur le sujet.

Vous pouvez également vérifier si vous établissez une liaison avec la bibliothèque d'exécution C dynamique ou statique. Si vos fichiers DLL sont liés à la bibliothèque d'exécution C statique, les fichiers DLL ont des segments distincts.

Par conséquent, si vous créez un objet dans une DLL et essayez de le libérer dans une autre DLL, vous obtiendrez le même message que celui affiché ci-dessus. Ce problème est référencé dans une autre question de dépassement de pile, Libération de la mémoire allouée dans une autre DLL .

Quel type de fonctions d’allocation utilisez-vous? J'ai récemment rencontré une erreur similaire en utilisant les fonctions d'allocation de style Heap *.

Il s’est avéré que je créais par erreur le segment de mémoire avec l’option HEAP_NO_SERIALIZE . Cela permet essentiellement aux fonctions de tas de s'exécuter sans sécurité de thread. Il s'agit d'une amélioration des performances s'il est utilisé correctement mais ne devrait jamais l'être si vous utilisez HeapAlloc dans un programme multithread [1]. Je mentionne cela uniquement parce que votre message mentionne que vous avez une application multithread. Si vous utilisez HEAP_NO_SERIALIZE n’importe où, supprimez-le et cela résoudra probablement votre problème.

[1] Cela est légal dans certaines situations, mais vous devez sérialiser les appels à Heap *, ce qui n'est généralement pas le cas pour les programmes multithreads.

Si ces erreurs se produisent de manière aléatoire, la probabilité que vous rencontriez des courses de données est élevée. Veuillez vérifier: modifiez-vous les pointeurs de mémoire partagée de différents threads? Intel Thread Checker peut aider à détecter de tels problèmes dans les programmes multithread.

En plus de rechercher des outils, envisagez de rechercher un coupable. Y a-t-il un composant que vous utilisez, peut-être pas écrit par vous, qui n'a peut-être pas été conçu ni testé pour fonctionner dans un environnement multithread? Ou simplement celui que vous ne savez pas que vous avez exécuté dans un tel environnement.

La dernière fois que cela m'est arrivé, il s'agissait d'un package natif utilisé avec succès depuis des années dans le traitement par lots. Mais c’était la première fois dans cette entreprise qu’elle était utilisée à partir d’un service Web .NET (multithread). C'était tout - ils avaient menti sur le code étant thread-safe.

Vous pouvez utiliser les macros VC CRT Heap-Check pour _CrtSetDbgFlag : _CRTDBG_CHECK_ALWAYS_DF ou _CRTDBG_CHUK_EVERY_16_DF .

J'aimerais ajouter mon expérience. Au cours des derniers jours, j'ai résolu une instance de cette erreur dans mon application. Dans mon cas particulier, les erreurs dans le code étaient les suivantes:

  • Suppression d'éléments d'une collection STL en effectuant une itération (je pense qu'il existe des indicateurs de débogage dans Visual Studio pour détecter ces éléments; je les ai interceptés lors de l'examen du code)
  • Celui-ci est plus complexe, je vais le diviser en étapes:
    • À partir d'un thread C ++ natif, rappelez le code géré
    • Sur les terres gérées, appelez Control.Invoke et disposez d'un objet géré qui enveloppe l'objet natif auquel le rappel appartient.
    • Puisque l'objet est toujours en vie dans le thread natif (il restera bloqué dans l'appel de rappel jusqu'à la fin de Control.Invoke ). Je devrais préciser que j’utilise boost :: thread , j’utilise donc une fonction membre comme fonction de thread.
    • Solution : utilisez Control.BeginInvoke (mon interface graphique est créée avec Winforms) afin que le thread natif puisse se terminer avant la destruction de l'objet (le rappel a précisément pour but de notifier que le thread a pris fin et que l'objet peut être détruit).

J'ai eu un problème similaire - et il est apparu de manière assez aléatoire. Quelque chose était peut-être corrompu dans les fichiers de construction, mais j'ai fini par le réparer en nettoyant d'abord le projet, puis en le reconstruisant.

Donc, en plus des autres réponses données:

Qu'est-ce qui peut causer ces erreurs? Quelque chose est corrompu dans le fichier de construction.

Comment puis-je les déboguer? Nettoyage du projet et reconstruction. Si le problème est résolu, c'est probablement le problème.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top