Fuite mémoire
Modérateur : xcasadmin
Fuite mémoire
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
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
Re: Fuite mémoire
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)
Re: Fuite mémoire
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
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
Re: Fuite mémoire
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.
Re: Fuite mémoire
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...
Re: Fuite mémoire
Vous pouvez mettre un morceau de code illustrant le probleme?
Re: Fuite mémoire
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).
(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;
}
Re: Fuite mémoire
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.
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.
Re: Fuite mémoire
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
Cordialement,
JKB
Re: Fuite mémoire
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;
Re: Fuite mémoire
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...
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...
Re: Fuite mémoire
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?
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?
Re: Fuite mémoire
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.
Re: Fuite mémoire
Je viens de tester avec le programme suivant
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?
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;
}
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?
Re: Fuite mémoire
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 :
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
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;
}
Cordialement,
JKB