2008-11-15 20 views
4

Avec Linux/GCC/C++, j'aimerais enregistrer quelque chose dans stderr chaque fois que malloc/free/new/delete sont appelés. J'essaie de comprendre les allocations de mémoire d'une bibliothèque, et je voudrais donc générer cette sortie pendant que j'exécute des tests unitaires. J'utilise valgrind pour la détection de fuites mem, mais je ne trouve pas d'option pour en faire uniquement des allocations de journaux.Sortir sur stderr dès que malloc/free est appelé

Des idées? Je suis à la recherche de la solution la plus simple possible. Recompiler la bibliothèque n'est pas une option.

Répondre

3

malloc_hook(3) vous permet d'interposer globalement votre propre fonction malloc. (Il y a __realloc_hook__free_hook etc. aussi bien, je viens de les ai laissés pour la simplicité.)

#include <stdio.h> 
#include <malloc.h> 

static void *(*old_malloc_hook)(size_t, const void *); 

static void *new_malloc_hook(size_t size, const void *caller) { 
    void *mem; 

    __malloc_hook = old_malloc_hook; 
    mem = malloc(size); 
    fprintf(stderr, "%p: malloc(%zu) = %p\n", caller, size, mem); 
    __malloc_hook = new_malloc_hook; 

    return mem; 
} 

static void init_my_hooks(void) { 
    old_malloc_hook = __malloc_hook; 
    __malloc_hook = new_malloc_hook; 
} 

void (*__malloc_initialize_hook)(void) = init_my_hooks; 
 
$ cat >mem.c <<'EOF' 
(the code above) 
EOF 
$ cc -fPIC -shared -o mem.so mem.c 
$ LD_PRELOAD=./mem.so ls 
0x7ffc14931adc: malloc(5) = 0xb40010 
0x7ffc1492c6b0: malloc(120) = 0xb40030 
0x7ffc1497f61a: malloc(12) = 0xb40010 
0x7ffc1492be38: malloc(776) = 0xb400b0 
… 

printf pourrait appeler malloc, ce qui est la raison pour laquelle nous défaisons le crochet temporairement. Faites attention à cela si vous connectez malloc de quelque manière que ce soit.

5

This article (faites défiler vers le bas) fournit une description très claire et concise de la façon de passer outre les new et delete opérateurs mondiaux en C++ (notez qu'il ne fournit pas un exemple pour new[], mais il est similaire dans le concept) .

En ce qui concerne la substitution de malloc et de gratuit, puisque vous travaillez sous Linux et avec GCC, la méthode la plus simple consiste à utiliser malloc_hook et free_hook. Here est une très bonne description de la façon dont ces fonctions fonctionnent.

+0

Ces deux solutions semblent nécessiter de recompiler la bibliothèque en question. –

1

Je ne l'ai pas testé moi-même, mais je suis sûr que ceux-ci travailleraient:

  • Puisque vous ne voulez pas de recompiler la bibliothèque, ce qui donne un sens de sortie (contre seulement « nouveau appelé 23 octets ") peut nécessiter l'obtention d'une trace de pile. Je me souviens avoir utilisé des fonctions pour naviguer dans la pile, mais je ne peux pas les trouver maintenant. Peut-être qu'un appel à system() et pstack (1) peut faire l'affaire.

  • Vous pouvez redéfinir l'opérateur new et delete, et placer cette nouvelle définition avant la bibliothèque std C++. Cela peut ne pas capturer les appels provenant des conteneurs et des composants standard que la bibliothèque en question utilise. Cela nécessiterait un réétiquetage.

  • L'utilisation peut utiliser LD_PRELOAD pour changer l'opérateur et supprimer dynamiquement. Cela ne nécessiterait pas de re-lien si votre application est liée dynamiquement.

Espérons que ces pointeurs aident, je suis désolé, je n'ai pas de recette.

15

Vous pouvez suivre les appels à malloc/gratuit avec ltrace:

#include <stdlib.h> 

int main (void) 
{ 
    void *ptr = malloc(10); 
    free(ptr); 

    return 0; 
} 


$ g++ test.cpp -o test 
$ ltrace -e malloc,free ./test 
malloc(10)          = 0x804a008 
free(0x804a008)         = <void> 
+++ exited (status 0) +++ 

Pour tracer nouvelle/supprimer des appels sans vous recompiler aurez probablement besoin d'utiliser quelque chose comme LD_PRELOAD pour remplacer les appels avec vos propres versions, c'est précisément ce que fait LeakTracer qui pourrait faire ce que vous voulez.