J’ai une fuite de mémoire dans le processus d’initialisation Upstart (pid 1), quelles options j’ai pour le déboguer?
EDIT: Suggérez-moi de vrais outils pour cela, mettre manuellement des printfs ou calculer les allocations de mémoire à la main ne va pas les couper. De plus, le dumping du kernel init et le contournement de ce problème ne sont pas vraiment une option.
UPD1: valgrind ne fonctionne pas. Remplacer / sbin / init sur la ligne de commande du kernel avec valgrind + init magic ne semble pas être une option car il essaie d’accéder à / proc pour self pour les smaps, mais ceux-ci ne sont pas disponibles avant l’exécution de init.
UPD2: dmalloc ne fonctionne pas non plus (ne comstack pas sur ARM).
Une solution de pauvre homme consisterait simplement à consigner chaque appel malloc
et free
, puis à parcourir les journaux et à rechercher un motif.
ld
fournit une fonctionnalité étonnante qui pourrait aider ici.
--wrap=symbol
Utilisez une fonction wrapper pour le symbole. Toute référence non définie à un symbole sera résolue en “__wrap_symbol”. Toute référence non définie à “__real_symbol” sera résolue en symbole.
Cela peut être utilisé pour fournir un wrapper pour une fonction système. La fonction wrapper devrait s’appeler “__wrap_symbol”. S’il souhaite appeler la fonction système, il doit appeler “__real_symbol”.
Voici un exemple sortingvial:
void * __wrap_malloc (size_t c) { printf ("malloc called with %zu\n", c); return __real_malloc (c); }
Si vous liez un autre code avec ce fichier en utilisant –wrap malloc, alors tous les appels à “malloc” appellent la fonction “__wrap_malloc” à la place. L’appel à “__real_malloc” dans “__wrap_malloc” appellera la vraie fonction “malloc”.
Vous souhaiterez peut-être également fournir une fonction “__real_malloc”, afin que les liens sans l’option –wrap réussissent. Si vous faites cela, vous ne devriez pas mettre la définition de “__real_malloc” dans le même fichier que “__wrap_malloc”; Si vous le faites, l’assembleur peut résoudre l’appel avant que l’éditeur de liens n’ait la possibilité de l’envoyer à “malloc”.
Juste pour être clair sur la façon dont cela est utile.
Comme ça:
void*__wrap_malloc( size_t c ) { void *malloced = __real_malloc(c); /* log malloced with its associated backtrace*/ /* something like: : , , .. */ return malloced } void __wrap_free( void* addr ) { /* log addr with its associated backtrace*/ /* something like: : , , .. */ __real_free(addr); }
Recomstackz les nouveaux symboles de débogage ( -g
) pour obtenir de jolis retours. Vous pouvez toujours optimiser ( -O2/-O3
) le code si vous le souhaitez.
Link Upstart avec le LD_FLAGS
supplémentaire LD_FLAGS
--wrap=malloc
, --wrap=free
.
Maintenant, partout où Upstart appelle malloc
le symbole sera résolu comme par magie dans votre nouveau symbole __wrap_malloc
. Magnifiquement, tout cela est transparent pour le code compilé tel qu’il se passe au moment du lien.
C’est comme caler ou instrumenter avec aucun des dégâts.
Exécutez le Upstart recompilé comme d’habitude jusqu’à ce que vous soyez sûr que la fuite s’est produite.
Parcourez les journaux pour malloced
incompatibilité entre malloced
et addr
.
Quelques notes:
--wrap=symbol
ne fonctionne pas avec les noms de fonctions qui sont en fait des macros. Alors #define malloc nih_malloc
attention à #define malloc nih_malloc
. C’est ce que libnih devrait utiliser à la place de --wrap=nih_malloc
et __wrap_nih_malloc
. Vous pouvez également utiliser init sans modifier, mais créer un wrapper qui définit la variable d’environnement MALLOC_CHECK sur 1 ou plus . Cela vous permettra de voir des diagnostics d’allocation de mémoire.
Une variante consiste à modifier légèrement le code source d’initialisation pour définir la variable d’environnement elle-même avant qu’elle ne commence à utiliser malloc.
Vous pouvez également, comme AmineK l’a suggéré, append du code de débogage au code source d’initialisation lui-même.
Vous pouvez instrumenter votre allocation de mémoire vous-même en accrochant des appels malloc / free et en comptant le nombre d’octets que vous allouez et que vous libérez à chaque fois.
Vous pouvez essayer de lier votre version de démarrage à TCMalloc de Google . Il est livré avec un vérificateur de tas intégré.
Le vérificateur de tas peut être activé de deux manières:
HEAPCHECK
sur l’un des {normal | ssortingct | draconien}. HEAPCHECK
sur local
et vérifiez le code manuellement avec les objects HeapProfileLeakChecker
. Je ne sais cependant pas comment définir une variable d’environnement pour init.
Que diriez-vous d’exécuter pmap sur le processus et d’examiner quels segments de mémoire sont en croissance. Cela peut vous donner une idée de ce que mange la mémoire. Un peu de script pourrait rendre ce processus presque automatique **.
** Dans une vie antérieure, j’ai écrit un script qui prendrait des instantanés d’un ensemble de processus en cours espacés de quelques secondes. La sortie de ce fichier a été introduite dans un script Perl qui identifiait les segments qui changeaient de taille. Je l’ai utilisé pour localiser plusieurs memory leaks dans certains codes commerciaux. [Je partagerais les scripts, mais ils sont couverts par IP (copyright) d’un employeur précédent.]