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 :
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)
}
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()
:
- un programme eBPF de type uRetProbe à chaque fois que l’on sort de la fonction
hello()
:
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.
-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.
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
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#
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