py-tt-debug 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. py_tt_debug-0.3.0/CHANGELOG.md +201 -0
  2. py_tt_debug-0.3.0/LICENSE +21 -0
  3. py_tt_debug-0.3.0/MANIFEST.in +7 -0
  4. py_tt_debug-0.3.0/PKG-INFO +193 -0
  5. py_tt_debug-0.3.0/README.md +163 -0
  6. py_tt_debug-0.3.0/ext/checkpoint.c +470 -0
  7. py_tt_debug-0.3.0/ext/checkpoint.h +26 -0
  8. py_tt_debug-0.3.0/ext/checkpoint_store.c +255 -0
  9. py_tt_debug-0.3.0/ext/checkpoint_store.h +55 -0
  10. py_tt_debug-0.3.0/ext/frame_event.h +18 -0
  11. py_tt_debug-0.3.0/ext/iohook.c +515 -0
  12. py_tt_debug-0.3.0/ext/iohook.h +11 -0
  13. py_tt_debug-0.3.0/ext/platform.h +41 -0
  14. py_tt_debug-0.3.0/ext/pyttd_native.c +52 -0
  15. py_tt_debug-0.3.0/ext/recorder.c +1373 -0
  16. py_tt_debug-0.3.0/ext/recorder.h +51 -0
  17. py_tt_debug-0.3.0/ext/replay.c +176 -0
  18. py_tt_debug-0.3.0/ext/replay.h +7 -0
  19. py_tt_debug-0.3.0/ext/ringbuf.c +366 -0
  20. py_tt_debug-0.3.0/ext/ringbuf.h +90 -0
  21. py_tt_debug-0.3.0/py_tt_debug.egg-info/PKG-INFO +193 -0
  22. py_tt_debug-0.3.0/py_tt_debug.egg-info/SOURCES.txt +69 -0
  23. py_tt_debug-0.3.0/py_tt_debug.egg-info/dependency_links.txt +1 -0
  24. py_tt_debug-0.3.0/py_tt_debug.egg-info/entry_points.txt +2 -0
  25. py_tt_debug-0.3.0/py_tt_debug.egg-info/requires.txt +6 -0
  26. py_tt_debug-0.3.0/py_tt_debug.egg-info/top_level.txt +2 -0
  27. py_tt_debug-0.3.0/pyproject.toml +56 -0
  28. py_tt_debug-0.3.0/pyttd/__init__.py +5 -0
  29. py_tt_debug-0.3.0/pyttd/__main__.py +2 -0
  30. py_tt_debug-0.3.0/pyttd/cli.py +198 -0
  31. py_tt_debug-0.3.0/pyttd/config.py +17 -0
  32. py_tt_debug-0.3.0/pyttd/errors.py +6 -0
  33. py_tt_debug-0.3.0/pyttd/main.py +25 -0
  34. py_tt_debug-0.3.0/pyttd/models/__init__.py +6 -0
  35. py_tt_debug-0.3.0/pyttd/models/base.py +8 -0
  36. py_tt_debug-0.3.0/pyttd/models/checkpoints.py +15 -0
  37. py_tt_debug-0.3.0/pyttd/models/constants.py +8 -0
  38. py_tt_debug-0.3.0/pyttd/models/frames.py +27 -0
  39. py_tt_debug-0.3.0/pyttd/models/io_events.py +17 -0
  40. py_tt_debug-0.3.0/pyttd/models/runs.py +11 -0
  41. py_tt_debug-0.3.0/pyttd/models/storage.py +40 -0
  42. py_tt_debug-0.3.0/pyttd/models/timeline.py +80 -0
  43. py_tt_debug-0.3.0/pyttd/protocol.py +86 -0
  44. py_tt_debug-0.3.0/pyttd/py.typed +0 -0
  45. py_tt_debug-0.3.0/pyttd/query.py +29 -0
  46. py_tt_debug-0.3.0/pyttd/recorder.py +121 -0
  47. py_tt_debug-0.3.0/pyttd/replay.py +58 -0
  48. py_tt_debug-0.3.0/pyttd/runner.py +43 -0
  49. py_tt_debug-0.3.0/pyttd/server.py +603 -0
  50. py_tt_debug-0.3.0/pyttd/session.py +688 -0
  51. py_tt_debug-0.3.0/pyttd/tracing/__init__.py +0 -0
  52. py_tt_debug-0.3.0/pyttd/tracing/constants.py +23 -0
  53. py_tt_debug-0.3.0/pyttd/tracing/enums.py +8 -0
  54. py_tt_debug-0.3.0/setup.cfg +4 -0
  55. py_tt_debug-0.3.0/setup.py +28 -0
  56. py_tt_debug-0.3.0/tests/__init__.py +0 -0
  57. py_tt_debug-0.3.0/tests/conftest.py +72 -0
  58. py_tt_debug-0.3.0/tests/test_checkpoint.py +183 -0
  59. py_tt_debug-0.3.0/tests/test_iohook.py +159 -0
  60. py_tt_debug-0.3.0/tests/test_models.py +82 -0
  61. py_tt_debug-0.3.0/tests/test_multithread.py +324 -0
  62. py_tt_debug-0.3.0/tests/test_native_stub.py +35 -0
  63. py_tt_debug-0.3.0/tests/test_phase6.py +299 -0
  64. py_tt_debug-0.3.0/tests/test_phase7.py +348 -0
  65. py_tt_debug-0.3.0/tests/test_recorder.py +175 -0
  66. py_tt_debug-0.3.0/tests/test_replay.py +145 -0
  67. py_tt_debug-0.3.0/tests/test_reverse_nav.py +298 -0
  68. py_tt_debug-0.3.0/tests/test_ringbuf.py +32 -0
  69. py_tt_debug-0.3.0/tests/test_server.py +454 -0
  70. py_tt_debug-0.3.0/tests/test_session.py +377 -0
  71. py_tt_debug-0.3.0/tests/test_timeline.py +234 -0
@@ -0,0 +1,201 @@
1
+ # Changelog
2
+
3
+ All notable changes to pyttd are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+
7
+ ## [0.3.0] - 2026-03-17
8
+
9
+ ### Added
10
+
11
+ #### Multi-Thread Recording
12
+ - All Python threads are now recorded with per-thread call stacks and global sequence ordering
13
+ - Per-thread SPSC ring buffers with lazy allocation on first frame entry
14
+ - Atomic sequence counter (`atomic_fetch_add`) for globally ordered events across threads
15
+ - Thread-local storage (TLS) for `call_depth` and `inside_repr` guards
16
+ - `ExecutionFrames.thread_id` field stores actual OS thread ID (`BigIntegerField`)
17
+ - Thread-aware navigation: `step_over`/`step_out` stay on current thread; `step_into`/`step_back` follow global sequence
18
+ - `Session.get_threads()` returns all threads seen during recording with names
19
+ - Stack reconstruction filters by target thread's ID
20
+ - Checkpoint skip guard: checkpoints are not created when multiple threads are active (fork is unsafe with threads)
21
+ - pthread key destructor marks orphaned thread buffers for flush thread cleanup
22
+
23
+ #### Tests
24
+ - 12 new multi-thread tests covering per-thread recording, stacks, thread-aware navigation
25
+
26
+ ### Changed
27
+ - Ring buffer system redesigned: per-thread SPSC buffers instead of single global buffer
28
+ - Main thread gets 8MB string pools; secondary threads get 2MB pools
29
+ - `g_sequence_counter` changed from static to atomic global for cross-thread visibility
30
+ - `g_call_depth` and `g_inside_repr` changed from static to TLS (`PYTTD_THREAD_LOCAL`)
31
+
32
+ ## [0.2.0] - 2026-03-15
33
+
34
+ ### Added
35
+
36
+ #### Fork-Based Checkpointing (Phase 2)
37
+ - `fork()` creates full-process snapshots for cold navigation
38
+ - Checkpoint store with static array of 32 entries and smallest-gap thinning eviction
39
+ - Pipe-based IPC protocol: 9-byte commands (RESUME/STEP/DIE) with length-prefixed JSON results
40
+ - Fast-forward mode in checkpoint children (counts sequence numbers without serialization)
41
+ - `PyOS_BeforeFork`/`PyOS_AfterFork_Child`/`PyOS_AfterFork_Parent` for Python 3.13+ compatibility
42
+ - Pre-fork flush thread synchronization via condvar protocol
43
+ - `Checkpoint` Peewee model for tracking checkpoint metadata
44
+ - `ReplayController` with `goto_frame` (cold) and `warm_goto_frame` (warm-only) methods
45
+ - `pyttd replay --last-run --goto-frame N` CLI subcommand
46
+
47
+ #### JSON-RPC Server & Debug Adapter (Phase 3)
48
+ - `pyttd serve --script s.py` starts JSON-RPC server over TCP (localhost only)
49
+ - Content-Length framed JSON-RPC protocol (`JsonRpcConnection` class)
50
+ - Two-thread server model: RPC thread (selector-based event loop) + recording thread
51
+ - Stdout/stderr capture via `os.pipe()` + `os.dup2()`
52
+ - Port handshake: server writes `PYTTD_PORT:<port>` to stdout
53
+ - Session navigation: `step_into`, `step_over`, `step_out`, `continue_forward` with breakpoints and exception filters
54
+ - Stack reconstruction from frame events with push/pop on call/return
55
+ - Variable queries (`get_variables_at`) with type inference from `repr()` values
56
+ - Expression evaluation (`evaluate_at`) for hover/watch/repl contexts
57
+ - VSCode extension with inline debug adapter (`DebugAdapterInlineImplementation`)
58
+ - Full DAP handler implementations for forward navigation
59
+ - Backend connection with spawn, TCP connect, JSON-RPC request/response correlation
60
+
61
+ #### Time-Travel Navigation (Phase 4)
62
+ - `step_back` — previous `line` event (always warm, sub-ms)
63
+ - `reverse_continue` — backward scan with breakpoint and exception filter matching
64
+ - `goto_frame` — jump to any frame by sequence number with line-snapping
65
+ - `goto_targets` — find all executions at a file:line (capped at 1000)
66
+ - `restart_frame` — jump to first line of a function containing a given frame
67
+ - Stack cache optimization for large backward jumps
68
+ - I/O hooks for deterministic cold replay: `time.time`, `time.monotonic`, `time.perf_counter`, `random.random`, `random.randint`, `os.urandom`
69
+ - Type-specific I/O serialization: IEEE 754 doubles for floats, length-prefixed for ints/bytes
70
+ - I/O replay mode in checkpoint children with pre-loaded event cursor
71
+ - `IOEvent` Peewee model for I/O event storage
72
+ - DAP `supportsStepBack`, `supportsGotoTargetsRequest`, `supportsRestartFrame` capabilities
73
+
74
+ #### Timeline Scrubber (Phase 5)
75
+ - Canvas-based timeline webview in the Debug sidebar
76
+ - SQL bucket aggregation with configurable bucket count for timeline summary queries
77
+ - Vertical bars scaled by call depth, color-coded (blue=normal, red=exception, orange=breakpoint)
78
+ - Yellow cursor line with triangle marker for current position
79
+ - Click to navigate, drag with 150ms throttle, mousewheel zoom
80
+ - Keyboard shortcuts: arrows=step, Home/End=bounds, PageUp/PageDown=zoom
81
+ - Zoom cache (max 4 entries) for responsive interaction
82
+ - DPR-aware canvas rendering with `ResizeObserver`
83
+ - Custom DAP events: `pyttd/timelineData` (bucket data), `pyttd/positionChanged` (cursor sync)
84
+ - Breakpoint markers update immediately on `setBreakPointsRequest`
85
+
86
+ #### CodeLens, Inline Values, Call History (Phase 6)
87
+ - CodeLens annotations above traced functions showing "TTD: N calls | M exceptions"
88
+ - Click CodeLens to navigate to first execution of a function
89
+ - `InlineValuesProvider` displays variable values inline during stepping
90
+ - Call history tree in Debug sidebar with lazy-loaded nesting via `get_call_children`
91
+ - Exception icons and incomplete call markers in call history
92
+ - `get_traced_files` RPC — distinct filenames from recording
93
+ - `get_execution_stats` RPC — per-function call/exception counts via GROUP BY with CASE WHEN
94
+ - `get_call_children` RPC — call/return pairing at target depth for tree loading
95
+
96
+ #### Polish, Packaging, CI (Phase 7)
97
+ - `pyttd --version` prints version
98
+ - `pyttd -v` / `--verbose` enables debug logging
99
+ - `pyttd serve --db path.pyttd.db` replay-only mode (no recording phase)
100
+ - `pyttd record` and `pyttd serve --script` validate script exists before starting
101
+ - `PYTTD_RECORDING=1` environment variable set during recording, cleared after stop
102
+ - Protocol robustness: 1 MB header accumulation limit, 10 MB Content-Length limit, non-ASCII header rejection
103
+ - PyPI packaging: pyproject.toml with classifiers, URLs, readme, cibuildwheel config
104
+ - `MANIFEST.in` for source distribution (headers, py.typed, tests)
105
+ - `py.typed` marker (PEP 561) for type checking support
106
+ - GitHub Actions CI: test matrix (Python 3.12/3.13, Linux/macOS), ASAN build, sdist smoke test
107
+
108
+ #### Tests
109
+ - Test count grew from 26 (v0.1.0) to 147 across 14 test files
110
+ - 70 VSCode extension Mocha tests (backendConnection, debugSession, providers)
111
+
112
+ ### Fixed
113
+
114
+ These fixes were developed between v0.1.0 and v0.2.0:
115
+
116
+ #### Critical (crash/data corruption prevention)
117
+
118
+ - **Data race on string pool `producer_idx`** — `ringbuf_pool_swap()` and `ringbuf_pool_reset_consumer()` were called outside the GIL in `flush_batch()`, racing with the producer thread. Moved both operations inside the GIL-protected section to serialize access. (`ext/recorder.c`)
119
+ - **NULL dereference in `flush_batch` on OOM** — `Py_DECREF(NULL)` (undefined behavior) if any `PyLong_From*`/`PyFloat_From*`/`PyUnicode_FromString` returned NULL. Added NULL checks for all 8 allocations per event; on failure, skips the event gracefully. (`ext/recorder.c`)
120
+ - **NULL dereference from `PyUnicode_AsUTF8`** — eval hook and all three trace function cases (`PyTrace_LINE`, `PyTrace_RETURN`, `PyTrace_EXCEPTION`) passed potentially-NULL strings to `should_ignore()`, `strstr()`, and `ringbuf_push()`. Added NULL checks; on failure, clears the error and skips the frame. (`ext/recorder.c`)
121
+ - **`strdup` NULL stored in ignore filters** — if `strdup()` returned NULL (OOM), the NULL pointer was stored and later passed to `strstr()`/`strcmp()` (undefined behavior). Now only stores and increments count on successful `strdup`. (`ext/recorder.c`)
122
+ - **`request_stop` interrupted the flush thread** — the stop-request check in the eval hook fired before the main-thread check, so the flush thread's Python calls (GIL-acquired imports, `db.close()`) would receive a spurious `KeyboardInterrupt`. Stop check is now gated on `g_main_thread_id`. (`ext/recorder.c`)
123
+
124
+ #### Correctness
125
+
126
+ - **`flush_interval_ms` parameter was ignored** — the parameter was parsed by `start_recording` but the flush thread hardcoded 10ms. Added `g_flush_interval_ms` global; the flush thread now uses the configured value on both POSIX and Windows. (`ext/recorder.c`)
127
+ - **Version-gated macros used dead `#elif defined()` branch** — `defined(_PyInterpreterState_SetEvalFrameFunc)` never matches (it's a function declaration, not a macro), making the `#elif` and `#else` branches identical dead code. Replaced with a clean `PY_VERSION_HEX` range check: `>= 0x030F0000` for 3.15+ and `#else` for 3.12-3.14. (`ext/recorder.c`)
128
+ - **`_cmd_query` created empty DB if file didn't exist** — `get_last_run()` called `connect_to_db()` which silently creates a new database, then `Runs.select().get()` raised an unhelpful `DoesNotExist`. Added `os.path.exists()` check with a clear error message before connecting. (`pyttd/cli.py`)
129
+ - **`sys.path[0]` restoration could raise `IndexError`** — if a user script cleared `sys.path`, the `finally` block's `sys.path[0] = old_path0` would crash. Now checks `len(sys.path) > 0` first and falls back to `sys.path.insert(0, ...)`. (`pyttd/runner.py`)
130
+ - **Negative `buffer_size` caused huge allocation** — a negative `int` silently cast to a large `uint32_t`. Added validation that rejects negative values with `ValueError`. (`ext/recorder.c`)
131
+ - **`PyDict_SetItemString` return values unchecked in `flush_batch`** — failures were silently ignored. Now checks return values; on error, clears the exception and continues. (`ext/recorder.c`)
132
+ - **`pyttd_get_recording_stats` had no NULL checks** — the 5 `PyLong_From*`/`PyFloat_From*` calls could return NULL on OOM, leading to `PyDict_SetItemString(dict, key, NULL)` and `Py_DECREF(NULL)`. Added NULL check with proper cleanup. (`ext/recorder.c`)
133
+ - **Windows `\` path separators not detected in ignore patterns** — `strchr(pattern, '/')` missed backslash-separated paths. Added `strchr(pattern, '\\')` check. (`ext/recorder.c`)
134
+
135
+ ### Removed
136
+
137
+ - **Dead code: `pyttd/performance/clock.py`** — `Clock` class was unused (holdover from pre-C-extension era).
138
+ - **Dead code: `pyttd/performance/performance.py`** — `TracePerformance` class was unused; also had a logic bug (`total_samples` initialized to 1 instead of 0).
139
+
140
+ ### Changed
141
+
142
+ - Updated project documentation: `exception_unwind` line_no limitation, interrupt mechanism (main-thread-only gating), pool swap/reset inside GIL, recorder.c description.
143
+ - Fixed misleading "Thread-local" comment on `g_locals_buf` — it is a static global, safe only because Phase 1 records the main thread exclusively. (`ext/recorder.c`)
144
+ - Added documentation comments for `call` event not capturing locals (frame may not be initialized) and `exception_unwind` not capturing locals (internal frame may not be valid after eval). (`ext/recorder.c`)
145
+
146
+ ## [0.1.0] - 2026-03-04
147
+
148
+ Initial implementation of Phases 0 and 1.
149
+
150
+ ### Added
151
+
152
+ #### C Extension (`pyttd_native`)
153
+ - PEP 523 frame eval hook for intercepting frame entry (`call` events)
154
+ - C-level trace function (`PyEval_SetTrace`) for `line`, `return`, `exception` events
155
+ - `exception_unwind` event recorded by eval hook when frame exits via exception propagation
156
+ - Lock-free SPSC ring buffer (C11 `<stdatomic.h>`, power-of-2 capacity, default 65536 slots)
157
+ - Double-buffered 8MB string pools with drop-on-full semantics
158
+ - JSON escaping helper (`json_escape_string()`) for locals serialization
159
+ - `call_depth` tracking in the eval hook (not trace function)
160
+ - Monotonic timestamp recording relative to recording start
161
+ - Flush thread with configurable interval, GIL management, and DB connection cleanup on exit
162
+ - `request_stop()` — atomic stop flag checked by eval hook, raises `KeyboardInterrupt`
163
+ - `set_ignore_patterns()` — substring (directory) and exact-match (basename/function) filtering
164
+ - `get_recording_stats()` — frame count, dropped frames, elapsed time, flush count, pool overflows
165
+ - Platform detection macros (`PYTTD_HAS_FORK`, Windows/POSIX)
166
+ - Stub functions for Phase 2+ (`create_checkpoint`, `restore_checkpoint`, `kill_all_checkpoints`, `install_io_hooks`, `remove_io_hooks`)
167
+
168
+ #### Python Backend
169
+ - `pyttd record script.py` — records all user-code frame events to `<script>.pyttd.db`
170
+ - `pyttd record --module pkg.mod` — records module execution
171
+ - `pyttd query --last-run --frames` — dumps recorded frames with source lines
172
+ - `@ttdbg` decorator for recording function execution
173
+ - `Recorder` class — Python wrapper around C recorder with run lifecycle management
174
+ - `Runner` class — user script/module execution via `runpy`
175
+ - `PyttdConfig` dataclass for configuration
176
+ - Custom exception hierarchy (`PyttdError`, `RecordingError`, `ReplayError`, `ServerError`)
177
+ - Peewee ORM models with deferred database pattern (`SqliteDatabase(None)`)
178
+ - `Runs` — run metadata (UUID PK, script path, timestamps, frame count)
179
+ - `ExecutionFrames` — frame events with indexes on `(run_id, sequence_no)`, `(run_id, filename, line_no)`, etc.
180
+ - Storage utilities: `connect_to_db`, `close_db`, `delete_db_files`, `initialize_schema`
181
+ - Query API: `get_last_run`, `get_frames`, `get_frame_at_seq`, `get_line_code`
182
+ - WAL mode, `busy_timeout: 5000`, batch insert (500 per batch)
183
+ - Stub subcommands for `replay` (Phase 2) and `serve` (Phase 3)
184
+
185
+ #### VSCode Extension (skeleton)
186
+ - TypeScript project structure with `package.json`, `tsconfig.json`
187
+ - DAP handler stubs in `pyttdDebugSession.ts`
188
+ - Backend connection stub in `backendConnection.ts`
189
+
190
+ #### Tests
191
+ - 26 tests passing across 4 test files
192
+ - `test_models.py` (7) — model creation, batch insert, WAL mode
193
+ - `test_native_stub.py` (7) — import check, stub functions raise `NotImplementedError`
194
+ - `test_recorder.py` (9) — recording, sequence monotonicity, events, locals, call depth
195
+ - `test_ringbuf.py` (3) — flush pipeline, dict keys, recording stats
196
+ - All tests use `tmp_path` fixture for DB isolation
197
+
198
+ #### Build System
199
+ - `pyproject.toml` — project metadata, dependencies, build system config
200
+ - `setup.py` — C extension build (`ext_modules`)
201
+ - Editable install via `pip install -e ".[dev]"`
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Parham Moini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include setup.py
5
+ recursive-include ext *.c *.h
6
+ recursive-include pyttd *.py py.typed
7
+ recursive-include tests *.py
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: py-tt-debug
3
+ Version: 0.3.0
4
+ Summary: Python Time-Travel Debugger — record, replay, and step backward through program execution
5
+ Author: pyttd contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/pyttd/pyttd
8
+ Project-URL: Repository, https://github.com/pyttd/pyttd
9
+ Project-URL: Bug Tracker, https://github.com/pyttd/pyttd/issues
10
+ Project-URL: Documentation, https://github.com/pyttd/pyttd/tree/main/docs
11
+ Keywords: debugger,time-travel,debugging,replay,tracing
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: C
17
+ Classifier: Topic :: Software Development :: Debuggers
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Operating System :: MacOS
20
+ Classifier: Operating System :: Microsoft :: Windows
21
+ Requires-Python: >=3.12
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: peewee>=3.17
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0; extra == "dev"
27
+ Requires-Dist: pytest-benchmark>=4.0; extra == "dev"
28
+ Requires-Dist: rich; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # pyttd
32
+
33
+ [![CI](https://github.com/pyttd/pyttd/actions/workflows/ci.yml/badge.svg)](https://github.com/pyttd/pyttd/actions/workflows/ci.yml)
34
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
35
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
36
+
37
+ **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.
38
+
39
+ ## What is Time-Travel Debugging?
40
+
41
+ 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:
42
+
43
+ - **Step backward** through execution to see exactly how state evolved
44
+ - **Reverse continue** to find the last time a breakpoint was hit
45
+ - **Jump to any frame** in the entire recording
46
+ - **Scrub a visual timeline** to navigate through call depth, exceptions, and execution flow
47
+ - **Inspect variables** at any point without re-running the program
48
+
49
+ pyttd is a **post-mortem replay debugger** — your script runs to completion, and then you debug the recorded trace.
50
+
51
+ ## Installation
52
+
53
+ > **Requires Python 3.12+** — uses CPython C API features introduced in 3.12.
54
+
55
+ ```bash
56
+ pip install pyttd
57
+ ```
58
+
59
+ Or from source:
60
+
61
+ ```bash
62
+ git clone https://github.com/pyttd/pyttd.git
63
+ cd pyttd
64
+ python3 -m venv .venv
65
+ .venv/bin/pip install -e ".[dev]"
66
+ ```
67
+
68
+ ## Quick Start
69
+
70
+ ### CLI
71
+
72
+ ```bash
73
+ # Record the included example
74
+ pyttd record examples/hello.py
75
+
76
+ # Query the recording
77
+ pyttd query --last-run --frames
78
+
79
+ # Replay and jump to a specific frame
80
+ pyttd replay --last-run --goto-frame 750
81
+ ```
82
+
83
+ ### VSCode
84
+
85
+ 1. Build the extension: `cd vscode-pyttd && npm install && npm run package`
86
+ 2. In VSCode: Extensions sidebar → `...` → **Install from VSIX** → select `pyttd-*.vsix`
87
+ 3. Add to `.vscode/launch.json`:
88
+
89
+ ```json
90
+ {
91
+ "type": "pyttd",
92
+ "request": "launch",
93
+ "name": "Time-Travel Debug",
94
+ "program": "${file}"
95
+ }
96
+ ```
97
+
98
+ 4. Press **F5** — your script records, then you navigate freely
99
+
100
+ ### Python API
101
+
102
+ ```python
103
+ from pyttd import ttdbg
104
+
105
+ @ttdbg
106
+ def my_function():
107
+ x = 42
108
+ return x * 2
109
+
110
+ my_function() # Records to <file>.pyttd.db
111
+ ```
112
+
113
+ ## Features
114
+
115
+ ### Recording
116
+ - C extension recorder using PEP 523 frame eval hooks (not `sys.settrace`)
117
+ - Lock-free per-thread SPSC ring buffers with background flush
118
+ - Fork-based checkpointing for fast cold navigation (Linux/macOS)
119
+ - Multi-thread recording with globally ordered sequence numbers
120
+ - I/O hooks for deterministic checkpoint replay (`time.time`, `random.random`, `os.urandom`)
121
+
122
+ ### Navigation
123
+ - Forward: step into/over/out, continue with breakpoints
124
+ - Reverse: step back, reverse continue with breakpoints and exception filters
125
+ - Jump: goto frame, goto targets (all executions of a line), restart frame
126
+ - Warm navigation (SQLite, sub-ms) for stepping; cold navigation (checkpoint restore, 50-300ms) for jumps
127
+
128
+ ### VSCode Extension
129
+ - Full DAP implementation with step-back and reverse-continue
130
+ - Canvas-based timeline scrubber with click/drag/zoom
131
+ - CodeLens annotations showing call and exception counts per function
132
+ - Inline variable values during stepping
133
+ - Call history tree with lazy-loaded nesting and exception markers
134
+ - Exception breakpoint filters (uncaught, all raised)
135
+
136
+ ## Architecture
137
+
138
+ Three-layer system:
139
+
140
+ | Layer | Technology | Responsibility |
141
+ |-------|-----------|----------------|
142
+ | C Extension (`pyttd_native`) | C, CPython API | Frame recording, ring buffer, checkpoints, I/O hooks |
143
+ | Python Backend (`pyttd/`) | Python, Peewee, SQLite | JSON-RPC server, session navigation, query API |
144
+ | VSCode Extension (`vscode-pyttd/`) | TypeScript | DAP handlers, timeline webview, CodeLens, inline values |
145
+
146
+ See [docs/architecture.md](docs/architecture.md) for the full design.
147
+
148
+ ## Platform Support
149
+
150
+ | Platform | Recording | Warm Navigation | Cold Navigation | Multi-Thread |
151
+ |----------|-----------|-----------------|-----------------|--------------|
152
+ | Linux | Full | Full | Full | Full |
153
+ | macOS | Full | Full | Partial* | Full |
154
+ | Windows | Full | Full | None | Full |
155
+
156
+ \* macOS: checkpoints skip when multiple threads are active.
157
+
158
+ ## Requirements
159
+
160
+ - **Python >= 3.12** (required for `PyUnstable_InterpreterFrame_*` C API)
161
+ - **C compiler** (GCC, Clang, or MSVC)
162
+ - **VSCode** (for the extension; CLI works standalone)
163
+
164
+ ## Known Limitations
165
+
166
+ - Variables are `repr()` snapshots — flat strings, not expandable objects
167
+ - Expression evaluation operates on recorded snapshots, not live values
168
+ - C extension internals are opaque (third-party C extension objects may have uninformative `repr()`)
169
+ - Windows: no cold navigation (no `fork()`)
170
+ - `exception_unwind` line number is from function entry, not the exception site
171
+ - Variable repr strings are capped at 256 characters
172
+
173
+ ## Documentation
174
+
175
+ - **[Getting Started](docs/getting-started.md)** — first recording walkthrough
176
+ - **[CLI Reference](docs/cli-reference.md)** — all commands and flags
177
+ - **[VSCode Guide](docs/vscode-guide.md)** — extension features and configuration
178
+ - **[API Reference](docs/api-reference.md)** — Python programmatic API
179
+ - **[Architecture](docs/architecture.md)** — system design and data flow
180
+ - **[Troubleshooting](docs/troubleshooting.md)** — common issues
181
+ - **[FAQ](docs/faq.md)** — frequently asked questions
182
+ - **[Contributing](CONTRIBUTING.md)** — how to contribute
183
+ - **[Changelog](CHANGELOG.md)** — version history
184
+
185
+ Development guides: [Building](docs/development/building.md) | [Testing](docs/development/testing.md) | [C Extension](docs/development/c-extension.md) | [Protocol](docs/development/protocol.md) | [Releasing](docs/development/releasing.md)
186
+
187
+ ## Contributing
188
+
189
+ Contributions welcome across C, Python, and TypeScript. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and guidelines.
190
+
191
+ ## License
192
+
193
+ MIT License. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,163 @@
1
+ # pyttd
2
+
3
+ [![CI](https://github.com/pyttd/pyttd/actions/workflows/ci.yml/badge.svg)](https://github.com/pyttd/pyttd/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
6
+
7
+ **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.
8
+
9
+ ## What is Time-Travel Debugging?
10
+
11
+ 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:
12
+
13
+ - **Step backward** through execution to see exactly how state evolved
14
+ - **Reverse continue** to find the last time a breakpoint was hit
15
+ - **Jump to any frame** in the entire recording
16
+ - **Scrub a visual timeline** to navigate through call depth, exceptions, and execution flow
17
+ - **Inspect variables** at any point without re-running the program
18
+
19
+ pyttd is a **post-mortem replay debugger** — your script runs to completion, and then you debug the recorded trace.
20
+
21
+ ## Installation
22
+
23
+ > **Requires Python 3.12+** — uses CPython C API features introduced in 3.12.
24
+
25
+ ```bash
26
+ pip install pyttd
27
+ ```
28
+
29
+ Or from source:
30
+
31
+ ```bash
32
+ git clone https://github.com/pyttd/pyttd.git
33
+ cd pyttd
34
+ python3 -m venv .venv
35
+ .venv/bin/pip install -e ".[dev]"
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ### CLI
41
+
42
+ ```bash
43
+ # Record the included example
44
+ pyttd record examples/hello.py
45
+
46
+ # Query the recording
47
+ pyttd query --last-run --frames
48
+
49
+ # Replay and jump to a specific frame
50
+ pyttd replay --last-run --goto-frame 750
51
+ ```
52
+
53
+ ### VSCode
54
+
55
+ 1. Build the extension: `cd vscode-pyttd && npm install && npm run package`
56
+ 2. In VSCode: Extensions sidebar → `...` → **Install from VSIX** → select `pyttd-*.vsix`
57
+ 3. Add to `.vscode/launch.json`:
58
+
59
+ ```json
60
+ {
61
+ "type": "pyttd",
62
+ "request": "launch",
63
+ "name": "Time-Travel Debug",
64
+ "program": "${file}"
65
+ }
66
+ ```
67
+
68
+ 4. Press **F5** — your script records, then you navigate freely
69
+
70
+ ### Python API
71
+
72
+ ```python
73
+ from pyttd import ttdbg
74
+
75
+ @ttdbg
76
+ def my_function():
77
+ x = 42
78
+ return x * 2
79
+
80
+ my_function() # Records to <file>.pyttd.db
81
+ ```
82
+
83
+ ## Features
84
+
85
+ ### Recording
86
+ - C extension recorder using PEP 523 frame eval hooks (not `sys.settrace`)
87
+ - Lock-free per-thread SPSC ring buffers with background flush
88
+ - Fork-based checkpointing for fast cold navigation (Linux/macOS)
89
+ - Multi-thread recording with globally ordered sequence numbers
90
+ - I/O hooks for deterministic checkpoint replay (`time.time`, `random.random`, `os.urandom`)
91
+
92
+ ### Navigation
93
+ - Forward: step into/over/out, continue with breakpoints
94
+ - Reverse: step back, reverse continue with breakpoints and exception filters
95
+ - Jump: goto frame, goto targets (all executions of a line), restart frame
96
+ - Warm navigation (SQLite, sub-ms) for stepping; cold navigation (checkpoint restore, 50-300ms) for jumps
97
+
98
+ ### VSCode Extension
99
+ - Full DAP implementation with step-back and reverse-continue
100
+ - Canvas-based timeline scrubber with click/drag/zoom
101
+ - CodeLens annotations showing call and exception counts per function
102
+ - Inline variable values during stepping
103
+ - Call history tree with lazy-loaded nesting and exception markers
104
+ - Exception breakpoint filters (uncaught, all raised)
105
+
106
+ ## Architecture
107
+
108
+ Three-layer system:
109
+
110
+ | Layer | Technology | Responsibility |
111
+ |-------|-----------|----------------|
112
+ | C Extension (`pyttd_native`) | C, CPython API | Frame recording, ring buffer, checkpoints, I/O hooks |
113
+ | Python Backend (`pyttd/`) | Python, Peewee, SQLite | JSON-RPC server, session navigation, query API |
114
+ | VSCode Extension (`vscode-pyttd/`) | TypeScript | DAP handlers, timeline webview, CodeLens, inline values |
115
+
116
+ See [docs/architecture.md](docs/architecture.md) for the full design.
117
+
118
+ ## Platform Support
119
+
120
+ | Platform | Recording | Warm Navigation | Cold Navigation | Multi-Thread |
121
+ |----------|-----------|-----------------|-----------------|--------------|
122
+ | Linux | Full | Full | Full | Full |
123
+ | macOS | Full | Full | Partial* | Full |
124
+ | Windows | Full | Full | None | Full |
125
+
126
+ \* macOS: checkpoints skip when multiple threads are active.
127
+
128
+ ## Requirements
129
+
130
+ - **Python >= 3.12** (required for `PyUnstable_InterpreterFrame_*` C API)
131
+ - **C compiler** (GCC, Clang, or MSVC)
132
+ - **VSCode** (for the extension; CLI works standalone)
133
+
134
+ ## Known Limitations
135
+
136
+ - Variables are `repr()` snapshots — flat strings, not expandable objects
137
+ - Expression evaluation operates on recorded snapshots, not live values
138
+ - C extension internals are opaque (third-party C extension objects may have uninformative `repr()`)
139
+ - Windows: no cold navigation (no `fork()`)
140
+ - `exception_unwind` line number is from function entry, not the exception site
141
+ - Variable repr strings are capped at 256 characters
142
+
143
+ ## Documentation
144
+
145
+ - **[Getting Started](docs/getting-started.md)** — first recording walkthrough
146
+ - **[CLI Reference](docs/cli-reference.md)** — all commands and flags
147
+ - **[VSCode Guide](docs/vscode-guide.md)** — extension features and configuration
148
+ - **[API Reference](docs/api-reference.md)** — Python programmatic API
149
+ - **[Architecture](docs/architecture.md)** — system design and data flow
150
+ - **[Troubleshooting](docs/troubleshooting.md)** — common issues
151
+ - **[FAQ](docs/faq.md)** — frequently asked questions
152
+ - **[Contributing](CONTRIBUTING.md)** — how to contribute
153
+ - **[Changelog](CHANGELOG.md)** — version history
154
+
155
+ Development guides: [Building](docs/development/building.md) | [Testing](docs/development/testing.md) | [C Extension](docs/development/c-extension.md) | [Protocol](docs/development/protocol.md) | [Releasing](docs/development/releasing.md)
156
+
157
+ ## Contributing
158
+
159
+ Contributions welcome across C, Python, and TypeScript. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and guidelines.
160
+
161
+ ## License
162
+
163
+ MIT License. See [LICENSE](LICENSE) for details.