Skip to main content

Python Time-Travel Debugger — record, replay, and step backward through program execution

Project description

pyttd

CI License: MIT Python 3.12+

pyttd (Python Time-Travel Debugger) is an open-source time-travel debugger for Python with full VSCode integration. It records complete program execution at the C level, then lets you step backward and forward, jump to any point in the trace, and visually scrub through a timeline — all from within your editor.

What is Time-Travel Debugging?

Traditional debuggers only move forward. If you step past a bug, you start over. pyttd records every execution frame during a single run, then drops you into a replay session where you can navigate freely in both directions:

  • Step backward through execution to see exactly how state evolved
  • Reverse continue to find the last time a breakpoint was hit
  • Jump to any frame in the entire recording
  • Scrub a visual timeline to navigate through call depth, exceptions, and execution flow
  • Inspect variables at any point without re-running the program
  • Track variable history across the entire recording
  • Set conditional, function, data, and log breakpoints — all work in both directions
  • Attach to running processes to start recording mid-execution
  • Export traces to Perfetto for external analysis

pyttd is a post-mortem replay debugger — your script runs to completion, and then you debug the recorded trace.

Installation

Requires Python 3.12+ — uses CPython C API features introduced in 3.12.

pip install py-tt-debug

Or from source:

git clone https://github.com/cydonknight/pyttd.git
cd pyttd
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"

Quick Start

CLI

# Record a script
pyttd record examples/hello.py

# Record with arguments
pyttd record examples/hello.py --args --verbose --count 5

# Record only specific functions
pyttd record examples/hello.py --include process_data --include validate

# Query the recording
pyttd query --last-run --frames

# List all runs in a database
pyttd query --list-runs --db hello.pyttd.db

# Replay and jump to a specific frame
pyttd replay --last-run --goto-frame 750

# Export to Perfetto trace format
pyttd export --db hello.pyttd.db -o trace.json
# Open trace.json at https://ui.perfetto.dev

# Clean up database files
pyttd clean --all --dry-run

VSCode

  1. Build the extension: cd vscode-pyttd && npm install && npm run package
  2. In VSCode: Extensions sidebar → ...Install from VSIX → select pyttd-*.vsix
  3. Add to .vscode/launch.json:
{
    "type": "pyttd",
    "request": "launch",
    "name": "Time-Travel Debug",
    "program": "${file}"
}
  1. Press F5 — your script records, then you navigate freely

To replay an existing recording without re-running, use an attach configuration:

{
    "type": "pyttd",
    "request": "attach",
    "name": "Replay Trace",
    "traceDb": "${workspaceFolder}/script.pyttd.db"
}

Python API

from pyttd import ttdbg

@ttdbg
def my_function():
    x = 42
    return x * 2

my_function()  # Records to <file>.pyttd.db

Or with explicit start/stop control:

from pyttd import start_recording, stop_recording

start_recording(db_path="trace.pyttd.db")
# ... your code ...
stats = stop_recording()

Attach to a Running Process

from pyttd import arm, disarm

# Start recording mid-execution
arm()
suspect_function()
stats = disarm()  # Returns recording stats

# Or use as a context manager
with arm():
    suspect_function()

# Or toggle via signal (kill -USR1 <pid>)
from pyttd import install_signal_handler
install_signal_handler()  # First USR1 arms, second disarms

Features

Recording

  • C extension recorder using PEP 523 frame eval hooks (not sys.settrace)
  • Lock-free per-thread SPSC ring buffers with background flush
  • Fork-based checkpointing for fast cold navigation (Linux/macOS)
  • Multi-thread recording with globally ordered sequence numbers
  • Async/await and generator support with coroutine frame tracking
  • I/O hooks for deterministic checkpoint replay — time.time, time.monotonic, time.perf_counter, time.sleep, random.random, random.randint, random.uniform, random.gauss, random.choice, random.sample, random.shuffle, os.urandom, uuid.uuid4, uuid.uuid1, datetime.datetime.now, datetime.datetime.utcnow
  • Secrets filtering — sensitive variable names (password, token, secret, api_key, etc.) automatically redacted during recording; configurable patterns with --secret-patterns
  • Selective function recording — --include / --exclude filter by function name, --include-file / --exclude-file filter by source file (glob patterns)
  • Expandable variable trees — dicts, lists, tuples, sets, and objects with __dict__ are serialized with structure, not just repr()
  • Runtime attach — arm() / disarm() API to start/stop recording from within a running process, or toggle via Unix signal
  • Checkpoint memory tracking and configurable limits

Navigation

  • Forward: step into/over/out, continue with breakpoints
  • Reverse: step back, reverse continue with breakpoints and exception filters
  • Conditional breakpoints — expressions evaluated against frame locals
  • Hit-count breakpoints — stop after N hits (supports >=N, >N, <=N, <N, ==N, %N)
  • Log points — emit structured log messages with variable interpolation without stopping
  • Function breakpoints — break on any call to a named function
  • Data breakpoints — break when a variable's value changes
  • Jump: goto frame, goto targets (all executions of a line), restart frame
  • Warm navigation (SQLite, sub-ms) for stepping; cold navigation (checkpoint restore, 50-300ms) for jumps

VSCode Extension

  • Full DAP implementation with step-back and reverse-continue
  • Canvas-based timeline scrubber with click/drag/zoom
  • CodeLens annotations showing call and exception counts per function
  • Inline variable values during stepping
  • Call history tree with lazy-loaded nesting and exception markers
  • Exception breakpoint filters (uncaught, all raised)
  • Function breakpoints, data breakpoints, conditional breakpoints, hit conditions, log points
  • Variable history tracking across the recording
  • Breakpoint verification — validates that breakpoints target executable lines; condition eval errors shown in Debug Console
  • Status bar with recording progress (frame count, dropped frames, DB size, checkpoint count and memory)
  • Keyboard shortcuts: Ctrl+Shift+F11 (step back), Ctrl+Shift+F5 (reverse continue)

Analysis & Export

  • Perfetto/Chrome Trace Event Format export — viewable in ui.perfetto.dev; preserves multi-thread structure
  • Variable history queries — track how a variable changes over time, with deduplication
  • Execution stats — per-function call counts, exception counts, and entry points
  • Checkpoint memory profiling — per-checkpoint RSS tracking

Database Management

  • Multi-run storage — multiple recording runs in a single database
  • Run eviction — --keep-runs N auto-evicts old runs; pyttd clean --keep N for manual cleanup
  • Custom DB paths — --db-path overrides the default <script>.pyttd.db location
  • Size monitoring — --max-db-size auto-stops recording when the database exceeds a threshold
  • Run selection — --run-id to query, replay, or export a specific run by UUID or prefix

CLI Reference

pyttd [--version] [-v|--verbose] <command>

Commands:
  record    Record script execution
  query     Query trace data
  replay    Replay a recorded session
  serve     Start JSON-RPC debug server (used by VSCode)
  export    Export trace data
  clean     Clean up database files

record

pyttd record script.py [options]

Options:
  --module                          Treat argument as a module name (e.g., pkg.mod)
  --checkpoint-interval N           Frames between checkpoints (default: 1000)
  --args VALUE [VALUE ...]          Arguments to pass to the script
  --no-redact                       Disable secrets redaction
  --secret-patterns PAT             Additional pattern to redact (repeatable)
  --include FUNC                    Only record functions matching this pattern (repeatable)
  --include-file GLOB               Only record functions in files matching this glob (repeatable)
  --exclude FUNC                    Exclude functions matching this pattern (repeatable)
  --exclude-file GLOB               Exclude files matching this glob (repeatable)
  --max-frames N                    Stop recording after approximately N frames (0 = unlimited)
  --db-path PATH                    Custom database path (default: <script>.pyttd.db)
  --max-db-size MB                  Auto-stop recording when DB exceeds this size in MB (0 = unlimited)
  --keep-runs N                     Keep only last N runs, evict older (0 = keep all)
  --checkpoint-memory-limit MB      Checkpoint memory limit in MB (0 = unlimited)

query

pyttd query [--last-run | --run-id UUID] [--list-runs] [--frames] [--limit N] [--db path.pyttd.db]

replay

pyttd replay [--last-run | --run-id UUID] --goto-frame N [--db path.pyttd.db]

serve

# Record and serve (used by VSCode extension)
pyttd serve --script script.py [--module] [--cwd DIR] [--checkpoint-interval N]
    [--include FUNC] [--exclude FUNC] [--include-file GLOB] [--exclude-file GLOB]
    [--max-frames N] [--env KEY=VALUE ...] [--env-file .env]
    [--db-path PATH] [--max-db-size MB] [--keep-runs N]

# Replay existing recording (no re-recording)
pyttd serve --db path.pyttd.db [--run-id UUID]

export

pyttd export --format perfetto --db path.pyttd.db [--run-id UUID] -o trace.json

clean

pyttd clean [--db path.pyttd.db] [--all] [--keep N] [--dry-run]

Options:
  --db PATH      Specific database file to clean
  --all          Delete all .pyttd.db files in current directory
  --keep N       Keep last N runs, evict the rest
  --dry-run      Show what would be deleted without deleting

Environment Variables

Variable Description
PYTTD_RECORDING Set to 1 during active recording; scripts can check os.environ.get('PYTTD_RECORDING')
PYTTD_ARM_SIGNAL Auto-install signal handler on import — e.g., PYTTD_ARM_SIGNAL=USR1 installs a SIGUSR1 toggle handler

Architecture

Three-layer system:

Layer Technology Responsibility
C Extension (pyttd_native) C, CPython API Frame recording, ring buffer, checkpoints, I/O hooks
Python Backend (pyttd/) Python, Peewee, SQLite JSON-RPC server, session navigation, query API
VSCode Extension (vscode-pyttd/) TypeScript DAP handlers, timeline webview, CodeLens, inline values

See docs/architecture.md for the full design.

Platform Support

Platform Recording Warm Navigation Cold Navigation Multi-Thread
Linux Full Full Full Full
macOS Full Full Partial* Full
Windows Full Full None Full

* macOS: checkpoints skip when multiple threads are active.

Requirements

  • Python >= 3.12 (required for PyUnstable_InterpreterFrame_* C API)
  • C compiler (GCC, Clang, or MSVC)
  • VSCode (for the extension; CLI works standalone)

Known Limitations

  • Expression evaluation operates on recorded snapshots, not live values
  • C extension internals are opaque (third-party C extension objects may have uninformative repr())
  • Windows: no cold navigation (no fork())
  • exception_unwind line number is from function entry, not the exception site
  • Variable repr strings are capped at 256 characters
  • Expandable variable children are capped at 50 entries, 1 level deep
  • Attach mode (arm()) disables checkpoints — cold navigation is unavailable for attached recordings; the initial call stack is synthesized from frame inspection at arm time
  • Tight loops with per-line events have high overhead (hundreds of times slower); use --include to scope recording for compute-heavy code
  • --max-frames is approximate — the actual frame count may slightly exceed the limit because events already in flight complete before the stop signal takes effect
  • Log points capture only the first hit per continue — if a log breakpoint's line executes multiple times before the next stopping point, only the first occurrence is logged

Testing

351 Python tests across 26 test modules + 95 VSCode extension (Mocha) tests:

# Run all Python tests
.venv/bin/pytest tests/ -v

# Run VSCode extension tests
cd vscode-pyttd && npm test

# Run overhead benchmarks
.venv/bin/python3 benchmarks/bench_overhead.py

Benchmarks cover I/O-bound, compute-bound, tight loop, deep recursion, many locals, and multi-thread workloads.

Documentation

Development guides: Building | Testing | C Extension | Protocol | Releasing

Contributing

Contributions welcome across C, Python, and TypeScript. See CONTRIBUTING.md for setup and guidelines.

License

MIT License. See LICENSE for details.

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

py_tt_debug-0.8.0.tar.gz (2.7 MB view details)

Uploaded Source

Built Distributions

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

py_tt_debug-0.8.0-cp313-cp313-win_amd64.whl (642.6 kB view details)

Uploaded CPython 3.13Windows x86-64

py_tt_debug-0.8.0-cp313-cp313-win32.whl (548.0 kB view details)

Uploaded CPython 3.13Windows x86

py_tt_debug-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (990.7 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

py_tt_debug-0.8.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl (1.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ i686

py_tt_debug-0.8.0-cp313-cp313-macosx_11_0_arm64.whl (85.7 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

py_tt_debug-0.8.0-cp312-cp312-win_amd64.whl (642.6 kB view details)

Uploaded CPython 3.12Windows x86-64

py_tt_debug-0.8.0-cp312-cp312-win32.whl (548.0 kB view details)

Uploaded CPython 3.12Windows x86

py_tt_debug-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (990.0 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

py_tt_debug-0.8.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl (1.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ i686

py_tt_debug-0.8.0-cp312-cp312-macosx_11_0_arm64.whl (85.7 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

File details

Details for the file py_tt_debug-0.8.0.tar.gz.

File metadata

  • Download URL: py_tt_debug-0.8.0.tar.gz
  • Upload date:
  • Size: 2.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for py_tt_debug-0.8.0.tar.gz
Algorithm Hash digest
SHA256 3069c9213c1fbd8bd5bc62ae09344564b9507e8bbea935619787456f47299fde
MD5 bea3abd69d5b1a152cbfaf281d657334
BLAKE2b-256 1d89f4d2f64e04f5e47fc2d35528cdf18008abd6a3999e593c10f4f4a2116158

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0.tar.gz:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: py_tt_debug-0.8.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 642.6 kB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for py_tt_debug-0.8.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 7fdaeae3d4b163664baad60b887fdc68573d517ebb2b6672059330b064e8bee9
MD5 169567bc8d7b5bed23fcaca7950be2b7
BLAKE2b-256 d0796257c50d4e94f773c236c4982668535fe85e8c2b73d80c0b8a0a563cdcc8

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp313-cp313-win_amd64.whl:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp313-cp313-win32.whl.

File metadata

  • Download URL: py_tt_debug-0.8.0-cp313-cp313-win32.whl
  • Upload date:
  • Size: 548.0 kB
  • Tags: CPython 3.13, Windows x86
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for py_tt_debug-0.8.0-cp313-cp313-win32.whl
Algorithm Hash digest
SHA256 98947495d345bda974bf8077f1771c6da78816166186acaeab3ed1a105d3fa7e
MD5 2ebf3722de83732bc3f9494482436952
BLAKE2b-256 c6324e339f18509f85edb9ce7feb15b5813503716ef7f1164f70f0994fd475d6

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp313-cp313-win32.whl:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 067d0b21eaaf05700ed82470cc2b763a9f134446c956a99f284df6f8c5e3677b
MD5 657441efedb5c001b67e0186dc56ee84
BLAKE2b-256 fb382060b49172dfd6dc5a7f95e7471663e54fad2cd8933b3d49cd814c948feb

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.8.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 40503a772565c6d47916bc3c4fc01ca57dd4388f8b255172b00e800cecf9ecb9
MD5 f510a3a07e7a94c8eb1d4923ed954cb8
BLAKE2b-256 8c482f4ca4140ff39324d0845722cd83a0e5e64d91db0dba4893f590d17d93b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.8.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b6c03f329f94f99ec8fb73e6ade1159713bc022d4ddd7dd196931e08c56cdcee
MD5 173b7365f012a43283f3249e89d77e4e
BLAKE2b-256 30d516056905cace32049aa6537830ecc47aaa77ffa4e80f434c04af1fe8de8f

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: py_tt_debug-0.8.0-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 642.6 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for py_tt_debug-0.8.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 0758b012eed692ad05ed0c235cfcaf3ff13c118b590db0e0e893b52037da8e48
MD5 51f28ca7ae6fd1cf48bcb3e47dafa20f
BLAKE2b-256 ab2f2d706a187cb0d1b69667d8a0d5bb111abfbfd288cf8b30f813c27c738ba1

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp312-cp312-win_amd64.whl:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp312-cp312-win32.whl.

File metadata

  • Download URL: py_tt_debug-0.8.0-cp312-cp312-win32.whl
  • Upload date:
  • Size: 548.0 kB
  • Tags: CPython 3.12, Windows x86
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for py_tt_debug-0.8.0-cp312-cp312-win32.whl
Algorithm Hash digest
SHA256 fe44753bb7b7aff237df85794bd4f21a854c9e11d882aa7366d83022d490339a
MD5 8fef93e4825d2a827f58b0106c72ab38
BLAKE2b-256 8d70245bb13ad224f80c3c8802a54c3feefd5a0c72faa44c51024f1ad411b859

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp312-cp312-win32.whl:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7b8ecd0058b9464d614ce4aef25c25b8912700963f320b93eebcdbc1134d3d2b
MD5 d5921dc6c08a6776a5ca23b7212e1485
BLAKE2b-256 168a6edf5af866044c7774affaa63e523aa6083d7dc2541d4a5e2568f907c8a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.8.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 cbc951acf072c34fc712416974c4dde3717133c024129581e46c4f006bf24d6c
MD5 dfa2c7acbd3ff2b6efe3a7ba503fc682
BLAKE2b-256 7a80dcc5e2bcec4b2c6081a15dbfb775f474454f5278a0be907181e1fee30630

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl:

Publisher: release.yml on cydonknight/pyttd

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

File details

Details for the file py_tt_debug-0.8.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.8.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 29344a9bd43d824071f82e7ed77eea8a46a2d9d314bf2dc2fb3f1a81618c17b8
MD5 3875a6cf4ce0a52bbaa792912eef4f17
BLAKE2b-256 ebd8325d8b1f3fcc2fbea1364f4fb587e50892b2b1c6445c5c136f41481b5562

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.8.0-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: release.yml on cydonknight/pyttd

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