Fuite mémoire

Bugs

Modérateur : xcasadmin

JKB
Messages : 20
Inscription : mar. juin 28, 2011 7:57 am
Localisation : Paris, France
Contact :

Fuite mémoire

Message par JKB » jeu. août 04, 2011 8:17 am

Bonjour à tous,

J'utilise actuellement giac 0.9.3 et il me semble qu'il y a un problème de fuite de mémoire. Voir pour cela les logs de valgrind :

16 bytes in 1 blocks are definitely lost in loss record 80 of 1,966
at 0x4C27297: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x8E8115: giac::gen::gen(giac::unary_function_ptr const&, int) (in /home/bertrand/rpl/build/src/rpl)
by 0x975BDC: giac::lexer_functions_register(giac::unary_function_ptr const&, char const*, int) (in /home/bertrand/rpl/build/src/rpl)
by 0xDA2C32: giac::unary_function_ptr::unary_function_ptr(giac::unary_function_abstract const*, int, int) (in /home/bertrand/rpl/build/src/rpl)

À chaque création d'un objet de type gen (depuis un programme C++), j'ajoute une erreur. Visiblement, le destructeur (ligne 951 de gen.cc) n'appelle jamais le 'delete' correspondant à l'opération 'new' du constructeur. À vrai dire, j'ai même l'impression que le 'switch' du destructeur n'est jamais exécuté.

Cordialement,

JKB

parisse
Messages : 5739
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Re: Fuite mémoire

Message par parisse » jeu. août 04, 2011 1:10 pm

oui, en fait ce sont des variables globales qui sont crees la (toutes les fonctions utilisateurs de giac) et ne sont jamais desallouees. Donc pas d'inquietude. Vous pouvez d'ailleurs utiliser le flag de compilation -DSTATIC_BUILTIN_LEXER pour eviter la construction de ces variables (la macro les definit alors comme des constantes, mais ca necessite de mettre a jour un fichier si on rajoute des fonctions utilisateurs, alors que dans le mode normal il n'y a rien a faire, c'est dynamique)

JKB
Messages : 20
Inscription : mar. juin 28, 2011 7:57 am
Localisation : Paris, France
Contact :

Re: Fuite mémoire

Message par JKB » jeu. août 04, 2011 4:56 pm

Je ne suis pas sûr que nous parlons de la même chose. Je ne parle pas des fonctions utilisateur de giax, je parle de ceci :

gen::gen(const unary_function_ptr * f,int nargs){
#ifdef SMARTPTR64
* ((longlong * ) this) = longlong(new ref_unary_function_ptr(*f)) << 16; <----- Là
#else
_FUNC_ = unsigned(* (unsigned *) f);
// __FUNCptr= new ref_unary_function_ptr(f);
#endif
type=_FUNC;
subtype=nargs;
}

qui est un champ de l'objet gen. À chaque fois que je crée un objet de type gen, je fais un new qui n'est jamais libéré. Rien que pour faire un test, j'ai écrit une fonction qui crée des objets gen (et qui en toute logique doit appeler le destructeur qui effectue un delete sur le champ en question). J'ai autant de pointeurs perdus que d'itération dans ma boucle.

Cordialement,

JKB

parisse
Messages : 5739
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Re: Fuite mémoire

Message par parisse » jeu. août 04, 2011 7:55 pm

je ne comprends pas, chaque fois qu'on cree un gen non atomique on fait un new, mais chaque fois que le destructeur est appele le delete correspondant est appele. Sauf peut-etre pour les variables globales ou locales statiques si l'OS detruit tout d'un coup.

JKB
Messages : 20
Inscription : mar. juin 28, 2011 7:57 am
Localisation : Paris, France
Contact :

Re: Fuite mémoire

Message par JKB » ven. août 05, 2011 7:16 am

Oui, moi non plus, je ne comprends pas. C'est pour cela que j'ai collé un cout << dans le constructeur et le même devant le destructeur correspondant et si le destructeur est appelé, il ne passe pas par le delete... Le souci est que je ne connais pas assez les arcanes de giac pour voir si le fait de sauter le delete est légitime ou non...

parisse
Messages : 5739
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Re: Fuite mémoire

Message par parisse » ven. août 05, 2011 8:37 am

Vous pouvez mettre un morceau de code illustrant le probleme?

JKB
Messages : 20
Inscription : mar. juin 28, 2011 7:57 am
Localisation : Paris, France
Contact :

Re: Fuite mémoire

Message par JKB » ven. août 05, 2011 10:08 am

Certainement. Voilà un bout de code qui présente le problème. Je ne détaille pas les arguments qui sont corrects
(des unsigned char * qui proviennent d'une fonction C).

Code : Tout sélectionner

            try
            {
                giac::context   contexte;

                gen expression(
                        string(reinterpret_cast<const char *>(argument_2)),
                        &contexte);
                identificateur variable(
                        string(reinterpret_cast<const char *>(argument_1)));
                gen valeur(string(reinterpret_cast<const char *>
                        (argument_3)), &contexte);

                gen resultat = limit(expression, variable, valeur, 0,
                        &contexte);
                string chaine = "'" + resultat.print() + "'";

                conversion_cas_vers_rpl(s_etat_processus,
                        reinterpret_cast<unsigned char *>(const_cast<char *>(
                        chaine.c_str())));
            }
            catch(bad_alloc exception)
            {
                s_etat_processus->erreur_systeme = d_es_allocation_memoire;
            }
            catch(...)
            {
                s_etat_processus->erreur_execution = d_ex_erreur_interne_rplcas;
            }

parisse
Messages : 5739
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Re: Fuite mémoire

Message par parisse » ven. août 05, 2011 10:46 am

je ne pense pas que ca pose de problemes, parce que le log de valgrind specifie bien
by 0x975BDC: giac::lexer_functions_register(giac::unary_function_ptr const&, char const*, int) (in /home/bertrand/rpl/build/src/rpl)
or lexer_functions_register est precisement la fonction qui met dans un map de facon dynamique les fonctions utilisateurs crees par giac.
Donc il n'y a pas de fuite a craindre, je pense que ca apparait la parce que ca doit etre le premier appel a la libgiac, et que c'est le chargement de la lib dynamique a ce point qui provoque la creation des fonctions utilisateurs une fois pour toute.

JKB
Messages : 20
Inscription : mar. juin 28, 2011 7:57 am
Localisation : Paris, France
Contact :

Re: Fuite mémoire

Message par JKB » ven. août 05, 2011 11:22 am

Oui mais non. J'ai collé ce bout de code dans une boucle infinie. Mon exécutable grossit de plus en plus et termine par une exception bad_alloc. Il y a bien un problème. Je veux bien que la première fois, on alloue quelque chose dans giac::lexer_functions_register, mais pas les fois suivantes lorsqu'on utilise une fonction qui a déjà été enregistrée.

Cordialement,

JKB

parisse
Messages : 5739
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Re: Fuite mémoire

Message par parisse » ven. août 05, 2011 11:54 am

Mais c'est precisement ce qui est fait au debut de la fonction:

Code : Tout sélectionner

 bool lexer_functions_register(const unary_function_ptr & u,const char * s,int parser_token){
      map_charptr_gen::const_iterator i = lexer_functions().find(s);
      if (i!=lexer_functions().end())
	return false;

JKB
Messages : 20
Inscription : mar. juin 28, 2011 7:57 am
Localisation : Paris, France
Contact :

Re: Fuite mémoire

Message par JKB » ven. août 05, 2011 1:43 pm

Dans ce cas, cela semble ne pas fonctionner.

Exemple :
1/ un seul calcul :
==31234== LEAK SUMMARY:
==31234== definitely lost: 35,024 bytes in 1,387 blocks
==31234== indirectly lost: 3,530 bytes in 146 blocks
==31234== possibly lost: 24,954 bytes in 145 blocks
==31234== still reachable: 96,473 bytes in 455 blocks

2/ 100 fois le même calcul :
==31336== LEAK SUMMARY:
==31336== definitely lost: 41,360 bytes in 1,486 blocks <- 1486 - 1387 = 99
==31336== indirectly lost: 10,658 bytes in 245 blocks <- même différence
==31336== possibly lost: 24,954 bytes in 145 blocks
==31336== still reachable: 96,473 bytes in 455 blocks
==31336== suppressed: 0 bytes in 0 blocks

3/ 10000 fois le même calcul :
==31409== LEAK SUMMARY:
==31409== definitely lost: 674,896 bytes in 11,385 blocks <- 11385 - 1387 = 9998
==31409== indirectly lost: 723,170 bytes in 10,141 blocks <- idem
==31409== possibly lost: 25,306 bytes in 150 blocks
==31409== still reachable: 96,473 bytes in 455 blocks
==31409== suppressed: 0 bytes in 0 blocks

Et en boucle infinie, ça explose littéralement...

parisse
Messages : 5739
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Re: Fuite mémoire

Message par parisse » ven. août 05, 2011 4:00 pm

S'agit-il toujours d'un passage par lexer_function_register?
Dans ce cas, il faudrait mettre un breakpoint avec 2 fois la meme chaine pour comprendre. Ceci dit je ne comprends pas bien comment il se fait que vous appelez cette fonction, c'est quoi les chaines que vous parsez?

JKB
Messages : 20
Inscription : mar. juin 28, 2011 7:57 am
Localisation : Paris, France
Contact :

Re: Fuite mémoire

Message par JKB » ven. août 05, 2011 6:31 pm

La chaîne est très simple : sin(x)/x pour faire un exemple. Et je crée à partir de cette chaîne un objet de type gen.

parisse
Messages : 5739
Inscription : mar. déc. 20, 2005 4:02 pm
Contact :

Re: Fuite mémoire

Message par parisse » sam. août 06, 2011 6:26 am

Je viens de tester avec le programme suivant

Code : Tout sélectionner

#include <giac/giac.h>
using namespace std;
using namespace giac;

int main(int argc,char ** argv){
  if (argc<2)
    return 1;
  int N=atoi(argv[1]);
  context ct;
  for (int i=0;i<N;i++){
    char s[]="sin(x)/x";
    gen g(s,&ct);
    cout << g+i << endl;
  }
  return 0;
}

compile avec g++ -g test.cc -lgiac -lgmp.
Je ne suis pas un habitue de valgrind, en faisant simplement
valgrind --leak-check=yes ./a.out 2 >& log
valgrind --leak-check=yes ./a.out 100 >& log1
et en comparant les log je ne constate aucune difference dans le leak summary a la fin. Qu'est-ce que ca donne chez vous?

JKB
Messages : 20
Inscription : mar. juin 28, 2011 7:57 am
Localisation : Paris, France
Contact :

Re: Fuite mémoire

Message par JKB » sam. août 06, 2011 9:20 am

Bon, je viens de compiler votre code :
g++ test.cpp librplcas.a -lpthread -ldl -lrt -I ../include/

librplcas.a est un grand machin statique qui contient tout ce qu'il faut pour giac (et accessoirement, c'est la bibliothèque que j'utilise pour mon test qui présente le problème).

Votre code ne présente pas de fuite, mais le mien en a une. Et cela provient de l'utilisation du contexte.
En effet, si je modifie votre code comme ceci :

Code : Tout sélectionner

int main(int argc,char ** argv){
  if (argc<2)
      return 1;
  int N=atoi(argv[1]);
  for (int i=0;i<N;i++){
  context ct;
    char s[]="sin(x)/x";
    gen g(s,&ct);
    cout << g+i << endl;
  }
  return 0;
}
Je me retrouve avec ma fuite de mémoire. Il y a donc deux solutions : soit libérer d'une façon ou d'une autre le contenu du contexte, soit se débrouiller pour avoir un contexte par thread (si j'ai bien compris ce qu'est un contexte). Rajouter un cleanup_context(&ct); à la fin de la boucle ne change rien au problème.

Cordialement,

JKB

Répondre