Le vérificateur d'application combiné aux outils de débogage pour Windows est une configuration incroyable. Vous pouvez obtenir les deux dans le cadre du Kit de pilotes Windows ou du SDK Windows plus léger . (Découvrez Application Verifier lors de la recherche d'une 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é, même si j'ai été surpris de la quantité de fonctionnalités d'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 à utiliser sous * nix que sous Windows. Valgrind est ridiculement flexible: j'ai débogué de gros logiciels serveur avec de nombreux problèmes de tas en l'utilisant.
Lorsque tout le reste échoue, vous pouvez fournir à votre propre opérateur global les surcharges new / delete et malloc / calloc / realloc - la manière de procéder varie un peu en fonction du compilateur et de 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 à partir de dmalloc et electricfence, et du livre étonnamment excellent Writing Solid Code :
- valeurs de sentinelle : 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 à détecter les débordements et les sous-débordements de la mémoire tampon, et le pointeur «sauvage» occasionnel)
- alloc fill : remplissez les nouvelles allocations avec une valeur magique non-0 - Visual C ++ le fera déjà pour vous dans les versions de débogage (aide à détecter l'utilisation des variables non initialisées)
- remplissage libre : remplissez la mémoire libérée avec une valeur magique non-0, conçue pour déclencher un segfault s'il est déréférencé dans la plupart des cas (aide à attraper les pointeurs pendants)
- gratuit retardé : ne retournez pas la mémoire libérée dans le tas pendant un certain temps, gardez-la libre remplie mais non disponible (aide à attraper plus de pointeurs pendants, capture à proximité des doubles-libres)
- suivi : pouvoir enregistrer où une allocation a été faite peut parfois être utile
Notez que dans notre système homebrew local (pour une cible intégrée), nous gardons le suivi séparé de la plupart des autres éléments, car la surcharge d'exécution est beaucoup plus élevée.
Si vous êtes intéressé par plus de raisons de surcharger ces fonctions / opérateurs d'allocation, jetez un œil à ma réponse à "Une raison de surcharger l'opérateur global nouveau et supprimer?" ; Outre l'auto-promotion éhontée, elle répertorie d'autres techniques utiles pour suivre les erreurs de corruption de tas, ainsi que d'autres outils applicables.
Parce que je continue de trouver ma propre réponse ici lors de la recherche de valeurs alloc / free / fence que MS utilise, voici une autre réponse qui couvre les valeurs de remplissage Microsoft dbgheap .
Vous pouvez détecter de nombreux problèmes de corruption de tas en activant Page Heap pour votre application. Pour ce faire, vous devez utiliser gflags.exe qui fait partie des outils de débogage pour Windows
Exécutez Gflags.exe et dans les options de fichier image pour votre exécutable, cochez l'option "Activer le tas de pages".
Maintenant, redémarrez votre exe et attachez-vous à un débogueur. Avec le tas de pages activé, l'application entrera dans le débogueur chaque fois qu'une corruption de tas se produit.
la source
Pour vraiment ralentir les choses et effectuer de nombreuses vérifications à l'exécution, essayez d'ajouter ce qui suit en haut de votre
main()
ou équivalent dans Microsoft Visual Studio C ++la source
Un article très pertinent est le débogage de la corruption du tas avec Application Verifier et Debugdiag .
la source
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 a été libéré dans le tas.
Utilisez un instrument qui ajoute une vérification automatique des limites à votre exécutable: c'est-à-dire valgrind sous Unix, ou un outil comme BoundsChecker (Wikipedia suggère également Purify et Insure ++) sous Windows.
Sachez que ceux-ci ralentiront votre application, ils peuvent donc être inutilisables si la vôtre est une application en temps réel doux.
Une autre aide / outil de débogage possible pourrait être HeapAgent de MicroQuill.
la source
Un conseil rapide, que j'ai obtenu de Détecter l'accès à la mémoire libérée est le suivant:
la source
Le meilleur outil que j'ai trouvé utile et que j'ai travaillé à chaque fois est la révision du code (avec de bons réviseurs de code).
Autre que la révision du code, je commencerais par essayer le tas de pages . La mise en place de Page Heap prend quelques secondes et, avec un peu de chance, il peut identifier votre problème.
Si vous n'avez pas de chance avec Page Heap, téléchargez les outils de débogage pour Windows de Microsoft et apprenez à utiliser WinDbg. Désolé, impossible de vous donner une aide plus spécifique, mais le débogage de la corruption de tas multi-thread est plus un art que de la science. Google pour "WinDbg heap corruption" et vous devriez trouver de nombreux articles sur le sujet.
la source
Vous pouvez également vérifier si vous établissez un lien avec la bibliothèque d'exécution C dynamique ou statique. Si vos fichiers DLL sont liés à la bibliothèque d'exécution statique C, les fichiers DLL ont des tas séparés.
Par conséquent, si vous deviez créer un objet dans une DLL et essayer de le libérer dans une autre DLL, vous obtiendrez le même message que vous voyez ci-dessus. Ce problème est référencé dans une autre question de débordement de pile, Libération de mémoire allouée dans une DLL différente .
la source
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 tas avec le
HEAP_NO_SERIALIZE
option. Cela rend essentiellement les fonctions de tas exécutées sans sécurité des threads. C'est une amélioration des performances si elle est utilisée correctement, mais ne devrait jamais être utilisée si vous utilisez HeapAlloc dans un programme multi-thread [1]. Je ne le mentionne que 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] Dans certaines situations, cela est légal, mais cela vous oblige à sérialiser les appels à Heap * et ce n'est généralement pas le cas pour les programmes multi-threads.
la source
Si ces erreurs se produisent au hasard, il y a une forte probabilité que vous rencontriez des courses de données. 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 un programme multithread.
la source
En plus de rechercher des outils, pensez à rechercher un coupable probable. Y a-t-il un composant que vous utilisez, peut-être non écrit par vous, qui n'a peut-être pas été conçu et testé pour fonctionner dans un environnement multithread? Ou simplement un que vous ne connaissez pas a fonctionné dans un tel environnement.
La dernière fois que cela m'est arrivé, c'était un package natif qui avait été utilisé avec succès à partir de jobs batch pendant des années. Mais c'était la première fois dans cette entreprise qu'il était utilisé à partir d'un service Web .NET (qui est multithread). C'était tout - ils avaient menti sur le fait que le code était thread-safe.
la source
Vous pouvez utiliser les macros VC CRT Heap-Check pour _CrtSetDbgFlag : _CRTDBG_CHECK_ALWAYS_DF ou _CRTDBG_CHECK_EVERY_16_DF .. _CRTDBG_CHECK_EVERY_1024_DF .
la source
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:
Control.Invoke
et supprimez un objet géré qui encapsule l'objet natif auquel appartient le rappel.Control.Invoke
fin). Je devrais préciser que j'utiliseboost::thread
, donc j'utilise une fonction membre comme fonction de thread.Control.BeginInvoke
plutôt (mon interface graphique est faite avec Winforms) afin que le thread natif puisse se terminer avant que l'objet ne soit détruit (le but du rappel est précisément de notifier que le thread s'est terminé et que l'objet peut être détruit).la source
J'ai eu un problème similaire - et il est apparu assez au hasard. Peut-être que quelque chose était 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:
Quel genre de choses peuvent causer ces erreurs? Quelque chose de corrompu dans le fichier de construction.
Comment les déboguer? Nettoyage du projet et reconstruction. S'il est résolu, c'était probablement le problème.
la source