Fix Rust uprobe tutorial and add CI tests in dedicated workflow (#197)

* Initial plan

* Fix issue #173: Update Rust uprobe documentation to use debug build and correct function symbol

Co-authored-by: yunwei37 <34985212+yunwei37@users.noreply.github.com>

* Add CI tests for Rust uprobe tutorial (issue #173)

Co-authored-by: yunwei37 <34985212+yunwei37@users.noreply.github.com>

* Update src/37-uprobe-rust/README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: 云微 <1067852565@qq.com>

* Update src/37-uprobe-rust/README.zh.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: 云微 <1067852565@qq.com>

* Use wildcard patterns for Rust symbol matching in bpftrace examples

Co-authored-by: yunwei37 <34985212+yunwei37@users.noreply.github.com>

* Add CI test to verify bpftrace can attach to Rust uprobe

Co-authored-by: yunwei37 <34985212+yunwei37@users.noreply.github.com>

* Separate bpftrace tests into new test-bpftrace.yml workflow

Co-authored-by: yunwei37 <34985212+yunwei37@users.noreply.github.com>

---------

Signed-off-by: 云微 <1067852565@qq.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yunwei37 <34985212+yunwei37@users.noreply.github.com>
Co-authored-by: 云微 <1067852565@qq.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Copilot
2026-02-10 12:57:01 -08:00
committed by GitHub
parent 95377603c6
commit 1d9958487a
3 changed files with 117 additions and 52 deletions

View File

@@ -69,9 +69,9 @@ Attaching 1 probe...
Function hello-world called
```
## A strange phenomenon: multiple calls, getting parameters
## Tracing function calls with multiple invocations and getting return values
For a more complex example, which includes multiple calls and parameter fetching:
For a more complex example, which includes multiple calls and retrieving return values:
```rust
use std::env;
@@ -99,18 +99,33 @@ fn main() {
}
```
We repeat a similar operation and notice a strange phenomenon:
First, we need to build in debug mode because the `hello` function gets inlined in release mode and won't have its own symbol:
```console
$ sudo bpftrace -e 'uprobe:args/target/release/helloworld:_ZN10helloworld4main17h2dce92cb81426b91E { printf("Function hello-world called\n"); }'
Attaching 1 probe...
Function hello-world called
$ cd args
$ cargo build
$ nm target/debug/helloworld | grep hello
0000000000016250 t _ZN10helloworld4main17ha3594bca2af541f6E
0000000000016540 t _ZN10helloworld5hello17h5f3a03dda56661e1E
```
At this point we expect the hello function to run several times, but bpftrace only prints out one call:
Note that in release mode (`cargo build --release`), only the `main` function symbol appears because `hello` gets inlined during optimization.
Now we can trace the `hello` function using its symbol. Since Rust mangles symbol names and includes a hash that changes with each compilation, we use a wildcard pattern to match any version of the `hello` function:
```console
$ args/target/release/helloworld 1 2 3 4
$ sudo bpftrace -e 'uprobe:target/debug/helloworld:_ZN10helloworld5hello* { printf("Function hello called\n"); }'
Attaching 1 probe...
Function hello called
Function hello called
Function hello called
Function hello called
```
When we run the program with multiple arguments, bpftrace correctly catches all calls to the `hello` function:
```console
$ ./target/debug/helloworld 1 2 3 4
Hello, world! 1 in 5
return value: 6
Hello, world! 2 in 5
@@ -121,29 +136,18 @@ Hello, world! 4 in 5
return value: 9
```
And it appears that bpftrace cannot correctly get the parameter:
We can also get the return value using Uretprobe. Again, we use a wildcard to match the symbol:
```console
$ sudo bpftrace -e 'uprobe:args/target/release/helloworld:_ZN10helloworld4main17h2dce92cb81426b91E { printf("Function hello-world called %d\n"
, arg0); }'
$ sudo bpftrace -e 'uretprobe:target/debug/helloworld:_ZN10helloworld5hello* { printf("Function hello returned: %d\n", retval); }'
Attaching 1 probe...
Function hello-world called 63642464
Function hello returned: 6
Function hello returned: 7
Function hello returned: 8
Function hello returned: 9
```
The Uretprobe did catch the return value of the first call:
```console
$ sudo bpftrace -e 'uretprobe:args/tar
get/release/helloworld:_ZN10helloworld4main17h2dce92
cb81426b91E { printf("Function hello-world called %d
\n", retval); }'
Attaching 1 probe...
Function hello-world called 6
```
This may due to Rust does not have a stable ABI. Rust, as it has existed so far, has reserved the right to order those struct members any way it wants. So the compiled version of the callee might order the members exactly as above, while the compiled version of the programming calling into the library might think its actually laid out like this:
TODO: Further analysis (to be continued)
Note: The wildcard pattern `_ZN10helloworld5hello*` matches the `hello` function symbol regardless of the hash suffix that Rust adds during compilation. You can use `nm target/debug/helloworld | grep hello` to see the exact symbol names if needed.
## References