Comment puis-je déboguer une fuite de mémoire possible dans un script Perl CGI?

J’ai une page Perl CGI héritée qui s’exécute sur Apache et qui traite une grande quantité de données Excel, en ajoutant à la firebase database si nécessaire. Il est traité en groupes de données et chaque groupe de données est envoyé à la firebase database.

Après chaque appel à la firebase database, la mémoire disponible de mon système diminue considérablement jusqu’au point où il ne rest plus de mémoire. Une fois que j’obtiens finalement l’erreur “Fin prématurée des en-têtes de script” et que le code HTTP 500 est renvoyé au client, la mémoire est libérée dans le système.

En parcourant le code (compliqué), je ne peux pas trouver où la fuite de mémoire pourrait se produire. Y a-t-il un truc ou un outil que je peux utiliser pour déterminer où va la mémoire?

La réponse courte est que ça craint d’être toi. Il n’y a pas de programme agréable et prêt à l’emploi que vous pouvez utiliser pour obtenir une réponse. Je suis désolé que je ne puisse pas être plus utile, mais sans voir aucun code, etc., il n’y a vraiment pas de meilleur conseil que quiconque puisse donner.

Je ne peux pas parler de votre situation particulière, mais voici certaines choses que j’ai faites dans le passé. Il est important de trouver le domaine général qui pose problème. Ce n’est pas très différent des autres techniques de débogage. D’habitude, je trouve qu’il n’y a pas de solution élégante à ces choses. Il vous suffit de retrousser vos manches et de tenir vos arm au coude dans la boue, quelle que soit la mauvaise odeur.

Tout d’abord, exécutez le programme en dehors du serveur Web. Si vous voyez toujours le problème à partir de la ligne de commande, soyez heureux: vous avez simplement (principalement) exclu un problème avec le serveur Web. Cela peut prendre un peu de travail pour créer un script d’encapsulation pour configurer l’environnement Web, mais cela finit par être beaucoup plus facile puisque vous n’avez pas besoin de relancer le redémarrage du serveur, etc. pour réinitialiser l’environnement.

Si vous ne pouvez pas répliquer le problème en dehors du serveur, vous pouvez toujours faire ce que je recommande ensuite, il est juste plus ennuyeux. S’il s’agit d’un problème de serveur Web et non d’un problème de la ligne de commande, la tâche devient la découverte de la différence entre les deux. J’ai rencontré des situations comme ça.

Si ce n’est pas un problème avec le serveur Web, commencez à diviser le script comme vous le feriez pour tout problème de débogage. Si la journalisation est activée, allumez-la et regardez le programme s’exécuter tout en enregistrant sa véritable utilisation de la mémoire. Quand ça explose? Il semble que vous l’avez réduit à quelques appels de firebase database. Si vous parvenez à l’exécuter à partir de la ligne de commande ou du débogueur, je trouverai une paire de points d’arrêt appropriés avant et après l’augmentation de la mémoire et les rapprocherez progressivement. Vous pouvez utiliser des modules tels que Devel :: Size pour examiner les tailles de mémoire des structures de données que vous suspectez.

De là, on ne fait que réduire les suspects. Une fois que vous avez trouvé le suspect, voyez si vous pouvez le reproduire dans un court exemple de script. Vous voulez éliminer autant de possibilités de facteurs consortingbutifs que possible.

Une fois que vous pensez avoir trouvé le code incriminé, vous pouvez peut-être poser une autre question qui montre le code si vous ne comprenez toujours pas ce qui se passe.

Si vous voulez être vraiment chic, vous pouvez écrire votre propre débogueur Perl. Ce n’est pas si dur. Vous avez la possibilité d’exécuter des sous-routines dans l’espace de noms de la base de DB au début ou à la fin des instructions. Vous avez vos profils de mémoire de liste de codes de débogage pour les choses que vous suspectez et recherchez les sauts dans les tailles de mémoire. Je ne voudrais pas essayer ceci à moins que tout le rest échoue.

Si le problème est dans le code Perl, vous pouvez avoir une référence qui pointe vers lui-même ou un nœud parent.

Voici un exemple rapide de code présentant ce comportement.

 { my @a; @a = [\@a]; } 

Il se présente généralement sous la forme d’un object, qui référence un object parent.

 { package parent; sub new{ bless { 'name' => $_[1] }, $_[0] } sub add_child{ my($self,$child_name) = @_; my $child = child->new($child_name,$self); $self->{$child_name} = $child; # saves a reference to the child return $child; } } { package child; sub new{ my($class,$name,$parent) = @_; my $self = bless { 'name' => $name, 'parent' => $parent # saves a reference to the parent }, $class; return $self; } } { my $parent = parent->new('Dad'); my $child = parent->add_child('Son'); # At this point both of these are true # $parent->{Son}{parent} == $parent # $child->{parent}{Son} == $child # Both of the objects **would** be destroyed upon leaving # the current scope, except that the object is self-referential } # Both objects still exist here, but there is no way to access either of them. 

La meilleure façon de résoudre ce problème est d’utiliser Scalar :: Util :: fragen .

 use Scalar::Util qw'weaken'; { package child; sub new{ my($class,$name,$parent) = @_; my $self = bless { 'name' => $name, 'parent' => $parent }, $class; weaken ${$self->{parent}}; return $self; } } 

Je recommanderais de laisser la référence à l’object parent, de l’enfant, si possible.

Comment écrivez-vous à la firebase database? Si vous utilisez l’un des packages DBI ou des wrappers personnalisés, veillez à vider tous les objects mis en cache ou les variables mises en cache. Ces types de problèmes de saturation de la mémoire sont relativement courants et sont généralement représentatifs d’un cache d’objects partagés quelque part qui persiste.

Choses à essayer:

  • Effacer les variables d’object quand elles sont terminées
  • Faites évoluer votre connexion à la firebase database (ceci est extrême, mais selon la manière dont vous vous connectez, cela peut résoudre le problème).
  • Ne chargez qu’un groupe de données à la fois et vider les variables ou les objects d’usine qui le chargent entre les groupes.

J’espère que ça aide certains.