mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-11-01 21:24:12 +08:00
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:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user