IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

crash inexpliqué sur 'new'


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 45
    Points : 44
    Points
    44
    Par défaut crash inexpliqué sur 'new'
    Bonjour,

    J'ai un crash inexpliqué dans mon code, je pensais être un cador en C++ ;-), et je me retrouve à poil, complètement désarmé.
    Si l'un de vous avait une idée, pas forcemment pour trouver le bug, mais des pistes pour continuer mes investigations...

    Le contexte:
    C++ & STL, sous Windows XP, compilé avec Mingw/GCC

    Le code:
    J'ai une classe 'BIT_VECTOR' qui est équipée "toutes options":
    (constructeur, constructeur de copie, destructeur, opérateur d'affectation, etc...)
    et qui fait de l'allocation dynamique (voir plus loin).

    Quand je la teste individuellement, tout semble aller pour le mieux.
    Quand je l'utilise dans mon applicatif, j'ai systématiquement un plantage, et Windows qui râle:
    soit "XXXX.exe a rencontré un problème, etc",
    soit "Erreur d'application:L'instruction à "0x7cc9..." emploie l'adresse mémoire "0xaddd..", la mémoire ne peut pas être "read" "
    (Pour moi ce dernier message ressemble for à une erreur liée à la STL)

    En utilisant gdb (dont je ne sais pas bien me servir), le callstack me dit que je suis dans des fonctions de
    "c:\windows\system32\ntdll.dll", ca commence par l'appel de "Ntdll!RtlpNtMakeTemporaryKey()", mais aucune fonction à moi n'apparait. Bref, pas grand chose d'intéressant.

    En loggant (dans cerr/stderr, redirigé dans un fichier avec freopen) ce qui se passe un peu partout, j'observe que le plantage se produit (si le log est fidèle, au sens temporel...) lors de l'allocation dynamique dans le constructeur de copie.

    C'est à dire, j'ai le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
        cerr << "Before new, NbIndex=" << NbIndex << endl;
        bits = new (nothrow) uint[NbIndex];
        if( bits == 0 )
        {
            cerr << "Allocation failed\n";
            exit(0);
        }
        cerr << "After new\n";
    J'ai ajouté le 'nothrow' pour être sûr que ça ne soit pas lié à une exception mal gérée (je ne les utilise pas dans mon code).

    Et le log affiche le "Before", et rien après. J'ai vérifié la valeur de NbIndex, et le 'uint' est défini par
    typedef unsigned int uint;

    Bref, qu'est ce qui pourrait faire planter 'new' ??? Ceci se produit dans une fonction qui vient empiler des objets de ce type dans un vecteur STL, avec push_back (et donc, en tout logique, appel du constr. de copie, et c'est là que ça plante).

    Je suis un peu sec. J'ai pensé à un problème de corruption mémoire, mais ça ne devrait pas planter à cet endroit logiquement...
    Quels sont les outils que je pourrais utiliser pour trouver ?

    Ci-dessous des bouts de code (en virant les trucs lié au logging). Y'a rien de particulièrement inhabituel, mais bon, qui sait si un truc ne m'a pas échappé...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
     
    class BIT_VECTOR
    {
        private:
            uchar NbBits;
            uchar NbDims;
            uint  NbIndex;
            uint* bits;
            static uchar def_NbBits;  ///< alloué et initialisé correctement
            static uchar def_NbDims;  ///< dans le .cpp
     
        public:
            BIT_VECTOR( uchar NbDimensions=def_NbDims, uchar NbBitsPerDim=def_NbBits );
            BIT_VECTOR( const BIT_VECTOR& bv );
            ~BIT_VECTOR();
            BIT_VECTOR& operator =  ( const BIT_VECTOR& bv );
    };
     
     
    /// Constructor
    BIT_VECTOR::BIT_VECTOR( uchar NbDimensions, uchar NbBitsPerDim )
    {
        NbBits = NbBitsPerDim;
        NbDims = NbDimensions;
        ComputeNbIndex();
        bits = new (nothrow) uint[NbIndex];
        if( bits == 0 )
        {
            cerr << "Allocation failed\n";
            exit(0);
        }
        for( uint i=0; i<NbIndex; i++ )
            bits[i] = 0;
    }
     
    /// Copy-constructor
    BIT_VECTOR::BIT_VECTOR( const BIT_VECTOR& bv )
    {
     
        NbBits  = bv.NbBits;
        NbDims  = bv.NbDims;
        NbIndex = bv.NbIndex;
     
        bits = new (nothrow) uint[NbIndex];
        if( bits == 0 )
        {
            cerr << "Allocation failed\n";
            exit(0);
        }
     
        for( uint i=0; i<NbIndex; i++ )
        {
            bits[i] = bv.bits[i];
        }
    }
     
    /// Destructor
    BIT_VECTOR::~BIT_VECTOR()
    {
        delete[] bits;
    }
     
    /// Assignment operator
    BIT_VECTOR& BIT_VECTOR::operator = ( const BIT_VECTOR& bv )
    {
        assert( bv.IsSameType( *this ) );
     
        for( uint i=0; i<NbIndex; i++ )
            bits[i] = bv.bits[i];
     
        return *this;
    }
    Merci pour toute idée qui me permettrait d'avancer !

  2. #2
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Bonjour,
    Citation Envoyé par akirira
    Merci pour toute idée qui me permettrait d'avancer !
    Quelques idées en vrac :

    1)
    Citation Envoyé par akirira
    Erreur d'application:L'instruction à "0x7cc9..." emploie l'adresse mémoire "0xaddd..", la mémoire ne peut pas être "read"
    0xaddddddd, ça ressemble a un nombre magique. Il faudrait vérifier ce que ça veut dire pour ton compilateur/OS (par exemple sous windows avec visual studio, en debug les variables non initialisées sont fixées à 0xcccccccc)

    2)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    /// Assignment operator
    BIT_VECTOR& BIT_VECTOR::operator = ( const BIT_VECTOR& bv )
    {
        assert( bv.IsSameType( *this ) );
     
        for( uint i=0; i<NbIndex; i++ )
            bits[i] = bv.bits[i];
     
        return *this;
    }
    Est-ce que tu es certain que les tableaux dynamiques bits et bv.bits font la même taille ? Dans le cas contraire la boucle for va jardiner la mémoire d'un ou l'autre tableau. Typiquement, pour éviter ce genre de problème, l'opérateur d'affectation d'un conteneur similaire à std::vector ressemble à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    BIT_VECTOR& BIT_VECTOR::operator=(const BIT_VECTOR& x)
    {
       if (this != &x) 
       {
          BIT_VECTOR tmp(x);
          this->swap(tmp) ; // à implémenter (par un simple échange des pointeurs bits)
       }
       return *this;
    }
    3)
    Citation Envoyé par akirira
    si le log est fidèle, au sens temporel...
    Pour être fidèle temporellement, il faudrait remplacer tous les \n par des std::endl, pour forcer à flusher le flux vers la console.

  3. #3
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Autres idées :
    - Compiler le code avec un autre compilateur, voir ce que ça dit.
    - Réduire le programme au minimum voir si c'est reproductible (et le poster ici quand il ne fait plus que le minimum et nous permet de le compiler directement chez nous).
    - Ton erreur ressemble à de la corruption de mémoire, et a peut-être lieu à un tout autre endroit, même si elle ne se révèle que là. Des outils existent pour tester ça (valgrind, intel parallel studio...).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 45
    Points : 44
    Points
    44
    Par défaut
    Citation Envoyé par Arzar Voir le message
    2) Est-ce que tu es certain que les tableaux dynamiques bits et bv.bits font la même taille ?
    C'est justement le rôle de la fonction-membre IsSameType() dans le assert() de vérifier que les 2 objets ont les mêmes paramètres.

    Citation Envoyé par Arzar Voir le message
    Typiquement, pour éviter ce genre de problème, l'opérateur d'affectation d'un conteneur similaire à std::vector ressemble à :
    [...]
    Intéressante écriture, beaucoup plus classe, merci. Je vais essayer ça, bien que je ne pense pas que ça vienne de là.

    Citation Envoyé par Arzar Voir le message
    Pour être fidèle temporellement, il faudrait remplacer tous les \n par des std::endl, pour forcer à flusher le flux vers la console.
    Ca se tient dans l'absolu, mais il me semblait que la différence entre cout et cerr, c'était justement que cerr n'était pas "tamponné" (buffered, c'est bien mieux en fait). Ou je me trompe ?

    Citation Envoyé par JolyLoic Voir le message
    Autres idées :
    - Ton erreur ressemble à de la corruption de mémoire, et a peut-être lieu à un tout autre endroit, même si elle ne se révèle que là. Des outils existent pour tester ça (valgrind, intel parallel studio...).
    Ouais, c'est ce que je pense aussi finalement, c'est là ou ça coince.
    Valgrind, c'est Linux/MaxOsX only. Je télécharge actuellement la version d'éval de Intel parallel studio, et sinon y'a l'air d'avoir d'autres trucs:
    [ame]http://en.wikipedia.org/wiki/Memory_debugger[/ame]

    Je vous tiendrais au courant, merci.

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 45
    Points : 44
    Points
    44
    Par défaut
    Citation Envoyé par akirira Voir le message
    Je vous tiendrais au courant, merci.
    Bon, je reprends ce fil après quelque temps, et après être passé à d'autres choses, et j'en profite pour le tagger "résolu"

    Après bien des heures (qui m'ont permis d'utiliser Valgrind via une machine virtuelle) j'ai trouvé le (très bête) bug:j'accédais à un vector STL avec la syntaxe mon_vect[indice], et évidemment... 'indice' était hors limites.

    Ca me sert de bonne leçon, désormais, je n'utilise plus que la syntaxe mon_vect.at(indice), qui présente l'avantage de crasher.... à l'endroit ou je tente l'accès, et pas des kilomètres plus loin !!!

  6. #6
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Bah perso, en tout cas avec visual studio, j'active simplement le mode debug et toutes les erreurs d'accès en [] aux std::vector lance une assertion (ainsi que toutes les erreurs d'itérateurs invalides)... Et ça me semble vraiment surprenant que la STL fournie ave Mingw ne le fasse pas.

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Bah perso, en tout cas avec visual studio, j'active simplement le mode debug et toutes les erreurs d'accès en [] aux std::vector lance une assertion (ainsi que toutes les erreurs d'itérateurs invalides)... Et ça me semble vraiment surprenant que la STL fournie ave Mingw ne le fasse pas.
    Salut,
    Il me semble que Visual fait ce genre de vérification même en release. Ce qui perso me dérange un peu plus.

  8. #8
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Ouaip j'ai eu le soucis avec des macro comme SCL_SECURE ou encore _HAS_ITERATOR_DEBUGGING (qui n'est pas désactivé en release.. va savoir pourquoi), forcément ça m'avait tué les perfs. (facteur de 10 je crois ...)
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. crash inexpliqué avec sort()
    Par sebkramm dans le forum SL & STL
    Réponses: 8
    Dernier message: 05/01/2009, 21h23
  2. Lenteur inexpliquée sur l'explorateur
    Par thedada dans le forum VBA Word
    Réponses: 8
    Dernier message: 18/11/2008, 00h18
  3. Réponses: 6
    Dernier message: 10/04/2008, 10h22
  4. Crash disque sur linux
    Par Invité dans le forum Matériel
    Réponses: 6
    Dernier message: 08/11/2006, 18h34
  5. Segmentation fault sur new[] et delete[]
    Par Don ViP dans le forum C++
    Réponses: 4
    Dernier message: 30/04/2006, 00h29

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo