De la gestion de la mémoire, de la corruption de tas et de C ++

J’ai donc besoin d’aide. Je travaille sur un projet en C ++. Cependant, je pense avoir réussi à corrompre mon tas. Ceci est basé sur le fait que j’ai ajouté un std::ssortingng à une classe et que je lui ai assigné une valeur provenant d’un autre std::ssortingng :

 std::ssortingng hello = "Hello, world.\n"; /* exampleSsortingng = "Hello, world.\n" would work fine. */ exampleSsortingng = hello; 

se bloque sur mon système avec un dump de stack. Donc, en gros, je dois arrêter et parcourir tout mon contenu de gestion de code et de mémoire et découvrir où je me suis trompé. La base de code est encore petite (environ 1000 lignes), donc c’est facilement faisable.

Pourtant, je suis au-dessus de ma tête avec ce genre de choses, alors j’ai pensé que je le lancerais là-bas. Je suis sur un système Linux et j’ai valgrind avec valgrind , et bien que je ne sache pas exactement ce que je fais, il a signalé que le destructeur de std::ssortingng était un libre invalide. Je dois admettre que le terme “corruption de tas” provient d’une recherche Google; tous les articles généraux sur ce genre de choses seraient également appréciés.

(Avant la rm -rf ProjectDir , rm -rf ProjectDir en C #: D)

EDIT: Je n’ai pas précisé, mais ce que je demande, c’est un moyen de diagnostiquer ce type de problèmes de mémoire. Je sais que std :: ssortingng est correct, c’est quelque chose que j’ai fait (ou un bogue, mais il n’y a pas de problème avec Select). Je suis sûr que je pourrais vérifier le code que j’ai écrit et vous, les personnes très intelligentes, comprendriez le problème en un rien de temps, mais je veux append ce type d’parsing de code à ma “boîte à outils”, pour ainsi dire.

Ce sont des mécanismes relativement peu coûteux pour éventuellement résoudre le problème:

  1. Gardez un œil sur ma question concernant la corruption de tas – je mets à jour les réponses au fur et à mesure qu’elles disparaissent. Le premier consistait à équilibrer new[] et delete[] , mais vous le faites déjà.
  2. Donne plus à Valgrind ; c’est un excellent outil, et j’espère seulement qu’il était disponible sous Windows. Je ne ralentis que votre programme d’environ la moitié, ce qui est plutôt bon comparé aux équivalents Windows.
  3. Pensez à utiliser les outils de performance de Google en tant que remplacement malloc / new.
  4. Avez-vous nettoyé tous vos fichiers d’object et recommencé? Peut-être que votre fichier make est … “suboptimal”
  5. Vous assert() pas assez dans votre code. Comment puis-je savoir cela sans l’avoir vu? Comme la soie dentaire, personne assert() suffisamment dans son code. Ajoutez une fonction de validation pour vos objects et appelez-la au début et à la fin de la méthode.
  6. Êtes-vous en train de comstackr ? Sinon, fais-le.
  7. Trouvez-vous un outil anti-peluche tel que PC-Lint . Une petite application comme la vôtre pourrait tenir dans la page de démonstration de PC-lint , ce qui signifie qu’aucun achat ne vous convient!
  8. Vérifiez que vous supprimez les pointeurs après les avoir supprimés. Personne n’aime les pointeurs qui pendent. Même concert avec des pointeurs déclarés mais non alloués.
  9. Arrêtez d’utiliser des tableaux. Utilisez un vecteur à la place.
  10. N’utilisez pas de pointeurs bruts. Utilisez un pointeur intelligent . N’utilisez pas auto_ptr ! Cette chose est … surprenante; sa sémantique est très étrange. Choisissez plutôt l’un des pointeurs intelligents Boost ou un élément de la bibliothèque Loki .

Nous avions un jour un bogue qui échappait à toutes les techniques habituelles, comme Valgrind, Purify, etc. Le crash ne s’était jamais produit que sur des machines disposant de beaucoup de mémoire et uniquement sur de grands ensembles de données d’entrée.

Nous avons fini par le localiser à l’aide de points de surveillance du débogueur. Je vais essayer de décrire la procédure ici:

1) Trouvez la cause de la panne. D’après votre exemple de code, il semblerait que la mémoire de “exempleChaine” soit corrompue et ne puisse donc pas être écrite. Continuons avec cette hypothèse.

2) Définissez un point d’arrêt au dernier emplacement connu pour lequel “exampleSsortingng” est utilisé ou modifié sans problème.

3) Ajoutez un sharepoint surveillance au membre de données de ‘exampleSsortingng’. Avec ma version de g ++, la chaîne est stockée dans _M_dataplus._M_p . Nous voulons savoir quand ce membre de données change. La technique GDB utilisée est la suivante:

 (gdb) p &exampleSsortingng._M_dataplus._M_p $3 = (char **) 0xbfccc2d8 (gdb) watch *$3 Hardware watchpoint 1: *$3 

J’utilise évidemment Linux avec g ++ et gdb ici, mais je pense que les points de surveillance de la mémoire sont disponibles avec la plupart des débogueurs.

4) Continuez jusqu’à ce que le sharepoint contrôle se déclenche:

 Continuing. Hardware watchpoint 2: *$3 Old value = 0xb7ec2604 "" New value = 0x804a014 "" 0xb7e70a1c in std::ssortingng::_M_mutate () from /usr/lib/libstdc++.so.6 (gdb) where 

La commande gdb where commande donnera une trace en arrière montrant ce qui a entraîné la modification. Il s’agit d’une modification parfaitement légale, auquel cas il suffit de continuer – ou si vous êtes chanceux, ce sera la modification due à la corruption de mémoire. Dans ce dernier cas, vous devriez maintenant pouvoir examiner le code qui cause réellement le problème et, espérons-le, le résoudre.

La cause de notre bogue était un access à un tableau avec un index négatif. L’index était le résultat de la conversion d’un pointeur sur un modulos ‘int’ de la taille du tableau. Valgrind et al. car les adresses mémoire allouées lors de l’exécution sous ces outils n’ont jamais été ” > MAX_INT ” et n’ont donc jamais donné lieu à un index négatif.

Oh, si vous voulez savoir comment résoudre le problème, c’est simple. D’abord, prenez un poulet mort. Ensuite, commencez à le secouer .

Sérieusement, je n’ai pas trouvé de moyen cohérent de détecter ce type de bugs. Comme il y a tellement de problèmes potentiels, il n’ya pas une simple liste de contrôle à examiner. Cependant, je recommanderais ce qui suit:

  1. Soyez à l’aise dans un débogueur.
  2. Commencez à fouiller dans le débogueur pour voir si vous pouvez trouver quelque chose qui ressemble à du poisson. Vérifiez en particulier ce qui se passe pendant la exampleSsortingng = hello; ligne.
  3. Assurez-vous qu’il se bloque réellement sur exampleSsortingng = hello; ligne, et pas lors de la sortie d’un bloc englobant (ce qui pourrait provoquer le déclenchement de destructeurs).
  4. Vérifiez toute magie de pointeur que vous pourriez faire. Pointeur arithmétique, casting, etc.
  5. Vérifiez toutes vos allocations et désallocations pour vous assurer qu’elles sont bien appariées (pas de désallocations doubles).
  6. Assurez-vous de ne pas renvoyer de références ou de pointeurs aux objects de la stack.

Il y a beaucoup d’autres choses à essayer aussi. Je suis sûr que d’autres personnes donneront également des idées.

Quelques endroits pour commencer:

Si vous êtes sur Windows et que vous utilisez Visual C ++ 6 (personne ne l’utilise encore aujourd’hui), son implémentation de std :: ssortingng n’est pas threadsafe et peut conduire à ce genre de chose.

Voici un article que j’ai trouvé et qui explique nombre des causes courantes des memory leaks et de la corruption.

À mon ancien lieu de travail, nous avions utilisé Compuware Boundschecker pour résoudre ce problème. C’est commercial et très coûteux, alors peut-être pas une option.

Voici quelques bibliothèques gratuites qui peuvent être utiles

http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx

J’espère que cela pourra aider. La corruption de la mémoire est un endroit insipide!

Il pourrait s’agir d’une corruption massive, mais il est tout aussi probable qu’il s’agisse d’une corruption de stack. Jim a raison. Nous avons vraiment besoin d’un peu plus de contexte. Ces deux sources ne nous en disent pas beaucoup isolément. Cela pourrait être causé par un certain nombre de choses (ce qui est la vraie joie de C / C ++).

Si vous ne souhaitez pas poster votre code, vous pouvez même le publier sur un serveur et publier un lien. Je suis sûr que vous obtiendrez beaucoup plus de conseils de cette façon (certains d’entre eux ne sont sans doute pas liés à votre question).

Le code était simplement un exemple d’échec de mon programme (il était alloué sur la stack, Jim). Je ne cherche pas réellement “ce que j’ai mal fait”, mais plutôt “comment diagnostiquer ce que j’ai mal fait”. Apprendre à un homme à pêcher et tout ça. Bien que regardant la question, je n’ai pas été assez clair. Dieu merci pour la fonction d’édition. : ‘)

De plus, j’ai corrigé le problème std :: ssortingng. Comment? En le remplaçant par un vecteur, en compilant, puis en remplaçant la chaîne. Il y avait constamment des crashs, et ça a été corrigé même si … ça ne pouvait pas. Il y a quelque chose de méchant, et je ne sais pas quoi. Je voulais vérifier la fois où j’ai alloué manuellement de la mémoire sur le tas, cependant:

  this->map = new Area*[largestY + 1]; for (int i = 0; i < largestY + 1; i++) { this->map[i] = new Area[largestX + 1]; } 

et en le supprimant:

 for (int i = 0; i < largestY + 1; i++) { delete [] this->map[i]; } delete [] this->map; 

Je n’ai jamais alloué de tableau 2d avec C ++. Cela semble fonctionner.

De plus, j’ai corrigé le problème std :: ssortingng. Comment? En le remplaçant par un vecteur, en compilant, puis en remplaçant la chaîne. Il y avait constamment des crashs, et ça a été corrigé même si … ça ne pouvait pas. Il y a quelque chose de méchant, et je ne sais pas quoi.

On dirait que vous avez vraiment agité un poulet. Si vous ne savez pas pourquoi cela fonctionne maintenant, alors il est toujours en panne et vous garantira presque toujours de vous croquer plus tard (après avoir ajouté encore plus de complexité).

Exécuter Purifier.

C’est un outil quasi-magique qui signalera que lorsque vous écrasez de la mémoire, vous ne devriez pas toucher, fuir de la mémoire en ne libérant pas des choses, en libérant deux fois, etc.

Cela fonctionne au niveau du code machine, de sorte que vous n’avez même pas besoin du code source.

L’une des conférences téléphoniques des fournisseurs les plus agréables à laquelle j’ai participé a été lorsque Purify a découvert une fuite de mémoire dans leur code et nous avons pu demander: “est-il possible que vous ne libériez pas de mémoire dans votre fonction foo ()” et que vous entendiez la étonnement dans leurs voix.

Ils pensaient que nous étions en train de déboguer des dieux, mais nous leur avons révélé le secret afin qu’ils puissent exécuter Purify avant que nous n’ayons à utiliser leur code. 🙂

http://www-306.ibm.com/software/awdtools/purify/unix/

(C’est assez cher mais ils ont un téléchargement eval gratuit)

L’une des techniques de débogage que j’utilise fréquemment (sauf dans les cas d’extrême étrangeté) consiste à diviser pour régner. Si votre programme échoue actuellement avec une erreur spécifique, divisez-le en deux et voyez s’il contient toujours la même erreur. Évidemment, le truc est de décider où diviser votre programme!

Votre exemple en tant que donné ne montre pas assez de contexte pour déterminer où l’erreur pourrait être. Si quelqu’un d’autre essayait votre exemple, cela fonctionnerait bien. Donc, dans votre programme, essayez de supprimer autant de choses supplémentaires que vous ne nous avez pas montrées et voyez si cela fonctionne. Si tel est le cas, rajoutez l’autre code petit à petit jusqu’à ce qu’il commence à échouer. Ensuite, la chose que vous venez d’append est probablement le problème.

Notez que si votre programme est multithread, vous avez probablement de gros problèmes. Sinon, vous devriez pouvoir le réduire de cette façon. Bonne chance!

Outre des outils tels que Boundschecker ou Purify, votre meilleur pari pour résoudre des problèmes comme celui-ci est simplement de vraiment bien lire le code et de vous familiariser avec le code sur lequel vous travaillez.

La corruption de mémoire est l’un des problèmes les plus difficiles à résoudre. En général, ce type de problèmes est résolu en passant des heures / des jours dans un débogueur et en remarquant quelque chose comme “hé, le pointeur X est utilisé après avoir été supprimé!”.

Si cela vous aide, vous réussirez mieux à mesure que vous gagnerez de l’expérience.

Votre allocation de mémoire pour le tableau semble correcte, mais assurez-vous de cocher également tous les endroits où vous accédez au tableau.

Comme je peux le constater, votre code ne contient aucune erreur. Comme il a été dit plus de contexte est nécessaire.

Si vous n’avez pas encore essayé, installez gdb (le débogueur gcc) et comstackz le programme avec -g. Cela comstackra les symboles de débogage que gdb peut utiliser. Une fois que vous avez installé gdb, lancez-le avec le programme (gdb). Ceci est une astuce utile pour utiliser gdb.

Définissez un point d’arrêt pour la fonction qui génère le bogue et voyez quelle est la valeur de exampleSsortingng. Faites de même pour le paramètre que vous transmettez à exampleSsortingng. Cela devrait au moins vous dire si les std :: ssortingngs sont valides.

J’ai trouvé que la réponse de cet article était un bon guide sur les pointeurs.

Autant que je sache, votre code est correct. En supposant que exampleSsortingng est un std :: ssortingng ayant la scope de classe que vous décrivez, vous devriez pouvoir l’initialiser / l’assigner de cette façon. Peut-être il y a un autre problème? Peut-être qu’un extrait de code réel aiderait à le mettre en contexte.

Question: exampleSsortingng est-il un pointeur sur un object chaîne créé avec new?