OVH Cloud OVH Cloud

debug de fuites memoire

64 réponses
Avatar
3dsman
salut!
je suis a la recherche d'un outil efficace et pas trop compliquer pour
detecter les fuites memoires dans mes softs.
quelqu'un en aurais il un bien a me conseiller?
merci

10 réponses

1 2 3 4 5
Avatar
Jean-Baptiste Nivoit
wrote:
Arnaud Meurgues wrote:
wrote:
Au moins sous Linux.

pour info, sous NT, il y a une fonction StackWalk qui permet de

parcourir la pile:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/stackwalk64.asp

jb.


Avatar
3dsman
Dans le message 42d5044f$0$31208$,

Michel Michaud wrote:

Juste un mot pour préciser qu'il y a déjà ce qu'il faut sous
Visual C++, si jamais c'est ce que tu utilises...

Est ce que vous parlez de :


_CrtSetReportMode()
_malloc_dbg()



Plutôt

#include <crtdbg.h>

int main()
{
int debugHeapFlag= _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
_CrtSetDbgFlag(debugHeapFlag | _CRTDBG_LEAK_CHECK_DF);
...

À la fin de l'exécution (en mode debug), on a une liste des
zones allouées non libérées, donc les fuites habituellement.



heu moi je suis pas encore tres doué
il suffit de mettre ca dans le main() ???



Avatar
3dsman
et bien merci a tous (meme si j'ai été un peu largué a un moment :-) )
j'ai apris plein de choses :-)
Avatar
Michel Michaud
Dans le message 42d6c863$0$2421$,
#include <crtdbg.h>

int main()
{
int debugHeapFlag= _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
_CrtSetDbgFlag(debugHeapFlag | _CRTDBG_LEAK_CHECK_DF);
...

À la fin de l'exécution (en mode debug), on a une liste des
zones allouées non libérées, donc les fuites habituellement.



heu moi je suis pas encore tres doué
il suffit de mettre ca dans le main() ???


Oui, c'est tout. Il faut ensuite exécuter en mode debug.
S'il y a des fuites, elles te seront indiqués. Par exemple :

#include <crtdbg.h>
int main()
{
int debugHeapFlag= _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
_CrtSetDbgFlag(debugHeapFlag | _CRTDBG_LEAK_CHECK_DF);

new long(0x224D4D22);
new long; // ou std::malloc(sizeof(long));
}

Donne (dans la fenêtre de debug) :

Detected memory leaks!
Dumping objects ->
{51} normal block at 0x00321030, 4 bytes long.
Data: < > CD CD CD CD
{50} normal block at 0x00320FF0, 4 bytes long.
Data: <"MM"> 22 4D 4D 22
Object dump complete.

La mémoire non initialisée est indiquée par des CD; les lignes
Data montrent le contenu en texte si possible (entre <>) et en
hexa; les {nn} sont les numéros de blocs alloués si je me souviens
bien. Avec les adresses des blocs et ces informations, on a déjà de
quoi se mettre sous la dent pour trouver d'où viennent les fuites.
C'est pas mal pour un outil gratuit et déjà intégré, mais c'est
avec VC seulement (il y a peut-être un équivalent avec d'autres
compilateurs).

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
3dsman
Dans le message 42d6c863$0$2421$,


#include <crtdbg.h>

int main()
{
int debugHeapFlag= _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
_CrtSetDbgFlag(debugHeapFlag | _CRTDBG_LEAK_CHECK_DF);
...

À la fin de l'exécution (en mode debug), on a une liste des
zones allouées non libérées, donc les fuites habituellement.



heu moi je suis pas encore tres doué
il suffit de mettre ca dans le main() ???



Oui, c'est tout. Il faut ensuite exécuter en mode debug.
S'il y a des fuites, elles te seront indiqués. Par exemple :

#include <crtdbg.h>
int main()
{
int debugHeapFlag= _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
_CrtSetDbgFlag(debugHeapFlag | _CRTDBG_LEAK_CHECK_DF);

new long(0x224D4D22);
new long; // ou std::malloc(sizeof(long));
}

Donne (dans la fenêtre de debug) :

Detected memory leaks!
Dumping objects ->
{51} normal block at 0x00321030, 4 bytes long.
Data: < > CD CD CD CD
{50} normal block at 0x00320FF0, 4 bytes long.
Data: <"MM"> 22 4D 4D 22
Object dump complete.

La mémoire non initialisée est indiquée par des CD; les lignes
Data montrent le contenu en texte si possible (entre <>) et en
hexa; les {nn} sont les numéros de blocs alloués si je me souviens
bien. Avec les adresses des blocs et ces informations, on a déjà de
quoi se mettre sous la dent pour trouver d'où viennent les fuites.
C'est pas mal pour un outil gratuit et déjà intégré, mais c'est
avec VC seulement (il y a peut-être un équivalent avec d'autres
compilateurs).

ok merci je vais donc essayer ca aussi :-)




Avatar
Arnaud Meurgues
Jean-Baptiste Nivoit wrote:

pour info, sous NT, il y a une fonction StackWalk qui permet de
parcourir la pile:


Ça c'est chouette. Merci.

Arnaud

Avatar
kanze
Arnaud Meurgues wrote:
wrote:

l'autre. En dur, direct dans le code -- je n'avais pas pensé
au visiteur.


Oui. J'ai longtemps hésité sur la manière d'utiliser le
visiteur. Je me demandais si c'était à lui de stocker les
blocs ou si cela devait être fait par l'allocateur. Je n'ai
pas trouvé de réponse satisfaisante.


Je crois que c'est à l'allocateur de gérer la liste des blocs.

Peut-être que les stocker dans l'allocateur et ne fournir au
visiteur que des itérateurs sur la liste des blocs mémoire de
l'allocateur serait une meilleure solution ?


Je ne sais pas. J'ai actuellement deux chose :

-- Il y a beaucoup de codé en dur, pas trop élégant, mais qui
marche. Ces codés en dur doivent bien représenter 90% de ce
que je fais. C'est ici que le visiteur me semble
intéressant. J'inscris des visiteurs auprès de l'allocateur,
et chaque fois qu'un bloc est alloué ou libéré, j'appelle
tous les visiteurs.

Le codé en dur n'est pas sans avantages non plus. Si on a
besoin des données supplémentaires, on peut les placer où tu
veux. Donc, par exemple, je crée des zones de garde avant et
après le bloc, avec un contenu bien défini, et je vérifie le
contenu lors de la libération. Ça permet à detecter pas mal
de débordements de la zone. Et je ne vois pas trop comment
un visiteur ferait pour allouer ces blocs, surtout en
s'assurant qu'ils soient immédiatement avant et après. On
perd de la separation de concernes, mais il y a certaines
choses qui sont peut-être trop difficiles autrement. En
revanche, chaque fois que j'ai voulu ajouter une
fonctionalité, j'ai dû me plonger dans les détails de
l'implémentation. C'est tout ce qu'il y a sauf élégant.

-- J'ai aussi un espèce de visiteur limité, pour des choses
comme afficher les blocs encore alloués. Si j'avais à le
faire aujourd'hui, j'utiliserais probablement plutôt un
itérateur ici.

Le principe de base est assez simple : il faut
éventuellement s'assurer que toute la pile est en mémoire
(toujours le cas sur des IA-32, mais il faut une instruction
machine spéciale sur un Sparc).


Ah ? Sur Sparc, toute la pile n'est pas nécessairement en
mémoire ?


Ça dépend de ce que tu entends par « pile ». Mais certaines
choses que tu t'attendrais à trouver dans la pile, comme les
adresses de retour ou les pointeurs au trame précédant, ne sont
pas forcément en mémoire. Même pour les fonctions au-dessus de
celle où tu te trouves.

Note bien que le but, c'est d'aller vite. Et que les accès
mémoire sont cher. Sur un Sparc, l'appel d'une fonction
n'implique aucun accès mémoire, sauf la lecture du code à
l'adresse où on va.

Ensuite, tu obtiens l'adresse de départ -- soit en pêchant
dans un buffer de setjmp,


Ah. Je ne suis pas (plus) très familier avec ce genre de
fonctions. Je n'ai jamais eu à m'en servir.


Pour obtenir l'adresse de démarrage pour rémonter la pile, c'est
loin d'être évident. En fait, mon code pour le Sparc, je l'ai
copié d'ailleurs. Et puisqu'il marche... Si j'avais à le faire
moi-même, j'utiliserais sans doute une technique comme celle que
j'utilise pour l'Intel. Avec le coup d'assembleur en plus,
évidemment.

d'une variable locale. Puis, il ne reste plus que des
reinterpret_cast et de l'arithmétique sur des pointeurs pour
régarder ce que tu veux dans la pile. En fait, la plus
difficile, en général, c'est de savoir où se trouvent
exactement dans la pile les informations que tu cherches.


Ben oui. Ça nécessite une bonne documentation du compilateur,
de l'OS et du matériel pour savoir comment il organise la
pile.


Ça dépend. Souvent, on peut déviner pas mal avec des dumps hexa
jusicieux.

Au moins sous Linux. Mais j'imagine que la seule chose qui
risque de changer sous Windows, c'est ce que tu ajoutes à
l'adresse de fp dans le constructeur. (Selon le compilateur,
il se peut aussi qu'il vaut mieux que le constructeur ne
soit pas inline, voir qu'on se sert d'une fonction
complétement séparée pour obtenir l'adresse de début. Mais
le code ici fonctionne sous Linux avec des versions récentes
de g++, compilé sans optimisation.)

Note aussi qu'avec g++, tu as des fonctions
__builtin_return_address et __builtin_frame_address, chacun
qui prend un paramètre pour spécifier le profondeur.


Malheureusement, il se trouve que g++ est un des compilateurs
que je n'utilise pas (moi, c'est msvc sous Windows, CC sous
Solaris, aCC sous HP, essentiellement).


Ce sont les compilateurs que j'utilise le plus, aussi. Sauf que
ça fait un moment que je ne travaille plus sous HP. (En fait, HP
est le seul processeur où je n'ai pas réussi à trouver
l'organisation de la pile à partir des dump hex.)

En plus, j'aurais aimé trouver les noms des fonctions, plutôt
que simplement les adresses. Et ça, ça nécessite de savoir
lire les infos de debug...


D'une partie, c'est probablement ce qu'il y a de plus facile.
C'est normalisé (ELF, etc.). En revanche, il en faut beaucoup de
code. Avec nm sous Unix, tu peux obtenir une liste des symboles
triés par adresse -- il y a une option à LINK pour faire la même
choses sous Windows, je crois. De là, ce n'est pas trop
difficile à exploiter.

Sinon, au moins sous les Unix, il y a une commande addr2line
dans les binutils de GNU. Il doit y avoir un moyen de
l'utiliser. Reste le problème de manglage -- addr2line peut en
traiter quelques un, mais pas tous.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Gabriel Dos Reis wrote:
Arnaud Meurgues writes:

[...]

| > Note aussi qu'avec g++, tu as des fonctions
| > __builtin_return_address et __builtin_frame_address, chacun qui
| > prend un paramètre pour spécifier le profondeur.

mais ces machins ne marchent pas bien pour une profondeur
autre que zéro.


Ils ne sont garantis que pour une profondeur de zéro. Mais
d'après ce que j'ai compris, il marche plus ou moins bien, selon
la plateforme. Si ta plateforme est une où il marche plutôt
bien, ça serait bête de s'en priver (à moins d'avoir écrit le
code avant d'en connaître l'existance:-)).

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Gabriel Dos Reis
writes:

| Gabriel Dos Reis wrote:
| > Arnaud Meurgues writes:
|
| > [...]
|
| > | > Note aussi qu'avec g++, tu as des fonctions
| > | > __builtin_return_address et __builtin_frame_address, chacun qui
| > | > prend un paramètre pour spécifier le profondeur.
|
| > mais ces machins ne marchent pas bien pour une profondeur
| > autre que zéro.
|
| Ils ne sont garantis que pour une profondeur de zéro. Mais
| d'après ce que j'ai compris, il marche plus ou moins bien, selon
| la plateforme.

Tu as compris quelque chose ; moi j'ai essayé en vrai sur quelques
machines et de plus j'ai eu des bugs reports.

| Si ta plateforme est une où il marche plutôt
| bien, ça serait bête de s'en priver

Tout à fait exact, mais il me semble devoir signaler l'aspect
restrictif de la chose parce que j'ai déjà reçu beaucoup de bug
reports (visiblement d'utilisateurs qui ont préféré oublier cet aspect
de la chose).

-- Gaby
Avatar
kanze
Gabriel Dos Reis wrote:
writes:

| Gabriel Dos Reis wrote:
| > Arnaud Meurgues writes:

| > [...]

| > | > Note aussi qu'avec g++, tu as des fonctions
| > | > __builtin_return_address et __builtin_frame_address, chacun qui
| > | > prend un paramètre pour spécifier le profondeur.

| > mais ces machins ne marchent pas bien pour une profondeur
| > autre que zéro.

| Ils ne sont garantis que pour une profondeur de zéro. Mais
| d'après ce que j'ai compris, il marche plus ou moins bien,
| selon la plateforme.

Tu as compris quelque chose ; moi j'ai essayé en vrai sur
quelques machines et de plus j'ai eu des bugs reports.

| Si ta plateforme est une où il marche plutôt bien, ça serait
| bête de s'en priver

Tout à fait exact, mais il me semble devoir signaler l'aspect
restrictif de la chose parce que j'ai déjà reçu beaucoup de
bug reports (visiblement d'utilisateurs qui ont préféré
oublier cet aspect de la chose).


Évidemment, si on s'en sert sans lire la documentation... :-)

En ce qui concerne des bug reports -- une fois au début de ma
carrière, j'ai travaillé dans le support technique. Jamais plus.
Il y avait effectivement des utilisateurs qui appelaient pour
n'importe quoi. (Mon programme ne marche pas -- il doit y avoir
un bug dans le compilateur.)

Mais tu as eu raison de le signaler. Même si nous savons
qu'Arnaud n'est pas ce genre d'utilisateur.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

1 2 3 4 5