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

Observons une fonction simple de ton programme avec des uProbes

·916 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 2: Cet article

Nous avons vu ce qu’était un programme de type uProbe dans la première partie : un moyen de sonder les fonctions de vos programmes.

Dans cette partie, nous allons d’abord créer un programme très simple en Go et nous allons le faire réagir avec deux programmes eBPF :

  • Le premier de type uProbe
  • Le deuxième de type uRetProbe

Je suppose que vous êtes déjà dans un environnement pour développer avec Aya et que vous avez installé le compilateur go et bpftrace. Si ce n’est pas le cas, vous pouvez utiliser le lab Killercoda :

Killer coda screenshot


Créons un programme pour tester les uProbes
#

Un code tout simple
#

Pour changer du Rust, nous allons créer un petit programme en Go :

// hello.go
package main

import "fmt"

func hello() int {
    fmt.Println("Hello, world!")
    return 3
}

func main() {
    ret := hello()
    fmt.Println("Returned:", ret)
}
Vous pouvez tester pour un autre programme compilé comme C/C++ ou Rust si vous préférez.

Comment va-t-on activer des uProbes ?
#

Le but de l’article est d’activer :

  • un programme eBPF de type uProbe à chaque fois que l’on rentre dans la fonction hello() :

Go uprobe

  • un programme eBPF de type uRetProbe à chaque fois que l’on sort de la fonction hello() :

Go uretprobe

Maintenant qu’on a vu les tenants et les aboutissants de l’article, passons à la compilation du programme.

Compilons le programme
#

Pour le compiler, il suffit de taper :

go build -gcflags="all=-N -l" -o hello hello.go

On peut remarquer qu’on a ajouté une option -gcflags="all=-N -l". Normalement, on n’a pas besoin d’utiliser cette option pour compiler un programme Go :

go build -o hello hello.go

Cela fonctionne également. Mais alors pourquoi utiliser cette option ?

  • -gcflags veut dire go compiler flags ce sont des options passées au compilateur Go.
  • all permet de dire que les options s’appliquent à tous les packages compilés.

Les options spécifiées au compilateur Go sont :

  • -N : par défaut, le compilateur modifie (mangle) le nom de la fonction. Cette option permet de désactiver cette modification.
En anglais, to mangle peut se traduire par modifier, mutiler ou même défigurer. En informatique, name mangling se traduit par décoration de nom. On a complètement gommé le côté péjoratif du terme !
  • -l : par défaut, le compilateur inline les fonctions c’est à dire qu’elle intègre directement le contenu de la fonction dans le code appelant permettant ainsi aux programmes d’être plus performants. Cette option permet de désactiver cet inline.

Go inline

Ainsi ces options vont nous permettre de garder les fonctions et de les rendre lisible, ce qui va nous simplifier pour trouver le point d’attache pour notre programme eBPF.


Comment 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/hello.

Nom de la fonction
#

Une fois que tu as répondu à la première question, il reste une seconde question :

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

Que faut-il répondre ? On serait tenté de répondre hello vu qu’on a mis l’option -N qui désactive la décoration de nom lors de la compilation.

Mais c’est un peu plus compliqué. Le compilateur Go modifie tout de même légèrement le nom de la fonction pendant l’étape de compilation.

Nous l’avons vu dans le précédent épisode pour trouver toutes les fonctions d’un binaire, il suffit d’utiliser bpftrace :

bpftrace -l 'uprobe:/home/cloud_user/hello:*'
  • l’option -l permet de lister toutes les probes disponibles
  • uprobe : le type de programme eBPF
  • /home/cloud_user/hello : l’emplacement du binaire
  • * : le joker (0 ou plusieurs caractères)

Le format d’affichage est alors :

uprobe:/home/cloud_user/hello:[fonction1]
uprobe:/home/cloud_user/hello:[fonction2]
uprobe:/home/cloud_user/hello:[fonction3]
uprobe:/home/cloud_user/hello:[fonction4]
etc 

C’est pas de bol : le nom de la fonction est le même que le nom de mon fichier… On ne peut pas faire de | grep hello. Comment s’en sortir ? avec awk, cut ou pire une regex ? Faisons simple :

bpftrace -l 'uprobe:/home/cloud_user/hello:*hello*'

Et ça va vous répondre :

uprobe:/home/cloud_user/hello:main.hello

Ainsi le nom réel de la fonction est main.hello

Si vous ne pouvez pas installer bpftrace, vous pouvez utiliser la commande nm. Elle permet de voir tous les symboles qui sont présents dans un fichier binaire.

Maintenant qu’on a la réponse aux deux questions, nous pouvons créer nos programmes eBPF.

Commençons par le programme de type uProbe.


Créons un programme eBPF de type uProbe
#

Go uprobe

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 2: Cet article