Journal realloc

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
13
7
sept.
2012

Sur mon netbook avec 1Go de RAM, je déteste particulièrement les programmes qui bouffent de la mémoire dans le vide. Et quand un programme qui affiche la date, le niveau de batterie et deux trois autres informations mangent 4 fois plus que ce qu'utilise xmonad, je suppose un problème.

Je récupère le code source et je regarde. Du C qui est du C++ ou le contraire, mais bon, passons. Je n'ai pas prévu de faire la moindre correction au code. Je regarde juste … et je m'étrangle sur les deux lignes suivantes :

/* ... */
src_dup = (char*) realloc(src_dup, dup_len * sizeof(char));
sprintf(src_dup + dup_idx, "%s", templates[tmpl_num - 1]);
/* ... */

Si on lit la page de man de realloc :

void *realloc(void *ptr, size_t size);

[...]

The realloc() function returns a pointer to the newly allocated memory, which
is suitably aligned for any kind of variable and may be different from  ptr,
or NULL  if  the  request  fails.  If size was equal to 0, either NULL or a
pointer suitable to be passed to free() is returned.  If realloc() fails the
original block is left untouched; it is not freed or moved.

Une utilisation correcte de realloc en C devrait ressembler à ça :

/* ... */
tmp=realloc(ptr, new_size);
if(tmp!=NULL) {
    ptr=tmp;
    /* ... */
}

Si la ré-allocation n'est pas possible, NULL est retourné. Ensuite le pointeur retourné peut être différent de celui passé en paramètre, il est donc nécessaire d'assigner la valeur retournée au bon pointeur.
Vous me croyez pas ? La FAQ C ne laisse aucun doute.

Voilà, c'est tous ce que j'avais à dire.

Et puisqu'il semble qu'il ait une tradition qui fasse grincer des dents, voici quelque chose en rapport avec le journal.

PS: Il faudrait que je patch les 15 realloc de conky, mais comme je sais pas si c'est du C ou du C++, je sais pas si je vais le faire.

  • # Rapport avec la conso mémoire.

    Posté par  . Évalué à 10. Dernière modification le 07 septembre 2012 à 16:45.

    C'est mal de pas faire de test de pointeurs à NULL… Mais il est ou le rapport avec la conso mémoire?
    Je veux dire, ce genre d'erreur est très commune, surtout dans ce genre de cas (logiciel non critique, et si la taille des donnée à allouer est très petite, peu de chance que ça foire), donc ne t'offusque pas chaque fois que tu en vois une ;)

    • [^] # Re: Rapport avec la conso mémoire.

      Posté par  (site web personnel) . Évalué à 8.

      Comme c'est fait là, si la ré-allocation échoue, on a plus de pointeur vers la mémoire allouée avant la ré-allocation donc on ne peut plus libérer la mémoire donc on a une fuite mémoire.

      Je veux dire, ce genre d'erreur est très commune, surtout dans ce genre de cas (logiciel non critique, et si la taille des donnée à allouer est très petite, peu de chance que ça foire), donc ne t'offusque pas chaque fois que tu en vois une ;)

      C'est une erreur très commune, c'est pour ça que j'en fais un journal. Que le logiciel ne soit pas "critique" ou que les données soient "petites" ne constitue pas une excuse pour coder n'importe comment.

      "It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell

      • [^] # Re: Rapport avec la conso mémoire.

        Posté par  (site web personnel) . Évalué à 10.

        Comme dit en dessous, tu passes à côté du problème. Si realloc renvoie NULL, tu auras un SEGFAULT la ligne suivante, dès qu'on essaiera d'écrire avec sprintf. Du coup ta fuite mémoire vient forcément d'ailleurs.

        • [^] # Re: Rapport avec la conso mémoire.

          Posté par  . Évalué à -10.

          Pas du tout, il a raison, mais son code correctif est incomplet.

          Ça serait plutôt ça:

          tmp = realloc(ptr, size);
          
          if (tmp == NULL)
          { /* die... */ }
          
          if (tmp != ptr)
            free(ptr); // ICI
          
          ptr = tmp;
          
          blablabla
          
          

          Si on ne compare a aucun moment tmp et ptr, le premier pointeur sera perdu dans la mémoire assez souvent. C'est ballo

          • [^] # Re: Rapport avec la conso mémoire.

            Posté par  . Évalué à 6.

            realloc() changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged to the minimum of the old and new sizes; newly allocated memory will be uninitialized. If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc(). If the area pointed to was moved, a free(ptr) is done.

            Please do not feed the trolls

            • [^] # Re: Rapport avec la conso mémoire.

              Posté par  . Évalué à 1.

              A ma décharge, ce n'était pas cité dans le journal et c'était bien plus logique pour expliquer une fuite de mémoire.

    • [^] # Re: Rapport avec la conso mémoire.

      Posté par  (site web personnel) . Évalué à 5.

      plus généralement, le mal c'est la sombre flemme de vérifier les retours de fonctions ou appels systèmes !

  • # Et ?

    Posté par  . Évalué à 3. Dernière modification le 07 septembre 2012 à 16:47.

    Je vois pas trop où es ton souci.

    Si la ré-allocation n'est pas possible, NULL est retourné.

    Ok, il aurait du tester le != NULL. C'est pas la fin du monde non plus.

    Ensuite le pointeur retourné peut être différent de celui passé en paramètre, il est donc nécessaire d'assigner la valeur retournée au bon pointeur.

    C'est pas ce qui est fait ?

    src_dup = (char*) realloc(src_dup, dup_len * sizeof(char));
    
    
    • [^] # Re: Et ?

      Posté par  (site web personnel) . Évalué à 0.

      Oui c'est ce qui est fait et si c'est NULL, on a un bloc mémoire avec plus un seul pointeur pointant dessus pour le libérer. Et donc on a une fuite de mémoire ! Et si on utilise un ordinateur portable qui ne redémarre jamais (entrer/sortir de veille) et bien la fuite peut prendre des proportions énormes !

      "It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell

      • [^] # Re: Et ?

        Posté par  (Mastodon) . Évalué à 10.

        Si NULL est retourné, le programme va SEGFAULT à plus ou moins court terme. Du coup, la fuite mémoire, c'est plus vraiment un gros problème.

        • [^] # Re: Et ?

          Posté par  . Évalué à 4. Dernière modification le 08 septembre 2012 à 00:07.

          Pis bon, si les reallocs/mallocs et autres callocs comment à retourner NULL, clairement le test d'un retour d'appel système ne sera à très court terme plus le principal problème. OOMKiller dans la place \o/.

          (hors limits et quotas, parce que c'est pas fun!)

          • [^] # Re: Et ?

            Posté par  . Évalué à 5.

            Ou pas.
            Tu peux en profiter pour dropper un gros bout de memoire qui est utile mais pas vital.
            Ou tout simplement, juste parce que tu va te bananer ne veut pas dire qu'il faut le faire a la rache.

            Tu peux vouloir fermer un fichier proprement, logger que telle partie de l'appli a pas pu avoir sa memoire, (vainement) tenter de sauver qq chose, ou tout simplement prevenir l'utilisateur que "attention cherie, ca va trancher".

            Envoyer ton appli au tas comme un cochon, c'est un peu violent quand meme.

            Oui, c'est chiant et lourd, mais fallait pas choisir le C si la facilite de development etait un critere important :)

            Linuxfr, le portail francais du logiciel libre et du neo nazisme.

      • [^] # Re: Et ?

        Posté par  (site web personnel) . Évalué à 7.

        Reste l'option de tuer le process, ça devrait libérer la mémoire… puis faire des trucs avec le pointeur NULL y a des chances qu'a un endroit ou l'autre ça segfault… ce qui libérera la ram… bon je ===>[]

      • [^] # Re: Et ?

        Posté par  . Évalué à 1.

        Bien vu effectivement.

  • # malloc() et realloc() sous linux

    Posté par  . Évalué à 10.

    À vrai dire, sous linux par défaut, malloc() et realloc() ne retournent finalement jamais NULL. Ce qu'il se passe plutôt, c'est qu'on reçoit toujours un pointeur, et c'est au moment où on se met à l'utiliser qu'il alloue réellement la mémoire.

    Si au moment où on utilise la mémoire, celle-ci n'est pas disponible parce que la mémoire est pleine, alors le OOM-killer rentre en jeu et va tuer un process (que ça soit le process lui-même ou un autre).

    Cette technique de lazy allocation permet notamment d'allouer plus de mémoire que ce qui est disponible physiquement (tant que toute cette mémoire allouée n'est pas utilisée). Ce comportement peut être modifié par des paramètres overcommit dans /sys/.

    Cependant, même avec l'overcommit totalement désactivé, le noyau linux continuera à renvoyer un pointeur non-NULL, même quand il n'y a plus de mémoire disponible, et OOM-killer sera appelé comme d'habitude quand la mémoire sera accédée.

    Au final sous linux il y a à priori une seule façon d'avoir ces fonctions qui retournent NULL, c'est en cas de fragmentation trop importante de l'espace d'adressage du processus qui rendrait impossible une allocation de la taille souhaitée, et c'est vraiment très très rare.

    Voilà la raison pour laquelle vérifier le retour de malloc() et realloc() est en général assez peu utile, et pourquoi realloc() est assez souvent non testé pour NULL (les développeurs testent en général quand même avec malloc() car c'est plus facile à faire que avec realloc()).

    Je préfère personellement quand même tester la valeur de retour quand même, ça permet d'être tranquille en cas de portage sur un autre unix, ça diminue les retours de logiciels d'analyse statique tel que clang et ça évite les questions en cas de revue de code.

    • [^] # Re: malloc() et realloc() sous linux

      Posté par  . Évalué à 1.

      Au final sous linux il y a à priori une seule façon d'avoir ces fonctions qui retournent NULL, c'est en cas de fragmentation trop importante de l'espace d'adressage du processus qui rendrait impossible une allocation de la taille souhaitée, et c'est vraiment très très rare.

      Sauf en 32 bits. Où il est facile de se retrouver dans ce cas après avoir alloué puis libéré de grosses quantitées de mémoire (~1 GB ). D'où l'intérêt d'utiliser un noyau 64 bits dans ce genre de cas.

      • [^] # Re: malloc() et realloc() sous linux

        Posté par  . Évalué à 4.

        Au final sous linux il y a à priori une seule façon d'avoir ces fonctions qui retournent NULL, c'est en cas de fragmentation trop importante de l'espace d'adressage du processus qui rendrait impossible une allocation de la taille souhaitée, et c'est vraiment très très rare.

        Sauf en 32 bits. Où il est facile de se retrouver dans ce cas après avoir alloué puis libéré de grosses quantitées de mémoire (~1 GB ). D'où l'intérêt d'utiliser un noyau 64 bits dans ce genre de cas.

        Oui, mais pas seulement.

        Ici, on est en userland, donc on parle de mémoire virtuelle. Sur 32-bit, cette espace est de 4GiB, dont 1GiB (*) est réservé pour l´espace d´adressage noyau (les pages de code (text) du noyau est mappé dans l´espace virtuel de tous les processus, mais elles ne sont pas nécessairement exécutables : cela sert par exemple à appeler les syscals; les pages de data ne sont pas (toutes?) mappées dans l´espace d´adressage du noyau).

        Donc, il reste virtuellement (Haha!) 3GiB de disponible pour chaque processus en userland. Si ton processus essaye d´allouer en séquence des blocs de (par exemple) 1MiB sans les libérer, alors arrivera un moment ou même l´espace virtuel sera entièrement utilisé.

        Bien sûr, tu as aussi raison. Si tu alloues des blocs de 1MiB en séquence jusqu`à plus-soif, que tu en libères alternativement un sur deux, et qu´ensuite tu tentes d´allouer un bloc de 2MiB, alors tu as virtuellement de la place, mais pas contigüe, donc l´allocation vas échouer.

        Donc, la règle générale deviendrai un truc du genre :

        sous Linux (et aussi la plupart des vrais OS), l´allocation (malloc et consorts) n´échoue jamais, sauf dans certains cas d´exception (mémoire fragmentée, épuisement de l´espace de mémoire virtuelle).

        (*) la découpe noyau/userland est par défaut 1GiB/3GiB, mais 2GiB/2GiB ou 3GiB/1GiB est aussi possible.

        Hop,
        Moi.

    • [^] # Re: malloc() et realloc() sous linux

      Posté par  . Évalué à 10.

      Voilà la raison pour laquelle vérifier le retour de malloc() et realloc() est en général assez peu utile

      Sous Linux peut-être, mais pas quand on écrit du C portable.

    • [^] # Re: malloc() et realloc() sous linux

      Posté par  . Évalué à 10.

      À vrai dire, sous linux par défaut, malloc() et realloc() ne retournent finalement jamais NULL. Ce qu'il se passe plutôt, c'est qu'on reçoit toujours un pointeur, et c'est au moment où on se met à l'utiliser qu'il alloue réellement la mémoire.

      Ok, c'est l'overcommit.

      Cependant, même avec l'overcommit totalement désactivé, le noyau linux continuera à renvoyer un pointeur non-NULL, même quand il n'y a plus de mémoire disponible, et OOM-killer sera appelé comme d'habitude quand la mémoire sera accédée.

      C'est faux :

      # cat test.c 
      #include <stdio.h>
      #include <stdlib.h>
      
      
      #define BLOCK_SIZE (1024*1024)
      
      
      int main(int argc, char *argv[])
      {
              unsigned long size = 0;
      
              while (malloc(BLOCK_SIZE) != NULL)
                      size += BLOCK_SIZE;
      
              printf("malloc() returned NULL after %uMB\n", (size/(1024*1024)));
      
              exit(EXIT_SUCCESS);
      }
      
      

      Test :

      # /sbin/sysctl -a | grep overcommit
      vm.overcommit_memory = 0
      vm.overcommit_ratio = 50
      vm.nr_overcommit_hugepages = 0
      
      # ./test
      malloc() returned NULL after 3057MB
      
      

      Normal, je suis sur 32-bit, plus d'espace d'adressage.

      Maintenant, et activant l'overcommit strict :

      # sysctl vm.overcommit_memory=2
      vm.overcommit_memory = 2
      # sysctl vm.overcommit_ratio=50
      # ./test
      malloc() returned NULL after 626MB
      
      

      En faisant un memset() sur le block retourné par malloc(), dans le premier cas le process se fait tuer par l'OOM killer, dans le deuxième le comportement est le même.

  • # Ne le fait pas.

    Posté par  (site web personnel) . Évalué à 9.

    Ne le fait pas.

    Dans un programme utilisateur ça n'a aucun intérêt.

    Le mieux que tu puisse faire c'est un truc du style
    C
    assert(src_dup);

    Si tu veux éviter les éventuelles faille de sécurités. Mais pour un 'bête' programme utilisateur du style concky, il n'y a pas grands risque. Tout ce que tu fais c'est perdre des cycles à tester et rajoute du code qui prends de la place en cache.

    Gérer les "OOM" (Out Of Memory) est inutile pour deux raison:

    1 - C'est impossible de tester tout les cas possible d'échec de realloc. Et du code non testé à de forte chance de ne pas fonctionner

    2 - Les systèmes d'exploitation moderne "overcommit" la mémoire. Ça veux dire que même si il n'y a plus de mémoire, réalloc va quand même fonctionner. Et c'est au moment ou cette mémoire va être utilisée que le noyaux va devoir récupérer de la mémoire en tuant un processus (OOM killer) (en utilisant des heuristique, mais pas forcément celui qui demande la mémoire).

    Bien sûr il y a des cas ou c'est nécessaire de gérer ça correctement (en embarqué par exemple). Mais pas ici.

    • [^] # Re: Ne le fait pas.

      Posté par  (site web personnel) . Évalué à 6.

      Bien sûr il y a des cas ou c'est nécessaire de gérer ça correctement (en embarqué par exemple). Mais pas ici.

      Pour être plus précis: les cas en questions sont assez nombreux et dépendent du contexte d'éxécution. Quand tu programme tu ne sais souvent pas où ton code va finir après avoir été copié/collérefactoré vingt fois.

      Le mieux que tu puisse faire c'est un truc du style [assert]

      Pour donner un autre exemple sur un serveur ou dans une bibliothèque c'est vraiment pas ce qu'il y a de mieux à faire. Il faut tester et respectivement renvoyer une erreur au client (par ex indiquant de réessayer après un temps d'attente exponentiel en fonction du nombre d'erreurs) ou renvoyer à l'appelant un truc qui lui permet de faire ce qui convient.

      Tout ce que tu fais c'est perdre des cycles à tester et rajoute du code qui prends de la place en cache

      Et accessoirement te conformer à l'API.

      C'est impossible de tester tout les cas possible d'échec de realloc. Et du code non testé à de forte chance de ne pas fonctionner

      C'est pourtant facile d'injecter ce genre d'erreur.

      Les systèmes d'exploitation moderne "overcommit" la mémoire

      Ça dépends entre autres de l'humeur de ton sysadmin, et dans tout les cas c'est pas dans l'API.

      • [^] # Re: Ne le fait pas.

        Posté par  (site web personnel) . Évalué à -1.

        Et accessoirement te conformer à l'API.

        Quand tu as ce genre d'erreur pour des petites demande de création mémoire comme un bête outil de l'exemple, que l'outil soit conforme ou pas à l'API est le dernier de tes soucis… à la limite si tu demandais un realloc(100*1000*1000), je comprendrai que tu rales que ce ne soit pas pris en compte, mais la : le programmeur a juste autre chose à foutre que de blinder de test un programme dans le but d'arriver à un niveau de logiciel de pilotage d'avion, alors que c'est juste un petit programme. Après, si tu sponsorises ce genre de développement, il sera sans doute content de le faire pour toi. En attendant : tout le monde (à part les gens qui n'ont rien à voir dans l'histoire : ni utilisateur ayant un crash ou une perte mémoire car il a été démontré que ce n'est pas possible, ni le dév') s'en fout!

        • [^] # Re: Ne le fait pas.

          Posté par  (site web personnel) . Évalué à -4.

          le programmeur a juste autre chose à foutre que de blinder de test un programme dans le but d'arriver à un niveau de logiciel de pilotage d'avion, alors que c'est juste un petit programme.

          Je m'étonne de ne pas l'avoir déjà vu donc je me/le lance : le vrai problème c'est qu'il y a encore des gens pour utiliser un langage aussi archaïque et dangereux que le C (surtout pour un programme de très haut niveau qui sert à afficher des graphiques pouet pouet ; pour de l'embarqué ou pour un noyau c'est autre chose).

          • [^] # Re: Ne le fait pas.

            Posté par  . Évalué à 4.

            Vieux troll velu qui ne passera pas.

            On peut faire dans le très dangereux en C++, Java, Python, Perl comme en C. On peut aussi faire du C de manière très safe. Le C est peut-être archaïque mais c'est le langage le plus généraliste et éprouvé qui soit.

            • [^] # Re: Ne le fait pas.

              Posté par  . Évalué à 2.

              Puis quand on cherche à faire light, c'est l'idéal, on peut contrôler on ne peux plus finement la taille du tas.

              Please do not feed the trolls

              • [^] # Re: Ne le fait pas.

                Posté par  (site web personnel) . Évalué à 2.

                Puis quand on cherche à faire light, c'est l'idéal, on peut contrôler on ne peux plus finement la taille du tas.

                C'est seulement vrai si tu connais bien ton allocateur de mémoire.

                De plus, en C tes données ne peuvent être déplacées après avoir été allouées, donc un programme C est particulièrement vulnérable à la fragmentation de la mémoire.

                • [^] # Re: Ne le fait pas.

                  Posté par  . Évalué à 1.

                  De plus, en C tes données ne peuvent être déplacées après avoir été allouées, donc un programme C est particulièrement vulnérable à la fragmentation de la mémoire.

                  J'espère ne pas dire trop de bêtise mais il me semble quand même qu'en faisant de la copie (on alloue un nouvel espace mémoire puis on y copie le contenu des autres espaces mémoire) on peut défragmenter cette mémoire. Sur que c'est du boulot pour le programmeur mais un programme en C n'est pas particulièrement (plus que ça) vulnérable à la fragmentation mémoire.

                  • [^] # Re: Ne le fait pas.

                    Posté par  (site web personnel) . Évalué à 2.

                    Indice: pointeur.

                  • [^] # Re: Ne le fait pas.

                    Posté par  . Évalué à 3.

                    C'est le bon vieux problème de l'invalidation des pointeurs ou des itérateurs: Si tu à plusieurs pointeurs/itérateurs qui sont éparpillés partout qui pointent vers des éléments de ton espace mémoire, alors si tu déplace ton espace mémoire, ces anciens pointeurs/itérateurs deviennent invalides et il faut soit les mettre à jour soit arrêter de les utiliser.

                    C'est un problème qui arrive quelque soit le langage. La seule solution, c'est soit de laisser le programmeur bien choisir ses structures de données pour qu'il sache qui pointe vers quoi, soit que le langage l'impose à sa place…

                    • [^] # Re: Ne le fait pas.

                      Posté par  . Évalué à 1.

                      Pour "résoudre" ce problème, ne serait-il pas envisageable de faire une table de traduction pointeurs <-> adresse-mémoire ? En cas de déplacement, seul l'adresse-mémoire change, mais pas le pointeur, ça reste donc transparent pour le programme.

                      Ceci aurait par contre un coût, puisqu'il faudrait alors passer par cette table, pour savoir où lire/écrire dans la mémoire, donc un ralentissement (mais de quel ordre ?)

                      Le jeu en vaut-il la chandelle ? Ne m'y connaissant pas du tout dans ce domaine, c'est juste là une réflexion rapide que je viens d'avoir avec moi-même…

                      • [^] # Re: Ne le fait pas.

                        Posté par  . Évalué à 3.

                        Si c'est tout à fait envisageable, la question de si c'est utile va dépendre de l'application, mais si tu as besoin de passer par là j'aurais tendance a dire que d'autre pistes sont envisageable, comme allouer les structure de données en statique, avec une façon de retrouver lesquelles sont vides (via un flag ou une pile), ou allouer un gros chunck de mémoire et utiliser tes propres allocateurs adapté à ton besoin; globalement tu auras plus a gagné en faisant un truc adapté qu'une défragmentation manuelle qui peut, selon l'état de la mémoire ne pas changer grand chose, voire planter, tout en utilisant un bon paquet de ressources.

                        Il ne faut pas décorner les boeufs avant d'avoir semé le vent

        • [^] # Re: Ne le fait pas.

          Posté par  . Évalué à 10.

          Euh non desole, c'est une question d'hygiene de base de mon point de vue.

          Le dev qui laisse aller des trucs aussi basique, c'est vraiment qu'il se fiche de son code, qu'il ne pense pas loin et qu'il a certainement plein d'autres problemes dans son code.

          Ton bete outil, il va un jour etre utilise a travers Apache par exemple, avec parametres passes par l'utilisateur parce qu'apres tout, ton outil est juste genre un convertisseur ou autre.
          Et le jour ou ton malloc il va rater et tout le monde s'en fiche, ben tu te retrouves avec une faille de securite potentiellement exploitable selon les operations faites apres le malloc.

        • [^] # Re: Ne le fait pas.

          Posté par  (site web personnel) . Évalué à 9.

          Quand tu as ce genre d'erreur pour des petites demande de création mémoire comme un bête outil de l'exemple, que l'outil soit conforme ou pas à l'API est le dernier de tes soucis… à la limite si tu demandais un realloc(100*1000*1000),

          Quelles que soient ses motivations, un programmeur codant comme un cochon reste un programmeur codant comme un cochon. Et les cochons écrivent des programmes difficiles à debugger, difficiles à modifier, … autrement dit ils renoncent à l'aspect soft du software. Même si c'est un choix qui peut être parfois viable, il ne doit jamais être présenté comme anodin!

        • [^] # Re: Ne le fait pas.

          Posté par  . Évalué à 6.

          Bon je ne comprends pas vraiment là. Chez tout plein de gens, il est naturel de fournir des versions static inline un peu de cette façon-là:

          /* from some_util_file.h */
          /* #includes omitted */
          
          static inline void fatal(const char *msg) {
              perror(msg);
              exit(errno);
          }   
          
          /* ... */
          
          
          static inline void * s_malloc(size_t sz) {
              void *p = malloc(sz);
              if (!p) fatal("Couldn't allocate memory");
              return p;
          }
          
          /* ... */
          
          

          Bref, de proposer des versions « configurables » (car par ex là, ça va forcément toujours planter alors que parfois il existe des moyens de moyenner). Est-ce que ce ne serait tout simplement pas plus simple de toujours faire ainsi ? Comme ça au moins, on sait que quelqu'un, quelque part prend en charge ce genre de trucs, et le code du « vrai » programme n'a pas besoin d'être encombré de vérifications en tous genres…

      • [^] # Re: Ne le fait pas.

        Posté par  (site web personnel) . Évalué à 3.

        Pour donner un autre exemple sur un serveur ou dans une bibliothèque c'est vraiment pas ce qu'il y a de mieux à faire. Il faut tester et respectivement renvoyer une erreur au client (par ex indiquant de réessayer après un temps d'attente exponentiel en fonction du nombre d'erreurs)

        Pas si simple. Il faut aussi s'assurer que tout ce passe comme prévu : de ne pas garder de mutex ver ouillé par erreur, de bien libérer toutes les ressources qui ont été allouée par la fonction. De garder l'état du programme dans un état correcte, sans structure de donnée à moitié initialisées.

        En plus l'utilisateur de la bibliothèque devra faire pareil et ajouter du code pour gérer cette erreur.

        Et tout ça pour pas grand chose au final. Le plus simple est de terminer le programme immédiatement. Et de bien récupérer en cas de crash.

        Les programmes qui ont vraiment besoin de gérer les cas d'OOM sont plutôt rares. Et il vaux mieux ne pas gaspiller de temps et de ressources à le faire.

        • [^] # Re: Ne le fait pas.

          Posté par  . Évalué à 10.

          Non franchement non, pas d'accord.

          Le BABA du programmer correct est la gestion d'erreur. Une allocation de ressource qui flanche ca arrive, et ca doit etre gere. Ton utilisateur quand il voit ton soft lui exploser dans les doigts, c'est vraiment pas ce qu'il attend et il ne comprendra meme pas pourquoi, sans parler du fait qu'il y a risque de faille de securite (selon les operations faites apres l'alloc) si tu ne le geres pas.

          • [^] # Re: Ne le fait pas.

            Posté par  (site web personnel) . Évalué à -1.

            Le BABA du programmer correct est la gestion d'erreur.

            La question que je me pose, c'est comment gérer correctement ce genre d'erreur quand ton programme fait une tache critique, tu va forcément faire de la merde, non ?

            Ensuite, une autre question,

            pour new, j'ai lu ici et ailleurs que ca lançait une execption.

            J'ai lu sur d'autre site que ca renvoyait NULL.

            N'ayant jamais vu de code C++ tester le cas, j'imagine que ca quitte "proprement" via une exception, non ?

            • [^] # Re: Ne le fait pas.

              Posté par  . Évalué à 4.

              Ben t'as un chemin d'erreur normalement, si ton allocation rate, tu defais ce que tu avais fais et tu retournes une erreur.

              Il y a forcement de tres rares cas ou il faut fermer l'application/tuer le systeme, mais dans ces cas-la, faut le faire le plus proprement possible (tu fermes les fichiers que tu ecris, tu logges un msg d'erreur, tu informes l'utilisateur, etc…)

              Pour C++ normalement tu peux faire ton new avec std::nothrow qui forcera l'allocateur sans exception (tu recevras un NULL a la place), mais le defaut est de lancer une exception.

              Quand a l'exception, ca va quitter le programme oui, mais c'est de nouveau pas forcement le bon resultat a avoir…

        • [^] # Re: Ne le fait pas.

          Posté par  (site web personnel) . Évalué à 3.

          Pas si simple. Il faut aussi s'assurer que tout ce passe comme prévu : de ne pas garder de mutex ver ouillé par erreur, de bien libérer toutes les ressources qui ont été allouée par la fonction. De garder l'état du programme dans un état correcte, sans structure de donnée à moitié initialisées.

          Eh oui, programmer n'est pas facile!

          En plus l'utilisateur de la bibliothèque devra faire pareil et ajouter du code pour gérer cette erreur.

          Et en plus il va écrire un programme tout autour! Quelle idée!

          Les programmes qui ont vraiment besoin de gérer les cas d'OOM sont plutôt rares. Et il vaux mieux ne pas gaspiller de temps et de ressources à le faire.

          En réalité tous les programmes sont susceptibles de rencontrer un OOM, à cause du voisin qui a bouffé toute la place. Si tes programmes sont instables en cas d'OOM cela signifie que si un programme ange toute la mémoire les autres partent en cacahuète. Super.

          • [^] # Re: Ne le fait pas.

            Posté par  (site web personnel) . Évalué à 3.

            Ptet qu'on pourrait lister les programmes qui gerent correctement et gracieusement les situations OOM alors. Puisque tout le monde ici à l'air super au point sur comment faire ça plus elegamment qu'avec un assert, j'imagine que ces programmes sont très nombreux.

            • [^] # Re: Ne le fait pas.

              Posté par  (site web personnel) . Évalué à 4.

            • [^] # Re: Ne le fait pas.

              Posté par  . Évalué à 0.

              Ils sont tres nombreux oui, a peu pres tous les softs serveurs, et chez nous tous les autres softs aussi(du moins tout ce qui est dans Windows et Office). Si on voit ca en code review c'est immediatement marque comme "a corriger" (et evidemment on en rate quelque uns qui atterissent dans le produit, on est pas parfait)

              • [^] # Re: Ne le fait pas.

                Posté par  (site web personnel) . Évalué à 4.

                Pour tout dire j'aurais preferé qu'on me cite des applications open-source, dont on peut voir le code et constater comme c'est beau et robuste à l'intérieur.

                On peut par exemple consulter l'avis de l'ineffable Lennart Poetterring, (qui cite D-BUS comme exemple de demon robuste aux echecs de mallocs):

                http://developer.pardus.org.tr/people/ozan/blog/?p=14

                Je le cite: The OOM situation has been discussed quite often in various
                projects. On modern systems it has become pretty clear that for normal
                userspace software only an aborting malloc() makes sense for a couple
                of reasons: (…)

                • [^] # Re: Ne le fait pas.

                  Posté par  (site web personnel) . Évalué à 9.

                  Et sa conclusion:

                  So in summary, OOM-safety is wrong:

                  • Because it increases your code size by 30%-40%
                  • You're trying to be more catholic than the pope, since various systems services you build on and interface with aren't OOM-safe anyway
                  • You are trying to solve the wrong problem. Real OOM wil be signalled via SIGKILL, not malloc() returning NULL.
                  • You are trying to solve the wrong problem. Make sure your app never loses data, not only when malloc() returns NULL
                  • You can barely test the OOM codepaths
                  • [^] # Re: Ne le fait pas.

                    Posté par  (site web personnel) . Évalué à 1.

                    c'est exactement ce que je pensais, vouloir tester toutes les allocations, ça n'est pas gratuit en place mémoire, et ça rend le code moins lisible. De plus il vaut mieux un crash net, qui libère des ressources pour que le reste puisse fonctionner.

                    Je crois que je n'ai jamais vu ce cas de figure (malloc qui retourne null), j'aime pas le code mort.

                    • [^] # Re: Ne le fait pas.

                      Posté par  . Évalué à 5.

                      Le poids de cette gestion d'erreurs peut être minime en traitant l'erreur une fois pour toute dans une fonction

                      void * foo_malloc(size_t size){
                          void * tmp = malloc(size);
                          if(tmp == NULL)
                              crasher_proprement();
                          return tmp;
                      }
                      
                      

                      Please do not feed the trolls

                      • [^] # Re: Ne le fait pas.

                        Posté par  (site web personnel) . Évalué à -4.

                        ou dans le gestionnaire de signaux, ça serait pas le bon endroit?

                        • [^] # Re: Ne le fait pas.

                          Posté par  . Évalué à 4.

                          en interceptant SEGFAULT ?

                          le problème du segfault est que tu n'en connais pas son origine exacte, tu ne sais pas si tes données sont dans un état cohérent
                          et pour SIGKILL, de mémoire il ne peut être intercepté

                          • [^] # Re: Ne le fait pas.

                            Posté par  (site web personnel) . Évalué à -1. Dernière modification le 09 septembre 2012 à 13:07.

                            Oui, d'accord, seulement son code ne sert qu'a fermer ce que l'OS ne ferme pas lorsque le process est tué, il ne s'agit pas de faire des choses compliqués genre afficher un message d'erreur ou faire une sauvegarde, il ne maintient aucune variable d'état. C'est exactement ce que je dis de faire (pas traiter l'erreur au cas par cas).

                            Le problème avec son approche, c'est qu'en fonction des options de compilation et de l'architecture tu ne pourras pas avoir d'info sur l'endroit ou l'erreur à eu lieu. tandis qu'avec un bon vieux SIGSEGV j'ai une info sur l'endroit exacte (c'est discutable du point de vu de l'utilisateur).

                            • [^] # Re: Ne le fait pas.

                              Posté par  . Évalué à 2.

                              tu as raison, si tu te contentes de crasher proprement comme en effet il indique

                              moi j'utilisais cette méthode pour sauver ce qui était sauvable. Ce n'est pas aussi complexe que du "cas pas cas", ce qui implique d'autres contraintes (données en accès, fd ouvert, allocation en début d'opérations et non en cour, etc…).

                • [^] # Re: Ne le fait pas.

                  Posté par  . Évalué à 0.

                  Tu attaches un debugger, tu mets un breakpoint sur msvcrt!malloc, quand le breakpoint est touche, tu regardes l'assembleur juste apres l'appel, et tu verras une comparaison sur le registre eax, qui est le registre contenant la valeur de retour…

                  • [^] # Re: Ne le fait pas.

                    Posté par  (site web personnel) . Évalué à 5.

                    Juste pour voir j'ai lancé wordpad avec "MS Application Verifier" qui tourne et dans lequel seul "Low Resource Simulation" est coché. Il m'a affiché 3 fois "La mémoire de votre ordinateur est insuffisante" (bien) puis il a planté (moins bien)

            • [^] # Re: Ne le fait pas.

              Posté par  . Évalué à 3.

              Puisque tout le monde ici à l'air super au point sur comment faire ça plus elegamment qu'avec un assert, j'imagine que ces programmes sont très nombreux.

              Nombreux je ne sais pas, mais un langage haut niveau (implémenté en C, donc ce n'est pas hors sujet) peut lever une exception :

              >>> b"x" * (2**48)
              Traceback (most recent call last):
                File "<stdin>", line 1, in <module>
              MemoryError
              
              
              • [^] # Re: Ne le fait pas.

                Posté par  (site web personnel) . Évalué à 3. Dernière modification le 08 septembre 2012 à 15:45.

                test fait sur un petit netbook avec peu de mémoire sous ubuntu:

                >>> import random;
                >>> l=[random.random() for s in xrange(0,2**27)]
                Killed
                
                

                Donc ça ne protege pas contre toutes les situations où la mémoire vient à manquer et où le systéme se lance dans une partie de ball-trap sur les process suspects

                • [^] # Re: Ne le fait pas.

                  Posté par  . Évalué à 2.

                  Tout à fait, le problème ici étant probablement l'overcommit (le programme ne peut rien faire pour détecter le problème). N'empêche que ça marche tout de même dans pas mal de cas.

      • [^] # Re: Ne le fait pas.

        Posté par  . Évalué à 4.

        Quand tu programme tu ne sais souvent pas où ton code va finir après avoir été copié/collérefactoré vingt fois.

        Et comment fais-tu pour protéger ton code qui sera copié/collé et modifié en introduisant un bug ? C'est à moi d'imaginer ce que des gens pourraient faire de mon code en le modifiant ? Chacun sa merde.

        Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.

    • [^] # Re: Ne le fait pas.

      Posté par  . Évalué à 2.

      Le mieux que tu puisse faire c'est un truc du style
      C
      assert(src_dup);

      Euh non, parce que si tu fais ca, en cas d'echec d'allocation, ton programme il meurt. Tuer un soft parce qu'une allocation a echoue c'est franchement crade, tu avortes la requete ou l'operation, tu logges une erreur si besoin est, mais tu continues de tourner.

      • [^] # Re: Ne le fait pas.

        Posté par  . Évalué à 5. Dernière modification le 08 septembre 2012 à 00:14.

        Puis à priori on compile avec NDEBUG pour utiliser un soft (surtout si on veut qu'il soit léger), donc l'assert saute. L'assert est plus adapté pour contrôler la validité d'arguments passé à une fonctions qu'a gérer ce genre d'erreurs.

        Please do not feed the trolls

      • [^] # Re: Ne le fait pas.

        Posté par  (site web personnel) . Évalué à 3.

        En même temps ça dépend beaucoup du type de programme et d'alloc, non ? Dans certains cas, arrêter le programme s'il peut pas avoir sa mémoire paraît assez logique et loin d'être dramatique ; c'est sûr que si c'est gimp qui crashe avec toutes tes images ouvertes parce que t'as essayé de crééer une image 100000*100000, c'est plus emmerdant.

        • [^] # Re: Ne le fait pas.

          Posté par  . Évalué à 3.

          Arreter le programme peut-etre oui, selon ce que le soft fait, mais il faut le faire proprement , pas avec un SEGFAULT ou un assert.

          • [^] # Re: Ne le fait pas.

            Posté par  (site web personnel) . Évalué à 0.

            Peut être pour un serveur ou une application critique. Mais pour un programme avec une interface graphique, ça n'en vaux pas la peine.

            Ça demande énormément de travail pour aucun gain.

            • [^] # Re: Ne le fait pas.

              Posté par  (site web personnel) . Évalué à 5.

              c'est sur, le jour ou en manque de RAM ton firefox va quitter completement sur un simple Ctrl-T …. au lieu de te dire, non, pas un nouvel onglet merci. tu sera heureux ?

              • [^] # Re: Ne le fait pas.

                Posté par  (site web personnel) . Évalué à 1.

                Désolé, mais c'est pas déjà le cas ?
                Qu'est-ce qui se passe d'après toi, quand il n'y a plus de mémoire avec firefox ?

              • [^] # Re: Ne le fait pas.

                Posté par  . Évalué à 2.

                Heureux, je ne sais pas, mais de toute façon il n'y a rien de critique qui tourne dans un navigateur. Et sinon s'il n'y a plus de mémoire, si firefox gère ça "correctement" dans ton idée (c'est à dire ne pas ouvrir ton onglet) tu seras content de savoir qu'il peut se faire OOMkiller d'ici quelque instants, parce qu'une autre application aura besoin de mémoire.

                Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.

              • [^] # Re: Ne le fait pas.

                Posté par  (site web personnel) . Évalué à 2.

                hum je sais pas si tu as déjà vu firefox mourir dans une situation où la mémoire manque, mais moi je l'ai vu à moultes reprises sur un laptop tout moisi et il se fait juste flinguer par le OOM-killer sans sommations

                • [^] # Re: Ne le fait pas.

                  Posté par  . Évalué à 0.

                  De nouveau, rien a voir.

                  malloc te retourne null si l'espace memoire de Firefox est fragmente et si il tente un gros alloc, meme si il reste plein de RAM. OOM-killer ne levera pas un doigt dans ce cas.

                • [^] # Re: Ne le fait pas.

                  Posté par  (site web personnel) . Évalué à 1.

                  je prenais le cas du navigateur et les onglets comme exemple, j'ai pas lu le code de ff pour vérifier si j'ai fait le bon choix en le citant …

            • [^] # Re: Ne le fait pas.

              Posté par  . Évalué à 0.

              Tu penses ?

              Tu es en train de mettre la derniere touche a ton livre de 400 pages, et tu as Firefox en arriere plan qui bouffe toute la RAM, alors que OpenOffice essaie de sauver, il se retrouve avec un malloc qui rate, tu penses qu'il devrait exploser en vol et corrompre le document ou s'en sortir proprement ?

              • [^] # Re: Ne le fait pas.

                Posté par  (site web personnel) . Évalué à -1.

                Et si il y a une coupure de courent, ou que te femme débranche la mauvaise prise pour brancher l'aspirateur alors que tu met la dernière touche ?
                Comme on est jamais à l'abri des bugs, il faut toujours gérer les crash proprement. Autosave, et modification atomiques des fichier (enregistrer une copie, et atomiquement remplacer l'original).
                Et donc tu ne perds pas plus de quelque minutes de travail.

                • [^] # Re: Ne le fait pas.

                  Posté par  . Évalué à 3.

                  Alors vu qu'il y a une possibilite qui existe que ca merde et corrompe le fichier (prise de courant), autant ne jamais gerer les problemes d'allocation et laisser le fichier etre corrompu dans tous les cas ?

                  On va aller loin avec une approche pareille, et tu en fais quoi du fait que l'utilisateur n'a aucun moyen de comprendre ce qui s'est passe ?

                  Qu'il doit se retaper ce qu'il a perdu avec les softs qui n'ont pas d'auto-save ? (et le soft pourrait planter au milieu de l'auto-save hein…)
                  Que cette approche a la porc ouvre la porte a plein de failles de securite ?
                  Que cette approche peut corrompre la configuration du soft et l'empecher de redemarrer plus tard ?
                  etc…

                  • [^] # Re: Ne le fait pas.

                    Posté par  . Évalué à 1.

                    Ça me rappelle ce que disent les dev d'erlang. Grosso modo "les bugs sont des problèmes comme les autres qui peuvent être traités comme les autres".

                    Please do not feed the trolls

                  • [^] # Re: Ne le fait pas.

                    Posté par  (site web personnel) . Évalué à 4.

                    autant ne jamais gérer les problèmes d'allocation et laisser le fichier etre corrompu dans tous les cas ?

                    Tu m'a mal compris.

                    Je voulais dire que vu qu'il est toujours possible que ça crash, il faut faire en sorte que les conséquences soient minimes en cas de crash. Merci autosave.

                    Et que vu que ce n'est pas si grave si ça crash, alors autant laisser crasher dans le cas de OOM.
                    En effet, gérer proprement toutes les situation de OOM est un travail énorme.

                    les softs qui n'ont pas d'auto-save

                    Je préfère des soft qui ont l'auto-save et pas de gestion d'OOM que l'inverse

                    le soft pourrait planter au milieu de l'auto-save

                    C'est pourquoi l'enregistrement doit se faire de façon atomique, pour que la sauvegarde précédente soit toujours là.
                    Pareil pour la configuration.

                • [^] # Re: Ne le fait pas.

                  Posté par  (site web personnel) . Évalué à 2.

                  a vrai dire, a part quelques précautions comme un save régulier, un soft a peu d'armes contre une coupure de courant …

                • [^] # Re: Ne le fait pas.

                  Posté par  . Évalué à -1.

                  Effectivement.
                  Et si un meteorite s'ecrase sur la machine, alors le disque dur est foutu, et donc c'est pas la peine de s'emmerder a gerer correctement des erreurs softs.

                  Linuxfr, le portail francais du logiciel libre et du neo nazisme.

                  • [^] # Re: Ne le fait pas.

                    Posté par  (site web personnel) . Évalué à 3.

                    Faut pas déconner : C'est réel, la vraie vie, que le courant te lâche plus souvent qu'un malloc à NULL.

                    La priorité est clairement la gestion des coupures de courant (ou simplement un crash de l'OS, même Linux).

                    • [^] # Re: Ne le fait pas.

                      Posté par  . Évalué à 2.

                      Dans le monde mobile, tu vas t'approcher des limites de memoires plus souvent que des pb de batteries.
                      D'ou des designs adaptes, certes.

                      Linuxfr, le portail francais du logiciel libre et du neo nazisme.

                      • [^] # Re: Ne le fait pas.

                        Posté par  (site web personnel) . Évalué à -1.

                        Mais que vient faire la limite de mémoire ici???

                        ON NE PARLE PAS DE FUITE MEMOIRE.

                        On parle de crash (dans l'exemple donné, il n'y a pas de fuite mémoire, si realloc retourne NULL, ça a déjà été dit que ça crashe)

                        Je me répète donc : Oui, il ne faut pas laisser les fuites mémoires pour un programme permanent (pour un programme qui s’exécute 5 minute, rien à foutre. Encore une fois, la chasse à ces problème dépend du contexte, ça ne DOIT PAS être une règle fixe). Chasser le realloc à NULL, ton OS a DEJA un gros problème, rien à foutre qu'il crashe ce petit logiciel.

                        • [^] # Re: Ne le fait pas.

                          Posté par  . Évalué à 2.

                          Mais que vient faire la limite de mémoire ici???

                          ON NE PARLE PAS DE FUITE MEMOIRE.

                          Ben si. Si realloc ou malloc échoue, il y a de fortes chances que ce soit dû à une fuite mémoire qui a épuisé la mémoire disponible.
                          (l'autre cas courant étant l'erreur de programmation)

                        • [^] # Re: Ne le fait pas.

                          Posté par  . Évalué à -1.

                          Je parle pas de fuite, je parle de pas planter ton soft comme un boeuf parce que t'as la flemme de verifier tes codes de retour.
                          L'utilisateur en sait rien qu'il n'y a plus de memoire, la moindre des choses ca serait se lui dire.

                          Ensuite, sur un soft client, avec UI et tout le tralala, je suis a peunpres sur que tu as un paquet de memoire que tu peux liberer (toutes les vues non affichees). Droppes ca, tu recuperes un gros paquet de memoire. Et c'est marrrant, c'est le modele d'ios, l'os va te demander de liberer des ressources quand tu t'approches de la limite.

                          Sur un soft serveur, t'as tres probablement un cache en memoire qq part que tu peux vider histoire de liberer des ressources.
                          Et clairement, la moindre des choses a faire c'est de logger une ligne pour mettre les admins au courant que ca a plante a cause de ca.

                          Apres, t'es libre de considerer que c'est ok de se mettre au tas comme ca, et d'avoir pour seule reaction de hausser les epaules en disant "a foutre, ca prend du temps", ca veut pas dire que c'est acceptable comme attitude.

                          Linuxfr, le portail francais du logiciel libre et du neo nazisme.

                    • [^] # Re: Ne le fait pas.

                      Posté par  . Évalué à 1.

                      J'oubliais le plus important: ton soft ne peut rien faire contre une panne de courant. Il peut faire qq chose s'il se retrouve short en memoire.

                      Linuxfr, le portail francais du logiciel libre et du neo nazisme.

                    • [^] # Re: Ne le fait pas.

                      Posté par  . Évalué à 3.

                      C'est réel, la vraie vie, que le courant te lâche plus souvent qu'un malloc à NULL.

                      Dans ma vraie vie à moi, j'ai plus souvent vu des échecs d'allocation mémoire que des coupures de courant.
                      (et pourtant je te parle de mon chez moi domestique sans onduleur ; pour un serveur dans un datacenter c'est probablement encore plus flagrant)

                      • [^] # Re: Ne le fait pas.

                        Posté par  . Évalué à 6.

                        Ca dépend. Pour ceux qui sont à TH2, les coupures de courant régulières permettent de libérer la mémoire avant qu'une fuite de mémoire ne vautre le serveur…

  • # realloc() et fragmentation du tas

    Posté par  . Évalué à 5.

    Comme expliqué ci-dessus, dans ce cas il n'y aura pas de fuite mémoire, en cas d'OOM ça finit soit avec le OOM killer soit en segfault.
    Mais realloc() a une particularité intéressante qui rend facile l'introduction d'une fragmentation du tas : lorsqu'on appelle un realloc() avec une nouvelle taille inférieure à la taille d'origine, certaines implémentations [1] ne réduisent pas le bloc d'origine. C'est-à-dire qu'une séquence du style :

    p1 = malloc(1MB)
    p1 = realloc(p1, 1KB)
    p2 = malloc(1MB)
    p2 = realloc(p2, 1KB)
    [...]
    
    

    Conduit à une grosse fragmentation du tas. On a eu ce genre de problème dans cPython.

    [1] La glibc n'est pas affectée (http://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ca1d73f875150a17955df9e0aa2dc33e277aacb5;hb=HEAD#l4168), mais la libc de OS-X si par exemple.

  • # Paie ton code d'amateur quand même ...

    Posté par  . Évalué à 2.

    • sizeof(char) vaut toujours 1 d'après la norme.
    • on ne caste pas le retour de malloc() / realloc()
    • on teste les codes de retour
    • [^] # Re: Paie ton code d'amateur quand même ...

      Posté par  (site web personnel) . Évalué à 4.

      sizeof(char) vaut toujours 1 d'après la norme.

      Certes, et en même temps c'est pas gênant de l'écrire (a priori le compilo va optimiser) et je trouve que c'est pas forcément un mal de le mettre personnellement (je trouve que ça permet de voir directement qu'il n'y a pas eu d'oubli de multiplier par la taille du type). Enfin je dis pas que c'est le truc à faire, mais moi je trouve ça plus cohérent d'avoir un "x * sizeof (char)" au milieu de "y * sizeof (int)" et "z * sizeof (double)", même si c'est inutile.

      on ne caste pas le retour de malloc() / realloc()

      Il me semble qu'en C++ tu te chopes une erreur ou un warning si tu le fais pas.

      • [^] # Re: Paie ton code d'amateur quand même ...

        Posté par  (site web personnel) . Évalué à 3.

        en effet le style indique un utilisateur de g++ plutôt que gcc
        par contre pour le sizeof je préfère franchement un sizeof(*ptr) plutôt qu'un sizeof(type) …

        • [^] # Re: Paie ton code d'amateur quand même ...

          Posté par  . Évalué à 4.

          Tiens, puis accessoirement, puisqu'on en parle et contrairement à une idée répandue, « sizeof » n'utilise pas de parenthèses. On les met quand on veut connaître la taille d'un type mais, dans ce cas, c'est au nom de ce type qu'elles s'appliquent (comme un cast). C'est une erreur compréhensible parce que d'une part, les mettre forme toujours une expression syntaxiquement correcte et (surtout) « sizeof » est le seul mot-clé réservé du C qui se comporte comme une fonction.

      • [^] # Re: Paie ton code d'amateur quand même ...

        Posté par  . Évalué à 4.

        Si tu utilise malloc en C++, c'est normal de te faire battre, puisque tu à tendu le bâton.

        Pour information, en C++, new te force à spécifier le type, et ne retourne jamais NULL (ça lance une exception à la place).

        Et sinon, conky est compilé avec un compilo C … donc aucune raison de caster le malloc.

      • [^] # Re: Paie ton code d'amateur quand même ...

        Posté par  (site web personnel) . Évalué à 5.

        Il me semble qu'en C++ tu te chopes une erreur ou un warning si tu le fais pas.

        En C, caster les mallocs sert seulement à ne pas voir qu'on a oublié le header stdlib.h. Du coup ton compilateur génère un prototype de fonction dont le type de retour est int. Si tes entiers et tes pointeurs n'ont pas la même taille, c'est l'hosto direct!

    • [^] # Re: Paie ton code d'amateur quand même ...

      Posté par  . Évalué à 2.

      le seul point valide de ton commentaire porte sur le test des codes de retour. Le reste, c'est de la pure convention pour lesquels les deux comportements sont admissibles (sizezof(char) ou pas, et caste de *alloc ou pas).

      Sur un extrait de lignes, ça fait un peu court pour en déduire quoi que ce soit sur le professionnalisme de l'auteur du code, non ?

  • # xmonad n'est pas si léger que ça.

    Posté par  . Évalué à 2.

    J'ai eu envie d'essayer récemment. Certes, il occupe très peu de RAM, mais le CPU !
    Perso, de la RAM, j'en ai, mais je me soucie de l'autonomie de ma batterie, et xmonad réveille le CPU plus souvent que le wifi. C'est peut être un bug, j'utilisais l'overlay haskell de gentoo.

    Please do not feed the trolls

  • # Où va la mémoire ?

    Posté par  (site web personnel) . Évalué à 4.

    Ce qu'il faut faire pour voir pourquoi ton programme prends autant de mémoire:

    • T'assurer que c'est bien le tas (heap) qui prends la mémoire, et pas un fichier mapper (par exemple, le code des bibliothèques, ou un fichier de polices)

    • valgrind --tool=massif, et regarder le résultat avec massif-visualizer

  • # Idem avec Conky

    Posté par  . Évalué à 1. Dernière modification le 08 septembre 2012 à 00:17.

    J'utilisais Conky, et j'avais le même problème de fuite mémoire. Comme l'ordinateur tourne 24/7, le processus de Conky arrivait rapidement à bouffer plus de 300~400M de mémoire, point auquel il devenait gênant et qui valait donc que je le dégage.

    Ne m'y connaissant pas des masses, j'ai rapidement cherché des informations sur le net à ce sujet, et visiblement plusieurs personnes s'en plaignaient sur différents forums. Ne sachant toujours pas si la fuite venait de Conky ou de mon fichier conkyrc (qui affichait une belle horloge à aiguilles, un calendrier et les jauges d'utilisation mémoire, disque et processeur), j'ai dégagé le logiciel pour ne plus être ennuyé.

  • # Chaque amélioration compte

    Posté par  . Évalué à 6.

    J'ai pu lire une ou deux fois que ce n'était pas une correction très importante ou qu'elle n'était peut-être pas responsable de la fuite mémoire. Sans toucher à ce débat, j'aimerais ajouter quelque chose. Et alors ? C'est une raison pour ne pas améliorer l'application ?

    L'immobilisme mène à la dégradation du code, la dégradation mène à la non maintenabilité, la non maintenabilité mène à la colère, la colère au côté obscur. Dark Vador, c'est un peu de votre faute finalement.

    • [^] # Re: Chaque amélioration compte

      Posté par  (site web personnel) . Évalué à 1.

      Dans l'exemple cité, il n'y a pas de fuite mémoire (ça crashe).
      Ce n'est pas le problème.

      Je suis d'accord qu'il faut faire attention aux fuites mémoire car ça fait chier tout le monde. Mais je persiste à penser qu'une personne n'a pas forcément à penser à "tout" comme elle fait une appli "toute petite et sans quelqu'un pour payer". Après, si quelqu'un paye parce que ça vaut le coup… Perso, le mec qui me fait du code "super secure" pour un prototype par exemple "parce qu'il faut toujours bien coder", je le paye moitié moins car 50% du temps est pour son plaisir (soit l'affaire prend et j'ai des sous pour refaire une passe "plus propre" après quelques refactoring, soit ça part à la poubelle et on s'en fout du realloc à NULL)

      C'est une raison pour ne pas améliorer l'application ?

      Ce n'est pas le soucis non plus. Personne n'a dit, il me semble, qu'il ne fallait surtout pas le faire. Perso, ce que j'ai dit c'est que c'est pas le plus important (ça crashe, ça libère tout et tu as vraiment des problèmes énormes sur ton OS pour n'avoir rien à foutre de ce programme "à la con". On ne parle toujours pas de fuite mémoire). Envoie un patch, tu as fait le boulot. Mais je trouve limite de demander que le code pour un programme "à la con" soit 100% parfait surtout si on ne paye rien (oui toujours l'argent… Si vous acceptez de travailler gratos, faites-moi signe)

      C'est comme la sécurité : pas la peine de faire une triple authentification pour accéder à des données non sensibles. Il faut savoir s'adapter suivant l'importance de la chose. Dire "il faut toujours faire", c'est un peu gros.

      • [^] # Re: Chaque amélioration compte

        Posté par  (site web personnel) . Évalué à 5.

        Perso, le mec qui me fait du code "super secure" pour un prototype par exemple "parce qu'il faut toujours bien coder", je le paye moitié moins car 50% du temps est pour son plaisir

        Ce genre de "robustesse" a effectivement un coût, ce que pas mal de commentateurs semblent ignorer. Pour un prototype c'est carrément contre-productif de faire des vérifications de ce genre, mais c'est parfois également vrai sur des logiciels de production. Ceux qui ont développé par exemple dans Catia en savent quelque chose : vérifier si le pointeur est NULL avant chaque utilisation, ça a un coût en temps de développeur. (Cela a également un coût en temps d'exécution, qui n'est pas négligeable en C++ car on n'a pas de JIT pour éliminer ces NULL-checks.)

        La question qui se pose c'est "est-ce que l'amélioration vaut le temps qu'elle coûte" (comme toujours en ingénierie logicielle), et pour des programmes non critiques la réponse est non sans hésiter. Lorsqu'un développeur passe son temps à écrire de la gestion d'erreur-qui-n'arrivera-pas, il ne fait pas autre chose de plus productif.

        Concernant ce journal on est tout simplement dans le ridicule. Si realloc() returne NULL, ce qui n'arrive essentiellement jamais sous Linux, le programme va crasher (ce que Conky fait assez souvent par ailleurs). C'est un comportement tout à fait correct.

        • [^] # Re: Chaque amélioration compte

          Posté par  (site web personnel) . Évalué à 2.

          Ce genre de "robustesse" a effectivement un coût, ce que pas mal de commentateurs semblent ignorer.

          Je crois surtout qu'il y a pas mal de commentateurs qui programment de temps en temps, et "ont le temps". A moins qu'ils disent des choses qu'ils ne font pas (qui montre son code?). Mais je constate quand même qu'en ce samedi on a plus de monde qui "assume" et dit que oui, c'est une connerie de gérer les malloc/realloc à NULL. Ouf! Ca fait du bien de lire ça, plus réaliste.

          • [^] # Re: Chaque amélioration compte

            Posté par  . Évalué à 2.

            Je crois surtout qu'il y a pas mal de commentateurs qui programment de temps en temps, et "ont le temps".

            Le temps, ça se prend, que ce soit pour les tests unitaires ou la gestion d'erreur. Mais bon, c'est sûr que c'est pas l'idéal si le but est de finir mentionné dans http://thedailywtf.com/

      • [^] # Re: Chaque amélioration compte

        Posté par  . Évalué à 3.

        Tu as une façon curieuse de voir le problème, compte tenu du contexte :)

        Perso, le mec qui me fait du code "super secure" pour un prototype par exemple "parce qu'il faut toujours bien coder", je le paye moitié moins car 50% du temps est pour son plaisir

        On ne parle pas des applications que certains d'entre nous doivent maintenir quand ils sont au travail mais d'un logiciel libre dont les patchs peuvent être fournis par la communauté. Selon le temps et les compétences de chacun, il est probable que des petits patchs de ce genre soient produits et je dis simplement qu'il ne faut pas hésiter à les pousser si le hasard fait qu'on passe par là.

        Mais je trouve limite de demander que le code pour un programme "à la con" soit 100% parfait surtout si on ne paye rien

        Hors sujet ;) Je suis passé vite sur certains commentaires mais il me semble que personne n'exige cela. Pas moi en tout cas. D'ailleurs je n'exige rien, j'encourage simplement la correction de ces petits problèmes souvent délaissés car je pense qu'il est préférable de corriger en 10 secondes plutôt que de se poser la question pendant 10 minutes. Ça permet d'aller dans le bon sens. Pour ce qui est d'un travail rémunéré, le problème est autre. Pour ce qui est d'un travail plus consommateur en temps, le problème est autre.

        Pour finir, je préciserais que mon commentaire traitait des petites erreurs ou améliorations possibles et non exclusivement de ce problème.

      • [^] # Re: Chaque amélioration compte

        Posté par  (site web personnel) . Évalué à 5.

        Perso, le mec qui me fait du code "super secure" pour un prototype par exemple "parce qu'il faut toujours bien coder", je le paye moitié moins car 50% du temps est pour son plaisir (soit l'affaire prend et j'ai des sous pour refaire une passe "plus propre" après quelques refactoring, soit ça part à la poubelle et on s'en fout du realloc à NULL)

        Le problème de cette approche prototype, c'est qu'il ait de grand risque que la phase de mise au propre passe par une réécriture complète. Et là tous les bénéfices du prototype vite fait sont perdus.
        De plus la phase de prototype, il y a moins de pression donc on peut prendre un peu plus de temps. Par contre avant la version finale pour la mise en production, c'est le stress, donc la mise au propre est secondaire. Et c'est pour ça qu'on a tant de logiciel en version "stable" qui ne sont rien d'autre que des bétas voir des alphas. Mais dans le monde logiciel, c'est facile de le faire, il n'y a aucune responsabilité.

        En prototypage, on peut ne pas faire la gestion d'erreur, mais la prévoir. Ça coûte pas beaucoup de temps et ça permet de se concentrer sur le fonctionnement :

        ptr=malloc(size);
        if(!ptr) {
          /* TODO */
        } else {
          memcpy(ptr, pscr, size);
          /* ... etc ... */
        }
        
        

        Et quand le temps est disponible, c'est plus facile de mettre au propre : grep TODO *.

        Mais c'est vrai que dans des projets, le rapport temps/propreté peut poser problème, particulièrement dans les petites structures.

        "It was a bright cold day in April, and the clocks were striking thirteen" - Georges Orwell

        • [^] # Re: Chaque amélioration compte

          Posté par  . Évalué à 4.

          Le problème de cette approche prototype, c'est qu'il ait de grand risque que la phase de mise au propre passe par une réécriture complète.

          Surtout, l'expérience enseigne qu'un « prototype » finit bien souvent utilisé en production, parce que tout recoder est une perte de temps quand on a un truc qui marche.

  • # sprintf et arithmétique de pointeurs

    Posté par  (site web personnel) . Évalué à 7.

    Moi c'est surtout cette ligne là qui me fait dresser les poils de la barbe :

    sprintf(src_dup + dup_idx, "%s", templates[tmpl_num - 1]);

    pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.