Skip to main content
Background Image
  1. Ebpf-Another-Types/

Let’s Observe a Simple Function in Your Program with uProbes

·837 words·4 mins·
Joseph Ligier
Author
Joseph Ligier
CNCF ambassador | Kubestronaut 🐝
Table of Contents
Getting Started with eBPF uProbes in Aya - This article is part of a series.
Part 2: This Article

We saw what a uProbe-type program was in part one: a way to probe the functions of your programs.

In this part, we will first create a very simple program in Go and make it interact with two eBPF programs:

  • The first is a uProbe type
  • The second is a uRetProbe type

I assume you are already in an environment for developing with Aya and that you have installed the go compiler and bpftrace. If not, you can use the Killercoda lab:

Killer coda screenshot


Let’s create a program to test the uProbes
#

A very simple code
#

For a change from Rust, we’re going to create a small program in Go:

// hello.go
package main

import "fmt"

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

func main() {
    ret := hello()
    fmt.Println("Returned:", ret)
}
You can test another compiled program such as C/C++ or Rust if you prefer.

How do we activate uProbes?
#

The purpose of this article is to activate:

  • an eBPF program of type uProbe each time we enter the hello() function:

Go uprobe

  • an eBPF program of type uRetProbe each time we exit the hello() function:

Go uretprobe

Now that we have seen the ins and outs of the article, let’s move on to compiling the program.

Let’s compile the program
#

To compile it, just type:

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

You may notice that we added an option -gcflags="all=-N -l". Normally, you don’t need to use this option to compile a Go program:

go build -o hello hello.go

This also works. So why use this option?

  • -gcflags means go compiler flags; these are options passed to the Go compiler.
  • all means that the options apply to all compiled packages.

The options specified to the Go compiler are:

  • -N: by default, the compiler modifies (mangles) the function name. This option disables this modification.
In English, to mangle means to modify, mutilate, or even disfigure.
  • -l: by default, the compiler inlines functions, i.e., it directly integrates the content of the function into the calling code, thus allowing programs to perform better. This option allows you to disable this inline.

Go inline

These options will allow us to keep the functions and make them readable, which will make it easier for us to find the attachment point for our eBPF program.


How to find the attachment point?
#

Now that we have created and compiled the small program, we need to figure out how to trigger the uProbe or uRetProbe type eBPF program. When using cargo generate for the aya repo, we need to answer two questions: where is the binary name and which function is being observed. Let’s take a closer look.

Name of the binary
#

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

To ensure the portability of the eBPF program, you must answer with the absolute path of the binary. For example, I created it here: /home/cloud_user/hello.

Function name
#

Once you have answered the first question, there is a second question:

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

What should you answer? You might be tempted to answer hello since you set the -N option, which disables name decoration during compilation.

But it’s a little more complicated than that. The Go compiler still slightly modifies the function name during the compilation stage.

As we saw in the previous episode, to find all the functions in a binary, just use bpftrace:

bpftrace -l 'uprobe:/home/cloud_user/hello:*'
  • The -l option lists all available probes
  • uprobe: the type of eBPF program
  • /home/cloud_user/hello: the location of the binary
  • *: the wildcard (0 or more characters)

The display format is then:

uprobe:/home/cloud_user/hello:[function1]
uprobe:/home/cloud_user/hello:[function2]
uprobe:/home/cloud_user/hello:[function3]
uprobe:/home/cloud_user/hello:[function4]
etc

That’s unfortunate: the function name is the same as my file name… We can’t do | grep hello. How can we get around this? With awk, cut, or worse, a regex? Let’s keep it simple:

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

And it will respond with:

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

So the real name of the function is main.hello

If you can’t install bpftrace, you can use the nm command. It allows you to see all the symbols that are present in a binary file.

Now that we have the answers to both questions, we can create our eBPF programs.

Let’s start with the uProbe-type program.


Let’s create an eBPF uProbe program
#

Go uprobe

Let’s test with bpftrace
#

Continued reading reserved for premium members ✨

The full article is only available to premium members.

Becoming a premium member is easy: just make a small donation 💖

In exchange, you will receive for 1 year (early bird offer):

  • Access to all full articles as soon as they are published
  • Early reading before public release
  • Participation in supporting this independent blog
  • Exclusive access to my vacation photos in Dubai

Your donation will help:

  • Make me less dependent on large platforms
  • Encourage me to create more technical content
  • Lift the paywall more quickly for everyone

👉 Become a premium member now

Getting Started with eBPF uProbes in Aya - This article is part of a series.
Part 2: This Article