Skip to content

uprobes Usage

Linux sysadmins often need to trace user-space applications for performance analysis or debugging. Unlike kernel probes (kprobes), ** uprobes** are designed to trace functions in user-space programs and shared libraries with minimal overhead. By leveraging eBPF (Extended Berkeley Packet Filter), uprobes allow you to instrument arbitrary user-space code, such as C libraries or custom binaries, without modifying the source or recompiling. This makes them ideal for monitoring application behavior in production environments.


Key Concepts

Uprobes work by attaching to specific memory addresses in user-space programs. These addresses can correspond to function symbols (e.g., main, strcpy) or raw memory offsets. When a probe is triggered, eBPF programs execute to collect data, such as function arguments, return values, or timestamps. The key advantages include:

  • Low overhead: Uprobes avoid the need for kernel module loading or invasive instrumentation.
  • Flexibility: Trace any function in user-space binaries or shared libraries.
  • Precision: Capture detailed context (e.g., stack traces, arguments) with minimal performance impact.

Setting Up BCC for Uprobe Tracing

The BCC (BPF Compiler Collection) toolkit provides utilities like bpftrace and trace to manage uprobes. Ensure BCC is installed on your system:

sudo apt install bpftrace  # Debian/Ubuntu
sudo dnf install bpftrace  # Fedora

To trace a user-space function, use the uprobe keyword in a BPF program. For example, to trace the main function of a binary:

sudo bpftrace -e 'uprobe:/usr/bin/myapp:main { printf("Hit main()\n"); }'

Replace /usr/bin/myapp with the full path to your target binary. The function name must match exactly (case-sensitive).


Tracing User-space Functions

Example 1: Trace a Specific Function

To trace the strdup function from glibc:

sudo bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libc.so.6:strdup { printf("strdup called\n"); }'

Example 2: Capture Return Values

Use uretprobe to trace return values. For example, to log the length of a string copied by strcpy:

sudo bpftrace -e '
uprobe:/usr/bin/myapp:strcpy { arg1 = str(arg1); }
uretprobe:/usr/bin/myapp:strcpy { printf("Copied %s (len=%d)\n", arg1, __arg1); }
'

Example 3: Trace Dynamic Libraries

For shared libraries, specify the full path to the .so file:

sudo bpftrace -e 'uprobe:/usr/lib/x86_64-linux-gnu/libcurl.so.4:curl_easy_perform { printf("curl_easy_perform called\n"); }'

Handling Shared Libraries and Symbol Resolution

If the target binary is stripped of symbols, you may need to use memory addresses instead of function names. For example:

sudo bpftrace -e 'uprobe:/path/to/bin:0x400500 { printf("Hit probe at 0x400500\n"); }'

Use tools like readelf or nm to find symbol addresses in unstripped binaries. For shared libraries, ensure the path is correct and the library is loaded at runtime.


Troubleshooting

  • Function not found: Verify the binary path and function name. Use nm or objdump to check symbols.
  • Permissions: Ensure the tracer has access to the target binary (e.g., run with sudo).
  • Stripped binaries: Use memory addresses or attach to shared libraries with known paths.

Key takeaways

  • Uprobes enable low-overhead tracing of user-space functions and shared libraries.
  • Use BCC tools like bpftrace to attach probes to specific functions or memory addresses.
  • Combine uprobe with uretprobe to capture function arguments and return values.
  • Handle stripped binaries by using memory offsets or tracing shared libraries.
  • Always validate paths and symbols to avoid probe failures.