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
  • Status bar with recording progress (frame count, dropped frames, DB size)
  • 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 warns 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 N frames (0 = unlimited)
  --db-path PATH                    Custom database path (default: <script>.pyttd.db)
  --max-db-size MB                  Warn 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
  • Tight loops with per-line events have high overhead (hundreds of times slower); use --include to scope recording for compute-heavy code

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.7.0.tar.gz (129.9 kB 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.7.0-cp313-cp313-win_amd64.whl (72.1 kB view details)

Uploaded CPython 3.13Windows x86-64

py_tt_debug-0.7.0-cp313-cp313-win32.whl (68.9 kB view details)

Uploaded CPython 3.13Windows x86

py_tt_debug-0.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (223.8 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

py_tt_debug-0.7.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl (219.5 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ i686

py_tt_debug-0.7.0-cp313-cp313-macosx_11_0_arm64.whl (81.4 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

py_tt_debug-0.7.0-cp312-cp312-win_amd64.whl (72.0 kB view details)

Uploaded CPython 3.12Windows x86-64

py_tt_debug-0.7.0-cp312-cp312-win32.whl (68.9 kB view details)

Uploaded CPython 3.12Windows x86

py_tt_debug-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (223.3 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

py_tt_debug-0.7.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl (218.7 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ i686

py_tt_debug-0.7.0-cp312-cp312-macosx_11_0_arm64.whl (81.3 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

File details

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

File metadata

  • Download URL: py_tt_debug-0.7.0.tar.gz
  • Upload date:
  • Size: 129.9 kB
  • 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.7.0.tar.gz
Algorithm Hash digest
SHA256 1b9d58843c21f076def9421cfae08a838c5ee186f0de35b2b47c5afe7e2f5207
MD5 23802a5edca427fc1009a386f8e75aad
BLAKE2b-256 e5f58834c06958e0b9b54815004faf777e476841706493e70805e5779c4d38d4

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.7.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 56ef18e38774816ef59f4897d4a7b94c7f7369e10bd7a51ce76c87c2097978c9
MD5 b52a5ed9ccd1d64046993bac0902fb9c
BLAKE2b-256 1f1c4a4503e9db9ba69915b46d9d2193c7b7d7cf2ae09743d500180eaeb091d1

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp313-cp313-win32.whl.

File metadata

  • Download URL: py_tt_debug-0.7.0-cp313-cp313-win32.whl
  • Upload date:
  • Size: 68.9 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.7.0-cp313-cp313-win32.whl
Algorithm Hash digest
SHA256 33a863220aa0c06115e0b44c233a7bfd5e27c74f0fa3b339f9b76dfbdb005421
MD5 ff37a595165d66dd12e55e97d4202008
BLAKE2b-256 4951ab3767005adbff4396478dede87ca7bab711a00bcec0740f858fa1253046

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 a796d8dda91bfb7d0d4f4b324fc8cd2178082cda83c39f5afb45c6ec9c2ab47e
MD5 74b5d5c244db5ef824e9cddc11a86582
BLAKE2b-256 d05019d5baec1e8689076dfc38acc9a51edda5b6174b5947c5bd84e2af4bd69f

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.7.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 887da5da0d02f1737f666af3daf129980b8419144f17328a985ce15813bb56c1
MD5 6e79b013b35988a640deecfb49235abf
BLAKE2b-256 c103297124f7d0e6c05ee1ba91fb5ec4b0fd2819c6f9d99a88d9f98eadcc89d8

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.7.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 ac863ee970dfea7495232a9821c9ab39960bbb6d0678b4d776244140b5bc5d95
MD5 45feb4198968b4964bce0e48909ff7cd
BLAKE2b-256 4da6629ebf7e8b1ac6008787f1a3aad7827c51406fa7062fe89fcf43e04774f7

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.7.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 23e96d74d8b20345b9ec22de10d33e2e66dfa482d6549758f1ecbb9023d34280
MD5 3a4a53001b8ba686fe2398f7c19517e6
BLAKE2b-256 9a9bc9d9ef613386d7b8af5a38ce6c61bdac5b3abc1e68c4a03f80fae13d12fb

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp312-cp312-win32.whl.

File metadata

  • Download URL: py_tt_debug-0.7.0-cp312-cp312-win32.whl
  • Upload date:
  • Size: 68.9 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.7.0-cp312-cp312-win32.whl
Algorithm Hash digest
SHA256 7dd9c2236686d33bd49a80d7f4ef8e4708e02f36b36ea7d4a452e2e51fd1f42a
MD5 e88498fe7311b0b7d86ae451e64122ec
BLAKE2b-256 c10b2666aef4ff7d97a13355c933eb474f05f090407214bf04640fe3de8c5014

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 04f4df6db93c92d2d78105ea66af943f2344243e4a4204396288d60b1c8f3464
MD5 226ef1943ed0a565f9f329d15ce4ade8
BLAKE2b-256 46af3fa3a713e79ec63095527a9ceea74fa23f569a3ffcfbce842c8b899a6e63

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.7.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 30f4ff15566a7cc24f43aa8b2edfa9316965cc1648ffbed59b45a0f9c5e5eff0
MD5 caf1b7014ba0a86ef1ce91ee0fd42999
BLAKE2b-256 5d39ae8a06102d0602ad92628a0970c70ce6b68fab2c790696a6200cf83e4a0f

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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.7.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for py_tt_debug-0.7.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b6d972e7ba29f47f1e995ae0c77acdebc44edc8583a259fe900ce3e929f0a1d5
MD5 dd6b33df456b1494ba2ad4d031e46c0a
BLAKE2b-256 43745e95a14adc0581b25ca2c82f7d221d1af2f1ddb0918c583b6be1854663f5

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_tt_debug-0.7.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