Initstart init perd de la mémoire, comment le déboguez-vous?

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”.


Mettre à jour

Juste pour être clair sur la façon dont cela est utile.

  • Ajoutez un fichier personnalisé à la compilation de Upstart.

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:

  • La fonctionnalité --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 .
  • Utilisez les fonctions de retour en arrière de gcc.
  • Toutes ces modifications n’affectent que l’exécutable Upstart recompilé.
  • Vous pouvez à la place vider les journaux dans une firebase database sqlite, ce qui peut faciliter la recherche de discordances entre mallocs et libérés.
  • vous pouvez vous connecter au format d’une instruction d’insertion SQL, puis les insérer simplement dans une firebase database post-mortem pour une parsing plus approfondie.

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:

  • définir la variable d’environnement HEAPCHECK sur l’un des {normal | ssortingct | draconien}.
  • définissez 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.]

  • John