Skip to main content

Python profiler using USDT probes for low-overhead performance monitoring with bpftrace

Project description

🐍 pyusdt 🐝

A Python profiler using USDT (User-level Statically Defined Tracing) probes for low-overhead performance monitoring.

Overview

pyusdt instruments Python code execution with USDT probes that can be traced using bpftrace. It uses Python's sys.monitoring API for efficient function-level tracing.

This tool is particularly designed to enable bpftrace workflows where traces need to span both kernel and userspace, allowing you to correlate Python function calls with kernel events in a single trace session.

Zero-overhead when not traced: pyusdt uses dynamic callback registration combined with USDT semaphores. When no tracer is attached, monitoring callbacks are not registered with sys.monitoring, resulting in essentially zero performance impact on your Python code. When bpftrace or another tracer attaches, callbacks are automatically enabled within ~100ms.

Requirements

  • Python 3.12+ (for sys.monitoring API)
  • Linux with USDT support
  • bpftrace

Building

Make sure gcc and Python development headers are installed. Compile the USDT probe extension:

make

This creates libpyusdt.so, a Python C extension module with embedded USDT probes.

Usage

Run any Python script with USDT monitoring:

python -m pyusdt <script.py> [args...]

Example:

python -m pyusdt sleep.py

Configuration

Adjust the polling interval for tracer detection (default: 100ms):

# Check for attached tracers every 50ms
PYUSDT_CHECK_MSEC=50 python -m pyusdt sleep.py

# Check every 500ms (lower overhead, slower tracer detection)
PYUSDT_CHECK_MSEC=500 python -m pyusdt sleep.py

Tracing with bpftrace

Use the included bpftrace script to trace function calls:

sudo bpftrace sample.bt -c "python -m pyusdt sleep.py"

Or attach to a running process:

# In terminal 1:
python -m pyusdt sleep.py

# In terminal 2:
sudo bpftrace sample.bt -p $(pgrep -f "python -m pyusdt")

Example bpftrace Script

Here's a simple one-liner to trace Python function entries:

sudo bpftrace -e 'usdt:./libpyusdt.so:pyusdt:PY_START { printf("%s (%s:%d)\n", str(arg0), str(arg1), arg2); }' -c "python -m pyusdt sleep.py"

Note: When using pip install pyusdt, the library path will be different (installed in your Python site-packages). Use -p <PID> to attach to a running process instead of specifying the library path, and bpftrace will automatically find the loaded library.

Available USDT probes:

  • PY_START - Function entry: (function, file, line, offset)
  • PY_RESUME - Generator/coroutine resume: (function, file, line, offset)
  • PY_RETURN - Function return: (function, file, line, offset, retval)
  • PY_YIELD - Generator yield: (function, file, line, offset, yieldval)
  • CALL - Function call: (function, file, line, offset, callable)
  • LINE - Line execution: (function, file, line)

The included sample.bt script traces all 6 probe types with detailed output.

Testing

Run the test suite:

make test

How it Works

  1. libpyusdt.so - Python C extension module with USDT probe definitions and sys.monitoring integration
  2. pyusdt/__init__.py - Minimal Python wrapper that imports the C extension
  3. pyusdt/__main__.py - Entry point for python -m pyusdt execution
  4. sample.bt - bpftrace script to display traced function calls
  5. usdt.h - Header-only USDT library from libbpf/usdt

When the pyusdt module is imported, the C extension starts a background thread that polls USDT semaphores to detect when a tracer (like bpftrace) attaches. Only when a tracer is active does pyusdt register callbacks with Python's sys.monitoring API (see PEP 669). When the tracer detaches, callbacks are automatically unregistered.

The following monitoring events are captured and exposed as USDT probes when tracing is active:

  • PY_START - Function entry
  • PY_RESUME - Generator/coroutine resumption
  • PY_RETURN - Function return with return value
  • PY_YIELD - Generator yield with yielded value
  • CALL - Function calls
  • LINE - Line-by-line execution

Each event triggers its corresponding USDT probe with relevant context (function name, filename, line number, and event-specific data).

Zero-Overhead Design

pyusdt achieves true zero overhead when not being traced through:

  1. USDT Semaphores: The libbpf/usdt library uses kernel-managed semaphores that are incremented when tracers attach
  2. Dynamic Callback Registration: A background thread polls semaphores every 100ms and only registers sys.monitoring callbacks when needed
  3. Automatic Enable/Disable: When bpftrace attaches, monitoring activates within ~100ms; when it detaches, monitoring stops immediately

This means you can leave pyusdt enabled in production with virtually no performance impact until you need to trace.

References

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyusdt-0.1.1.tar.gz (24.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyusdt-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (30.2 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

pyusdt-0.1.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (29.4 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

pyusdt-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (30.2 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

pyusdt-0.1.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (29.4 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.5+ x86-64

File details

Details for the file pyusdt-0.1.1.tar.gz.

File metadata

  • Download URL: pyusdt-0.1.1.tar.gz
  • Upload date:
  • Size: 24.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyusdt-0.1.1.tar.gz
Algorithm Hash digest
SHA256 9a4d2b025898add727bf5e8ceef670e5283964f47b0626994fcac02d1ce8ef0f
MD5 dd7dbbc1127b9d78da6cb17da7d7f042
BLAKE2b-256 b394b5605a954d5369b8f3cd1f908b35bc8a4b6424807da2a9f388da74695492

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyusdt-0.1.1.tar.gz:

Publisher: release.yml on natoscott/pyusdt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyusdt-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pyusdt-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 41fab4768738138f1e17387ab1d85e811a3e769fd0188b258388be3736f4efe5
MD5 bb2a61c6dd14611e18fb4daf909c6944
BLAKE2b-256 5c6076d09eea26526ffe9d2af810e3f93c94d8d0c288ac53ac720a7181cd60b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyusdt-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on natoscott/pyusdt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyusdt-0.1.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pyusdt-0.1.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e547232ddb1d807f3fc123e9aa7c139c67baaca1fb57247b55e54e950b65afb8
MD5 eae7ed97d02934d92ab66ef90634527b
BLAKE2b-256 718e16d7d25c415c8c5c722ce4161d8e460e1ea8d234eb28daf40a6940a4beaa

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyusdt-0.1.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on natoscott/pyusdt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyusdt-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pyusdt-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 864b2f25cb6ab6cec86a74364312d648f5b32de046648098cb2071a72854aeda
MD5 2ce8c1b7619dc129b85ea49c53d452e5
BLAKE2b-256 cdf86ca0d3d4f70c42eeac6983a02560cbb596bd9a1320e209daa847aea1090b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyusdt-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on natoscott/pyusdt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyusdt-0.1.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pyusdt-0.1.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b76b529ba79a9dc7adf21bb83d2f1c2998b4923d1244517058ecdd3411c97ca9
MD5 8ffe450b6713ba21bf10f43416520c71
BLAKE2b-256 c8f0ec8c6d04b0e76d821c37c71bebb7c6a22cccd012aaa7193a76bace8d30fa

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyusdt-0.1.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on natoscott/pyusdt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page