Comment déboguer les erreurs de corruption de tas?

je suis en train de déboguer une application C++ (native) multi-threadée sous Visual Studio 2008. En des occasions apparemment aléatoires, Je reçois un "Windows a déclenché un point de rupture..."erreur avec une note que cela pourrait être dû à une corruption dans le tas. Ces erreurs ne détruiront pas toujours l'application immédiatement, bien qu'il soit probable qu'elle se détruise peu de temps après.

le gros problème avec ces erreurs est qu'elles apparaissent seulement après que la corruption a effectivement eu lieu, ce qui rend ils sont très difficiles à suivre et à déboguer, surtout sur une application multi-threadée.

  • quelles sortes de choses peuvent causer ces erreurs?

  • Comment puis-je les déboguer?

des Conseils, des outils, des méthodes, des enlightments... sont les bienvenus.

150
demandé sur Peter Mortensen 2009-06-18 04:01:42

14 réponses

"151910920 Application" Vérificateur combiné avec les Outils de Débogage pour Windows est d'une étonnante installation. Vous pouvez obtenir les deux comme une partie de la Windows Driver Kit ou le plus léger Windows SDK . (Découvert sur le vérificateur D'Application lors de la recherche d'une question précédente sur un tas de corruption question .) J'ai utilisé BoundsChecker et Insure++ (mentionné dans d'autres réponses) dans le passé aussi, bien que j'ai été surpris combien de fonctionnalité était dans le vérificateur D'Application.

clôture électrique (alias "efence"), dmalloc , valgrind , et ainsi de suite sont tous dignes de mention, mais la plupart d'entre eux sont beaucoup plus faciles à obtenir courant sous *nix que Windows. Valgrind est ridiculement flexible: j'ai débogué de gros logiciels de serveur avec de nombreux problèmes de tas en utilisant.

Quand tout le reste échoue, vous pouvez fournir votre propre opérateur global les surcharges new/delete et malloc/calloc / realloc -- comment le faire variera un peu selon le compilateur et la plate-forme -- et ce sera un peu un investissement -- mais cela pourrait se révéler payant à long terme. La liste des caractéristiques souhaitables devrait sembler familière de dmalloc et electricfence, et le livre étonnamment excellent Writing Solid Code :

  • sentinelle des valeurs : permettre à un peu plus d'espace avant et après chaque alloc, en respectant au maximum l'alignement exigence; remplir avec des numéros de magie (aide à attraper les dépassements de mémoire tampon et underflows, et des "sauvages", pointeur)
  • alloc fill : remplir les nouvelles allocations avec une valeur magique non-0 -- Visual C++ le fera déjà pour vous dans les constructions de débogage (aide à attraper l'utilisation de var non initialisées)
  • free fill : remplissez la mémoire libérée avec une valeur magique non-0, conçu pour déclenchez un segfault s'il est déréférencé dans la plupart des cas (aide à attraper des pointeurs pendants)
  • retardé libre : ne pas retourner libéré de la mémoire vers le tas pendant un certain temps, garder libre rempli, mais pas disponible (aide à attraper les plus bancales les pointeurs, les captures premier double-libère)
  • tracking : être capable d'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 tracking séparé de la plupart des autres choses, parce que le temps d'exécution est beaucoup plus élevé.


si vous êtes intéressé par Plus de raisons de surcharger ces fonctions d'attribution/opérateurs, jetez un oeil à ma réponse à " toute raison de surcharger opérateur global nouveau et supprimer?" ; mis à part l'autopromotion éhontée, il énumère d'autres techniques qui sont utile pour le suivi des erreurs de corruption de tas, ainsi que d'autres outils applicables.

118
répondu leander 2017-05-23 11:47:32

vous pouvez détecter beaucoup de problèmes de corruption tas en activant tas de Page pour votre application . Pour ce faire, vous devez utiliser gflags.exe qui fait partie des outils de débogage Pour Windows

Run Gflags.exe et dans les options de fichier Image pour votre exécutable, cochez l'option "Activer le tas de Page".

redémarrez maintenant votre exe et attachez-le à un débogueur. Avec Page Heap activée, l'application se cassera en débogueur à chaque fois toute corruption de tas se produit.

34
répondu Canopus 2018-10-03 03:13:25
13
répondu Peter Mortensen 2013-02-03 23:22:57

pour vraiment ralentir les choses et effectuer beaucoup de vérification d'exécution, essayez d'ajouter ce qui suit Au début de votre main() ou l'équivalent dans Microsoft Visual Studio C++

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
11
répondu Dave Van Wagner 2012-01-13 23:44:18

quelles sortes de choses peuvent causer ces erreurs?

faire des choses méchantes avec la mémoire, p.ex. écrire après la fin d'un tampon, ou écrire à un tampon après qu'il a été libéré de nouveau au tas.

Comment déboguer?

utilisez un instrument qui ajoute la vérification automatisée des limites à votre exécutable: par exemple valgrind sur Unix, ou un outil comme BoundsChecker (Wikipedia suggère également de purifier et D'assurer++) sur Windows.

méfiez-vous que ceux-ci vont ralentir votre application, de sorte qu'ils peuvent être inutilisables si la vôtre est une application douce-en temps réel.

un autre outil/outil de débogage possible pourrait être le tas de MicroQuill.

8
répondu ChrisW 2009-06-18 00:09:33

un conseil rapide, que j'ai reçu de détection d'accès à la mémoire libérée est ceci:

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

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif
8
répondu StackedCrooked 2009-06-18 08:54:18

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

mis à part la révision du code, j'essaierais d'abord page tas . Page Heap met quelques secondes à se configurer et avec de la chance, il pourrait identifier 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 apprenez à utiliser le WinDbg. Désolé ne pouvait pas vous donner plus de détails aide, mais déboguer la corruption multi-threaded tas est plus un art que la science. Google pour" corruption WinDbg tas " et vous devriez trouver de nombreux articles sur le sujet.

5
répondu Shing Yip 2009-06-18 04:20:42

vous pouvez également vérifier si vous êtes en lien avec la Bibliothèque dynamique ou statique C runtime. Si vos fichiers DLL sont liés avec la bibliothèque d'exécution C statique, alors les fichiers DLL ont des tas séparés.

donc, si vous deviez créer un objet dans une DLL et essayer de le libérer dans une autre DLL, vous obtiendriez 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érer la mémoire attribué dans une DLL différente .

4
répondu dreadpirateryan 2017-05-23 10:30:00

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

il s'est avéré que j'ai créé par erreur le tas avec l'option HEAP_NO_SERIALIZE . Cela fait essentiellement fonctionner les fonctions de tas sans sécurité de filetage. C'est une amélioration de la performance si utilisé correctement mais ne devrait jamais être utilisé si vous utilisez HeapAlloc dans un programme multi-threaded [1]. Je mentionne cela parce que votre post mentionne que vous avez une application multi-threadée. Si vous utilisez HEAP_NO_SERIALIZE n'importe où, supprimez cela et cela va probablement résoudre votre problème.

[1] Il y a certaines situations où cela est légal, mais cela exige que vous sérialisiez les appels vers Heap* et ce n'est généralement pas le cas pour les programmes multi-threads.

3
répondu JaredPar 2009-06-18 00:25:27

si ces erreurs se produisent au hasard, il y a de fortes chances que vous ayez rencontré des courses de données. S'il vous plaît, vérifiez: modifiez-vous les pointeurs de mémoire partagée à partir de différents threads? Le vérificateur de threads Intel peut aider à détecter de tels problèmes dans le programme multithreaded.

3
répondu Vladimir Obrizan 2009-06-18 17:39:51

en plus de chercher des outils, pensez à chercher un coupable probable. Est-il un composant que vous souhaitez utiliser, peut-être pas écrit par vous, qui n'ont pas été conçus et testés pour fonctionner dans un environnement multithread? Ou tout simplement un qui vous ne savoir a couru dans un tel environnement.

la dernière fois que ça m'est arrivé, c'était un paquet natif qui avait été utilisé avec succès depuis des années. Mais c'était la première fois à cette société qu'il avait été utilisé à partir d'un.net service web (qui est multithreaded). C'est tout - ils avaient menti sur le fait que le code était sécurisé.

1
répondu John Saunders 2009-06-18 00:29:20

vous pouvez utiliser VC CRT tas-vérifier les macros pour _CrtSetDbgFlag : _CRTDBG_CHECK_ALWAYS_DF or _CRTDBG_CHECK_EVERY_16_DF .. _CRTDBG_CHECK_EVERY_1024_DF .

0
répondu KindDragon 2011-11-07 11:28:36

j'aimerais ajouter mon expérience. Depuis quelques jours, j'ai résolu une instance de cette erreur dans ma demande. Dans mon cas particulier, les erreurs dans le code:

  • Supprimer des éléments d'une collection STL tout en itérant dessus (je crois qu'il y a des drapeaux de débogage dans Visual Studio pour attraper ces choses; je l'ai attrapé pendant la révision du code)
  • celui-ci est plus complexe, je vais le diviser par étapes:
    • de a C++ natif fil, de retour d'appel dans le code managé
    • dans les terres gérées, appeler Control.Invoke et disposer d'un objet géré qui enveloppe l'objet natif auquel appartient le callback.
    • puisque l'objet est toujours vivant 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 , donc j'utilise une fonction de membre comme la fonction thread.
    • Solution : utilisez Control.BeginInvoke (mon GUI est fait avec des Winforms) à la place de sorte que le thread natif puisse se terminer avant que l'objet ne soit détruit (le but du callback est précisément de notifier que le thread s'est terminé et que l'objet peut être détruit).
0
répondu dario_ramos 2012-05-23 16:40:39

j'ai eu un problème similaire et il est tout à fait au hasard. Peut-être que quelque chose était corrompu dans les fichiers de construction, mais j'ai fini par le réparer en nettoyant le projet d'abord puis la reconstruction.

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

quelles sortes de choses peuvent causer ces erreurs? Quelque chose de corrompu dans le fichier de construction.

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

0
répondu Marty 2016-10-30 22:18:33