 Si on encroie le site web sur lequel il est développé, guppy-pe.srch.net, c'est un environnement de programmation Python composé de deux parties, une librairie qui s'appelle IP, et un langage de spécification qui s'appelle GSL. Et la dernière release date 2009, et il n'y a pas de release officielle qui fonctionne avec Python 2.7. Là, je suis sûr avec ça, je vous ai complètement convaincu de l'utiliser. Pas vrai. Mais en réalité, en fait, guppy, contrairement à ce que dit la documentation qui en plus est bien indigeste et sans image, guppy c'est l'outil ultime d'analyse d'espace-mémoire de votre process Python. C'est un truc qui vous permet de faire des snapshots de votre espace-mémoire, d'analyser quelles sont les objets qu'il y a dedans, lesquels prennent le plus de mémoire, de comparer les snapshots, donc de chercher des flux de mémoire. Et en plus de tout ça, il est capable de faire ça pendant que votre programme est en train de tourner sans interro pour son exécution. Là, déjà, ça doit être un peu mieux quand même, non ? Donc, je vais faire un petit rappel sur la gestion de mémoire de Python parce qu'elle n'est pas toujours ultra claire pour tout le monde, donc Python a deux mécanismes dans sa gestion de mémoire automatique. Tout d'abord, le reference counting, parmi lequel en fait, dès qu'on perd la dernière référence à un objet, il est immédiatement détruit, pas d'exception. Ça, ça marche très bien jusqu'au moment où on commence à avoir des références circulaires entre les objets, donc l'objet A fait référence à l'objet B, qui fait référence à l'objet C, qui fait référence à l'objet A, ça, ça veut dire que leurs références ne tomberont jamais à zéro. Donc, pour pallier à ça, dans Python, il y a un garbage collector qui est lancé à un intervalle régulier et qui ne sert qu'à une seule chose, c'est briser les cycles de références circulaires, ce qui fait ensuite que les objets sont détruits puisque leurs références ne tombent à zéro. Petites informations supplémentaires sur le garbage collector, ils ne fonctionnent pas avec les objets qui implémentent une méthode underscore underscore del, c'est-à-dire un finaliseur. Donc si vous avez des cycles de référence avec des objets dont les classes implémentent cette méthode, vous allez avoir des fuites puisque ces objets ne seront jamais effacés. Et le garbage collector fonctionne avec trois générations. Lorsqu'un objet est créé, il est ajouté à la génération 0. S'il survit à une collecte, il est passé dans la génération 1 et s'il survit à une collecte de la génération 1, il est passé dans la génération 2. La génération 0 est sweepée par le garbage collector dès qu'on a 700 objets qui sont alloués sans qu'il y ait d'autres objets qui aient été désalloués. Et ensuite, les autres générations, la génération 1 est collectée toutes les 10 collectes de la génération 0 et la génération 2, toutes les 10 collectes de la génération 1. Tout ça est bien sûr configurable avec l'aide du module GC. Vous avez GC.getThreshold qui vous renvoie ça et vous avez GC.setThreshold qui fait l'inverse. Et vous pouvez avoir GC.getCount pour savoir où est-ce que vous en êtes dans ce process. Donc il vous renvoie par exemple 201.0, ce qui signifie que dans 500 objets créés, il y aura une collecte qui sera faite et dans 9 collectes de la génération 0, il y aura une collecte de la génération 1. Ce n'est pas un GC qui s'exécute dans un spread séparé ou quoi que ce soit. Pendant la collection, le programme est entièrement arrêté. Pour plus d'informations, référez-vous à cette URL, la documentation va vraiment très bien fichu. Donc du coup, les fuites mémoires, en règle générale, on va trouver 3 grosses classes. On a les références cycliques avec des objets qui implémentent d'elles. On a les références qu'on a oubliées dans le programme parce qu'on s'est pas rendu compte qu'on gardait des références vers des objets et puis finalement, ils ne sont pas effacés. Et il y a les fuites dans les modules d'extension en C avec lesquels malheureusement, Guppy ne peut pas vous aider. Donc vous allez gagner le droit de vous reposer sur les outils traditionnels de détection de fuites mémoires sans Guppy. Je suis désolé. Pour les autres cas, vous pouvez installer Guppy. Alors si jamais vous utilisez Python version 2.6 ou inférieur, vous faites juste pip install Guppy, ça marchera tout seul. Si vous utilisez la 2.7, il faut que vous alliez installer la dernière version depuis subversion, donc pip install, cette longue URL-là. Ça nécessite évidemment d'avoir un compilateur installé sur votre machine, donc sous Windows, c'est très pénible. Sous Mac OS et sous Linux, par contre, ça marche très bien. Il n'y a pas d'ennui. Ça marche avec Clang, ça marche avec GCC et vous avez ensuite juste à intégrer Guppy dans votre programme qui fuit. Donc au début de votre programme, vous rajoutez juste cette ligne qui va importer le remote monitor. Et ensuite, dans un autre terminal, vous exécutez ça et puis vous lancez votre programme. Le remote monitor, c'est un thread de monitoring séparé qui est exécuté dans votre programme et auquel la console interactive de Guppy permet de se connecter pour ensuite aller réaliser plein d'opérations amusantes du snapshotting et tout ça. Ce qui signifie que le remote monitor, vous pouvez l'avoir dans votre gros serveur web qui tâche, qui fait tourner des applications pyramides, you-wis-gui and jinx, tout ça, ça marche très bien. Par contre, c'est très déconseillé de médecin en production, évidemment, puisque vous ouvrez un socket sur votre machine qui permet d'exécuter des commandes arbitraires dans votre espace mémoire piton sans rentrer de mot de passe, sans authentification. Donc, on ne peut pas s'utiliser en prod. Voilà, ou alors faites attention. Donc, je vais vous montrer comment est-ce que ça fonctionne en pratique. Où est-ce qu'il a mis ma fenêtre, Adrien ? Voilà. Les deux premiers chels sont à moi. Maintenant, j'en ai trois, dont deux qui sont à moi. Donc, hop, on va exécuter sublime texte, sublime texte, gérer réaliser un sublime programme. J'ai décidé de réimplémenter Pokémon en piton. Et si, pour les attraper, tous vous rendez vous compte. Donc, j'ai ces deux super classes, une représentation d'un Pokémon et la représentation du Pokédex, qui est la super encyclopédie des Pokémons. Et comme pour le moment, je suis un gros flemmard et que je n'ai pas écrit grand chose dans mon programme, j'ai juste un début de boucle qui fait voyager des gens dans l'univers de Pokémon et qui cherchent des informations et qui les rendent dans le Pokédex. C'est formidable. Et puis, si vous lisez le programme, vous vous rendrez compte qu'il y a eu une fuite mémoire super obvious. Et oui, vous vous rendez compte si on lance pitonpokédex.pi.pok, ça attrape des Pokémon. C'est formidable. Vous avez vu la science quand même. Piton mesdames et messieurs. Bon, et du coup, si je lance un top derrière, je vais me rendre compte que mon occupation mémoire, elle augmente petit à petit et c'est nul. Et je ne sais pas où est-ce qu'elle est, ma fuite mémoire parce que je fais semblant de ne pas savoir où je les mise. Donc là, j'ai mon programme. Je décommente ma ligne magique importe guppi.fi.rm. Je sauvegarde. Et dans mon deuxième terminal, je vais lancer ma commande magique dont je oublie que j'oublie tout le temps, puisque le programme, c'est que l'environnement de la programmation s'appelle guppi, mais la librairie intéressante s'appelle ipi, qui est disponible dans le module hpy qui est évidemment hop. Donc là, j'ai ma console de monitoring qui est ouverte. Donc je vais lancer mon programme avec le remote monitor comme ça. Et là, je vois connexion 1 ouverte, puisque avec un seul monitor, je peux m'amuser à monitorer plusieurs programmes à la suite. Donc je fais sc1 pour connecter, pour me connecter à ça. Et ensuite, je tape int qui m'envoie dans une console interactive. Et ça, c'est une console interactive piton qui est dans l'espace mémoire de mon process, pardon. Et donc, je peux faire des snapshots d'espace mémoire avec hp.ip, comme ça. Et donc là, je vois qu'est-ce que j'ai qui utilise beaucoup de mémoire dans mon espace mémoire. Donc c'est classé par taille. Donc j'ai par exemple 11% de mon espace mémoire qui est pris par 4228 dictionnaires. Pourquoi des dictionnaires n'est pas des objets ? Parce que dans son système objet, piton stocke les attributs de chaque objet dans un dictionnaire. C'est pour ça qu'on voit dictofmain.pokémon, parce que ça veut dire que c'est le dictionnaire de stockage des instances de pokémon. Si c'était juste des dictionnaires créés à la Brutale, il y aurait marqué dict no owner. Et vous constaterez d'ailleurs qu'il y a 4228 objets pokémon qui sont juste derrière. Mais là où ça devient fort, c'est que je peux sauvegarder ces snapshots. Comme ça. On va attendre quelques secondes comme ça. Bon, ça capture 150 pokémon par seconde. Donc ça devrait être pas mal. J'ai un pokédex qui est très efficace. Hop, deuxième snapshot. Et maintenant je fais h2-h1. Et ça, ça m'indique les objets qui ont été créés entre les deux moments où j'ai fait mes snapshots. Je peux faire h1-h2 pour avoir l'inverse, mais il n'y a rien puisque mon programme ne détruit essentiellement pas d'objets. Et alors là où ça va devenir réellement puissant, c'est que si je veux, je fais l'égal liste de h2-h1. Ils sont voulu accrocher là-dessus. Zéro puisque la première ligne, c'est l'édictionnaire de pokémon qui m'intéresse. Point node. Et avec ça, j'ai accès aux objets eux-mêmes. Ça, c'est tous les dictionnaires qui ont été créés par mon programme. Donc déjà, ça peut commencer à donner une bonne idée de qu'est-ce qui se passe, qu'est-ce qui fuit, je peux voir exactement quels sont les objets. Mais si jamais, ce n'est pas assez. Je prends mon premier dictionnaire qui est un eye-visor formidable, un pokémon numéro 2 de type herbe et poison. Et avec ça, attention, ça devient fort parce que je peux importer le module GC. Et là, j'ai la méthode qui tue GC.getreferrers. Que fait Getreferrers ? Il va me renvoyer une liste de tous les objets qui font référence à ce truc-là. Et si je fais l'N2R1, je vois qu'il y a neuf objets. Alors, qu'est-ce qu'il y a comme objet ? On va regarder. Alors ça, c'est la mémoire de mon interprateur interactif puisque j'ai affiché mon objet tout à l'heure. Donc, c'est tous les trucs de mes bulletins. Ça ne m'intéresse pas. On va regarder le suivant. Le suivant, il y a une grosse liste que peut-ce donc être. C'est une grosse liste de dico. Et oui, si je ne dis pas de bêtises, c'est le snapshot que j'ai fait. C'est la liste que j'ai créée justement sur laquelle je viens d'appeler Getreferrers. Donc, ce n'est toujours pas très intéressant. Par contre, si j'arrive là, je fais tiens, c'est un pokémon. D'ailleurs, je peux appeler des méthodes dessus. Ça marche. Oui, en plus, j'ai quand même une implémentation hyper poussée du langage pokémon. S'il y a des experts en NLP, je suis prêt à vous donner des cours. Il n'y a pas de problème. Mais donc ça, c'est le pokémon lui-même. Donc, c'est le pokémon dans lequel les attributs d'instances sont stockés dans ce dico. Et lui, je vais le stocker là-dessus aussi et je peux encore faire un petit Getreferrers dessus. Donc, R2-0, toujours pareil parce que je l'ai affiché tout à l'heure. Donc, il a valu le underscore il y a quelques instants. Et là, le 1, c'est une immense liste de pokémon. Je me dis, ah, mais où est-ce que j'ai une liste de pokémon dans mon programme? Dans le Pokédex, j'ai une belle liste self.pokémons. Oui, je sais que normalement, pokémon, ça ne prend pas de S au pluriel, mais je trouve ça hyper confusant quand on écrit un programme. Et là, je me rends compte que ma méthode catch, elle va ouvrir le fichier pokémon.exe et elle se contente de faire un append sans jamais clirer la liste. Là, je fais, ah bon sang, il y a une fuite mémoire, je sais où elle est. Donc, à ce stade-là, je peux arrêter mon programme qui en était à pratiquement 50 000 pokémons attrapés pendant ma sublime démo. Derrière lui, il détecte que le socket s'est fermé. Je liste les connexions, je n'ai plus rien. Et je peux arrêter mon truc en vous en contrôlter. Ça ne marche jamais comme il faut. Et voilà. Donc, évidemment, dans le monde réel, c'est un petit peu plus difficile. Il faut utiliser un petit peu plus d'intuition, mais en règle générale, on s'en sort pas mal pour de la recherche de fuite mémoire, en faisant des snapshots de l'espace mémoire, en faisant des différences et en ayant regardé qu'est-ce qui prend de la place et en suivant des cycles de référence. Je n'ai malheureusement pas de méthode universelle à vous recommander pour trouver de manière garantie une fuite. Mais utiliser Gupi, c'est formidable. Ça m'a vraiment beaucoup, beaucoup aidé à la fois chez Ludia, où je fais des jeux Facebook avec Adrien et Eric, parce que Eric, il ne le faisait pas le dire, mais il travaille avec nous. Oui, je balance mes collègues. Et à mon ancien travail, il y a Wipelé en France, où on avait des programmes pitons qui tournaient sur des set-up box posés au-dessus de télévision et qui devaient donc tourner pendant de préférence au moins une semaine sans avoir à être redémarré. Et pas énormément de mémoire et pas de swap. Donc quand s'affuyer, c'était mal. Est-ce que vous avez des questions ?