Enregistrer la trace de la stack java lorsqu’un appel système spécifique est effectué?

Le contexte : Je travaille sur un serveur réseau basé sur Java qui fuit accidentellement des tuyaux. Tous les quelques jours, il atteint ses 40 000 descripteurs de fichiers et masortingces. L’utilisation de lsof sur le serveur avant la mort montre qu’il est obstrué par des tuyaux. Les tuyaux se connectent à eux-mêmes, plutôt qu’à un autre processus.

Aucune partie de la base de code ne crée ou utilise des tuyaux – que nous pouvons voir.

Certaines versions plus anciennes de la JVM ont créé et diffusé un canal lors de la création d’un socket. Toutefois, cela se produit sur java 1.7.0_75 qui, selon moi, ne souffre pas de ce bogue.

Ma question est la suivante : à l’aide de la chaîne d’outils Linux moderne (comme perf), il est possible de capturer le processus lorsqu’il appelle l’appel système pipe(2) , ce qui est, à mon avis, le seul moyen de créer des tubes. De plus, est-il possible de récupérer une trace de stack Java à partir de cela?

Compte tenu de ces informations, il devrait être possible de répondre à la question “Qui crée des pipes et pourquoi?”

Sur java 1.7.0_75 (ou pré-java 8 update 60), vous ne pouvez obtenir que des informations limitées de perf sur la stack d’appels d’un événement car la stack sera tronquée (voir ci-dessous).

Vous pouvez obtenir des événements de sharepoint trace à l’échelle du système sur les appels sys à pipe et fermer avec la commande perf suivante, ou similaire.

 perf record -e 'syscalls:sys_enter_pipe*' -e 'syscalls:sys_enter_close' -ag -- sleep 10 

Pour obtenir la stack complète:

  1. Testez sur un système de test avant de lancer la production.
  2. Installez Java 8 Update 60 ou version ultérieure
  3. Exécutez Java avec -XX: + PreserveFramePointer pour éviter les stacks tronquées
  4. En option (comme c’est le cas s’il ne s’agit pas d’un code prêt pour la production), installez et exécutez perf-map-agent à partir de Github pour résoudre les symboles Java JIT à utiliser par perf
  5. Exécutez le “enregistrement de perf” ci-dessus ou similaire lorsque le problème se produit
  6. Exécuter “script de perf” pour générer les événements de sharepoint trace et les traces de stack associées

Une stack tronquée sera une stack qui n’a pas de fonction de démarrage de thread au bas, par exemple:

 java 19575 [018] 10600910.346655: syscalls:sys_enter_pipe: fildes: 0x7f353b9f7f80 7f3809cff0b7 __pipe (/usr/lib64/libc-2.17.so) 7f37f59aecb9 [unknown] (/tmp/perf-19375.map) 7f37f5e83150 [unknown] (/tmp/perf-19375.map) edb4639ef8034082 [unknown] ([unknown]) 

Une stack complète peut ressembler davantage à:

 java 21553 [009] 10601254.522385: syscalls:sys_enter_pipe: fildes: 0x7f545322f340 7f54527180b7 __pipe (/usr/lib64/libc-2.17.so) 7f543d007760 [unknown] (/tmp/perf-21552.map) 7f543d0007a7 [unknown] (/tmp/perf-21552.map) 7f5451ce1be6 JavaCalls::call_helper (/usr/java/jdk1.8.0_60/jre/lib/amd64/server/libjvm.so) 7f5451fe7b27 Reflection::invoke (/usr/java/jdk1.8.0_60/jre/lib/amd64/server/libjvm.so) 7f5451feb237 Reflection::invoke_method (/usr/java/jdk1.8.0_60/jre/lib/amd64/server/libjvm.so) 7f5451d705fb JVM_InvokeMethod (/usr/java/jdk1.8.0_60/jre/lib/amd64/server/libjvm.so) 7f543da669ed [unknown] (/tmp/perf-21552.map) 7f543d0007a7 [unknown] (/tmp/perf-21552.map) 7f5451ce1be6 JavaCalls::call_helper (/usr/java/jdk1.8.0_60/jre/lib/amd64/server/libjvm.so) 7f5451d23182 jni_invoke_static (/usr/java/jdk1.8.0_60/jre/lib/amd64/server/libjvm.so) 7f5451d3fb8a jni_CallStaticVoidMethod (/usr/java/jdk1.8.0_60/jre/lib/amd64/server/libjvm.so) 7f5452bfcbcc JavaMain (/usr/java/jdk1.8.0_60/jre/lib/amd64/jli/libjli.so) 7f5452e12df5 start_thread (/usr/lib64/libpthread-2.17.so) 

Exécutez avec perf-map-agent pour permettre à perf de résoudre les fonctions JITted [unknown] en méthodes Java.

Il existe divers autres guides sur la façon de procéder, y compris le travail de Brendan Gregg http://techblog.netflix.com/2015/07/java-in-flames.html