Exploring USDT Probes on Linux

·

Motivation

Enhancing the observability and traceability of system software is crucial for building large, complex infrastructure. Minimizing monitoring overhead is challenging yet rewarding, as it aids in performance analysis and troubleshooting in production environments. While tools like strace and ltrace provide limited insights into system calls and library calls, they introduce significant performance overhead, making them unsuitable for production debugging.

Userland Statically Defined Tracing (USDT)—a feature from DTrace—allows embedding trace points into applications for low-overhead runtime instrumentation. Although Linux lacked native USDT support historically, advancements in kernel event sources (e.g., uprobe) and eBPF have enabled USDT on Linux.


Tracing System

Overview

A Linux tracing system comprises three layers:

  1. Event Sources: Generate tracing data (e.g., tracepoints, kprobes).
  2. Tracing Framework: Kernel-based data collection (e.g., ftrace, eBPF).
  3. Frontend Tools: User interfaces for data visualization (e.g., perf, bcc).

Terminology

Linux Tracing Technical Stack

Event Sources

Tracing Frameworks

Frontend Tools


USDT Implementation

Inside USDT

  1. Compilation: USDT probes are compiled as nop instructions, with metadata stored in ELF's .note.stapstd section.
  2. Runtime: Tools like uprobe replace nop with int3 (breakpoint) to trigger probes.
  3. Semaphores: Optional semaphores enable/disable probes dynamically.

Prerequisites (Ubuntu Example)

sudo apt-get install systemtap-sdt-dev

Registering Probes

Via ftrace (uprobe)

sudo perf probe -x /bin/bash 'readline%return'

Via bcc (eBPF)

sudo /usr/share/bcc/tools/trace -p PID "u:/path/to/binary:probe"

Enabling USDT Programmatically

Libstapsdt

Generates shared objects with USDT probes at runtime using dlopen. Useful for dynamic languages (e.g., Node.js):

const USDT = require("usdt");
const provider = new USDT.USDTProvider("nodeProvider");
provider.addProbe("probeName", "int", "char *");

Wrapping Up

USDT on Linux leverages uprobe and eBPF for low-overhead tracing. Key takeaways:

👉 Explore advanced USDT use cases


FAQ

Q1: What are the overhead implications of USDT?
A1: USDT minimizes overhead by using static probes and optional semaphores for dynamic control.

Q2: Can USDT be used with interpreted languages?
A2: Yes, via runtime wrappers like libstapsdt (e.g., Node.js, Python).

Q3: How does USDT compare to dynamic tracing?
A3: USDT offers predictable low overhead, while dynamic tracing provides flexibility at runtime.

👉 Learn more about Linux tracing tools