Question

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::string à une classe et lui attribuer une valeur d'une autre std::string:

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

plante sur mon système avec un vidage de pile.Donc, en gros, je dois arrêt et parcourir tous mes trucs de gestion de code et de mémoire et découvrir où j'ai foiré.La base de code est encore petite (environ 1 000 lignes), donc c'est facilement réalisable.

Pourtant, je suis dépassé par ce genre de choses, alors j'ai pensé que je le lancerais là-bas.Je suis sur un système Linux et j'ai fouillé avec valgrind, et même si je ne savais pas complètement ce que je fais, il a signalé que le std::stringLe destructeur de était un free invalide.Je dois admettre que j'ai obtenu le terme « Heap Corruption » à partir d'une recherche Google ;tout article à usage général sur ce genre de choses serait également apprécié.

(En avant rm -rf ProjectDir, à refaire en C# :D)

MODIFIER:Je n'ai pas été clair, mais ce que je demande, ce sont des conseils pour diagnostiquer ce genre de problèmes de mémoire.Je sais que le truc std::string est correct, donc c'est quelque chose que j'ai fait (ou un bug, mais il n'y a pas de problème avec la sélection).Je suis sûr que je pourrais vérifier le code que j'ai écrit et vous, les gens très intelligents, verriez le problème en un rien de temps, mais je souhaite ajouter ce type d'analyse de code à ma « boîte à outils », pour ainsi dire.

Était-ce utile?

La solution

Il s’agit de mécanismes relativement peu coûteux permettant de résoudre éventuellement le problème :

  1. Gardez un oeil sur mon question de corruption en tas - Je mets à jour les réponses au fur et à mesure qu'elles arrivent.La première était d'équilibrer new[] et delete[], mais vous le faites déjà.
  2. Donner valgrind plus d'essai;c'est un excellent outil, et j'aimerais seulement qu'il soit disponible sous Windows.Je ne ralentis votre programme que de moitié environ, ce qui est plutôt bien par rapport aux équivalents Windows.
  3. Pensez à utiliser le Outils de performances Google en remplacement de malloc/new.
  4. Avez-vous nettoyé tous vos fichiers objets et recommencé ?Peut-être que votre fichier make est..."sous-optimal"
  5. Vous n'êtes pas assert()il y en a assez dans votre code.Comment puis-je le savoir sans l'avoir vu ?Comme la soie dentaire, personne assert()C'est suffisant dans leur code.Ajoutez une fonction de validation pour vos objets et appelez-la au début et à la fin de la méthode.
  6. Es-tu compilation -mur?Sinon, faites-le.
  7. Trouvez-vous un outil anti-peluches comme PC-Lint.Une petite application comme la vôtre pourrait s'intégrer dans le Démo PC-Lint page, ce qui signifie aucun achat pour vous !
  8. Vérifiez que vous annulez les pointeurs après les avoir supprimés.Personne n’aime un pointeur qui pend.Même concert avec des pointeurs déclarés mais non alloués.
  9. Arrêtez d'utiliser des tableaux.Utiliser un vecteur plutôt.
  10. N'utilisez pas de pointeurs bruts.Utiliser un pointeur intelligent.Ne pas utiliser auto_ptr!Cette chose est...surprenant;sa sémantique est très étrange.Choisissez plutôt l'un des Boostez les pointeurs intelligents, ou quelque chose hors de la bibliothèque Loki.

Autres conseils

Nous avons eu une fois un bug qui échappait à toutes les techniques habituelles, valgrind, purify, etc.Le crash ne s'est produit que sur des machines dotées de beaucoup de mémoire et uniquement sur de grands ensembles de données d'entrée.

Finalement, nous l'avons retrouvé à l'aide des points de surveillance du débogueur.Je vais essayer de décrire la procédure ici :

1) Trouvez la cause de la panne.Il ressort de votre exemple de code que la mémoire de "exampleString" est corrompue et ne peut donc pas être écrite.Continuons avec cette hypothèse.

2) Définissez un point d'arrêt au dernier emplacement connu où "exampleString" est utilisé ou modifié sans aucun problème.

3) Ajoutez un point de surveillance aux données membres de 'exampleString'.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 pour cela est la suivante :

(gdb) p &exampleString._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 point de surveillance soit déclenché :

Continuing.
Hardware watchpoint 2: *$3

Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where

Le GDB where La commande donnera une trace indiquant ce qui a entraîné la modification.Il s'agit soit d'une modification parfaitement légale, auquel cas continuez simplement, soit si vous avez de la chance, ce sera la modification due à la corruption de la mémoire.Dans ce dernier cas, vous devriez maintenant pouvoir revoir le code qui est vraiment causant le problème et, espérons-le, le résoudre.

La cause de notre bug était un accès à un tableau avec un index négatif.L'index était le résultat de la conversion d'un pointeur vers un 'int' modulo la taille du tableau.Le bug a été manqué par 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'a donc jamais abouti à un indice négatif.

Oh, si vous voulez savoir comment déboguer le problème, c'est simple.Tout d’abord, procurez-vous un poulet mort.Alors, commence à le secouer.

Sérieusement, je n'ai pas trouvé de moyen cohérent de traquer ce genre de bugs.Parce qu’il y a tellement de problèmes potentiels, il n’existe pas de liste de contrôle simple à parcourir.Cependant, je recommanderais ce qui suit :

  1. Familiarisez-vous avec un débogueur.
  2. Commencez à parcourir le débogueur pour voir si vous pouvez trouver quelque chose qui semble louche.Vérifiez surtout ce qui se passe pendant la exampleString = hello; doubler.
  3. Vérifiez qu'il plante réellement sur le exampleString = hello; ligne, et non à la sortie d'un bloc englobant (ce qui pourrait provoquer le déclenchement des destructeurs).
  4. Vérifiez toute magie de pointeur que vous pourriez faire.Arithmétique du pointeur, casting, etc.
  5. Vérifiez toutes vos allocations et désallocations pour vous assurer qu'elles correspondent (pas de double désallocation).
  6. Assurez-vous de ne renvoyer aucune référence ou pointeur vers des objets sur la pile.

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

Quelques points de départ :

Si vous êtes sous Windows et que vous utilisez Visual C++6 (j'espère que personne ne l'utilise encore ces jours-ci), l'implémentation de std::string n'est pas thread-safe et peut conduire à ce genre de chose.

Voici un article que j'ai trouvé qui explique de nombreuses causes courantes de fuites de mémoire et de corruption.

Sur mon ancien lieu de travail, nous avons utilisé Compuware Boundschecker pour nous aider.C’est commercial et très cher, ce n’est donc 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 où il est nul !

Il peut s'agir d'une corruption de tas, mais il est tout aussi probable qu'il s'agisse d'une corruption de pile.Jim a raison.Nous avons vraiment besoin d'un peu plus de contexte.Ces deux sources ne nous disent pas grand-chose isolément.Cela peut être dû à un certain nombre de choses (ce qui est la vraie joie du C/C++).

Si vous êtes à l'aise pour publier votre code, vous pouvez même le publier en entier sur un serveur et publier un lien.Je suis sûr que vous obtiendrez beaucoup plus de conseils de cette façon (dont certains sans aucun rapport avec votre question).

Le code était simplement un exemple de l'échec de mon programme (il était alloué sur la pile, Jim).Je ne cherche pas réellement « qu’ai-je fait de mal », mais plutôt « comment puis-je diagnostiquer ce que j’ai fait de mal ».Apprenez à un homme à pêcher et tout ça.En regardant la question, je ne l'ai pas dit assez clairement.Merci mon Dieu pour la fonction d'édition.:')

De plus, j'ai résolu le problème std::string.Comment?En le remplaçant par un vecteur, en compilant, puis en remplaçant à nouveau la chaîne.Il était plante constamment là-bas, et cela a été corrigé même si... ce n'était pas possible.Il y a quelque chose de méchant là-dedans, et je ne sais pas quoi.Je voulais cependant vérifier la seule fois où j'alloue manuellement de la mémoire sur le tas :

 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++ auparavant.Cela semble fonctionner.

De plus, j'ai résolu le problème std::string.Comment?En le remplaçant par un vecteur, en compilant, puis en remplaçant à nouveau la chaîne.Il plantait constamment là-bas, et cela a été corrigé même si... ce n'était pas possible.Il y a quelque chose de méchant là-dedans, et je ne sais pas quoi.

On dirait que vous avez vraiment secoué un poulet.Si tu ne sais pas pourquoi cela fonctionne maintenant, puis il est toujours cassé et il est quasiment garanti de vous mordre à nouveau plus tard (après avoir ajouté encore plus de complexité).

Exécutez Purifier.

C'est un outil presque magique qui signalera lorsque vous écrasez la mémoire que vous ne devriez pas toucher, que vous perdez de la mémoire en ne libérant pas d'éléments, en double-libérant, etc.

Cela fonctionne au niveau du code machine, vous n'avez donc même pas besoin d'avoir le code source.

L'une des conférences téléphoniques des fournisseurs les plus agréables à laquelle j'ai participé a eu lieu lorsque Purify a découvert une fuite de mémoire dans son code, et nous avons pu demander : "est-il possible que vous ne libériez pas de mémoire dans votre fonction foo()" et entendre le l'étonnement dans leurs voix.

Ils pensaient que nous déboguions des dieux, mais nous leur avons ensuite révélé le secret afin qu'ils puissent exécuter Purify avant que nous devions utiliser leur code.:-)

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

(C'est assez cher mais ils ont un téléchargement d'évaluation gratuit)

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

Votre exemple tel que donné ne montre pas suffisamment de contexte pour déterminer où pourrait se situer l'erreur.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 alors.Si tel est le cas, ajoutez l'autre code petit à petit jusqu'à ce qu'il commence à échouer.Ensuite, la chose que vous venez d'ajouter est probablement le problème.

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

Outre des outils comme Boundschecker ou Purify, votre meilleur pari pour résoudre des problèmes comme celui-ci est simplement de devenir très bon en lecture de code et de vous familiariser avec le code sur lequel vous travaillez.

La corruption de la mémoire est l'une des choses les plus difficiles à résoudre et ces types de problèmes sont généralement résolus en passant des heures/jours dans un débogueur et en remarquant quelque chose comme "hé, le pointeur X est utilisé après sa suppression !".

Si cela peut aider, c'est quelque chose dans lequel vous vous améliorez à mesure que vous gagnez en expérience.

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

Comme je peux le voir, votre code ne contient aucune erreur.Comme cela a été dit, davantage de contexte est nécessaire.

Si vous ne l'avez pas déjà essayé, installez gdb (le débogueur gcc) et compilez le programme avec -g.Cela compilera les symboles de débogage que gdb pourra utiliser.Une fois que gdb est installé, exécutez-le avec le programme (gdb ). Ce est un cheatsheat utile pour utiliser gdb.

Définissez un point d'arrêt pour la fonction qui produit le bogue et voyez quelle est la valeur de exampleString.Faites également la même chose pour le paramètre que vous transmettez à exampleString.Cela devrait au moins vous dire si les std::strings sont valides.

J'ai trouvé la réponse de Cet article être un bon guide sur les pointeurs.

Autant que je sache, votre code est correct.En supposant que exampleString est un std::string qui a une portée de classe comme vous le décrivez, vous devriez pouvoir l'initialiser/l'attribuer de cette façon.Peut-être y a-t-il un autre problème ?Peut-être qu'un extrait de code réel aiderait à le mettre en contexte.

Question:exampleString est-il un pointeur vers un objet chaîne créé avec new ?

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