Aller au contenu
Background Image
  1. Ebpf-Another-Types/

Profilons un petit programme avec des uProbes

·873 mots·5 mins·
Joseph Ligier
Auteur
Joseph Ligier
CNCF ambassador | Kubestronaut 🐝
Sommaire
Apprenons uProbe avec eBPF et Aya - Cet article fait partie d'une série.
Partie 4: Cet article

Nous avons vu ce qu’était un programme de type uProbe dans la première partie. Cela peut être ainsi un moyen de profiler un programme.

Le but de cet article est donc de regarder le temps que met une fonction d’un programme écrit en Rust à s’exécuter.

Cela permettra également de voir la différence avec un programme écrit en Go pour la récupération des points d’attache des programmes eBPF de type uProbe.

Je suppose que vous êtes déjà dans un environnement pour développer avec Aya et que vous avez installé bpftrace.

Si ce n’est pas le cas, vous pouvez utiliser le lab Killercoda :

Killer coda screenshot


Présentation du programme Rust pour tester les uProbes
#

Calculer les décimales de 𝛑
#

Avant de créer le programme eBPF, nous allons donc déjà récupérer un petit programme en Rust.

Pour que ça soit un peu plus inventif qu’un programme hello world, j’ai choisi un programme qui affiche une estimation du nombre 𝛑. En faisant varier le nombre de décimal, on peut facilement changer la durée d’exécution de la fonction.

Le nombre 𝛑 (Pi) est un nombre qui permet, par exemple, de calculer le périmètre d’un cercle. Ce nombre a la particularité d’avoir une infinité de décimales ce qui rend leur calcul complexe. En 2022, on en a déterminé les 100 mille milliards premières décimales.

Voici les commandes pour le récupérer et le compiler :

git clone https://github.com/littlejo/pi_digits_rust
cd pi_digits_rust
cargo build

Pour tester le binaire généré, on peut alors lancer la commande suivante :

./target/debug/pi_digits_pure_rust 10 #Pour avoir 10 décimales de Pi

Ce qui répond :

3.1415926536

On a bien 10 décimales.

Le but de cet article est de récupérer le temps que met la fonction dédiée au calcul des décimales de Pi (pi_times_10n_rounded()).

Vous pouvez tester pour un autre programme compilé comme C/C++, Go, Zig ou V de votre choix pour comparer.

Comment va-t-on procéder ?
#

Pour cela nous allons utiliser :

  • un programme de type uProbe pour mesurer le temps à l’arrivée de la fonction
  • un programme de type uRetProbe pour mesurer le temps à la sortie de la fonction

Ainsi en soustrayant le temps d’entrée du temps de sortie, on va trouver la durée totale pour calculer les décimales de 𝛑.

profiling uprobe

Avant cela, il faut d’abord trouver le point d’attache pour ce programme écrit en Rust pour ces deux programmes eBPF.


Trouver le point d’attache
#

Maintenant qu’on a créé et compilé le petit programme, il faut trouver comment déclencher le programme eBPF de type uProbe ou uRetProbe. Lorsqu’on utilise cargo generate pour le repo aya, il faut répondre à deux questions : où se trouve le nom du binaire et quelle fonction observée. Voyons cela en détail.

Nom du binaire
#

🤷   Target to attach the (u|uret)probe? (e.g libc):

Pour garantir la portabilité du programme eBPF, il faut répondre le chemin absolu du binaire. Par exemple je l’ai créé là : /home/cloud_user/pi_digits/target/debug/pi_digits_pure_rust.

Comme le chemin est un peu long, pour la suite, le nom du binaire sera en chemin relatif pour une meilleur lisibilité de l’article : ./pi_digits_pure_rust)

Nom de la fonction
#

🤷   Function name to attach the (u|uret)probe? (e.g getaddrinfo):

Que faut-il répondre ?

On l’a déjà vu : on peut utiliser la commande bpftrace, par exemple :

bpftrace -l \
'uprobe:./pi_digits_pure_rust:*pi_times*'

On trouve alors :

uprobe:./pi_digits_pure_rust:pi_times_10n_rounded

La fonction est donc bien pi_times_10n_rounded.

Astuces
#

Si vous êtes attentifs, dans la deuxième partie, on avait vu que, par défaut, le compilateur Go :

  • inlinait les fonctions et donc on ne pouvait pas les voir
    • On a dû rajouter l’option de compilation -l pour empêcher cela
  • modifiait le nom de la fonction (mangle)
    • On a dû rajouter l’option de compilation -N pour empêcher cela

Rappelons ce qu’est un inline (ici avec le compilateur Go) via un schéma :

Go inline

Rust fait également cela par défaut. Mais ce n’est pas au niveau global que je l’ai fait mais pour la fonction en particulier.

J’ai ainsi dû rajouter deux attributs dans le code devant la fonction cible :

#[no_mangle]
#[inline(never)]
pub fn pi_times_10n_rounded(n: usize) -> num_bigint::BigInt {
//[...]
}
  • #[no_mangle] : ne pas modifier le nom de la fonction
  • #[inline(never)] : ne jamais inliner la fonction

Maintenant qu’on a défini le point d’attache. Nous allons pouvoir commencer à créer nos deux programmes eBPF.


Récupérons le temps de lancement des programmes eBPF
#

Commençons simple en créant les deux programmes eBPF et en récupérant juste leur temps de lancement.

profiling timing

Suite de l’article réservée aux membres premium ✨

L’article complet est accessible uniquement aux membres premium.

Devenir membre premium, c’est simple : il suffit de faire un petit don 💖

En échange, vous aurez pendant 1 an (offre early bird) :

  • Accès à tous les articles complets dès leur publication
  • Lecture anticipée avant la mise en ligne publique
  • Participation au soutien de ce blog indépendant
  • Accès exclusif à mes photos de vacances à Dubaï

Votre don permet de :

  • Me rendre moins dépendant des grandes plateformes
  • M’encourager à créer plus de contenu technique
  • Lever le paywall plus rapidement pour tous

👉 Devenir membre premium dès maintenant

Apprenons uProbe avec eBPF et Aya - Cet article fait partie d'une série.
Partie 4: Cet article