Aller au contenu
AFUP AFUP Day 2025 Baromètre Planète PHP PUFA
 

Bienvenue dans la matrice !

Description

Un processus PHP qui s'emballe sur un environnement sans debuggeur ? Un webservice qui semble fournir une réponse suspecte ? Un processus tout à coup très lent ? Un programme bloqué avec 0% d'utilisation CPU ? Une application avec un comportement inattendu ? Cela vous est déjà certainement arrivé, avec cette question: comment savoir ce qui se passe dans ces cas là, sans toucher au code PHP ? Des outils systèmes (strace, ltrace, lsof, perf-trace, ...) permettent d'intercepter et d'analyser les échanges entre les procesus et le noyau Linux. Ils peuvent s'appliquer aussi bien sur un processus PHP que sur n'importe quel autre processus s'exécutant sur la machine. Bien utilisés, ils permettent de rentrer à l'intérieur du processus et de comprendre ce qu'il est en train de faire. Le contenu des entrées et sorties, les blocages, les temps passés dans les différentes tâches, etc.... Toutes ses informations sont disponibles avec les bons outils. Alors passez en coulisse des processus et venez déchiffrer la matrice avec moi lors de cette présentation !

Conférence donnée lors du PHP Tour Montpellier 2018, ayant eu lieu les 17 et 18 mai 2018.

Informations complémentaires

Vidéo

Le speaker

Benoit JACQUEMONT

Benoit Jacquemont est tombé dans le web en 2000 après avoir une première expérience sur des logiciels d'encaissements. De Java à PHP, de Oracle à MySQL et des applications de gestions aux sites de eCommerce, il roule sa bosse sur les projets, toujours à forte composante OpenSource, au sein du groupe Smile. Il en devient le CTO en 2009 alors que l'entreprise compte près de 450 salariés. En janvier 2013, il co-fonde Akeneo avec Frédéric de Gombert, Nicolas Dupont et Yoav Kutner. L'aventure startup commence alors pour lui, en tant que CTO d'Akeneo.

Commentaires

Ouvre des possibilités très intéressantes !
Arnaud Buathier, le 17/05/2018
Super conf ! Tu as mis en place des démo qui sont un réel plus pour illustrer ta présentation :) On s'identifie facilement aux problématiques techniques
Mathieu Girard, le 17/05/2018
Excellente conference ! J'ai appris plein de choses, ça démystifie pas mal ce qui s'échange entre un processus et le kernel ! Merci beaucoup ! Et bravo ! ?
Mathieu Santostefano, le 17/05/2018
Simple, clair, efficace, utile, drôle... merci.
Grégoire HUBERT, le 17/05/2018
Super fun et intéressant ! Ca change des talks habituels et ca fait du bien ;)
Arnaud Lafon, le 17/05/2018
Belle conf. Les exemples et les démos donnent plus de concret. Merci Benoit.
Sébastien CAUMES, le 17/05/2018
J'ai beaucoup appris, merci !! J'apprécie toujours ta faculté de rendre les choses simples :)
lnc, le 19/05/2018
Très bien présenté, démos très parlants. Limpide. En revanche, ne parle quasiment que de strace alors que la description mentionne lsof, perf-trace, ... on reste sur sa faim de se côté là. Peut-être proposer des confs sur l'utilisation de ces aures outils ?
Steven VAN POECK, le 22/05/2018
Top
Benjamin Lévêque, le 25/05/2018

Transcript

Benoît Jacquemont, je suis cofondateur et directeur technique d'Akeneo qui est un éditeur de solutions de gestion de catalogue open source en PHP, mais je ne veux pas du tout vous parler de ça je ne veux pas faire la pub pour Akeneo moi je vais vous parler de ce qui se passe dans les processus, dans les processus dans ce qu'ils interagissent avec le monde extérieur mais avant ça avant ça je vais vous présenter quelqu'un. Je vais vous présenter Bob ! Alors déjà, Bob ! Bonjour Bob ! Voilà, merci.

Alors Bob, c'est notre petit programme. Alors ce qui est sympa avec ce que je vais vous présenter, c'est que Bob il peut être écrit en PHP, il peut être écrit en Go, il peut être écrit en n'importe quoi ! Et ça fonctionnera tout toujours.

Alors Bob, c'est un petit programme qui devient donc un processus quand on l'exécute. Vous voyez il y a un petit PID qui apparaît maintenant lorsqu'on a lancé et Bob comme beaucoup de processus il fait des trucs.

Par exemple il a un fichier XML, il va aller lire le fichier XML via le file_get_contents() que vous connaissez tous.

Il récupère des données XML, qu'il interprète. Ça peut être par exemple un fichier de configuration pour accéder à la base de données. Je pense que vous avez tous fait ça : XML, YAM, INI ou autre.

Donc il a sa configuration pour accéder à la base de données il va donc, on a une jolie base de données, il va ouvrir une connexion à la base de données, passer ses paramètres d'identification et puis maintenant qu'il a ouvert ses connexions et qu'il est identifié, il va pouvoir faire des "select" et obtenir des réponses.

Et puis à un moment, il va avoir besoin de faire une API REST, de faire appel à une API REST sur un serveur HTTP donc c'est récupère un utilisateur sur l'API REST.

Avec une réponse en JSON.

Voilà donc il vit pleinement sa vie de processus Bob, il est à fond ! Il fait des trucs, il parle avec tout le monde, il fait la fête.

Est-ce que ça ça vous parle est ce que vous avez déjà petites choses qui font des choses comme ça ? Est-ce que vous avez déjà demandé à vos programmes de faire des choses comme ça ? Non jamais ! Bon... ah... zut tout tombe à l'eau ! J'imagine que vous avez déjà fait des trucs comme ça ! Et bien tout ça c'est un mensonge ! Tout ça c'est une illusion. Bob il vit dans une matrice mais Bob ne le sait pas. Et nous en tant que développeur souvent on ne le sait plus, même si on sait que derrière c'est pas exactement comme ça que ça se passe.

Qu'est ce qu'il y a derrière cette illusion ? Derrière le fait que Bob il a l'impression de travailler sur des fichiers, Bob il a l'impression de pouvoir ouvrir des connexions http, de pouvoir dialoguer avec la base de données. En fait c'est faux ! C'est le noyau, qui communique avec tout ça.

Bob il ne voit pas le noyau, il ne sait même pas qu'il existe et peut-être que Bob il est heureux grâce à ça ! Mais nous on sait qu'il existe le noyau. On sait que c'est le noyau qui fait toutes les opérations parce que, déjà, c'est le noyaux qui a tout les drivers système. C'est lui qui est capable d'accéder aux différents éléments du matériel: le disque dur, la carte réseau, c'est lui qui a les drivers du filesystem également. Il accède à ext4 ou des choses comme ça.

Il vérifie également si vous avez les droits d'ouvrir des connexions, d'aller sur des fichiers. Donc vous avez toujours un noyau qui interagit.

Et ça c'est super cool ! C'est super cool pourquoi ? Parce que les échanges qu'il y a entre Bob et le noyau, à l'insu de Bob bien entendu. Ces échanges on va pouvoir aller les sniffer, en pouvoir aller les espionner, regardez ce qui se passe à l'intérieur.

Alors déjà, oui, première chose votre programme il n'a jamais accès au monde réel. Ça c'est clair ! Ça passe toujours à travers le noyau. Et donc les échanges qui sont ici, on va pouvoir les sniffer avec un outil qui s'appelle strace.

Ok. Alors quel intérêt ça va avoir de pouvoir sniffer ces échanges, je vais vous l'expliquer un petit peu après, d'abord je vais vous expliquer comment fonctionne strace. Comment ça marche.

Donc il sniffe les syscalls, donc les appels système.

Comment ça fonctionne, il y a deux façons de les utiliser. La première qui est une façon un peu plus classique, c'est à dire je fais fait strace et je lance mon programme avec strace. Donc tout les échanges systèmes vont être listés de cette façon là, est une autre façon qui est beaucoup plus intéressante, c'est qu'on peut s'attacher un procès qui est déjà en cours d'exécution.

Un procès qu'on n'a pas lancé avec des configurations particulières ou avec un paramètre ou même une extension PHP particulière, non. N'importe quel procès qui est déjà en cours d'exécution qui peut être même sur une prod ,même si c'est des choses qu'on évite de faire sur la prod.

Néanmoins s'il y a déjà quelque chose qui est en train de se passer on peut s'attacher un process et obtenir des informations. Alors on va faire un petit exemple.

Je sais pas si le code super lisible ! Néanmoins on voit un petit Hello World en PHP. Comment je vais faire, je fais un strace de hello world.

Donc, j'ai mon petit hello world ici. Voilà, ça marche ,c'est cool PHP ! Et je vais pouvoir faire mon petit strace de PHP hello world, bam ! Ça y est là je suis dans la matrice ! Ouais c'est la fête là ! Alors c'est un peu comme dans le film où il y a le gars qui est devant ses écrans, il y a des trucs qui défilent personnes comprend rien, il dit "ouais mais avec l'habitude moi j'arrive à voir la femme en rouge" et tout ! Ça vous rappellera le film.

Toujours est il qu'en fait on va réussir à décoder cette matrice.

On va pouvoir décoder parce que c'est pas si compliqué. Tout ce que vous voyez à l'écran ce sont des appels au noyau, un peu comme si c'était l'API du noyau, en fait c'est l'API du noyau et chaque appel est une fonction. Par exemple là j'ai une fonction qui s'appelle execve, qui prend des paramètres.

Donc là j'ai une chaîne de caractères, après j'ai un tableau de chaînes de caractères et puis encore un autre tableau où il ne me montre pas le contenu parce qu'il y en a un peu trop et qui retourne un code.

Alors vous allez me dire ok c'est bien joli, mais execve mais qu'est ce que ça peut être ? Et bien pour ça on va utiliser une commande, LA commande la plus importante à connaître sous unix, man.

C'est la seule à connaître en fait, tout le reste vous pouvez le découvrir à partir du man, parce qu'on peut faire un man man.

Et le man man c'est cool parce qu'il nous dit que le manuel est séparé en différentes sections et dans la section 2, j'ai les appels système.

Donc je vais demander, ok donc man de la section 2 de execve. Qu'est ce que c'est que ce truc ! Ah bah ça n'existe pas, je me suis trompé ! Voilà execve ! Alors vous allez avoir le page du man, pour ceux qui n'ont jamais vu une page de man ça ressemble beaucoup à la doc PHP, qui était vraiment, bah... ils ont repris le format. Vous avez le le nom, le synopsis, comment ça fonctionne, la description, les valeurs de retour qui devait être là en bas etc il manque juste les commentaires avec des exemples bizarres que vous trouvez sur la PHPDoc mais à part ça à part ça, c'est assez similaire.

Donc execve qu'est ce que ça nous dit, ça nous dit que c'est une fonction qui prend un fichier, ce sera le fichier à exécuter les, l'ensemble du tableau des argv c'est exactement comme le $argv que vous avez en PHP et puis un tableau qui contient toutes les variables d'environnement qui sont passés au programme et ça retourne un entier qui, qui dit quoi d'ailleurs ? Un entier, si je regarde la return value.

Voilà bah il retourne un -1 si ça déconne et sinon, sinon il retourne 0 ça se passe bien.

Donc déjà, première chose intéressante, c'est que le PHP qui est ici, vous le voyez il a réussi à retrouver l'exécutable, donc on sait que c'est bien /usr/bin/php. Alors il y a d'autres façons de le faire vous connaissez le witch ou whereis. Et là, qu'est qu'on voit, on a le tableau des argv.

Et là vous reconnaissez le tableau qui est indexée par 0, le argv de 0 c'est bien la commande qui a été passée au programme pour l'exécution du programme, et après vous avez les paramètres.

Je vais sauter rapidement à la fin parce qu'il y a pas mal de choses, pas à la fin de la presse à la fin du truc là ! Parce qu'il y a quand même beaucoup de choses alors il y a, en fait vous allez voir que c'est très verbeux, je vous expliquerai comment on peut filtrer pour vraiment cibler les choses, parce qu'il se passe beaucoup de choses hein tout ce que vous voyez avec des mmap c'est le memory management dans linux, donc effectivement c'est le programme il demande des accès à la mémoire.

Celui qui va m'intéresser c'est ce petit morceau ici.

Ce petit morceau ici, ça s'appelle la méthode write. Je fais mon petit man 2 write, qu'est ce que c'est ? Ça écrit sur un file descriptor.

Voilà ça écrit avec, on lui passe un file descriptor un string et une taille, le trucs à écrire. Alors déjà qu'est ce que c'est qu'un file descriptor, parce que ça c'est peut-être une question qu'on peut se poser.

En fait c'est exactement la même chose que ce que vous avez lorsque vous faites un fopen en PHP, vous vous retrouvez avec une stream, c'est un identifiant qui permet d'accéder à une stream. Et bien un file descriptor c'est la même chose sauf que c'est encore plus simple en dans l'API du noyau, c'est que c'est des entiers numérique qui s'incrémentent. Sauf qu'il y a trois files descriptors qui sont toujours les mêmes.

Le premier, c'est le zéro.

C'est le file descriptor de l'entrée standard, en fait c'est un fichier, le clavier est un fichier sous unix et toutes les entrées sont des fichiers.

Donc le zéro, c'est le standard input, c'est un fichier en lecture seule, on ne peut pas écrire dans clavier, on peut que le lire. Vous avez le standard output qui est un fichier en écriture seule et l'error output. Ça vous parle peut-être les 1 et les 2 puisque qu'on vous redirigez la sortie d'erreur c'est 2 > et donc ça dit je dérive, le file desciptor numéro 2 je le renvoie vers autre chose.

Et à partir de 3 ben en fait c'est n'importe quel autre fichier qui a été ouvert.

Et donc quand je regarde mon petit write (qui était ici pardon !), je vois que j'écris sur le file descriptor 1 j'écris Hello World, qui doit faire 12 caractères et ça me renvoie 12 caractères. Alors là on a effectivement la sortie qui arrive au milieu, donc ça pollue un peu l'affichage, je vais la dégager.

tous ça, la sortie standard vers /dev/null et on a le truc qui nous intéresse, qui est ici. Donc on voit en fait que pour pour même écrire sur le prompt le processus va passer par le noyau pour lui demander d'écrire sur le bon file descriptor.

Alors j'ai dit c'est très verbeux, on va utiliser une méthode assez simple pour filtrer, en fait on peut demander à strace de n'afficher que certaines parties de l'API pour pas afficher tout. Donc si j'avais voulu faire ça, ça m'aurait donné ça.

strace -e, je ne veux que des write Donc là c'est un peu plus visible déjà ça peu plus facile à comprendre.

Autre façon on peut également utiliser tout ce qui est les pipe unix, pour aller nettoyer, découper et faire des choses, des traitements comme on a des données assez importantes qui nous sortent de strace c'est toujours intéressant pour aller les extraire. Et puis moi mon conseil à moi, le truc qui m'a au fil des années d'utilisation de cet outil là c'était plutôt de tout balancer dans un fichier.

Donc ne pas filtrer, pour en fait pouvoir après, parce que souvent ce qui est difficile c'est de reproduire le problème.

Vous connaissez tous le truc. Si j'arrive à reproduire le problème de manière un peu laborieuse, ce que j'aime bien c'est avoir le maximum d'informations pour pouvoir traiter l'information sans avoir oublié de mettre ça dans le filtre, il faut que je rajoute tel ou tel appel. Non là on balance tout dans un fichier et puis c'est beaucoup plus simple pour traiter après.

Et bien maintenant je vous propose de vous parler un petit peu de cas réel d'utilisation que j'ai eu de cet outil là qui au cours de différents projets avec un premier cas. Est ce que vous êtes déjà demandé pourquoi tel paramètre de PHP avait-elle valeur ? Telle variable avait-elle valeur ? En gros où est-ce qui était lu cette satanée valeur qu'on ne voyait pas, par exemple je sais pas moi, le max_execution_time Vous avez l'impression qu'il est à telle valeur dans votre fichier de configuration et en réalité il est différent, peut-être parce qu'il est lu dans un autre fichier.

Est ce que ça vous est déjà arrivé de vous demander quels étaient les fichiers de PHP de configuration qui étaient lus ? ouais ! Avec toujours cette question, mais pourquoi est-ce que j'ai cette valeur là ? Et ben on va le faire d'une manière assez simple.

On va nettoyer un peut l'écran ! Et on va stracer un php -i avec open, open c'est l'ouverture des fichiers, open c'est pareil que fopen en PHP donc j'ouvre mon...

je fais mon php -i je balance la sortie standard de php -i à la poubelle, parce que ça ne m'intéresse pas, moi ce qui m'intéresse c'est ce qui fait dedans et là je vois tous les fichiers qui ont été ouverts, alors open on peut regarder, vous allez voir c'est vraiment la même chose que le fopen on a un certain avantage en PHP c'est que c'est un langage qui était beaucoup inspiré par le C et qu'est ce qu'on voit ici, on voit que open on passe le fichier, on passe les flags, c'est comme le fopen vous passez le flag, les read, les rw, les choses comme ça et vous retournez ici un int qui est le file descriptor.

Donc là je vois que j'ai des open sur des librairies externes, cela ne m'intéresse pas des masses, par contre ce qui va m'intéresser c'est à partir d'ici.

Je vois que le premier fichier qu'il essaie d'ouvrir, le premier fichier ini qu'il essaie d'ouvrir c'est un fichier qui s'appellerait php-cli.ini et qui se trouve dans le même répertoire que le binaire.

Alors celui là pour le trouve ! Si un petit malin s'amusait à vous coller un petit fichier php.ini au même endroit que le binaire avec des variables un peu bizarres, suis là vous allez rigoler pour le retrouver. On a la chance ici il nous dit -1, donc c'est une erreur, il nous dit qu'il ne trouve pas le fichier.

Après il va le chercher dans le /etc/php/7.1/cli php-cli.ini, ça c'est des choses qui peut être aussi défini lors la compilation PHP après /usr/bin/php.ini, bah tiens voilà un autre dans /usr/bin, bien sûr c'est un bon endroit pour les fichiers de configuration ! Et puis après on voit les fichiers qui sont lus avec le premier, celui-ci /etc/php/7.1/cli/php.ini et également tous les fichiers des extensions, je suis sur une Debian donc c'est découpé par extension.

Et on voit l'ordre réel de lecture de chaque fichier avec en face le file descriptor associé, ce qui signifie qu'il a réussi à ouvrir le fichier, que le fichier est réellement lu par le programme.

est juste après ce qui est intéressant aussi, c'est qu'on voit également toutes les extensions PHP qui sont chargées.

Les .so et toujours avec le file descriptor > -1, ce qui fait qu'il a bien réussi à les charger à chaque fois.

Donc déjà en faisant cette simple opération, je n'ai pas bidouillé PHP, je n'ai rien changé sur mon système, j'ai juste strace installé je suis capable de comprendre le fonctionnement de PHP lorsqu'il s'intéresse à lire des fichiers.

Un autre exemple Il ya quelques années, c'était au début des années 2000, on faisait des sites web du portail, et on avait un portail.

Vous connaissez le principe, on fait un développement, on met en prod ça se passe super bien et au bout d'un mois paf, 5 secondes dans la vue sur la homepage ! Ça vous est peut-être arriver aussi, non ? Alors bien entendu on reproduit pas en dev, bien entendu on reproduit pas en pré-prod, on se dit à tous les coups c'est un coût du système ça ! À tous les coups c'est un problème matériel, ça doit être la carte réseau qui prend full duplex, une connerie comme ça ! Donc on demande aux admins sys de nous prendre la machine, de commander exactement la même machine chez le constructeur de l'installer, de copier le disque octet par octet sur une machine à côté et là on reproduit pas non plus ! Alors qu'est ce que ça peut être ? alors j'essaie de reproduire un peu.

Le problème, donc j'ai un truc qui s'appelle, c'est comme si c'était mon serveur web.

Voilà ! Donc j'ai mon procès 1132, je vais le stracé ! Donc là ce qui est intéressant c'est que je suis en mode "le procès est déjà démarré", il a déjà démarré, il est en prod etc et je vais m'attacher ou plutôt je vais demander au noyau de me filer tous les échanges qu'il a avec le process.

Et là, ça commence à donner des informations intéressantes et on voit, qu'on est bloqué sur poll ! Mais qu'est-ce qu'il fait poll ? Alors poll il attend sur un fichier.

Ok ! D'accord ! Alors comme on voit qu'on est bloqué sur un truc, je vais rajouter une petite option à strace qui est l'option -T majuscule qui permet d'avoir en fait le temps d'exécution de chacun des appels système.

Donc là on voit par exemple ici j'ai des trucs où ça prend quelques nano secondes.

Et là si je remonte à mon poll qui est bloqué, si je ne dis pas de bêtises, le voilà le poll qui est bloqué ! Et qu'est ce que je vois celui-ci a pris 5 secondes.

Donc j'ai une attente sur un file descriptor qui prend 5 secondes.

Je remonte un petit peu, mon file descriptor c'est le 3, et je vois que en fait c'est un appel vers un service externe qui permet à priori de récupérer des données météo.

Et là je remonte encore et je vois que effectivement le 3, le file descriptor en numéro 3, on a utilisé connect dessus, donc on s'est connecté sur une socket tcp/ip dessus, sur le port 80 de cette machine là. Évidemment ce n'est pas une adresse locale à l'époque.

Et ça déjà ça nous donne une information intéressante parce que sur le portail en question, ça se faisait beaucoup à l'époque on affichait la météo, parfois on affichait même l'heure.

Je ne sais pas qui y en a qui ont fait ça, afficher l'heure sur un site web c'est magique ! Donc on affichait la météo et qu'est ce qui s'était passé ? Et bien en fait c'est un service payant de météo et au bout d'un mois on avait épuisé tout les crédits, et donc ce que faisait le service, au lieu de renvoyer une erreur en fait il avait réduit la bande passante disponible pour accéder à leur service et du coup on se prenait 5 secondes dans la vue.

Alors bien entendu ça s'est réglé d'une manière pas très technique, ça s'est réglé en achetant plus de crédits et en monitorant ce qui se passait de ce côté là. Mais ce qui est intéressant c'est de voir que avec, je pars de zéro information je sais juste que mon procès il est lent, à et bien mon procès il est lent parce que j'ai un appel qui est fait sur un service externe qui permet de récupérer des informations de météo sur tel serveur et tout ça juste avec en appelant du strace sans avoir à modifier votre programme, passer en mode debug ou des choses comme ça.

Un autre exemple, ah non pardon celui là, lsof ça peut être aussi un outil intéressant parce que je laisse le petit programme de tourner et lsof c'est un outil qui vous liste tout les fichiers ouverts et en fait on aurait pu déjà avoir des informations intéressantes en utilisant lsof lsof -p 1132 puisqu'on voit ici vous voyez le 0, 1, 2, ,3, ce dont je vous parlais tout à l'heure, et le 3 c'est bien une connexion TCP, ça aurait pu donner un indice qu'on avait une connexion qui était ouverte assez longtemps sur un service externe.

Déjà lsof ça peut aussi vous aider, c'est assez complémentaire avec les autres outils.

Un autre exemple. J'ai un process qui démarrait, il bouffe du cpu un petit peu, il fait des trucs donc j'ai un procès qui fait des trucs d'ailleurs il fait des trucs comme son nom l'indique ça n'a pas l'air de donner grand chose hein : j'ai rien en sortie tout ça je fais un top qu'est ce que je vois je vois un peu le process, il est là, il fait des trucs 5% de cpu c'est pas la folie, à priori il dialogue avec mysql, donc on imagine qu'il fait des requêtes pour en avoir un peu plus le coeur net mais c'est pas non plus 100%, c'est pas...

Je me connecte avec mysql, sécurité ! Est donc là je fais un petit show processlist, qui va me dire qu'est ce qu'il est en train de faire.

Ok là j'ai une requête, ok je n'ai rien je n'ai rien bref c'est pas il fait des trucs mais ça avance pas et en plus c'est lent et en plus ça bouffe pas de cpu mais qu'est ce qu'il peut bien faire ? Est ce que quelqu'un s'est déjà posé la questions mais qu'est ce qu'il peut bien faire ce processus ? Nan ça vous est jamais arrivé ! Vous avez du bol ! Donc, c'est là que je vais sortir mon petit strace je récupère le pid 1161 Et là ça envoie ! Alors qu'on a l'impression qu'il fait rien le processus en tout cas il fait des trucs n'était pas clair.

Alors je vais essayer, je vois j'ai du poll, du write, du read donc je vois, j'ai du select. Donc déjà je vais filtrer un petit peu pour me concentrer sur les write.

Donc ça veut dire que mon programme écrit sur la socket qui permet de communiquer avec la base de données des requêtes sql.

J'utilise l'option -s qui permet en fait d'augmenter la taille des chaînes de caractères affichées parce que par défaut c'est pas grand chose c'est une vingtaine de caractères, j'en mets 1000, et là je regarde et là ce que je vois, c'est qu'il y a énormément de requêtes sql et je peux même on va du coup on va enlever ça, regarder combien elles prennent de temps chacune Parce que là vous avez, entre le read qui prend 18 nano secondes, le poll qui en prend 11 et le write qui en prend 15, heu pardon le write qui prend 176 nano secondes là j'ai le read qui en prend 15, en gros ça va extrêmement vite, on est en dessous de la milliseconde on n'est pas loin du 0,2 millisecondes et en fait ce qui se passe c'est qu'il y a tellement de requêtes, qu'en fait les processus ils font pas grand chose parce qu'ils répondent très vite et c'est juste les échanges entre les deux processus qui sont très lent. C'est le fait de passer l'information à la mémoire de le passer à travers la socket et puis après de la récupérer et donc ça, vu de l'extérieur c'est assez difficile à s'en rendre compte, et grâce à strace c'est beaucoup plus clair et non seulement c'est plus clair, mais en plus, comme j'ai le contenu de la requête je peux aller voir dans quelle partie du code il y a le problème et fixer le problème si si j'y arrive, là à priori c'est une boucle qui est mal faite etc.

donc ça c'est déjà c'est des éléments aussi intéressants pour un programme qui tournait déjà.

Un autre programme, imaginons vous avez un processus d'import, donc là c'était un import, dans un site de e-commerce il me semble et vous avez mis en place le truc, vous avez fait quelques fichiers de tests qui vous ont permis de tester que l'import fonctionnait bien, puis un jour le client vous envoie le fichier réel il fait deux cent mille lignes ok, vous dîtes, ouais ça va passer, hein ! Donc vous lancez le processus je lance mon processus Voilà ok pour le coup lui il fait des trucs là, vraiment il à 100% de cpu, ça processe. Vous revenez une heure plus tard il est à 100% de cpu c'est un process ! Alors là vous vous dîtes, vous vous êtes peut-être déjà posé la question, qu'est ce que je fais ? Est ce que je le coupe ? Mais peut-être qu'il va finir dans cinq secondes ! ou est-ce que je...

parce que peut-être qu'en le coupant je pourrais ajouter des options pour avoir plus d'informations, ou est-ce que je le laisse faire ! c'est le dilemme ! donc là on va essayer d'utiliser strace, encore une fois pour nous aider.

Et là, qu'est ce que je vois ? Il s'attache au processus et on voit qu'il fait des read dans le 3 qui est à priori le fichier qu'il est entrain de lire on pourrait faire un lsof pour s'en rendre compte, et on va faire un lsof pour s'en rendre compte.

Voilà on voit bien qu'il est en train de lire ce fichier là, c'est le file descriptor numéro 3 qui est un fichier regular donc un fichier normal.

Et il est en train de lire ce fichier. Alors c'est un fichier csv, il fait un fgetcsv vous connaissez un peu comment fonctionne le getcsv, il lit une ligne dans un fichier et puis après il vous la renvoie et puis après il passe à la suite, non en fait il fait pas ça du tout le fgetcsv. Ce qui se passe c'est que quand vous faite un fgetcsv PHP il lit 8 kilos, du fichier, le met dans un buffer et après, il va découper à l'intérieur, d'après le buffer c'est ce qu'on voit ici, le read du file descriptor n°3, voici ce qu'il a réussi à lire les 8 kilos, il a demandé 8 kilos, il a obtenu 8 kilos donc déjà ça c'est intéressant ça nous donne des petites informations sur comment fonctionne PHP en interne et surtout comme je peux afficher un peu plus d'informations depuis ce que j'ai dans les chaînes de caractères je vois, je sais que dans mon fichier CSV, j'ai un identifiant qui en début de ligne et c'est celui là ! Donc là je vois mon petit retour chariot donc, je sais que là je suis en début de la ligne suivante et je vais pouvoir utiliser mon petit grep - n ça vous indique le numéro de la ligne de mon fichier Voilà et je sais que je suis à la ligne 15 458 d'un fichier qui fait, un fichier qui fait 100 000 lignes ! Donc je suis à 15%, il y a de l'espoir, ça avance et ça c'est cool. Si j'étais à la deuxième ou troisième, je me dis jamais ça va finir.

Donc là je sais que ça avance et je sais où j'en suis et je peux dire bon je vais peut-être revenir dans une heure et voir s'il a avancé si c'est linéaire ça peut être intéressant, mais déjà ça vous donne cette information qui autrement est assez difficile à obtenir sans avoir à redémarrer votre processus.

Donc globalement, sniffer les appels système, donc espionner les appels système ça nous donne vraiment une meilleure compréhension de ce qui est en train de faire le processus de ce que le comportement du processus, donc ça c'est assez cool.

Alors tout à l'heure je vous ai dit que ça, c'était la réalité je vous ai un petit peu menti la réalité est un tout petit peu plus compliqué, pourquoi parce que votre processus il va rarement lui-même faire certaines opérations lire et interpréter du fichier xml vous n'allez pas le réimplémenter à chaque fois que vous faites un programme, enfin j'espère, sinon...

désolé ! Accéder à la base de données MySQL, vous n'allez par réimplémenter le protocole de communication MySQL, donc vous allez passer par des bibliothèques pour le faire, ça peut être voilà une petite bibliothèque de xml, une bibliothèque de db, puis bon ce qui est assez cool c'est qu'il y a plein de choses de base qui sont faites par la standard librairie comme tout ce qui est les malloc, les free la gestion des chaînes de caractères tout ça. Est ce qui est assez intéressant c'est qu'il existe un autre outil qui va permettre d'aller espionner les appels entre votre programme et la librairie qui l'utilise, il s'appelle ltrace est donc cet outil ce cas c'est cool c'est qu'elle a même ui que strace, c'est le même principe de fonctionnement soit on le lance avec la commande, soit on passe le processus, vous avez compris le principe ! On va faire un petit exemple Quelles sont les variables d'environnement qui sont lues par mon programme ? Alors les variables d'environnement, en fait elles sont fournies au contexte d'exécution du programme au début et il n'y a plus besoin de, le noyau lui on ne lui demande pas son avis, c'est pas lui qui fournit les variables d'environnement, donc je peux pas utiliser strace pour voir les variables d'environnement néanmoins je vais pouvoir aller voir, il y a une fonction qui fait ça, alors ce qui est cool c'est que dans le man vous avez également une section qui est les appels de bibliothèques, c'est la section 3 et cette fonction c'est getenv, étonnant ! et getenv, donc voilà elle fonctionne comme ça, ça retourne une chaîne de caractères et on lui passe une chaîne de caractères donc je vais pouvoir lancer, j'ai un petit programme, si je le retrouve ! ltrace -e de getenv de mon reading_env Non, ça ça marche pas, c'est PHP qu'il faut lancer. Effectivement ce n'est pas un processus.

donc là je vois toutes les variables d'environnement qui sont lues par le programme avec quand il y a une valeur, le résultat ici, par exemple le path.

Intéressant aussi, vous avez une petite variable d'environnement qui s'appelle et PHP_INI_SCAN_DIR vous pouvez setter, pour qu'il aille chercher des fichiers dans encore un autre endroit ! Là ça peut être vraiment la fête ! Ce qu'on voit aussi c'est qu'à chaque fois que vous appelez une bibliothèque ou une autre bibliothèque on vous dit, ah bah tiens PHP a appelé la libxml et la libxml elle même a eu besoin de getenv et elle a chopé des éléments comme ça et donc là par exemple mon petit programme il charge cette variable de là donc si je fais un export de MY_ENV_VAR=toto et que je refais mon ltrace dessus, qu'est ce que je vois, je vois bien que mon getenv il a bien la valeur toto voilà c'est assez classique, vous avez compris le principe.

Un autre exemple qu'on a eu aussi, c'est que lorsqu'on avait un web service, enfin on utilise un même service externe et temps en temps on obtenait des trucs bizarres, on ne savait pas trop d'où ça venait, c'était difficile parce qu'il y a beaucoup de traitements une fois que les données étaient récupérés et donc là on a sorti on a tenté le strace pour voir un peu qu'est ce qu'il y avait dans l'appel au web service.

webservice_call, alors là ok on sait que ce qui nous intéresse c'est read et write, et connect. Savoir si on se connecte sur le bon serveur aussi hein ça c'est toujours intéressant Donc voilà je me suis connecté sur un serveur externe et puis donc c'est la socket numéro 3 qu'on voit ici et quand j'écris sur la socket n°3, quand je lis et que j'écris sur la socket n°3 c'est des trucs bizarres, donc j'ai du http c'est pas clair ça ! À votre avis pourquoi ? C'est du ssl, on a un joli 443 ici et le ssl c'est la sécurisation de l'échange entre le processus initial jusqu'au...

endpoint final. Donc le noyau lui ce qu'il voit, il voit juste des éléments chiffrés, c'est sécurisé ! Ou pas ! Alors on va utiliser ltrace Pourquoi ltrace ça peut nous intéresser, puisque en fait lorsqu'on va faire du PHP et qu'on fait du ssl, on utilise la lib openssl donc on va pouvoir les sniffer des échanges qu'il y a entre PHP, qui lui parle avec des éléments qui sont en clair à la lib ssl, qui elle fait le chiffrement et le déchiffrement en retour. Et on va utiliser et on va être checker SSL_read et SSL_write man SSL_read voilà qui permet de lire depuis une connexion TLS et SSL.

Donc là on a des résultats intéressants mais un tout petit peu décevants quand même, vous attendiez un truc de fou, mais nan, un petit peu décevant, mais on n'y est pas encore on va y arriver là ont voit le SSL_write donc c'est effectivement ce que mon PHP il a envoyé à SSL pour dire : "maintenant tu me chiffres ça pour m'envoyer ça sur la connexion" et puis il read, c'est ce qu'il obtient en réponse, mais sauf que ici on a quoi ? On a quand même des entiers qui sont intéressants, il écrit 77 caractères, il a essayé d'envoyer 8 000 caractères, il en a envoyé 246, c'est la taille du buffer c'est pas très grave le problème c'est ces éléments là, ces deux éléments là si on regarde le SSL on va regarder le SSL_write pour commencer puisque c'est dans l'ordre, le SSL_write est bien là, ça devrait être une chaîne de caractères ! Donc on devrait voir des trucs écrits, sauf qu'en fait jusqu'à présent ltrace lorsqu'il choppe les appels de la bibliothèque, on est dans un mode où on a juste des pointeurs sur la mémoire sauf que derrière on ne sait pas ce que sont ces pointeurs puisque on est sur quelque chose qui est en binaire, qui est déjà compilé et pour le savoir en fait, ltrace à un fichier de configuration qui dit, bon pour chacun des appels, par exemple pour pcap ici, on va faire un getpass sur le password, je peux faire c'est un string. Sauf qu'il n'a pas la configuration, en tout cas sur debian de par défaut pour lire openssl. Mais c'est pas très grave parce qu'on va lui dire comment interpréter les éléments de openssl.

On va créer un fichier qui s'appelle ltrace.conf On va dire que SSL_write, ça prend un truc qui ne nous intéresse pas tellement, donc on va mettre un void, une string qui est le buffer, et puis c'est un int on va le refaire, c'est .ltrace dans le répertoire point virgule, est là qu'est ce que je vois quand je lance mon truc ça y est là, il a été capable d'aller choper le pointeur d'aller dans l'espace mémoire concerné et de dire c'est une chaîne de caractères et donc j'affiche. On faire le même chose pour le read.

Le read il fonction de comment ? Il fonctionne, voilà c'est vraiment la même signature.

En fait qu'est ce qui se passe ? C'est que ltrace à un fonctionnement assez particulier c'est que, il intercepte l'appel avant qu'il soit effectué.

Donc là c'est pas très grave pour un write, puisque tout les paramètres sont déjà connus avant qu'ils soient exécutés. Mais lorsqu'on fait un read le read il met la donnée dans le buffer après que la commande est été exécuté.

On peut pas prédire ce qu'il y avait dans le truc.

Donc on va simplement le configurer pour lui dire que la string qui est là il faut attendre que la commande est été exécuté, c'est avec un petit + et bam ! Et là on obtient la totalité de l'échange sécurisé ou pas ! Donc là je vois je fais appel à ma super API j'obtiens effectivement tous les headers http et j'obtiens mon petit json. On voit que là il y a une petite erreur ! Voilà, qui fait des encode de json à la main, voilà bon bref et donc là, j'ai réussi grâce à ltrace à aller choper des informations qui n'étaient pas si faciles à obtenir, même si vous snifez le réseau c'est pareil, vous auriez obtenu une information purement chiffrée. Là on se met juste avant que ce soit chiffré, on se met juste après le déchiffrement donc on récupère toutes les données.

Alors voilà ça c'était pour aller ltrace et strace, si vous avez envie vous d'aller plus loin encore il y a des outils qui sont encore plus puissants. L'un d'entre eux c'est gdb.

C'est l'universal debugger, ça vous permet de lancer des programmes, de faire des arrêts dessus, n'importe quel programme, même un programme PHP alors vous serez dans le code du moteur PHP, vous ne serez pas dans le code de votre PHP et puis ça me permet également d'aller modifier des espaces mémoire des choses assez intéressantes et un autre outil qui s'appelle perf qui permet lui de faire du profiling au niveau du noyau et donc d'avoir des informations très intéressantes sur les temps passé dans les appels système En conclusion les éléments principaux qui sont peut-être intéressants à souvenir pour strace et ltrace c'est que ça vous donne vraiment des informations très intéressantes sur le fonctionnement interne de votre programme et ça c'est vraiment ça peut être vraiment utile où j'ai donné quelques exemples qui étaient réels, basés sur mon expérience où on arrive effectivement à un diagnostic un premier diagnostic, qui soit vous donne une piste précise, soit parfois vous dit exactement quel est problème un autre élément, c'est technology agnostic, c'est à dire que c'est complètement universel vous fait du PHP, du GO, du Rust, du Java, du python N'importe quoi d'autre, du C.

Un des programmes que je vous ai montré, il est écrit en C celui qui faisait des trucs et bien c'est technology agnostic pourquoi, parce que aucun de ces programmes ne peut passer outre le noyau donc tous ces programmes ont besoin de dialoguer avec le noyau, quelque soit la technologie que vous utilisez, il y a toujours un noyau qui fait l'appel au monde extérieur Autre élément intéressant, c'est comme on passe par le noyau, pas besoin de changer l'application, pas d'extension PHP à installer, pas de configuration à faire sur l'application, ça marche directement via des appels au niveau du noyau.

Et bien entendu ça ne va pas remplacer les outils que vous avez déjà, ça ne va pas remplacer votre xdebug, ca ne va pas remplacer votre blackfire c'est des outils qui sont, un outil qui peut vous parfois vous permettre d'avoir des raccourcis sur des solutions même sur des diagnostics et aller plus rapidement dans vos résolutions de bugs.

Voilà, merci beaucoup pour votre présence, merci à l'AFUP de m'avoir fait confiance pour ce talk. Est-ce que vous avez des questions ? Je crois qu'on a une question Merci c'était très intéressant mais du coup est ce que c'est RGPD compliant ? Bah ça dépend ce que tu fais que les datas parce que effectivement tu peut choper des datas qui seraient des datas personnelles ça dépend si tu as les sauvegardes, des choses comme ça, mais je pense que le problème est plutôt de qui envoie les datas et et où elles transitent, et l'outil qui permet de les choper, c'est un sujet un peu à part.

Merci super conf aussi et il me semblait que pour faire du strace et ltrace il fallait être root.

Non en fait il faut que tu sois le même utilisateur qui a lancé le programme ou root il peut faire du strace, ltrace sur tout le monde.

Merci.

Désolé on n'a plus le temps pour des questions !