Writing your first .stp
SystemTap scripts look like a hybrid of C and Awk. The fundamental building block is the probe statement:
probe PROBE_POINT {
BODY
}
A script can contain multiple probe blocks. Each fires independently when its probe point is hit.
A complete working example: a script that probes the kernel function do_sys_open at its entry point, prints the calling process name, PID, and filename on every open, and — once a 10-second timer fires — prints the total count and exits.
Breaking this down:
global open_count— declares a script-global variable. SystemTap variables are untyped; the runtime infers integer vs. string from usage.kernel.function("do_sys_open")— fires at the entry of the kernel C functiondo_sys_open.$filename— accesses the function's local variablefilenamedirectly. The$prefix means "this is a kernel variable, not a script variable."user_string($filename)— safely copies a string from user-space memory into kernel-space. Never dereference user pointers directly.timer.s(10)— a time-based probe that fires every 10 seconds.
Key built-in helper functions:
| Function | Returns |
|---|---|
execname() |
Name of the currently executing process |
pid() |
Process ID |
tid() |
Thread ID |
uid() |
User ID of the calling process |
gettimeofday_s() |
Current Unix timestamp in seconds |
backtrace() |
Kernel stack trace as a string |
pp() |
The probe point that triggered this handler (useful for debugging) |
user_string(ptr) |
Safely read a string from user-space memory |
Aggregates — the killer feature for performance analysis:
SystemTap has built-in statistical aggregation operators that are safe to use from concurrent probe handlers: a .return probe can feed each call's elapsed time — measured between an @entry() capture and the return — into an aggregate with the <<< operator, and a periodic timer probe can then render that aggregate as a log-scale histogram with @hist_log(). These constructs make SystemTap genuinely powerful for latency analysis — not just event logging.