Qtap Version: v0.11.3+ Status: Advanced feature for syscall-level debugging
Overview
The --bpf-trace flag enables syscall-level tracing of network operations, providing deep visibility into how applications interact with the Linux kernel. This is an advanced debugging feature that shows:
Nginx accepts incoming HTTP connection from curl on fd 3.
2. Read HTTP Request from Client
Nginx reads 81 bytes of HTTP request from fd 3 (client socket).
3. Write HTTPS Request to Backend
Nginx writes 1806 bytes to fd 11 (backend HTTPS connection).
Note:"ssl": false appears here even though fd 11 is an HTTPS connection. This indicates Qtap is tracking TLS state at the per-process level rather than per-FD.
4. Read HTTPS Response from Backend
Nginx reads multiple chunks from fd 11 (backend response).
5. Write HTTP Response to Client
Nginx writes 941 bytes back to fd 3 (client) using writev.
Critical: This is where the nginx HTTP→HTTPS bug would manifest. If Qtap incorrectly marks the entire process as "has SSL" after seeing fd 11's TLS traffic, it might discard this writev data expecting SSL_write instead.
6. Write Access Log
Nginx writes 89 bytes to fd 4 (access log file). Qtap shows conn_info = NULL because fd 4 is not a network socket.
Filtering Options
Filter by Executable Name
How exe.contains works:
Matches substring anywhere in the executable path
Example: exe.contains:nginx matches both /usr/sbin/nginx and /custom/path/nginx-debug
No Filter (All Processes)
Warning: Tracing all processes generates massive log output and may impact system performance. Always use exe.contains filters in production.
Log Level Requirements
BPF trace output varies by log level:
Log Level
BPF Trace Output
warn
No syscall traces (silent)
info
Full syscall traces ✅ RECOMMENDED
debug
Full syscall traces + additional debug logs
Recommended configuration:
Debugging Use Cases
1. Missing Response Data
Symptom: Qtap captures request but shows 0 bytes received in response.
Diagnosis with BPF trace:
Look for:
writev or write syscalls that should contain response data
Check "ssl": false vs expected TLS state per-FD
Look for process_data messages showing why data was discarded
2. Per-FD TLS State Issues
Symptom: Mixed HTTP/HTTPS connections in same process cause capture failures.
Diagnosis:
Compare syscall traces for different file descriptors:
Check if "ssl": true/false is accurate per-FD
Look for process_data (not ssl) when SSL is expected (or vice versa)
Example buggy behavior:
3. Understanding Data Flow
Trace complete request lifecycle:
Group by file descriptor to see data flow:
Common Patterns
HTTP Server (Single FD)
HTTPS Client (Single FD with SSL)
Reverse Proxy (Two FDs - HTTP + HTTPS)
Troubleshooting
No "eBPF trace" Output
Check 1: Log level
Check 2: Filter matches
Check 3: Traffic generated
Check 4: Correct filter syntax
Too Much Output
Problem: Tracing all processes floods logs.
Solution: Add executable filter:
Syscall Not Appearing
Some syscalls may not appear if:
Different syscall variant used - e.g., readv instead of read
Buffered I/O - Application uses buffering, syscalls occur later
Connection pooling - FD reused, syscalls mixed with other requests
Performance Considerations
BPF trace adds overhead to every syscall:
Traffic Level
Overhead
Recommendation
< 10 req/sec
Negligible
Safe for production debugging
10-100 req/sec
Low (~5%)
Use with caution, monitor CPU
> 100 req/sec
Moderate (10-20%)
Only use in isolated environments
Best practices:
Use exe.contains filters to limit scope
Enable only during active debugging sessions
Monitor disk I/O (logs can grow rapidly)
Use log rotation if enabled long-term
Comparison: Regular Logs vs BPF Trace
Regular Qtap Logs (--log-level=info)
Pros: Human-readable, concise, shows final results Cons: No syscall details, can't debug per-FD issues
Key Takeaway: BPF trace is the most powerful debugging tool for understanding Qtap's eBPF-level behavior, especially for complex scenarios like mixed HTTP/HTTPS proxies.
{
"msg": "syscall/writev", // Syscall name
"caller": "syscall/writev", // Source of the call
"pid": 139929, // Process ID
"exe": "/usr/sbin/nginx", // Executable path
"fd": 3, // File descriptor number
"direction": 1, // 0=ingress (read), 1=egress (write)
"bytes": 941, // Number of bytes transferred
"ssl": false, // Whether SSL/TLS detected on this FD
"protocol": 0, // Protocol detection status
"open": false // Whether connection info is available
}
# Filter to nginx processes
--bpf-trace="mod:socket,exe.contains:nginx"
# Filter to curl processes
--bpf-trace="mod:socket,exe.contains:curl"
# Filter to any process with "python" in the path
--bpf-trace="mod:socket,exe.contains:python"
# Trace all processes (VERY VERBOSE!)
--bpf-trace="mod:socket"
--log-level=info --log-encoding=console
--bpf-trace="mod:socket,exe.contains:nginx"
// Correct: fd 3 is HTTP
{"msg": "syscall/writev", "fd": 3, "ssl": false}
// INCORRECT: fd 11 is HTTPS but shows ssl: false
{"msg": "syscall/write", "fd": 11, "ssl": false}