Comment localiser le processus Apache

J’ai parfois des processus Apache qui vont à 100% de l’utilisation du processeur et ne quittent jamais. Lorsque cela se produit 8 fois (8 processeurs), le serveur devient inutilisable. Selon l’état du serveur, le processus “hung” est un programme Perl personnalisé assez complexe, mais lorsque j’imprime des avertissements dans le journal des erreurs dans Perl, cela montre que le processus est toujours terminé et qu’il revient, mais apparemment après le retour. ça va dans une boucle ou quelque chose. Lorsque je lance strace sur le processus, il affiche juste des tonnes de lignes mmap2 / munmap, par exemple:

mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4329472, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d7000 mremap(0xb3d7c000, 4329472, 4333568, MREMAP_MAYMOVE) = 0xb3d7c000 munmap(0xb42d7000, 4329472) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 munmap(0xb42d6000, 4333568) = 0 mmap2(NULL, 4333568, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb42d6000 

Je n’ai aucune idée de ce que ça veut dire. Il le fait indéfiniment. Y a-t-il un moyen d’obtenir une vue d’ensemble de ce qu’il fait? Quelqu’un at-il vu quelque chose de semblable à cela?

De plus, bien que ce soit généralement une circonstance assez aléatoire, si je lance un rflush () avant de quitter le programme Perl, cela se produit presque toujours.

J’utilise mod_perl / 2.0.7, perl / 5.12.4, apache / 2.2.24. Cela se passait également dans quelques versions mineures; J’ai mis à jour et cela n’a rien amélioré. J’utilise aussi DBI, DBD: ODBC.

Ma meilleure supposition est une sorte de condition de contention / race, mais tracer le code avec la sortie “warn” n’indique pas un tel problème dans le Perl lui-même. Le code Perl utilise également des evals avec des signaux d’alarme de timeout qui ne se déclenchent jamais, il ne semble donc pas possible que le code Perl pose problème.

Toutes les idées seraient très appréciées.

Je pense que je l’ai résolu. Je ne sais toujours pas pourquoi rflush () aurait aggravé la situation, et je n’ai pas non plus trouvé de vue de haut niveau sur les résultats de strace, mais c’est la raison pour laquelle cela se faisait:

À un moment donné, j’ai ajouté le code suivant à la sous-routine “error”, appelée à chaque fois que le programme détecte une erreur interne:

 my $caller = ""; my $x = 1; while (caller($x) && $x < 10) { my $subroutine = (caller($x))[3]; $subroutine =~ s/^.*::([^:]+?)$/$1/gis; my $line = (caller($x))[2]; $caller = qq~->$subroutine(line $line)~.$caller if $subroutine; } $caller = "main".$caller; 

Tout ce que fait est d’imprimer le sous-programme dans lequel l’erreur s’est produite. Ainsi, par exemple, si le sous-programme “sub1” est appelé dans la partie principale du programme, il est appelé “sub2” à partir de la ligne 2345 une erreur se produit dans “sub2” sur la ligne 3456, alors le sous-programme “error” notera que l’erreur s’est produite dans “main (ligne 1234) -> sub1 (ligne 2345) -> sub2 (ligne 3456)”. Ceci est important pour le débogage, évidemment. Malheureusement, cela ne facilite pas le débogage du sous-programme “erreur” lui-même!

Dans la boucle “while”, il vérifie que $ x <10 (il ne doit pas y avoir 10 niveaux d’appels de sous-programmes). Ceci est destiné à empêcher l'itération à la volée. Malheureusement, la ligne pour incrémenter la variable $ x est manquante. Cela signifie qu'il va continuer à vérifier indéfiniment $ x <10 car $ x toujours = 1. La partie la plus délicate à ce propos est que la sous-routine "erreur" est appelée depuis un gestionnaire de signaux, qui semble provenir du programme principal. elle-même, alors le sous-programme "error" peut s'exécuter dans cette boucle infinie tant que le corps du programme semble se terminer. Cela rendait impossible d’imprimer ou d’avertir une vue linéaire de ce qui se passait, ce qui entraînait une grande confusion. Cela a également fait en sorte que cette boucle infinie soit exécutée en dehors de mon principal "eval" qui a un délai d'attente, de sorte que le délai d'attente n'a jamais arrêté la boucle comme prévu. Le programme se terminait à la toute dernière ligne et sortie, mais le processus qui le desservait continuait, absorbant 100% du processeur à cause de cette boucle infinie dans le gestionnaire de signal.

La solution ne comporte que quatre caractères: “$ x ++”. L’ajout de celle-ci dans la boucle while empêche l’itération infinie et permet en fait aux informations de débogage du gestionnaire de signaux de s’imprimer.