Userland Statically Defined Tracing (USDT)

Dans ce chapitre, nous sommes partis des tracepoints exposés volontairement par le noyau Linux, et nous avons vu comment perf probe nous permettait d’instrumenter du code noyau arbitraire (kprobe) ou du code utilisateur arbitraire (uprobe).

Les personnes attentives à la combinatoire auront remarqué qu’il y a une configuration que nous n’avons pas encore explorée, c’est celle d’une instrumentation exposée volontairement par les binaires utilisateurs. C’est le propos de l’instrumentation USDT que nous allons explorer maintenant.

Recherche d’instrumentation USDT

perf n’est malheureusement pas capable de découvrir toute l’instrumentation USDT disponible sur le système par lui-même, car cela impliquerait de rechercher et ouvrir tous les binaires du système de fichier à chaque appel à perf list, ce qui aurait un coût inacceptable.

A la place, perf conserve un cache de l’instrumentation observée dans les binaires qu’on l’a amené à ouvrir, par exemple en faisant l’analyse de piles d’appel acquises via --call-graph=dwarf.

Il est aussi possible d’ajouter manuellement des binaires à ce cache avec la commande perf buildid-cache, ce que nous allons faire maintenant pour toutes les bibliothèques de bases mentionnées à la fin de la section sur les uprobes, plus le chargeur de binaires ld :

perf buildid-cache -a /lib64/libc.so.6 \
&& perf buildid-cache -a /lib64/libm.so.6 \
&& perf buildid-cache -a /lib64/libpthread.so.0 \
&& perf buildid-cache -a /usr/lib64/libstdc++.so.6 \
&& perf buildid-cache -a /lib64/ld-linux-x86-64.so.2

(Vous noterez que chacune de ces commandes affiche un pager vide qui doit être quitté avec la touche “q”. Si vous trouvez un moyen de désactiver temporairement ce pager, je suis preneur.)

Ceci étant fait, l’instrumentation USDT exposée par ces binaires apparaîtra dans perf list :

perf list sdt
Sortie de perf list sdt

On voit que les bibliothèques GNU exposent pas mal d’instrumentation de ce type autour de la gestion mémoire, de la gestion d’exceptions (via throw/catch ou setjmp/longjmp), de pièges de performance liés à la libm, ou de la manipulation et synchronisation de threads via pthread.

Activation de l’instrumentation

Si vous regardez la liste ci-dessus, vous constaterez que certains de ces “points d’instrumentation” sont sujets à être fréquemment empruntés, notamment ceux ayant trait à la synchronisation de threads. Il ne serait pas acceptable d’y payer en permanence le coût en performance d’une instrumentation quand celle-ci n’est pas utilisée.

L’instrumentation USDT est donc désactivée par défaut et doit être activée explicitement. Et comme l’implémentation actuelle utilise des uprobes, cette instrumentation doit être effectuée par l’administrateur du système, via les opérations suivantes :

  1. Remplir le buildid-cache de root en relançant les commandes ci-dessus pour cet utilisateur (typiquement via sudo).
  2. Activer l’instrumentation USDT souhaitée avec la commande perf probe, avec la syntaxe utilisée par perf list.

Comme nous pouvons parfois être amené à activer un grand nombre de points d’instrumentation à fins exploratoires, c’est le bon moment pour mentionner qu’on peut passer à perf probe des motifs shell du style sdt_libpthread:*.

Invitez maintenant l’administrateur à activer l’instrumentation de la libpthread avec le jeu de commandes suivantes :

# Nécessite des privilèges administrateur !
sudo perf buildid-cache -a /lib64/libpthread.so.0 \
&& sudo perf probe sdt_libpthread:* \
&& sudo chown -R root:perf /sys/kernel/tracing/events/sdt_libpthread/

Exercice : Commencez par évaluer la fréquence des différents événements exposés par libpthread avec perf stat, puis prenez un événement suffisamment peu fréquent et suivez ses occurences avec perf trace.