# Scenario 6: USDT Probes - Custom Instrumentation ## Learning Objectives - Understand the difference between static and dynamic probes - Add USDT probes to C code - Trace probes with bpftrace - See how probes enable production debugging ## Background **Dynamic probes** (like `perf probe`): Added at runtime, can break on any function **Static probes** (USDT): Compiled into the binary, designed by the developer USDT probes are: - Nearly zero overhead when not traced - Stable API for debuggers/tracers - Self-documenting: probe names describe what's happening - Used by Python, Ruby, MySQL, PostgreSQL, and many other projects ## Prerequisites ```bash # Install USDT support and bpftrace sudo apt install systemtap-sdt-dev bpftrace ``` ## Files - `server.c` - Simulated server with USDT probes at key points ## Exercise 1: Build and Run ```bash make ./server 3 10 ``` This processes 3 batches of 10 requests each. ## Exercise 2: List the Probes ```bash # Using readelf readelf -n ./server | grep -A2 stapsdt # Or using perf sudo perf probe -x ./server --list ``` You should see probes like: - `myserver:server_start` - `myserver:request_start` - `myserver:request_end` - `myserver:request_error` ## Exercise 3: Trace with bpftrace ### Count requests by type ```bash # In terminal 1: sudo bpftrace -e ' usdt:./server:myserver:request_start { @types[arg1] = count(); } END { print(@types); } ' # In terminal 2: ./server 5 50 ``` ### Measure request latency ```bash sudo bpftrace -e ' usdt:./server:myserver:request_start { @start[arg0] = nsecs; } usdt:./server:myserver:request_end { $latency = (nsecs - @start[arg0]) / 1000000; @latency_ms = hist($latency); delete(@start[arg0]); } END { print(@latency_ms); } ' ``` ### Track errors ```bash sudo bpftrace -e ' usdt:./server:myserver:request_error { printf("ERROR: request %d failed with code %d\n", arg0, arg1); @errors = count(); } ' ``` ## Exercise 4: Trace with perf ```bash # Add probe sudo perf probe -x ./server 'myserver:request_start' # Record sudo perf record -e 'probe_server:*' ./server 3 20 # Report sudo perf report ``` ## How USDT Probes Work The `DTRACE_PROBE` macro inserts a NOP instruction: ```c DTRACE_PROBE2(myserver, request_start, req->id, req->type); ``` Compiles to something like: ```asm nop ; placeholder for probe ``` When you activate tracing: 1. Tracer finds the probe location (stored in ELF notes) 2. Replaces NOP with a trap instruction (INT3 on x86) 3. Trap triggers, tracer runs, returns control 4. When tracing stops, NOP is restored **Overhead when not tracing**: ~0 (just a NOP) **Overhead when tracing**: trap + handler execution ## Real-World Uses ### Python has USDT probes: ```bash # If Python was built with --enable-dtrace sudo bpftrace -e 'usdt:/usr/bin/python3:python:function__entry { printf("%s\n", str(arg1)); }' ``` ### MySQL probes for query tracking: ```bash sudo bpftrace -e 'usdt:/usr/sbin/mysqld:mysql:query__start { printf("%s\n", str(arg0)); }' ``` ## Discussion Questions 1. **When would you use USDT vs dynamic probes?** - USDT: Known important points, stable interface - Dynamic: Ad-hoc debugging, no source changes 2. **What's the trade-off of adding probes?** - Pro: Always available for debugging - Con: Must plan ahead, adds code complexity 3. **Why not just use printf debugging?** - Printf has overhead even when you don't need it - USDT has zero overhead until activated - USDT can be traced without rebuilding ## Advanced: Creating Custom Probes The probe macros from ``: ```c DTRACE_PROBE(provider, name) // No arguments DTRACE_PROBE1(provider, name, arg1) // 1 argument DTRACE_PROBE2(provider, name, arg1, arg2) // 2 arguments // ... up to DTRACE_PROBE12 ``` Arguments can be integers or pointers. Strings need special handling. ## Key Takeaways 1. **USDT probes are designed-in observability** 2. **Zero overhead when not actively tracing** 3. **bpftrace makes probe usage easy** 4. **Many production systems already have probes (Python, databases, etc.)** This is an advanced topic - the main takeaway for beginners is that such instrumentation exists and enables powerful production debugging.