174 votes

Comment déboguer les erreurs de corruption de tas ?

Je débogue une application C++ multithread (native) sous Visual Studio 2008. En des occasions apparemment aléatoires, j'obtiens une erreur "Windows a déclenché un point d'arrêt..." avec une note indiquant que cela pourrait être dû à une corruption du tas. Ces erreurs ne font pas toujours planter l'application immédiatement, mais il est probable qu'elle se plante peu après.

Le gros problème de ces erreurs est qu'elles n'apparaissent qu'après que la corruption a eu lieu, ce qui les rend très difficiles à repérer et à déboguer, surtout dans une application multithread.

  • Quelles sortes de choses peuvent provoquer ces erreurs ?

  • Comment puis-je les déboguer ?

Les conseils, outils, méthodes, éclairages... sont les bienvenus.

132voto

leander Points 6363

Vérificateur d'application combiné avec Outils de débogage pour Windows est une installation étonnante. (J'ai découvert l'existence d'Application Verifier en recherchant un système de gestion de la qualité. question précédente sur un problème de corruption de tas .) J'ai également utilisé BoundsChecker et Insure++ (mentionnés dans d'autres réponses) dans le passé, mais j'ai été surpris par la quantité de fonctionnalités présentes dans Application Verifier.

Clôture électrique (alias "efence"), dmalloc , Valgrind etc. méritent tous d'être mentionnés, mais la plupart d'entre eux sont beaucoup plus faciles à faire fonctionner sous *nix que sous Windows. Valgrind est ridiculement flexible : J'ai débogué de gros logiciels de serveur avec de nombreux problèmes de tas en l'utilisant.

Si tout le reste échoue, vous pouvez fournir vos propres opérateurs globaux new/delete et surcharges malloc/calloc/realloc -- la façon de le faire variera un peu en fonction du compilateur et de la plate-forme -- et ce sera un peu un investissement -- mais il peut s'avérer payant à long terme. La liste des fonctionnalités souhaitables devrait être familière à dmalloc et electricfence, ainsi qu'au livre, étonnamment excellent, intitulé Écrire du code solide :

  • valeurs sentinelles : laisser un peu plus d'espace avant et après chaque allocation, en respectant l'exigence d'alignement maximum ; remplir avec des nombres magiques (aide à attraper les débordements et les sous-débordements de tampon, et les pointeurs "sauvages" occasionnels).
  • remplissage de l'allocation : remplir les nouvelles allocations avec une valeur magique non-0 -- Visual C++ le fait déjà pour vous dans les constructions Debug (aide à détecter l'utilisation de variables non initialisées).
  • remplissage libre : remplir la mémoire libérée avec une valeur magique non-0, conçue pour déclencher un segfault si elle est déréférencée dans la plupart des cas (aide à attraper les pointeurs dangling).
  • sans retard : ne pas remettre la mémoire libérée dans le tas pendant un certain temps, la garder libre mais non disponible (aide à attraper plus de pointeurs pendants, attrape les doubles-freins proches).
  • suivi de la possibilité d'enregistrer le lieu où une allocation a été faite peut parfois être utile.

Notez que dans notre système local homebrew (pour une cible embarquée) nous gardons le suivi séparé de la plupart des autres choses, parce que l'overhead d'exécution est beaucoup plus élevé.


Si vous êtes intéressé par d'autres raisons de surcharger ces fonctions/opérateurs d'allocation, jetez un coup d'oeil à ma réponse à la question "Y a-t-il une raison de surcharger l'opérateur global new et delete ?" ; l'auto-promotion éhontée mise à part, il énumère d'autres techniques utiles pour repérer les erreurs de corruption de tas, ainsi que d'autres outils applicables.

3 votes

Une petite chose à noter à propos d'Application Verifier : vous devez enregistrer les symboles d'Application Verifier avant les symboles du serveur de symboles de Microsoft dans votre chemin de recherche de symboles, si vous l'utilisez... Il m'a fallu un peu de recherche pour comprendre pourquoi !avrf ne trouvait pas les symboles dont il avait besoin.

0 votes

Application Verifier m'a été d'une grande aide, et combiné à quelques devinettes, j'ai pu résoudre le problème ! Merci beaucoup, et à tous les autres aussi, pour avoir soulevé des points utiles.

0 votes

Application Verifier doit-il être utilisé avec WinDbg, ou doit-il fonctionner avec le débogueur de Visual Studio ? J'ai essayé de l'utiliser, mais il ne soulève aucune erreur ou ne fait apparemment rien lorsque je débogue dans VS2012.

36voto

Canopus Points 3154

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

Exécutez Gflags.exe et dans les options du fichier image de votre exécutable, cochez l'option "Enable Page Heap".

Maintenant, redémarrez votre exe et attachez-vous à un débogueur. Avec la fonction Page Heap activée, l'application entrera dans le débogueur dès qu'une corruption du tas se produira.

0 votes

Oui mais une fois que j'ai obtenu cet appel de fonction dans ma pile d'appels (après le crash dû à la corruption de la mémoire) : wow64!Wow64NotifyDebugger , que puis-je faire ? Je ne sais toujours pas ce qui ne va pas dans mon application.

0 votes

Je viens d'essayer gflags pour déboguer la corruption du tas ici, un petit outil TRÈS utile, fortement recommandé. Il s'est avéré que j'accédais à de la mémoire libérée, qui, lorsqu'elle est instrumentée avec gflags, entre immédiatement dans le débogueur... Pratique !

0 votes

Un outil formidable ! Je viens de trouver un bug, que j'ai cherché pendant des jours, parce que Windows ne dit pas l'adresse de la corruption, seulement que "quelque chose" est faux, ce qui n'est pas vraiment utile.

14voto

Dave Van Wagner Points 41

Pour vraiment ralentir les choses et effectuer de nombreuses vérifications au moment de l'exécution, essayez d'ajouter l'élément suivant au début de votre fichier main() ou équivalent dans Microsoft Visual Studio C++

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );

0 votes

Bien que cela ait rendu les choses très lentes pour moi, j'ai plutôt placé des appels à _CrtCheckMemory() avant et après certains endroits de mon code que je soupçonnais d'être à l'origine du problème. Un peu comme des "pièges à souris" pour mieux localiser l'endroit où l'erreur se produit.

13voto

8voto

ChrisW Points 37322

Quelles sortes de choses peuvent provoquer ces erreurs ?

Faire des choses vilaines avec la mémoire, par exemple écrire après la fin d'un tampon, ou écrire dans un tampon après qu'il ait été libéré dans le tas.

Comment puis-je les déboguer ?

Utilisez un instrument qui ajoute un contrôle automatique des limites à votre exécutable : par exemple valgrind sous Unix, ou un outil comme BoundsChecker (Wikipedia suggère aussi Purify et Insure++) sous Windows.

Sachez qu'ils ralentiront votre application, et qu'ils peuvent donc être inutilisables si votre application est un soft-real-time.

Une autre aide/outil de débogage possible pourrait être le HeapAgent de MicroQuill.

1 votes

La première chose à faire serait de reconstruire l'application avec le runtime de débogage (drapeau /MDd ou /MTd). Ceux-ci effectuent des vérifications supplémentaires à malloc et free, et sont souvent plus efficaces pour réduire l'emplacement du ou des bogues.

0 votes

Le HeapAgent de MicroQuill : Il n'y a pas beaucoup de choses écrites ou entendues à son sujet, mais pour la corruption de tas, il devrait être sur votre liste.

1 votes

BoundsChecker fonctionne bien en tant que test de fumée, mais n'envisagez même pas d'exécuter un programme sous lui tout en essayant d'exécuter également ce programme en production. Le ralentissement peut aller de 60x à 300x, selon les options que vous utilisez, et si vous utilisez ou non la fonction d'instrumentation du compilateur. Clause de non-responsabilité : Je suis l'un des gars qui maintiennent le produit pour Micro Focus.

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