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

Let’s Profile a Small Program Using eBPF uProbes

·840 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 4: This Article

We saw what a uProbe-type program was in part one: a way to profile a program.

The purpose of this article is therefore to look at the time it takes for a function in a program written in Rust to execute.

This will also allow us to see the difference with a program written in Go for retrieving the attachment points of uProbe-type eBPF programs.

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

Killer coda screenshot


Introduction to the Rust program for testing uProbes
#

Calculating the decimal places of 𝛑
#

Before creating the eBPF program, we will first retrieve a small program in Rust.

To make it a little more inventive than a hello world program, I chose a program that displays an estimate of the number 𝛑. By varying the number of decimal places, we can easily change the execution time of the function.

The number 𝛑 (Pi) is a number that can be used, for example, to calculate the circumference of a circle. This number has the particularity of having an infinite number of decimal places, which makes calculating them complex. In 2022, the first 100 trillion decimal places were determined.

Here are the commands to retrieve and compile it:

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

To test the generated binary, you can then run the following command:

./target/debug/pi_digits_pure_rust 10 #To get 10 decimal places of Pi

Which responds:

3.1415926536

We have 10 decimal places.

The purpose of this article is to retrieve the time taken by the function dedicated to calculating the decimal places of Pi (pi_times_10n_rounded()).

You can test another program compiled as C/C++, Go, Zig, or V of your choice for comparison.

How are we going to proceed?
#

To do this, we will use:

  • a uProbe-type program to measure the time at the start of the function
  • a uRetProbe-type program to measure the time at the end of the function

By subtracting the start time from the end time, we will find the total time required to calculate the decimal places of 𝛑.

profiling uprobe

Before that, we first need to find the attachment point for this Rust-written program for these two eBPF programs.


Finding 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 eBPF program. When using cargo generate for the aya repo, we need to answer two questions: where is the binary name located 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/pi_digits/target/debug/pi_digits_pure_rust.

As the path is a bit long, for the rest of this article, the name of the binary will be given as a relative path for better readability: ./pi_digits_pure_rust)

Function name
#

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

What should we answer?

We’ve already seen this: we can use the bpftrace command, for example:

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

We then find:

uprobe:./pi_digits_pure_rust:pi_times_10n_rounded

The function is therefore pi_times_10n_rounded.

Tips
#

If you pay close attention, in the second part, we saw that, by default, the Go compiler:

  • inlines functions, so we couldn’t see them
    • We had to add the -l compilation option to prevent this
  • modifies the function name (mangle)
    • We had to add the -N compilation option to prevent this

Let’s recall what an inline is (here with the Go compiler) using a diagram:

Go inline

Rust also does this by default. But I didn’t do it at the global level, but for the function in particular.

So I had to add two attributes in the code before the target function:

#[no_mangle]
#[inline(never)]
pub fn pi_times_10n_rounded(n: usize) -> num_bigint::BigInt {
//[...]
}
  • #[no_mangle]: do not modify the function name
  • #[inline(never)]: never inline the function

Now that we have defined the attachment point, we can start creating our two eBPF programs.


Let’s retrieve the launch time of eBPF programs
#

Let’s start simple by creating the two eBPF programs and just retrieving their launch time.

profiling timing

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 4: This Article