textual-debugger 0.2.10__tar.gz → 0.2.14__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.
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/PKG-INFO +699 -88
- {textual_debugger-0.2.10/src/tdb → textual_debugger-0.2.14}/README.md +695 -86
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/pyproject.toml +3 -2
- {textual_debugger-0.2.10 → textual_debugger-0.2.14/src/tdb}/README.md +695 -86
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/__init__.py +1 -1
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/_timeouts.py +14 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/bash/server.py +20 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/bash/session.py +63 -5
- textual_debugger-0.2.14/src/tdb/adapters/native/__init__.py +1 -0
- textual_debugger-0.2.14/src/tdb/adapters/native/tdb.h +269 -0
- textual_debugger-0.2.14/src/tdb/adapters/perl/Devel/TdbRemote.pm +363 -0
- textual_debugger-0.2.14/src/tdb/adapters/perl/padwalker.py +293 -0
- textual_debugger-0.2.14/src/tdb/adapters/perl/padwalker_src/Makefile.PL +38 -0
- textual_debugger-0.2.14/src/tdb/adapters/perl/padwalker_src/PadWalker.pm +160 -0
- textual_debugger-0.2.14/src/tdb/adapters/perl/padwalker_src/PadWalker.xs +645 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/perl/server.py +109 -10
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/perl/session.py +126 -8
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/server.py +65 -2
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/ruby/server.py +210 -16
- textual_debugger-0.2.14/src/tdb/adapters/ruby/tdb.rb +216 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/inspect.py +11 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/server.py +21 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/session.py +86 -2
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/app.py +400 -31
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/app_handlers/dap_events.py +34 -12
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/app_helpers.py +39 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/cli.py +276 -45
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/dap/client.py +106 -5
- textual_debugger-0.2.14/src/tdb/examine.py +302 -0
- textual_debugger-0.2.14/src/tdb/info.py +197 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/keybindings/__init__.py +86 -3
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/base.py +126 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/bash.py +11 -1
- textual_debugger-0.2.14/src/tdb/languages/cpp.py +451 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/go.py +25 -2
- textual_debugger-0.2.14/src/tdb/languages/native_tools.py +187 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/ocaml.py +76 -6
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/perl.py +11 -2
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/powershell.py +8 -2
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/python.py +13 -1
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/registry.py +68 -2
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/ruby.py +8 -1
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/rust.py +54 -2
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/tcsh.py +10 -1
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/mcp/server.py +12 -9
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/mcp/session.py +1 -1
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/persist.py +44 -9
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/replay.py +28 -8
- textual_debugger-0.2.14/src/tdb/replay_tui.py +332 -0
- textual_debugger-0.2.14/src/tdb/run_mode.py +523 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/gdb_script.py +9 -1
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/server/handlers.py +29 -3
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/server/runner.py +10 -0
- textual_debugger-0.2.14/src/tdb/session/breakpoint_sync.py +158 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/controller.py +474 -34
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/state.py +27 -0
- textual_debugger-0.2.14/src/tdb/session/terminal.py +268 -0
- textual_debugger-0.2.14/src/tdb/source_edit.py +141 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/breakpoint_view.py +11 -0
- textual_debugger-0.2.14/src/tdb/widgets/code_editor.py +738 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/code_view.py +422 -20
- textual_debugger-0.2.14/src/tdb/widgets/console_view.py +125 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/evaluate_console.py +12 -7
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/modals.py +13 -4
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/variable_view.py +52 -2
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/PKG-INFO +699 -88
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/SOURCES.txt +14 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/requires.txt +4 -1
- textual_debugger-0.2.10/src/tdb/adapters/perl/Devel/TdbRemote.pm +0 -93
- textual_debugger-0.2.10/src/tdb/languages/cpp.py +0 -204
- textual_debugger-0.2.10/src/tdb/run_mode.py +0 -305
- textual_debugger-0.2.10/src/tdb/session/terminal.py +0 -121
- textual_debugger-0.2.10/src/tdb/widgets/console_view.py +0 -35
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/LICENSE +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/setup.cfg +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/bash/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/bash/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/bash/declares.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/bash/tdb_harness.sh +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/ocaml/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/ocaml/lldb_formatters.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/perl/Devel/TdbCompile.pm +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/perl/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/perl/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/perl/helpers.pl +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/perl/protocol.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/perl/refs.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/locate.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/output.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/tdb_launch.ps1 +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/ruby/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/ruby/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/seqs.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/breakpoints.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/cli.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/guardian.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/instrumenter.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/models.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/protocol.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/runtime.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/scanner.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/transport.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/app_handlers/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/app_handlers/inspection.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/app_handlers/routing.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/app_handlers/ui_panels.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/breakpoint_hook.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/dap/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/dap/messages.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/dap/protocol.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/dap/reverse.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/dap/types.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/eval_mode.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/go_concurrency/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/go_concurrency/analyzer.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/go_concurrency/classifier.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/go_concurrency/collector.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/go_concurrency/models.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/inspection.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/inspection_full.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/languages/errors.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/mcp/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/mcp/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/post_mortem.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/processes_cache.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/analyzer.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/classifier.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/collector.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/models.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/base.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/gdb.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/lldb.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/lldb_script.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/server/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/server/app.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/server/event_handler.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/server/rpc_types.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/child_processes.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/errors.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/event_bus.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/inspect_service.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/messages.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/post_mortem_loader.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/recorder.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/statement_stepper.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/session/textual_handler.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/source_analysis.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/_inspection_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/async_tasks_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/full_contents_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/goroutines_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/menu_bar.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/processes_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/rust_concurrency_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/stack_view.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/status_bar.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/tdb/widgets/threads_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/entry_points.txt +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: textual-debugger
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.14
|
|
4
4
|
Summary: A TUI debugger for Python, Perl, and other languages. Based on textual.
|
|
5
5
|
Author-email: Al Danial <al.danial@gmail.com>
|
|
6
6
|
License: textual-debugger is licensed to you under the MIT/X Consortium license:
|
|
@@ -53,7 +53,9 @@ Requires-Dist: debugpy>=1.8.12
|
|
|
53
53
|
Requires-Dist: pygments>=2.17.0
|
|
54
54
|
Requires-Dist: fastapi>=0.115.0
|
|
55
55
|
Requires-Dist: uvicorn>=0.30.0
|
|
56
|
-
Requires-Dist: mcp<
|
|
56
|
+
Requires-Dist: mcp<3.0.0,>=2.0.0
|
|
57
|
+
Provides-Extra: edit
|
|
58
|
+
Requires-Dist: textual[syntax]>=8.0.0; extra == "edit"
|
|
57
59
|
Provides-Extra: dev
|
|
58
60
|
Requires-Dist: pytest; extra == "dev"
|
|
59
61
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -115,7 +117,7 @@ It specifically supports modules
|
|
|
115
117
|
|
|
116
118
|
- supports remote attachment to Python, Perl, Ruby, Rust, C/C++, and Go programs
|
|
117
119
|
|
|
118
|
-
- includes a JSON-RPC server mode, an MCP mode, and a `SKILL.md` file that enable
|
|
120
|
+
- includes a JSON-RPC server mode, an MCP (v2) mode, and a `SKILL.md` file that enable
|
|
119
121
|
programmatic debug control, making it suitable for
|
|
120
122
|
automated, headless debugging workflows and AI-assisted debugging
|
|
121
123
|
|
|
@@ -123,34 +125,16 @@ automated, headless debugging workflows and AI-assisted debugging
|
|
|
123
125
|
built with `textual`, `prompt-toolkit`, `urwid`, `curses`, `rich`, and so on
|
|
124
126
|
|
|
125
127
|
- comes with a post-mortem exception hook that can be installed in Python programs
|
|
126
|
-
to have `tdb` pop open automatically at the first uncaught exception
|
|
128
|
+
to have `tdb` pop open automatically at the first uncaught exception, and a live
|
|
129
|
+
breakpoint hook (`tdb.breakpoint()` in Python, `Devel::TdbRemote::breakpoint()`
|
|
130
|
+
in Perl, `Tdb.breakpoint` in Ruby, `tdb.Breakpoint()` in Go, `tdb_breakpoint()`
|
|
131
|
+
in C/C++, `tdb::breakpoint()` in Rust, `Tdb.breakpoint ()` in OCaml) that opens
|
|
132
|
+
`tdb` paused at a line of your choosing
|
|
127
133
|
|
|
128
134
|
- can be entirely keyboard-driven
|
|
129
135
|
making it suitable for operation in non-graphical environments (mouse support is
|
|
130
136
|
available in graphical environments)
|
|
131
137
|
|
|
132
|
-
## Acknowledgments
|
|
133
|
-
|
|
134
|
-
Thank you:
|
|
135
|
-
|
|
136
|
-
- Will McGugan for the amazing `textual` module.
|
|
137
|
-
`tdb` would be a pale shadow of itself had I used any other TUI framework.
|
|
138
|
-
Fantastic work, Will.
|
|
139
|
-
|
|
140
|
-
- Microsoft for the Debug Adapter Protocol (DAP) and releasing
|
|
141
|
-
its implementation in `debugpy` and the Python Debugger extension for Visual Studio Code
|
|
142
|
-
as open source.
|
|
143
|
-
|
|
144
|
-
- Anthropic, for providing access to Claude Code through the
|
|
145
|
-
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
146
|
-
`tdb` was made almost entirely with Claude Code.
|
|
147
|
-
|
|
148
|
-
- OpenAI, for providing access to Codex through the
|
|
149
|
-
[Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
|
|
150
|
-
|
|
151
|
-
This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/)
|
|
152
|
-
Python debugger.
|
|
153
|
-
|
|
154
138
|
## Gallery
|
|
155
139
|
<p align="center">
|
|
156
140
|
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
|
|
@@ -190,7 +174,13 @@ uvx --from textual-debugger tdb my_program.py
|
|
|
190
174
|
# show comprehensive documentation in a terminal-based Markdown viewer
|
|
191
175
|
tdb --doc
|
|
192
176
|
|
|
193
|
-
#
|
|
177
|
+
# show the Help > About text, where tdb and its config, breakpoints and
|
|
178
|
+
# log files live, and the path + version of each interpreter and native
|
|
179
|
+
# debugger it would use (python, gdb, lldb-dap, perl, ruby, dlv,
|
|
180
|
+
# ocamlearlybird, bash, tcsh, pwsh)
|
|
181
|
+
tdb --info
|
|
182
|
+
|
|
183
|
+
# debug a Python program (stops at first line by default)
|
|
194
184
|
tdb my_program.py
|
|
195
185
|
|
|
196
186
|
# debug with arguments
|
|
@@ -225,7 +215,7 @@ tdb --python /path/to/venv/bin/python my_program.py
|
|
|
225
215
|
# step into, or stop at tracebacks in library code
|
|
226
216
|
tdb --no-just-my-code --python /path/to/venv/bin/python my_program.py
|
|
227
217
|
|
|
228
|
-
# run until first breakpoint or exit
|
|
218
|
+
# run until first breakpoint (persisted during a prior run) or exit
|
|
229
219
|
tdb --no-stop-on-entry my_program.py
|
|
230
220
|
|
|
231
221
|
# run the debuggee in an external terminal
|
|
@@ -250,7 +240,7 @@ tdb -r remotehost:5678 --local-root /my/code/dir --remote-root /app -k my_progra
|
|
|
250
240
|
# attach, which can leave it suspended after tdb quits
|
|
251
241
|
tdb -r remotehost:5678 --no-pause-on-attach
|
|
252
242
|
|
|
253
|
-
# separate tdb arguments from debuggee arguments with `--`
|
|
243
|
+
# separate tdb arguments from debuggee arguments with `--`
|
|
254
244
|
tdb --python /path/to/venv/bin/python -- my_program.py -k 17 --max 23.3
|
|
255
245
|
```
|
|
256
246
|
|
|
@@ -268,7 +258,7 @@ languages are supported out of the box:
|
|
|
268
258
|
| Language | Adapter(s) | Dependencies | Feature level |
|
|
269
259
|
|----------|------------|------------------------|---------------|
|
|
270
260
|
| Python | `debugpy` (default) | Python ≥ 3.11 | everything in this README |
|
|
271
|
-
| C / C++ (any native binary) | `gdb` (default), `lldb-dap` (alternate) | `gdb -i dap` requires GDB ≥ 14; `lldb-dap` ships with LLVM ≥ 17 (e.g. `apt install lldb`) | core debugging: breakpoints, stepping, stack, variables, evaluate console + remote attach (gdbserver/lldb-server, local symbol-bearing executable required) |
|
|
261
|
+
| C / C++ (any native binary) | `gdb` (default), `lldb-dap` (alternate) | `gdb -i dap` requires GDB ≥ 14 built with Python (RHEL 8/9: `dnf install gcc-toolset-14-gdb`, see [C/C++ tips](#cc-tips)); `lldb-dap` ships with LLVM ≥ 17 (e.g. `apt install lldb`) | core debugging: breakpoints, stepping, stack, variables, evaluate console + remote attach (gdbserver/lldb-server, local symbol-bearing executable required) |
|
|
272
262
|
| Rust (explicit `--lang rust`) | `gdb` (Linux default), `lldb-dap` (macOS default) | any rustc building with debug info; GDB ≥ 14 or LLVM `lldb-dap` ≥ 17 (concurrency ownership evidence additionally requires stable Rust 1.98) | core debugging + best-effort Rust concurrency inspection + remote attach |
|
|
273
263
|
| Perl | perl-tdb (bundled) | perl ≥ 5.18 on PATH | core debugging + remote attach |
|
|
274
264
|
| Bash | bash-tdb (bundled) | bash ≥ 4.4 on PATH | core debugging (no remote attach) |
|
|
@@ -315,6 +305,23 @@ The language is auto-detected from the debug target:
|
|
|
315
305
|
it (`tdb --lang cpp --adapter lldb-dap ./myprog`). Rust always requires the
|
|
316
306
|
explicit language selection (`tdb --lang rust target/debug/app`).
|
|
317
307
|
|
|
308
|
+
`--adapter` also accepts a full path to a `gdb`, `lldb-dap`, or `dlv`
|
|
309
|
+
executable (`tdb --adapter /opt/llvm/bin/lldb-dap ./myprog`,
|
|
310
|
+
`tdb --adapter /usr/bin/gdb-multiarch ./myprog`,
|
|
311
|
+
`tdb --adapter ~/go/bin/dlv ./main.go`). The adapter id is taken from the
|
|
312
|
+
file's basename — versioned and variant names such as `lldb-dap-21` and
|
|
313
|
+
`gdb-multiarch` work — and that exact executable is used for the session,
|
|
314
|
+
ahead of anything on `$PATH` or in `config.json`'s `adapters` map.
|
|
315
|
+
`tdb --info` shows which `gdb` and `lldb-dap` would be used otherwise.
|
|
316
|
+
|
|
317
|
+
`gdb`'s DAP mode needs GDB ≥ 14, so `tdb` checks `gdb --version` before
|
|
318
|
+
starting a session. A `gdb` that is provably too old is refused with a
|
|
319
|
+
message naming its path and version; when that `gdb` came from `PATH`
|
|
320
|
+
(not from `--adapter` or `config.json`), `tdb` first looks for a newer one
|
|
321
|
+
installed by Red Hat's Software Collections
|
|
322
|
+
(`/opt/rh/gcc-toolset-*/root/usr/bin/gdb`, `/opt/rh/devtoolset-*/...`) and
|
|
323
|
+
uses the newest that qualifies. See [C/C++ tips](#cc-tips) for RHEL 8.
|
|
324
|
+
|
|
318
325
|
> **Migration note:** extensionless Python scripts without a `python` shebang
|
|
319
326
|
> were previously assumed to be Python; they now require `--lang python`.
|
|
320
327
|
|
|
@@ -337,7 +344,7 @@ non-standard location, or change a language's default adapter, add to
|
|
|
337
344
|
Core debugging works identically for every language: breakpoints (incl.
|
|
338
345
|
conditions and persistence), stepping, continue/pause, run-to-cursor, stack
|
|
339
346
|
navigation, variable inspection, the evaluate console, syntax highlighting,
|
|
340
|
-
and the JSON-RPC / MCP programmatic modes.
|
|
347
|
+
and the JSON-RPC / MCP (v2) programmatic modes.
|
|
341
348
|
|
|
342
349
|
Python-specific features are hidden or return "not supported for this
|
|
343
350
|
language" message when debugging other languages: statement-granularity
|
|
@@ -359,7 +366,10 @@ language — Python, Perl, Bash, Tcsh, Ruby, and C/C++, OCaml native, or Rust
|
|
|
359
366
|
sessions via `--adapter lldb-dap` — see
|
|
360
367
|
[External Terminal Support](#external-terminal-support). Go and PowerShell do
|
|
361
368
|
not support `--terminal` yet (`dlv dap` and PSES have no terminal-routing
|
|
362
|
-
mode).
|
|
369
|
+
mode). Typing into the Console view to feed the program's stdin works for
|
|
370
|
+
Python, Perl, Bash, Tcsh, Ruby, and PowerShell, but not for C/C++, Rust,
|
|
371
|
+
OCaml, or Go, whose adapters spawn the debuggee themselves — see
|
|
372
|
+
[Console Output and Input](#console-output-and-input).
|
|
363
373
|
|
|
364
374
|
**Bash limitations (v1):** the bash adapter uses bash's own `DEBUG` trap and
|
|
365
375
|
has a smaller feature set than Python:
|
|
@@ -425,10 +435,16 @@ picture:
|
|
|
425
435
|
backtrace instead.
|
|
426
436
|
- No Windows support, no remote attach, and a stripped native binary that
|
|
427
437
|
byte-checking can't identify needs `--lang ocaml`.
|
|
438
|
+
- No stdin forwarding from the Console view (both adapters spawn the
|
|
439
|
+
program themselves); a program that reads stdin needs `--terminal`
|
|
440
|
+
(native, `--adapter lldb-dap`) or input from a file.
|
|
428
441
|
|
|
429
442
|
**Go limitations (v1):**
|
|
430
443
|
|
|
431
444
|
- `--terminal` is not supported (`dlv dap` has no terminal-routing mode).
|
|
445
|
+
- No stdin forwarding from the Console view: `dlv dap` spawns the program
|
|
446
|
+
itself, so a program that reads stdin must get it from a file or pipe on
|
|
447
|
+
its command line.
|
|
432
448
|
- The Goroutines workspace's wait graph shows channel-send/-receive,
|
|
433
449
|
mutex, and `WaitGroup` edges; a goroutine parked in a `select` is
|
|
434
450
|
classified but contributes no wait edge (Delve can't tell which of the
|
|
@@ -455,9 +471,17 @@ See [Go](#go) below for launch details.
|
|
|
455
471
|
If no breakpoint in a file can be
|
|
456
472
|
bound, `tdb` prints a console warning suggesting the program may lack
|
|
457
473
|
debug info.
|
|
474
|
+
- The Console view's stdin line is not available: `gdb -i dap` and
|
|
475
|
+
`lldb-dap` spawn the program themselves, so `tdb` has no handle on its
|
|
476
|
+
stdin. A program that reads stdin needs `--terminal` (with
|
|
477
|
+
`--adapter lldb-dap`) or its input from a file.
|
|
458
478
|
- Stack frames pointing into system libraries often have no source on disk;
|
|
459
479
|
the Code View shows a `<Could not read …>` placeholder while the stack,
|
|
460
480
|
variables, and evaluate console remain fully usable.
|
|
481
|
+
- For compiled executables, the Code View shows shows a brief note while
|
|
482
|
+
it attempts to load the associated source file. The note will identify
|
|
483
|
+
the failure reason if the source cannot be loaded (executable was built
|
|
484
|
+
without `-g`, or the adapter is not working, e.g. a GDB too old for DAP).
|
|
461
485
|
- GDB (the default adapter) has the most complete libstdc++
|
|
462
486
|
pretty-printing. `lldb-dap` (via `--adapter lldb-dap`) also debugs
|
|
463
487
|
GCC-built binaries fine. DWARF is compiler-neutral.
|
|
@@ -468,6 +492,80 @@ See [Go](#go) below for launch details.
|
|
|
468
492
|
- `--terminal` (see [External Terminal Support](#external-terminal-support))
|
|
469
493
|
requires `--adapter lldb-dap`; GDB's DAP mode has no terminal integration
|
|
470
494
|
and `tdb` refuses `--terminal` with the default `gdb` adapter.
|
|
495
|
+
- **GDB source-location quirk:** some GDB builds (RHEL 8 among them)
|
|
496
|
+
report no source file for a stopped frame -- `info source` answers
|
|
497
|
+
"No current source file" -- until a `list` command has selected the
|
|
498
|
+
default source file. `tdb` therefore sends `list` once, before its
|
|
499
|
+
first stack query, so the Code View can find the source. This is why a
|
|
500
|
+
fresh gdb session shows `Current source file is …` in `info source`
|
|
501
|
+
from the evaluate console without you having typed `list`.
|
|
502
|
+
- **RHEL 8 (and 9) gdb is too old for DAP:** the stock `/usr/bin/gdb` is
|
|
503
|
+
8.2 on RHEL 8 (10.2 on RHEL 9) and answers `gdb -i dap` with
|
|
504
|
+
``Interpreter `dap' unrecognized``. Install a newer one from the
|
|
505
|
+
GCC Toolset, which leaves `/usr/bin/gdb` alone:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
sudo dnf install gcc-toolset-14-gdb # gdb 14.2 under /opt/rh/gcc-toolset-14
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
`tdb` then finds `/opt/rh/gcc-toolset-14/root/usr/bin/gdb` by itself
|
|
512
|
+
whenever the `gdb` on `PATH` is missing or too old — no `scl enable`
|
|
513
|
+
needed (`tdb --info` shows it as `gdb used instead`). Two things pin
|
|
514
|
+
the old one and must be changed by hand: `--adapter /usr/bin/gdb`, and
|
|
515
|
+
an `"adapters": {"gdb": "/usr/bin/gdb"}` entry in `config.json` (`tdb`
|
|
516
|
+
seeds that entry with whatever `gdb` was on `PATH` the first time it
|
|
517
|
+
wrote the file). Either delete the entry or point it at the toolset
|
|
518
|
+
gdb. A GDB ≥ 14 that was built without Python support gives the same
|
|
519
|
+
``Interpreter `dap' unrecognized`` error (`gdb --configuration` shows
|
|
520
|
+
`--without-python`); use `--adapter lldb-dap` or a gdb built with
|
|
521
|
+
Python.
|
|
522
|
+
|
|
523
|
+
**Attach to a running process:** `tdb -a PID` attaches gdb (or `--adapter
|
|
524
|
+
lldb-dap`) to a live native process and stops it. On Linux tdb reads
|
|
525
|
+
`/proc/PID/exe` to identify the language (Go, OCaml, Rust, otherwise
|
|
526
|
+
C/C++) and to load symbols; pass the executable path as well to use a
|
|
527
|
+
different symbol file, and pass `--lang` off Linux. `--no-pause-on-attach`
|
|
528
|
+
leaves the process running, for a program about to stop itself. An
|
|
529
|
+
interpreter process (python3, perl, ruby, bash) is attached as C/C++; for
|
|
530
|
+
Python, Perl, and Ruby programs use `--remote-attach` instead.
|
|
531
|
+
|
|
532
|
+
**Live breakpoint hook:** the C/C++ counterpart of Python's
|
|
533
|
+
[`tdb.breakpoint()`](#live-breakpoint-hook). Include `tdb.h` (its directory
|
|
534
|
+
is the `tdb.h dir` row of `tdb --info`) and call `tdb_breakpoint()`:
|
|
535
|
+
|
|
536
|
+
```c
|
|
537
|
+
#include "tdb.h"
|
|
538
|
+
|
|
539
|
+
int compute(int n) {
|
|
540
|
+
int total = 0;
|
|
541
|
+
for (int i = 0; i < n; i++) total += i;
|
|
542
|
+
tdb_breakpoint(); /* tdb opens here, paused on the next line */
|
|
543
|
+
return total;
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
```bash
|
|
548
|
+
gcc -g -O0 -I "$(tdb --info | awk -F': ' '/tdb.h dir/ {print $2}')" -o prog prog.c
|
|
549
|
+
./prog # run it directly, not under tdb
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
When the call is reached, the program starts `tdb --lang cpp -a <its pid>
|
|
553
|
+
--no-pause-on-attach` on its own terminal (the `tdb` on `PATH`, or the one
|
|
554
|
+
named by `$TDB`), waits for tdb's gdb or lldb to attach, and stops in the
|
|
555
|
+
hook; tdb steps out so the stop lands on the line after the call, in your
|
|
556
|
+
own frame. Later calls reuse the running tdb. Quitting tdb (`Ctrl+q`)
|
|
557
|
+
detaches and the program runs on; the next call opens a fresh tdb. The
|
|
558
|
+
call is a no-op when stdin or stdout is not a terminal, and it warns and
|
|
559
|
+
continues when `tdb` cannot be found, exits before attaching, or cannot
|
|
560
|
+
attach within 60 s (Yama `ptrace_scope` 2 or 3, or a container without
|
|
561
|
+
`SYS_PTRACE`, blocks attaching). If a tdb whose attach failed is still
|
|
562
|
+
running, later calls warn once and return rather than open another tdb
|
|
563
|
+
over it. Build with `-g -O0` so locals stay
|
|
564
|
+
inspectable; a stripped binary attaches but never stops, because the hook's
|
|
565
|
+
stop symbol cannot be resolved. The header is C99 and C++ compatible and
|
|
566
|
+
header-only. Linux only: the hook grants ptrace access to tdb's debugger
|
|
567
|
+
with `PR_SET_PTRACER`; elsewhere it warns once and returns. See
|
|
568
|
+
`examples/C/breakpoint_hook_demo.c` and `examples/C++/breakpoint_hook_demo.cpp`.
|
|
471
569
|
|
|
472
570
|
### Rust
|
|
473
571
|
|
|
@@ -489,7 +587,9 @@ cargo rustc -- -C debuginfo=2 -C opt-level=0
|
|
|
489
587
|
# Equivalent direct rustc settings: rustc -C debuginfo=2 -C opt-level=0 src/main.rs
|
|
490
588
|
```
|
|
491
589
|
|
|
492
|
-
|
|
590
|
+
As for C/C++, the Console view cannot feed the program's stdin (the gdb and
|
|
591
|
+
lldb adapters spawn it themselves); use `--terminal` with `--adapter
|
|
592
|
+
lldb-dap` for interactive programs. A panic that terminates the debuggee opens the error modal with the parsed
|
|
493
593
|
panic message and backtrace; `tdb` injects `RUST_BACKTRACE=1` into launched
|
|
494
594
|
programs (your own value wins if you set one) so the frames are present.
|
|
495
595
|
|
|
@@ -520,6 +620,50 @@ When source paths differ, pair `--local-root` with `--remote-root`. Do not
|
|
|
520
620
|
expose the remote debug port to an untrusted network: prefer an SSH tunnel,
|
|
521
621
|
for example `ssh -L 2345:127.0.0.1:2345 host`, then attach to `127.0.0.1:2345`.
|
|
522
622
|
|
|
623
|
+
**Attach to a running process:** `tdb --lang rust -a PID` attaches gdb (or
|
|
624
|
+
`--adapter lldb-dap`) to a live Rust process and stops it; tdb loads
|
|
625
|
+
symbols from `/proc/PID/exe`, or from an executable path you pass as well.
|
|
626
|
+
Without `--lang`, tdb recognizes a non-stripped Rust binary by its runtime
|
|
627
|
+
symbols; a stripped one is debugged as C/C++.
|
|
628
|
+
|
|
629
|
+
**Live breakpoint hook:** the Rust counterpart of Python's
|
|
630
|
+
[`tdb.breakpoint()`](#live-breakpoint-hook). Depend on the `tdb` crate from
|
|
631
|
+
this repository (it has no dependencies) and call `tdb::breakpoint()`:
|
|
632
|
+
|
|
633
|
+
```toml
|
|
634
|
+
[dependencies]
|
|
635
|
+
tdb = { git = "https://github.com/AlDanial/tdb" } # or path = ".../rust/tdb"
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
```rust
|
|
639
|
+
fn compute(n: u64) -> u64 {
|
|
640
|
+
let total: u64 = (0..n).sum();
|
|
641
|
+
tdb::breakpoint(); // tdb opens here
|
|
642
|
+
total
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
```bash
|
|
647
|
+
cargo build && ./target/debug/prog # dev profile; run it directly, not under tdb
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
When the call is reached, the program starts `tdb --lang rust -a <its pid>
|
|
651
|
+
--no-pause-on-attach` on its own terminal (the `tdb` on `PATH`, or the one
|
|
652
|
+
named by `$TDB`), waits for tdb's gdb or lldb to attach, and stops in the
|
|
653
|
+
hook; tdb steps out so the stop lands in your own frame on, or just after,
|
|
654
|
+
the call line, as the compiler's line table attributes the return address.
|
|
655
|
+
Later calls reuse the running tdb. Quitting tdb (`Ctrl+q`)
|
|
656
|
+
detaches and the program runs on; the next call opens a fresh tdb. The
|
|
657
|
+
call is a no-op when stdin or stdout is not a terminal, and it warns and
|
|
658
|
+
continues when `tdb` cannot be found, exits before attaching, or cannot
|
|
659
|
+
attach within 60 s (Yama `ptrace_scope` 2 or 3, or a container without
|
|
660
|
+
`SYS_PTRACE`, blocks attaching). If a tdb whose attach failed is still
|
|
661
|
+
running, later calls warn once and return rather than open another tdb
|
|
662
|
+
over it. Use the dev profile so locals stay
|
|
663
|
+
inspectable. Linux only: the hook grants ptrace access to tdb's debugger
|
|
664
|
+
with `PR_SET_PTRACER`; elsewhere it warns once and returns. See
|
|
665
|
+
`examples/Rust/breakpoint_hook_demo/`.
|
|
666
|
+
|
|
523
667
|
In a Rust session the Threads action (`Alt+T`, or the Threads menu entry)
|
|
524
668
|
opens the Rust Concurrency workspace while the debuggee is stopped — a
|
|
525
669
|
fresh, bounded snapshot of threads, wait edges, and findings. The graph is
|
|
@@ -541,6 +685,24 @@ metadata for broader primitives and stronger ownership evidence.
|
|
|
541
685
|
`perl` ≥ 5.18 on `PATH`. It drives stock `perl5db` under the
|
|
542
686
|
hood, so it works with any Perl already on the system.
|
|
543
687
|
|
|
688
|
+
The [PadWalker](https://metacpan.org/pod/PadWalker) module v2.5, by Robin Houston,
|
|
689
|
+
is bundled with `tdb`. PadWalker provides a more complete view of lexical variables
|
|
690
|
+
in outer/caller frames and therefore provides a richer debugging experience.
|
|
691
|
+
PadWalker is an XS (compiled) module, so the bundled copy has to be built
|
|
692
|
+
against the exact perl that runs your program. `tdb` does this for you on the
|
|
693
|
+
first Perl launch: if the perl in use cannot already load PadWalker (from CPAN
|
|
694
|
+
or a distro package), `tdb` compiles the bundled sources, caches the result
|
|
695
|
+
per interpreter under its config directory (`~/.config/tdb/padwalker/` on
|
|
696
|
+
Linux/macOS, `%APPDATA%\\tdb\\padwalker\\` on Windows), and prepends that
|
|
697
|
+
directory to the debuggee's `PERL5LIB`. The build takes about a second and
|
|
698
|
+
needs perl's headers (`libperl-dev` on Debian/Ubuntu, `perl-devel` on
|
|
699
|
+
Fedora/RHEL) plus a C compiler; Strawberry Perl on Windows ships both. When
|
|
700
|
+
the build is not possible, `tdb` prints a one-line notice on the console and
|
|
701
|
+
falls back to a read-only pad walk, so outer-frame lexicals degrade but
|
|
702
|
+
debugging otherwise proceeds. `tdb --info` reports the source directory, the
|
|
703
|
+
cache directory, and whether a build exists for the `perl` on your `PATH`.
|
|
704
|
+
`TDB_PADWALKER_CACHE` overrides the cache location.
|
|
705
|
+
|
|
544
706
|
**Launching a script:**
|
|
545
707
|
|
|
546
708
|
```bash
|
|
@@ -637,18 +799,48 @@ export PERL5LIB=/opt/tdb-perl:$PERL5LIB
|
|
|
637
799
|
the two files in the same relative layout shown above; `helpers.pl` is a
|
|
638
800
|
sibling of the `Devel/` directory, not inside it.)
|
|
639
801
|
|
|
640
|
-
**
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
802
|
+
**Pause in attach mode** uses a control channel: after the debug connection
|
|
803
|
+
is up, `tdb` opens a second connection to the same port and asks
|
|
804
|
+
`Devel::TdbRemote` to accept it. From then on `pause` (or the `p` key) writes a
|
|
805
|
+
byte on that connection, the kernel delivers `SIGIO` to the debuggee, and the
|
|
806
|
+
program stops at its next statement -- the same mechanism `perl5db` uses for
|
|
807
|
+
Ctrl-C, so a program blocked in `sleep` or `select` is interrupted too. The
|
|
808
|
+
second connection reuses the debug port, so an SSH tunnel that forwards that
|
|
809
|
+
one port carries both. Requirements: the `Devel/TdbRemote.pm` copied to the
|
|
810
|
+
remote host must be at least the version shipped with this feature (an older
|
|
811
|
+
copy still attaches fine, but `pause` returns a "not available" error and the
|
|
812
|
+
Console view says why), and the remote perl must support `O_ASYNC` (Linux,
|
|
813
|
+
macOS, BSD; not Windows).
|
|
814
|
+
|
|
815
|
+
**Live breakpoint hook:** the Perl counterpart of Python's
|
|
816
|
+
[`tdb.breakpoint()`](#live-breakpoint-hook). Run your program normally (not
|
|
817
|
+
under `tdb`) and have it open `tdb` on itself when it reaches a line of
|
|
818
|
+
interest:
|
|
819
|
+
|
|
820
|
+
```perl
|
|
821
|
+
use Devel::TdbRemote; # first line of your program
|
|
822
|
+
...
|
|
823
|
+
Devel::TdbRemote::breakpoint(); # tdb opens here, paused on the next line
|
|
824
|
+
```
|
|
646
825
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
826
|
+
On the first call the program listens on an ephemeral loopback port, spawns
|
|
827
|
+
`tdb --lang perl -r 127.0.0.1:PORT` on the same terminal, waits for it to
|
|
828
|
+
attach, and stops on the statement after the call, in your own frame. Later
|
|
829
|
+
calls reuse the running `tdb`, which simply receives another stop. Quitting
|
|
830
|
+
`tdb` (`Ctrl+q`) detaches and lets the program run on; a `breakpoint()` reached
|
|
831
|
+
after that spawns a fresh `tdb`. The call is a no-op when stdin/stdout are not
|
|
832
|
+
a tty, so it is safe in code that sometimes runs headless, and if `tdb` cannot
|
|
833
|
+
be started (not on `PATH`, or it exits before attaching) it warns and the
|
|
834
|
+
program continues. `tdb` is found on `PATH`, or via the `TDB` environment
|
|
835
|
+
variable. The same arming caveat as above applies: `use Devel::TdbRemote;`
|
|
836
|
+
must be the first line (or use `PERL5OPT=-d:TdbRemote`), and `Devel::TdbRemote`
|
|
837
|
+
has to be on `@INC`, e.g.
|
|
838
|
+
|
|
839
|
+
```bash
|
|
840
|
+
PERL5LIB=$(tdb --info | sed -n 's/.*Devel::TdbRemote dir *: *//p') perl prog.pl
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
See [`examples/Perl/breakpoint_hook_demo.pl`](https://github.com/AlDanial/tdb/blob/main/examples/Perl/breakpoint_hook_demo.pl).
|
|
652
844
|
|
|
653
845
|
### Bash
|
|
654
846
|
|
|
@@ -738,6 +930,33 @@ exec` integration yet.
|
|
|
738
930
|
> race on launch; tdb detects a stalled/corrupted handshake and retries
|
|
739
931
|
> once automatically with a fresh `rdbg`, so this is usually invisible.
|
|
740
932
|
|
|
933
|
+
**Live breakpoint hook:** the Ruby counterpart of Python's
|
|
934
|
+
[`tdb.breakpoint()`](#live-breakpoint-hook). Run your program normally (not
|
|
935
|
+
under `tdb`) and have it open `tdb` on itself when it reaches a line of
|
|
936
|
+
interest:
|
|
937
|
+
|
|
938
|
+
```ruby
|
|
939
|
+
require 'tdb'
|
|
940
|
+
...
|
|
941
|
+
Tdb.breakpoint # tdb opens here, paused on the next line
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
The first call starts the debug gem's own debug server on an ephemeral
|
|
945
|
+
loopback port and plants the gem's usual one-shot breakpoint on the next
|
|
946
|
+
line. When that fires, the program parks and `tdb --lang ruby -r 127.0.0.1:PORT`
|
|
947
|
+
is spawned on the same terminal, attaches, and shows the stop in your own
|
|
948
|
+
frame. Later calls reuse the running `tdb`. Quitting `tdb` (`Ctrl+q`) detaches
|
|
949
|
+
and lets the program run on; a `Tdb.breakpoint` reached after that spawns a
|
|
950
|
+
fresh `tdb`. The call is a no-op when stdin/stdout are not a tty, and if `tdb`
|
|
951
|
+
is not on `PATH` (or in the `TDB` environment variable) it warns and the
|
|
952
|
+
program continues. Put `tdb.rb` on the load path, e.g.
|
|
953
|
+
|
|
954
|
+
```bash
|
|
955
|
+
RUBYLIB=$(tdb --info | sed -n 's/.*tdb.rb dir *: *//p') ruby prog.rb
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
See [`examples/Ruby/breakpoint_hook_demo.rb`](https://github.com/AlDanial/tdb/blob/main/examples/Ruby/breakpoint_hook_demo.rb).
|
|
959
|
+
|
|
741
960
|
### OCaml
|
|
742
961
|
|
|
743
962
|
`tdb` debugs OCaml through two independent adapters, chosen automatically
|
|
@@ -761,7 +980,7 @@ ocamlc -g -o my_program.byte "$PWD/my_program.ml"
|
|
|
761
980
|
```
|
|
762
981
|
|
|
763
982
|
Then, to ensure transition from bytecode to OCaml source in the
|
|
764
|
-
Code view, provide an explicit temporary breakpoint (`-t`)
|
|
983
|
+
Code view, provide an explicit temporary breakpoint (`-t`)
|
|
765
984
|
at the first line of execution:
|
|
766
985
|
|
|
767
986
|
```
|
|
@@ -841,6 +1060,52 @@ sessions can't `pause` a running program (so `--run` is unavailable for
|
|
|
841
1060
|
bytecode. Native sessions support `--run` normally); `Unix.fork` and Eio
|
|
842
1061
|
fibers are out of scope (only OCaml 5 domains are presented as threads).
|
|
843
1062
|
|
|
1063
|
+
**Attach to a running process:** `tdb -a PID` attaches lldb-dap (or
|
|
1064
|
+
`--adapter gdb`) to a live native OCaml process and stops it; tdb
|
|
1065
|
+
recognizes the OCaml runtime in `/proc/PID/exe` and always picks a native
|
|
1066
|
+
adapter (bytecode programs cannot be attached to). `--no-pause-on-attach`
|
|
1067
|
+
leaves the process running, for a program about to stop itself.
|
|
1068
|
+
|
|
1069
|
+
**Live breakpoint hook:** the OCaml counterpart of Python's
|
|
1070
|
+
[`tdb.breakpoint()`](#live-breakpoint-hook), for native code. Vendor the
|
|
1071
|
+
`ocaml/tdb` directory from this repository (or `opam pin` it), add
|
|
1072
|
+
`(libraries tdb)` to your executable's dune stanza, and call
|
|
1073
|
+
`Tdb.breakpoint ()`:
|
|
1074
|
+
|
|
1075
|
+
```ocaml
|
|
1076
|
+
let compute n =
|
|
1077
|
+
let total = ref 0 in
|
|
1078
|
+
for i = 0 to n - 1 do total := !total + i done;
|
|
1079
|
+
Tdb.breakpoint (); (* tdb opens paused on this line *)
|
|
1080
|
+
!total
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
```bash
|
|
1084
|
+
dune build && ./_build/default/prog.exe # dev profile; run it directly, not under tdb
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
Without dune: copy `tdb.ml`, `tdb.mli`, `tdb_stubs.c`, and `tdb.h` from
|
|
1088
|
+
`ocaml/tdb` next to your source and build with
|
|
1089
|
+
`ocamlopt -g -o prog tdb_stubs.c tdb.mli tdb.ml prog.ml`.
|
|
1090
|
+
|
|
1091
|
+
When the call is reached, the program starts `tdb --lang ocaml -a <its pid>
|
|
1092
|
+
--no-pause-on-attach` on its own terminal (the `tdb` on `PATH`, or the one
|
|
1093
|
+
named by `$TDB`), waits for tdb's lldb or gdb to attach, and stops in the
|
|
1094
|
+
hook, and tdb steps out of the hook's frames into your own. Unlike the
|
|
1095
|
+
other languages' hooks, the stop lands on the `Tdb.breakpoint ()` call
|
|
1096
|
+
line itself rather than the line after: both lldb and gdb map the hook's
|
|
1097
|
+
return address back to the call. ocamlopt also emits no debug info
|
|
1098
|
+
for locals, so the Variables view shows none for OCaml frames (globals
|
|
1099
|
+
and the stack are still available). Later calls reuse the running tdb.
|
|
1100
|
+
Quitting tdb (`Ctrl+q`) detaches and the program runs on; the next call
|
|
1101
|
+
opens a fresh tdb. The call is a no-op when stdin or stdout is not a
|
|
1102
|
+
terminal, and it warns and continues when `tdb` cannot be found, exits
|
|
1103
|
+
before attaching, or cannot attach within 60 s (Yama `ptrace_scope` 2 or
|
|
1104
|
+
3, or a container without `SYS_PTRACE`, blocks attaching). Keep debug
|
|
1105
|
+
info (`-g`, dune's dev profile). Linux only: the hook grants ptrace
|
|
1106
|
+
access to tdb's debugger with `PR_SET_PTRACER`; elsewhere it warns once
|
|
1107
|
+
and returns. See `examples/OCaml/breakpoint_hook_demo.ml`.
|
|
1108
|
+
|
|
844
1109
|
### Go
|
|
845
1110
|
|
|
846
1111
|
`tdb` debugs Go through [Delve](https://github.com/go-delve/delve)'s own
|
|
@@ -866,10 +1131,53 @@ tdb -r host:port --lang go # attach to a `dlv dap --listen` already running remo
|
|
|
866
1131
|
|
|
867
1132
|
`tdb --test ./pkg` debugs the package's tests under Delve's `test` mode;
|
|
868
1133
|
pass test-binary flags after `--`, e.g. `tdb --test ./pkg -- -run TestFoo`.
|
|
869
|
-
`-a`/`--attach`
|
|
870
|
-
`tdb`
|
|
1134
|
+
`-a`/`--attach` works for Go and, through gdb/lldb-dap, for C/C++, Rust,
|
|
1135
|
+
and OCaml; on Linux `tdb` identifies the language from `/proc/PID/exe`
|
|
871
1136
|
without `--lang` (elsewhere pass `--lang go` alongside `-a`). Attaching
|
|
872
|
-
stops the process immediately so you get control right away
|
|
1137
|
+
stops the process immediately so you get control right away;
|
|
1138
|
+
`--no-pause-on-attach` leaves it running instead, for a program that is
|
|
1139
|
+
about to stop itself (see the live breakpoint hook below).
|
|
1140
|
+
|
|
1141
|
+
**Entry stop:** Delve's own `stopOnEntry` halts at the process entry point
|
|
1142
|
+
before any goroutine exists (no stack, no source). `tdb` instead runs to
|
|
1143
|
+
`main.main`, so the default entry stop lands on your program's first line
|
|
1144
|
+
with its source on screen; `--no-stop-on-entry` skips it as usual.
|
|
1145
|
+
|
|
1146
|
+
**Live breakpoint hook:** the Go counterpart of Python's
|
|
1147
|
+
`tdb.breakpoint()`. Import the `tdb` module from this repository and call
|
|
1148
|
+
`tdb.Breakpoint()` where you want the debugger to open:
|
|
1149
|
+
|
|
1150
|
+
```go
|
|
1151
|
+
import "github.com/AlDanial/tdb/go/tdb"
|
|
1152
|
+
|
|
1153
|
+
func compute(n int) int {
|
|
1154
|
+
total := 0
|
|
1155
|
+
for i := 0; i < n; i++ {
|
|
1156
|
+
total += i
|
|
1157
|
+
}
|
|
1158
|
+
tdb.Breakpoint() // tdb opens here, paused on the next line
|
|
1159
|
+
return total
|
|
1160
|
+
}
|
|
1161
|
+
```
|
|
1162
|
+
|
|
1163
|
+
```bash
|
|
1164
|
+
go get github.com/AlDanial/tdb/go/tdb
|
|
1165
|
+
go build -gcflags=all=-N -l -o prog . && ./prog # run it directly, not under tdb
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
When the call is reached, the program starts `tdb --lang go -a <its pid>
|
|
1169
|
+
--no-pause-on-attach` on its own terminal (the `tdb` on `PATH`, or the one
|
|
1170
|
+
named by `$TDB`), waits for tdb's Delve to attach, and traps with
|
|
1171
|
+
`runtime.Breakpoint()`; tdb steps out of the trap so the stop lands on the
|
|
1172
|
+
line after the call, in your own frame. Later `tdb.Breakpoint()` calls
|
|
1173
|
+
reuse the running tdb. Quitting tdb (`Ctrl+q`) detaches and the program
|
|
1174
|
+
runs on; the next call opens a fresh tdb. The call is a no-op when stdin
|
|
1175
|
+
or stdout is not a terminal, and it warns and continues when `tdb`
|
|
1176
|
+
cannot be found or exits before attaching. Build with
|
|
1177
|
+
`-gcflags=all=-N -l` so locals stay inspectable. Linux only: the hook
|
|
1178
|
+
grants ptrace access to tdb's Delve with `PR_SET_PTRACER` (Yama
|
|
1179
|
+
`ptrace_scope=1`); elsewhere it warns once and returns. See
|
|
1180
|
+
`examples/Go/breakpoint_hook_demo/`.
|
|
873
1181
|
|
|
874
1182
|
**Remote attach:** start Delve's own DAP server against your program
|
|
875
1183
|
first —
|
|
@@ -971,6 +1279,12 @@ Notes:
|
|
|
971
1279
|
blocks attributed to tdb's own `tdb_launch.ps1` are skipped, so a bare
|
|
972
1280
|
`Write-Error` before the `exit` opens no modal at all.
|
|
973
1281
|
- `--terminal` and remote attach are not supported for PowerShell yet.
|
|
1282
|
+
- `Read-Host`, `$Host.UI.ReadLine()`, and `[Console]::ReadLine()` read
|
|
1283
|
+
from the Console view's stdin line (pwsh is no longer started with
|
|
1284
|
+
`-NonInteractive`, so mandatory-parameter and `-Confirm` prompts now wait
|
|
1285
|
+
for your input there instead of failing). pwsh echoes the prompt and the
|
|
1286
|
+
answer together once the line completes, so a bare `Read-Host` prompt
|
|
1287
|
+
appears only after you answer it; a preceding `Write-Host` shows at once.
|
|
974
1288
|
- Windows PowerShell 5.1 is not supported (pwsh 7 only). Running tdb *on*
|
|
975
1289
|
Windows against pwsh is designed for but not yet verified (experimental).
|
|
976
1290
|
|
|
@@ -980,7 +1294,7 @@ Notes:
|
|
|
980
1294
|
┌─ Header ──────────────────────────────────────────────┐
|
|
981
1295
|
├─ Menu Bar (File / Configure / Help)───────────────────┤
|
|
982
1296
|
│ │ │
|
|
983
|
-
│ Code View │ Console View (
|
|
1297
|
+
│ Code View │ Console View (stdio) │
|
|
984
1298
|
│ (source + breakpoints) ├───────────────────────────┤
|
|
985
1299
|
│ │ Variable View (tree) │
|
|
986
1300
|
│ ├───────────────────────────┤
|
|
@@ -1023,6 +1337,7 @@ A cursor line (blue) tracks your position; the current execution line is highlig
|
|
|
1023
1337
|
| Key | Menu |
|
|
1024
1338
|
|-----|------|
|
|
1025
1339
|
| `Alt+F` | File (open a different script to debug) |
|
|
1340
|
+
| `Alt+E` | Edit (Save, Revert to Disk, Discard and Exit Edit Mode, Open in $EDITOR) |
|
|
1026
1341
|
| `Alt+C` | Configure (Color Theme, Keybindings, Step Mode) |
|
|
1027
1342
|
| `Alt+T` | Threads |
|
|
1028
1343
|
| `Alt+P` | Processes |
|
|
@@ -1036,7 +1351,7 @@ written in the same language as the one currently being debugged.
|
|
|
1036
1351
|
|
|
1037
1352
|
**Navigation (vim-style by default):**
|
|
1038
1353
|
|
|
1039
|
-
By default the Code View is in Debug mode.
|
|
1354
|
+
By default the Code View is in Debug mode. `Escape` cycles Debug → Navigate → Edit → Debug.
|
|
1040
1355
|
In Navigate mode, you can move around the file with the following keys:
|
|
1041
1356
|
|
|
1042
1357
|
| Key | Action |
|
|
@@ -1050,7 +1365,43 @@ In Navigate mode, you can move around the file with the following keys:
|
|
|
1050
1365
|
| `n` / `N` | Next / previous search result |
|
|
1051
1366
|
| `PageUp` / `PageDown` | Scroll by page |
|
|
1052
1367
|
|
|
1053
|
-
|
|
1368
|
+
Press `Escape` again to enter Edit mode (below), and once more to return to Debug mode.
|
|
1369
|
+
|
|
1370
|
+
**Edit mode:**
|
|
1371
|
+
|
|
1372
|
+
Press `Escape` twice from Debug mode to open the current file in an editor
|
|
1373
|
+
inside the Code View. The pane title shows `[Edit]`, with `*` while there
|
|
1374
|
+
are unsaved changes. Editing keys follow the keybinding scheme:
|
|
1375
|
+
|
|
1376
|
+
| Scheme | Editing style | Save | Leave Edit mode |
|
|
1377
|
+
|--------|---------------|------|-----------------|
|
|
1378
|
+
| `vim` | vim-lite: normal / insert modes, counts, `h j k l w b e 0 ^ $ gg G`, `x dd dw D yy p P u Ctrl+R J`, `i a I A o O`, `/ ? n N` | `:w` or `Ctrl+S` | `Esc` (from normal mode), `:q`, `:wq`, `:q!` |
|
|
1379
|
+
| `emacs` | `Ctrl+N/P/F/B`, `Alt+F/B`, `Ctrl+A/E`, `Ctrl+K` / `Ctrl+Y`, `Ctrl+_` undo, `Ctrl+S` / `Ctrl+R` search | `Ctrl+X Ctrl+S` | `Esc` or `Ctrl+X Ctrl+C` |
|
|
1380
|
+
| `default` (Notepad-style) | arrows, Home/End, PgUp/PgDn, Delete/Backspace, Shift+arrows to select, `Ctrl+Z`/`Ctrl+Y`, `Ctrl+X`/`Ctrl+C`/`Ctrl+V` | `Ctrl+S` | `Esc` |
|
|
1381
|
+
|
|
1382
|
+
Leaving Edit mode, quitting, restarting, or opening another file with
|
|
1383
|
+
unsaved changes prompts: `s` save, `d` discard, `Esc` keep editing.
|
|
1384
|
+
|
|
1385
|
+
Saving does not change the running program. tdb shifts your breakpoints
|
|
1386
|
+
to their new lines, clears the current-line marker, and (when restart is
|
|
1387
|
+
available) reminds you to press `R` to restart with the new code. In
|
|
1388
|
+
`tdb --run` sessions, which cannot restart, you will see only "Saved
|
|
1389
|
+
`<file>`." Edit mode is unavailable when no file is loaded, for sources
|
|
1390
|
+
that are not on this machine (remote attach), during replay, and in
|
|
1391
|
+
post-mortem mode.
|
|
1392
|
+
|
|
1393
|
+
The `Edit` menu (`Alt+E`) offers Save, Revert to Disk, Discard and Exit
|
|
1394
|
+
Edit Mode, and Open in $EDITOR, which suspends tdb, runs `$VISUAL` /
|
|
1395
|
+
`$EDITOR` (`notepad` on Windows, `vi` otherwise) on the file, and reloads
|
|
1396
|
+
it when the editor exits.
|
|
1397
|
+
|
|
1398
|
+
Syntax highlighting inside the editor needs tree-sitter, an optional
|
|
1399
|
+
extra: `uv pip install "textual-debugger[edit]"` (highlights Python, Bash,
|
|
1400
|
+
Go, and Rust; other languages edit as plain text). Highlighting in the
|
|
1401
|
+
normal Code View is unaffected.
|
|
1402
|
+
|
|
1403
|
+
CRLF source files are written back with LF line endings after a save in
|
|
1404
|
+
Edit mode (a v1 limitation — no CRLF round-trip yet).
|
|
1054
1405
|
|
|
1055
1406
|
> **Note:** Many terminals send the byte sequence `ESC+f` for `Alt+F`, which Textual's
|
|
1056
1407
|
ANSI parser rewrites to `Ctrl+Right` (the readline "forward-word" convention).
|
|
@@ -1122,6 +1473,15 @@ Set a Python expression (e.g., `x > 10`) and/or a hit count (pause on the Nth hi
|
|
|
1122
1473
|
|
|
1123
1474
|
Breakpoints persist across session restarts.
|
|
1124
1475
|
|
|
1476
|
+
**Breakpoints set at the debugger's own prompt (gdb):** when debugging native code
|
|
1477
|
+
with `gdb`, commands typed in the Evaluate Console such as `b 83` or `delete 2`
|
|
1478
|
+
go straight to gdb. Switching to the Breakpoint View (`Ctrl+B`, or clicking it)
|
|
1479
|
+
synchronizes tdb's table with gdb's list: breakpoints created at the gdb prompt
|
|
1480
|
+
are adopted (with their conditions and enabled state) and become ordinary tdb
|
|
1481
|
+
breakpoints, and breakpoints deleted at the prompt are dropped. Watchpoints,
|
|
1482
|
+
catchpoints and `tbreak` temporaries are left to gdb. `lldb-dap` has no such
|
|
1483
|
+
prompt-level breakpoint channel, so nothing to sync there.
|
|
1484
|
+
|
|
1125
1485
|
### Variable Inspection
|
|
1126
1486
|
|
|
1127
1487
|
The Variable View shows a tree of scopes with all variables in the current frame. The scopes
|
|
@@ -1135,6 +1495,39 @@ the Variables View and press `Enter`
|
|
|
1135
1495
|
to display that variable in a modal. This simplifies inspection of
|
|
1136
1496
|
large or deeply nested data structures.
|
|
1137
1497
|
|
|
1498
|
+
#### Display list
|
|
1499
|
+
|
|
1500
|
+
Like `gdb`'s `display` command, a variable (or any expression) can be
|
|
1501
|
+
pinned so its value is re-read at every stop. Pinned entries appear
|
|
1502
|
+
under a **Display** scope at the top of the Variables View, but only
|
|
1503
|
+
while they are defined in the current frame: step out of the function
|
|
1504
|
+
that owns `b` and the entry vanishes, step back into it and the entry
|
|
1505
|
+
returns. Two ways to pin, two ways to unpin:
|
|
1506
|
+
|
|
1507
|
+
| | Evaluate Console | Variables View |
|
|
1508
|
+
|---|---|---|
|
|
1509
|
+
| pin | `display b` | right-click the variable's row |
|
|
1510
|
+
| unpin | `undisplay b` | left-click its row under **Display** |
|
|
1511
|
+
|
|
1512
|
+
```
|
|
1513
|
+
>>> display b
|
|
1514
|
+
```
|
|
1515
|
+
|
|
1516
|
+
```
|
|
1517
|
+
▼ Display
|
|
1518
|
+
b = 6
|
|
1519
|
+
▼ Locals
|
|
1520
|
+
a = 3
|
|
1521
|
+
b = 6
|
|
1522
|
+
```
|
|
1523
|
+
|
|
1524
|
+
Right-clicking a child of an expanded object pins the full path
|
|
1525
|
+
(`obj.count`, `items[2]`), and a bare `display` lists everything pinned.
|
|
1526
|
+
A click in the Variables View is recorded exactly as the typed command,
|
|
1527
|
+
so `--record`/`--replay` sessions reproduce it. Some terminals keep
|
|
1528
|
+
right-click for their own paste or context menu (Windows Terminal and
|
|
1529
|
+
the VS Code terminal do by default); use the `display` command there.
|
|
1530
|
+
|
|
1138
1531
|
### Call Stack
|
|
1139
1532
|
|
|
1140
1533
|
The Stack View shows the full call stack. Click a frame to navigate to its source
|
|
@@ -1162,6 +1555,45 @@ interactive evaluation of expressions in the current scope:
|
|
|
1162
1555
|
```
|
|
1163
1556
|
|
|
1164
1557
|
Variable values set here are reflected in the running code.
|
|
1558
|
+
The Variables View redraws after every entry, so an assignment typed at
|
|
1559
|
+
the prompt shows up immediately rather than at the next stop.
|
|
1560
|
+
|
|
1561
|
+
#### Interactive variables
|
|
1562
|
+
|
|
1563
|
+
A variable *created* at the prompt is tracked and listed under an extra
|
|
1564
|
+
**Interactive** scope in the Variables View, alongside Locals, Globals and
|
|
1565
|
+
the other language-specific scopes. The value is re-read at every stop,
|
|
1566
|
+
so it works like a watch list of the names you introduced:
|
|
1567
|
+
|
|
1568
|
+
```
|
|
1569
|
+
>>> newvar = 42
|
|
1570
|
+
```
|
|
1571
|
+
|
|
1572
|
+
```
|
|
1573
|
+
▼ Locals
|
|
1574
|
+
a = 3
|
|
1575
|
+
b = 6
|
|
1576
|
+
▼ Interactive
|
|
1577
|
+
newvar = 42
|
|
1578
|
+
```
|
|
1579
|
+
|
|
1580
|
+
What counts as "created" depends on the language, and so does how long the
|
|
1581
|
+
variable lives:
|
|
1582
|
+
|
|
1583
|
+
| Language | Creates an interactive variable | Lifetime |
|
|
1584
|
+
|---|---|---|
|
|
1585
|
+
| Python | `name = …` | the current frame (gone once it returns) |
|
|
1586
|
+
| Perl | `$name = …`, `@name = …`, `%name = …`, `our …` | the whole run (`my` lexicals vanish with the eval and are not tracked) |
|
|
1587
|
+
| Bash | `name=…`, `local`/`declare`/`export …` | the whole run; `local` follows the function |
|
|
1588
|
+
| tcsh | `set name …`, `setenv NAME …` | the whole run |
|
|
1589
|
+
| Ruby | `name = …`, `$name = …`, `@name = …` | globals and instance variables persist; plain locals live only in the eval's binding and read back as unavailable after the next step |
|
|
1590
|
+
| PowerShell | `$name = …`, `$global:name = …` | the function scope, or the whole run for `$global:` |
|
|
1591
|
+
| C/C++/Rust/OCaml under gdb | `set $name = …` (a gdb convenience variable) | the whole run |
|
|
1592
|
+
| C/C++/Rust/OCaml under lldb-dap | `int $name = …` (an lldb persistent variable) | the whole run |
|
|
1593
|
+
| Go (dlv), OCaml under ocamlearlybird | not possible: their evaluate cannot create variables | — |
|
|
1594
|
+
|
|
1595
|
+
A tracked name that no longer resolves (a Python local after its frame
|
|
1596
|
+
returned, for example) stays in the list and shows as `<unavailable>`.
|
|
1165
1597
|
|
|
1166
1598
|
### Cut / Paste
|
|
1167
1599
|
|
|
@@ -1172,12 +1604,35 @@ capture mouse events for their own use.
|
|
|
1172
1604
|
Instead, hold the `Shift` key while performing your conventional cut/paste keystrokes or mouse
|
|
1173
1605
|
operation to get the expected behavior.
|
|
1174
1606
|
|
|
1175
|
-
### Console Output
|
|
1607
|
+
### Console Output and Input
|
|
1176
1608
|
|
|
1177
1609
|
The Console View captures stdout (normal text) and stderr (red text) from the debuggee
|
|
1178
1610
|
in real time.
|
|
1179
1611
|
|
|
1180
|
-
|
|
1612
|
+
Programs that read from stdin (`input()`, `<STDIN>`, `read`, `gets`, …) get
|
|
1613
|
+
their input from the same view: while the program runs, an input line sits
|
|
1614
|
+
under the output log. `Ctrl+O` focuses it, `Enter` sends the line (plus a
|
|
1615
|
+
newline) to the program and echoes it in cyan next to the program's prompt,
|
|
1616
|
+
and `Ctrl+D` sends end-of-file. A bare `Enter` sends an empty line, which is
|
|
1617
|
+
a real answer to `input()`. The line disappears when the program exits or
|
|
1618
|
+
when the session has no stdin to feed.
|
|
1619
|
+
|
|
1620
|
+
Stdin forwarding works for the languages whose program `tdb` launches on a
|
|
1621
|
+
pipe it owns: Python, Perl, Bash, Tcsh, Ruby, and PowerShell. It is not
|
|
1622
|
+
available for the compiled languages — C/C++, Rust, OCaml, and Go — because
|
|
1623
|
+
their adapters (`gdb -i dap`, `lldb-dap`, `dlv dap`) spawn the debuggee
|
|
1624
|
+
themselves and give `tdb` no handle on its stdin; the input line stays
|
|
1625
|
+
hidden there. It is also unavailable for remote attach (`-r`), where the
|
|
1626
|
+
program's stdin belongs to whoever started it, and under `--terminal`,
|
|
1627
|
+
where the program reads from its own terminal window. For those cases run
|
|
1628
|
+
the program in an external terminal with `--terminal` (launch mode only),
|
|
1629
|
+
or give it its input another way (a file, a pipe on the command line).
|
|
1630
|
+
|
|
1631
|
+
Every line typed here is captured by `--record` as a `stdin` gesture and
|
|
1632
|
+
replayed by `--replay`/`--replay-tui`, and the JSON-RPC server exposes the
|
|
1633
|
+
same thing as the `stdin` / `stdin_eof` actions.
|
|
1634
|
+
|
|
1635
|
+
If your program prints a lot, or uses colors or
|
|
1181
1636
|
terminal control codes, run the program in an external terminal
|
|
1182
1637
|
with `--terminal` for a better experience.
|
|
1183
1638
|
The `--terminal` switch requires a graphical environment and a compatible
|
|
@@ -1289,6 +1744,12 @@ This behavior matches hitting `c` while in a conventional (that is, the Python
|
|
|
1289
1744
|
standard library's) `breakpoint()` session.
|
|
1290
1745
|
If you want to kill the program instead, use `Ctrl+c` in the terminal running the debuggee.
|
|
1291
1746
|
|
|
1747
|
+
Perl, Ruby, Go, C/C++, Rust, and OCaml programs get the same hook as
|
|
1748
|
+
`Devel::TdbRemote::breakpoint()`, `Tdb.breakpoint`, `tdb.Breakpoint()`,
|
|
1749
|
+
`tdb_breakpoint()`, `tdb::breakpoint()`, and `Tdb.breakpoint ()`; see
|
|
1750
|
+
[Perl](#perl), [Ruby](#ruby), [Go](#go), [C/C++ tips](#cc-tips),
|
|
1751
|
+
[Rust](#rust), and [OCaml](#ocaml).
|
|
1752
|
+
|
|
1292
1753
|
### Async Task Inspector
|
|
1293
1754
|
|
|
1294
1755
|
For programs using `asyncio`, the menu bar shows an **Async Tasks (N)** label with the count of
|
|
@@ -1477,7 +1938,8 @@ terminal around.
|
|
|
1477
1938
|
The debuggee runs in a separate window of the specified terminal, including
|
|
1478
1939
|
all of its stdin/stdout/stderr. Keyboard input, program output, and
|
|
1479
1940
|
anything the program itself draws to the terminal all happen in that
|
|
1480
|
-
window, not in `tdb`'s Console View
|
|
1941
|
+
window, not in `tdb`'s Console View (whose stdin line is hidden in this
|
|
1942
|
+
mode). Supported choices:
|
|
1481
1943
|
`xterm`, `konsole`, `gnome-terminal`, `ghostty`, `kitty`, `iterm2`, `warp`,
|
|
1482
1944
|
`wezterm`, `terminator`. The selected terminal must be on `PATH`. Debugging
|
|
1483
1945
|
(breakpoints, stepping, variable inspection, the evaluate console) proceeds
|
|
@@ -1487,35 +1949,75 @@ This feature only works in graphical environments where external terminals are a
|
|
|
1487
1949
|
|
|
1488
1950
|
### Run Mode (`--run`)
|
|
1489
1951
|
|
|
1490
|
-
`tdb`'s run mode
|
|
1491
|
-
|
|
1492
|
-
and
|
|
1493
|
-
From there you can inspect the program's state, navigate the stack, set and continue
|
|
1494
|
-
to breakpoints, and so on. After you finish inspecting, hitting `q` to quit brings up
|
|
1495
|
-
a modal that lets you either detach from the TUI and let the program resume running,
|
|
1496
|
-
or lets you terminate it.
|
|
1952
|
+
`tdb`'s run mode acts as a job shepherd.
|
|
1953
|
+
It runs your unmodified program at nearly full speed, without bringing up the TUI,
|
|
1954
|
+
and listens for two interrupt signals:
|
|
1497
1955
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1956
|
+
- `Ctrl-C` pauses the program and brings up TUI, placing you at the currently
|
|
1957
|
+
active line of code. From there you can debug interactively as usual. When
|
|
1958
|
+
you quit the TUI, you can either detach and let the program continue running, or terminate it.
|
|
1959
|
+
|
|
1960
|
+
- `Ctrl-\` temporarily pauses the program and prints a JSON-formatted stack trace to stdout
|
|
1961
|
+
or a log file, then resumes the program.
|
|
1501
1962
|
|
|
1502
1963
|
```bash
|
|
1503
1964
|
tdb --run my_program.py args...
|
|
1504
1965
|
```
|
|
1505
1966
|
|
|
1506
|
-
The debuggee's stdout/stderr stream straight to the terminal
|
|
1967
|
+
The debuggee's stdout/stderr stream straight to the terminal, and for the
|
|
1968
|
+
languages with stdin forwarding (see
|
|
1969
|
+
[Console Output and Input](#console-output-and-input)) the program reads
|
|
1970
|
+
its stdin from that terminal too. If the program exits on
|
|
1507
1971
|
its own, `tdb` exits with the same code:
|
|
1508
1972
|
|
|
1509
1973
|
```bash
|
|
1510
1974
|
tdb --run my_program.py; echo $? # my_program.py's own exit code
|
|
1511
1975
|
```
|
|
1512
1976
|
|
|
1513
|
-
**
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1977
|
+
**Windows caveat:** the Ctrl-Break trigger is untested. Unlike Ctrl-C, a console
|
|
1978
|
+
Ctrl-Break is delivered to every process attached to the console, including the debug
|
|
1979
|
+
adapter and the program itself, so it may terminate the program instead of snapshotting
|
|
1980
|
+
it. There is no signal-based alternative on Windows.
|
|
1981
|
+
|
|
1982
|
+
`tdb` pauses the program, writes one JSON line describing the call stack of every thread
|
|
1983
|
+
(plus asyncio tasks and multiprocessing children for Python, goroutines for Go, and
|
|
1984
|
+
the concurrency snapshot for Rust), and resumes the program. Ctrl-C behavior is
|
|
1985
|
+
unchanged. By default the line goes to stdout; `--examine-log DEST` sends it to a file
|
|
1986
|
+
instead (`-` means stdout; repeat the flag to write to several places):
|
|
1987
|
+
|
|
1988
|
+
```bash
|
|
1989
|
+
tdb --run --examine-log hang.jsonl my_program.py # file only, appended
|
|
1990
|
+
tdb --run --examine-log - --examine-log hang.jsonl my_program.py # both
|
|
1991
|
+
```
|
|
1992
|
+
|
|
1993
|
+
Each record looks like (abridged):
|
|
1994
|
+
|
|
1995
|
+
```json
|
|
1996
|
+
{"schema":1,"seq":1,"trigger":"SIGQUIT","status":"ok",
|
|
1997
|
+
"requested_at":"2026-09-07T14:02:11.482-07:00","landed_at":"2026-09-07T14:02:11.511-07:00",
|
|
1998
|
+
"elapsed_s":87.3,"language":"python","program":"/home/al/work/app.py",
|
|
1999
|
+
"processes":[{"pid":41213,"role":"parent","name":null,
|
|
2000
|
+
"threads":[{"id":1,"name":"MainThread","frames":[
|
|
2001
|
+
{"function":"wait","file":"/usr/lib/python3.13/threading.py","line":359},
|
|
2002
|
+
{"function":"main","file":"/home/al/work/app.py","line":42}]}],
|
|
2003
|
+
"tasks":[{"name":"worker-2","state":"PENDING","awaiting":"Lock.acquire","frames":["run (/home/al/work/app.py:18)"]}]}],
|
|
2004
|
+
"errors":[]}
|
|
2005
|
+
```
|
|
2006
|
+
|
|
2007
|
+
Frames are innermost first. `tasks` appears only for Python programs with live asyncio
|
|
2008
|
+
tasks; `goroutines` only for Go; `rust_concurrency` only for Rust. `status` is
|
|
2009
|
+
`"pending"` when the pause could not land within a few seconds (the program is blocked
|
|
2010
|
+
inside a single call) -- a second record with the same `seq` and `status: "ok"` follows
|
|
2011
|
+
when it does land. If you press Ctrl-C or the program exits before it lands, no
|
|
2012
|
+
completing record is written. `errors` lists any sub-collector that failed, so a partial
|
|
2013
|
+
record is explicit about what is missing. A stderr line confirms each capture and where
|
|
2014
|
+
it went.
|
|
2015
|
+
|
|
2016
|
+
Narrow a long capture with `jq`, for example only threads with a frame in your own code:
|
|
2017
|
+
|
|
2018
|
+
```bash
|
|
2019
|
+
jq -c '.processes[].threads[] | select(any(.frames[]; .file != null and (.file|test("/work/"))))' hang.jsonl
|
|
2020
|
+
```
|
|
1519
2021
|
|
|
1520
2022
|
**Quitting an adopted session** adds a third choice to the usual quit dialog:
|
|
1521
2023
|
- `d` (or `q`) -- detach and resume: the program keeps running headlessly; interrupt it
|
|
@@ -1541,14 +2043,19 @@ language.
|
|
|
1541
2043
|
blocking external call or syscall, the pause can't land until that call returns --
|
|
1542
2044
|
`tdb` prints a notice and opens the TUI once the stop actually arrives.
|
|
1543
2045
|
|
|
1544
|
-
**Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--server`,
|
|
2046
|
+
**Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--replay-tui`, `--server`,
|
|
1545
2047
|
`--headless`, `--mcp`, `--terminal`.
|
|
1546
2048
|
|
|
1547
2049
|
### Eval Mode (`-e`/`--eval`)
|
|
1548
2050
|
|
|
1549
|
-
Eval mode
|
|
1550
|
-
|
|
1551
|
-
|
|
2051
|
+
Eval mode is essentially a code injection mechanism. Rather than injecting
|
|
2052
|
+
native code though, only expressions that are valid in the Evaluate View
|
|
2053
|
+
can be injected (in Python, native expressions are processed correctly
|
|
2054
|
+
in the Evaluate View).
|
|
2055
|
+
|
|
2056
|
+
In this way, Eval mode automates the common debugging action of setting a
|
|
2057
|
+
breakpoint, running to it, evaluating (and printing the result of) an expression, then
|
|
2058
|
+
resuming execution, all without opening the TUI. `-e` takes two arguments, a location and an expression:
|
|
1552
2059
|
|
|
1553
2060
|
```bash
|
|
1554
2061
|
tdb --eval examples/digits_of_pi.py:25 "print(f'{nr=}')" examples/digits_of_pi.py
|
|
@@ -1568,7 +2075,7 @@ evaluated in each child process that reaches it.
|
|
|
1568
2075
|
- `-e` may be repeated to plant several eval points in one run.
|
|
1569
2076
|
- The expression is evaluated through the debug adapter (DAP `evaluate`,
|
|
1570
2077
|
context `repl`), just like the Evaluate console: side effects are real
|
|
1571
|
-
and persist in the running program, for example
|
|
2078
|
+
and persist in the running program, for example
|
|
1572
2079
|
`-e my_program.py:25 "limit = 10"` changes `limit` from line 25 onward.
|
|
1573
2080
|
- A bare expression's result is printed to stdout; statement-style
|
|
1574
2081
|
expressions (`print(...)`, assignments) print nothing beyond their own
|
|
@@ -1584,14 +2091,14 @@ evaluated in each child process that reaches it.
|
|
|
1584
2091
|
the eval point matches that bound line, so the expression still runs.
|
|
1585
2092
|
|
|
1586
2093
|
**Incompatible flags:** `--run`, `-r`, `-a`, `-k`/`-t`, `--record`,
|
|
1587
|
-
`--replay`, `--server`, `--headless`, `--mcp`, `--terminal`.
|
|
2094
|
+
`--replay`, `--replay-tui`, `--server`, `--headless`, `--mcp`, `--terminal`.
|
|
1588
2095
|
|
|
1589
2096
|
### Keybinding Schemes
|
|
1590
2097
|
|
|
1591
2098
|
```bash
|
|
1592
2099
|
tdb --keybindings vim my_program.py # default
|
|
1593
2100
|
tdb --keybindings emacs my_program.py
|
|
1594
|
-
tdb --keybindings default my_program.py
|
|
2101
|
+
tdb --keybindings default my_program.py # Notepad-style editing
|
|
1595
2102
|
```
|
|
1596
2103
|
|
|
1597
2104
|
The keybinding choice is saved to `~/.config/tdb/config.json` and remembered for subsequent
|
|
@@ -1673,6 +2180,8 @@ curl -s -X POST http://127.0.0.1:8150/rpc \
|
|
|
1673
2180
|
| `stack_down` | `[]` | Move down the call stack |
|
|
1674
2181
|
| `get_stack_trace` | `[]` | Full call stack |
|
|
1675
2182
|
| `get_output` | `[]` | Drain buffered stdout/stderr |
|
|
2183
|
+
| `stdin` | `["line"]` | Send a line (newline appended) to the program's stdin; errors when the session has no stdin (compiled languages, `--terminal`, remote attach) |
|
|
2184
|
+
| `stdin_eof` | `[]` | Close the program's stdin (Ctrl+D) |
|
|
1676
2185
|
| `get_source` | `["file_path"]` | Read a source file |
|
|
1677
2186
|
| `list_threads` | `[]` | List all threads |
|
|
1678
2187
|
| `inspect_thread` | `[thread_id]` | Inspect a specific thread |
|
|
@@ -1700,20 +2209,36 @@ Each is JSON with `event`, `data`, and `timestamp` fields.
|
|
|
1700
2209
|
|
|
1701
2210
|
`tdb --record session.jsonl prog.py` runs a normal TUI session and captures
|
|
1702
2211
|
your debugging actions including breakpoints (including `-k`/`-t` and persisted
|
|
1703
|
-
ones), stepping, continue/pause, Evaluate-console entries,
|
|
2212
|
+
ones), stepping, continue/pause, Evaluate-console entries, lines typed into
|
|
2213
|
+
the Console view for the program's stdin (and Ctrl+D), stack-frame
|
|
1704
2214
|
navigation, variable expansion, restart, quit. The session is
|
|
1705
2215
|
written to `session.jsonl` as
|
|
1706
2216
|
JSON-RPC commands. Works with launch mode (any language) and `-r`
|
|
1707
2217
|
remote attach.
|
|
1708
2218
|
|
|
1709
|
-
Replay it
|
|
1710
|
-
|
|
2219
|
+
Replay it three ways:
|
|
2220
|
+
|
|
2221
|
+
- `tdb --replay-tui session.jsonl` opens the normal TUI on the recorded
|
|
2222
|
+
program and performs every recorded action in front of you, at the
|
|
2223
|
+
recorded pace: breakpoints appear in the Code View, steps and continues
|
|
2224
|
+
move the cursor, Evaluate entries echo into the console with their
|
|
2225
|
+
results, stack navigation and variable expansion play out in their
|
|
2226
|
+
panels. The title bar shows progress (`tdb ▶ session.jsonl 3/11`) and a
|
|
2227
|
+
toast announces each action (`--replay-quiet` turns those toasts off).
|
|
2228
|
+
A failed action raises an error toast and replay continues. When the recording does not end with `quit`, the
|
|
2229
|
+
debugger stays open at the final stop for you to carry on by hand.
|
|
2230
|
+
`--replay-interval S` waits a fixed S seconds before each action
|
|
2231
|
+
instead of reproducing the recorded gaps. `--replay-timeout S` bounds
|
|
2232
|
+
each stop-wait (default 30 s). Exit code 0
|
|
2233
|
+
iff every action succeeded. Don't type into the TUI while it is
|
|
2234
|
+
replaying: stray keystrokes interleave with the recorded actions.
|
|
1711
2235
|
- `tdb --replay session.jsonl` launches the recorded
|
|
1712
2236
|
program headless, feeds every recorded command through the same RPC
|
|
1713
2237
|
dispatch `tdb --server` uses, and prints a transcript (recorded time,
|
|
1714
2238
|
command, verbatim result, interleaved program output). Exit code 0 iff
|
|
1715
2239
|
every command succeeded. Add `--timing` to reproduce the original
|
|
1716
|
-
pacing
|
|
2240
|
+
pacing or `--replay-interval S` for a fixed delay before each command,
|
|
2241
|
+
`--replay-timeout S` to bound each stop-wait (default 30 s).
|
|
1717
2242
|
- Against a live server: start `tdb --server prog.py`, then feed line 2
|
|
1718
2243
|
onward of the file to `POST /rpc` . Each line is a valid request body:
|
|
1719
2244
|
|
|
@@ -1730,9 +2255,10 @@ lists), breakpoint enable/disable toggles, variable expansions when the
|
|
|
1730
2255
|
adapter reports no `evaluateName` (currently the Perl adapter), and
|
|
1731
2256
|
File > Open program switches.
|
|
1732
2257
|
|
|
1733
|
-
## MCP Integration
|
|
2258
|
+
## MCP v2 Integration
|
|
1734
2259
|
|
|
1735
|
-
tdb ships a Model Context Protocol (MCP)
|
|
2260
|
+
tdb ships a Model Context Protocol (MCP), version 2,
|
|
2261
|
+
server (`tdb-mcp`) that exposes
|
|
1736
2262
|
the debugger as a curated set of tools an AI agent can call. The MCP
|
|
1737
2263
|
server is a third in-process consumer of the same dispatch handlers the
|
|
1738
2264
|
TUI and the HTTP server use so an agent gets the same lock semantics,
|
|
@@ -1830,13 +2356,14 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
|
|
|
1830
2356
|
[--keybindings {default,vim,emacs}]
|
|
1831
2357
|
[--terminal {xterm,konsole,gnome-terminal,ghostty,kitty,iterm2,warp,wezterm,terminator}]
|
|
1832
2358
|
[--local-root PATH] [--remote-root PATH]
|
|
1833
|
-
[--server] [--headless] [-k FILE:LINE|LINE] [--server-port SERVER_PORT] [-d] [--doc-text]
|
|
2359
|
+
[--server] [--headless] [-k FILE:LINE|LINE] [--server-port SERVER_PORT] [-d] [--doc-text] [--info]
|
|
1834
2360
|
[program] [args ...]
|
|
1835
2361
|
```
|
|
1836
2362
|
|
|
1837
2363
|
| Flag | Description |
|
|
1838
2364
|
|------|-------------|
|
|
1839
2365
|
| `-r HOST:PORT` | Attach to a remote debugpy server |
|
|
2366
|
+
| `-a`, `--attach PID` | Attach to a running local process by pid: Go (Delve) or a native C/C++, Rust, or OCaml program (gdb or lldb-dap). Linux identifies the language from `/proc/PID/exe`; elsewhere pass `--lang` and, for native programs, the executable path. |
|
|
1840
2367
|
| `--local-root PATH` | Local directory containing a copy of remote code (repeat to mirror multiple trees). Pair with `--remote-root`; counts must match. Required when `-k` sets a breakpoint against a remote debuggee whose code lives at a different path. |
|
|
1841
2368
|
| `--remote-root PATH` | Remote directory matched to `--local-root` (same CLI position via `zip()`). |
|
|
1842
2369
|
| `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated). Passing `-k` implies `--no-stop-on-entry` so the program runs straight to the first breakpoint. |
|
|
@@ -1846,17 +2373,25 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
|
|
|
1846
2373
|
| `--python PATH` | Python interpreter for the debuggee (Python targets only) |
|
|
1847
2374
|
| `--pv` | Shorthand for --python .venv/bin/python |
|
|
1848
2375
|
| `--lang LANGUAGE` | Debuggee language (`python`, `cpp`, `perl`); default: auto-detect from the target |
|
|
1849
|
-
| `--adapter ADAPTER` | Debug adapter within the language (e.g. `--lang cpp --adapter lldb-dap`)
|
|
2376
|
+
| `--adapter ADAPTER` | Debug adapter within the language (e.g. `--lang cpp --adapter lldb-dap`), or a full path to a `gdb`/`lldb-dap`/`dlv` executable to use instead of the one on `PATH`; default: the language's standard adapter |
|
|
1850
2377
|
| `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
|
|
1851
2378
|
(default: skipped). On uncaught exceptions, the crash modal always shows the full traceback
|
|
1852
2379
|
including library frames, regardless of this flag. |
|
|
1853
2380
|
| `--no-subprocess` | Disable debugpy's subprocess tracking (use when debugging `tdb` itself) |
|
|
1854
2381
|
| `--terminal TERM` | Run debuggee in the named external terminal: `xterm`, `konsole`,
|
|
1855
2382
|
`gnome-terminal`, `ghostty`, `kitty`, `iterm2`, `warp`, `wezterm`, or `terminator` |
|
|
1856
|
-
| `--keybindings SCHEME` | `
|
|
2383
|
+
| `--keybindings SCHEME` | `vim`, `emacs`, or `default` (Notepad-style editing); saved to config |
|
|
1857
2384
|
| `--server` | Enable JSON-RPC server alongside TUI |
|
|
1858
2385
|
| `--headless` | JSON-RPC server only, no TUI |
|
|
1859
2386
|
| `--server-port PORT` | Server port (default: 8150) |
|
|
2387
|
+
| `--record FILE` | Record this TUI session's debugging actions to FILE (JSON-RPC lines) |
|
|
2388
|
+
| `--replay FILE` | Replay a recording headless, printing a transcript (no program argument) |
|
|
2389
|
+
| `--replay-tui FILE` | Replay a recording inside the TUI at the recorded pace, so you can watch it |
|
|
2390
|
+
| `--replay-quiet` | With `--replay-tui`: no per-action toasts (errors and the final summary still show) |
|
|
2391
|
+
| `--timing` | With `--replay`: reproduce the recorded pacing (`--replay-tui` always does) |
|
|
2392
|
+
| `--replay-interval S` | With `--replay`/`--replay-tui`: fixed S-second delay before each action instead of the recorded gaps |
|
|
2393
|
+
| `--replay-timeout S` | With `--replay`/`--replay-tui`: per-action stop-wait timeout (default 30) |
|
|
2394
|
+
| `--examine-log DEST` | With --run: write each Ctrl-\ / SIGUSR2 stack snapshot to DEST (- = stdout; repeatable) |
|
|
1860
2395
|
|
|
1861
2396
|
## Configuration
|
|
1862
2397
|
|
|
@@ -1869,10 +2404,63 @@ On Windows, it uses `%APPDATA%\tdb\`.
|
|
|
1869
2404
|
| `config.json` | User preferences (keybinding scheme, color theme, step mode, adapter overrides) |
|
|
1870
2405
|
| `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
|
|
1871
2406
|
|
|
2407
|
+
### Sample `config.json`
|
|
2408
|
+
|
|
2409
|
+
Every key `tdb` reads, with its default or a representative value. Copy
|
|
2410
|
+
this to restore a deleted or corrupted file, then delete the entries you
|
|
2411
|
+
don't need: a missing key takes its default, an unknown key is ignored,
|
|
2412
|
+
and an invalid value (say a misspelled `step_mode`) falls back to the
|
|
2413
|
+
default instead of stopping `tdb`.
|
|
2414
|
+
|
|
2415
|
+
```json
|
|
2416
|
+
{
|
|
2417
|
+
"keybindings": "vim",
|
|
2418
|
+
"theme": null,
|
|
2419
|
+
"step_mode": "statement",
|
|
2420
|
+
"adapters": {
|
|
2421
|
+
"gdb": "/usr/bin/gdb",
|
|
2422
|
+
"lldb-dap": null,
|
|
2423
|
+
"dlv": "/home/me/go/bin/dlv",
|
|
2424
|
+
"perl": "/usr/bin/perl",
|
|
2425
|
+
"rdbg": "/usr/local/bin/rdbg",
|
|
2426
|
+
"bash": "/usr/bin/bash",
|
|
2427
|
+
"tcsh": "/usr/bin/tcsh",
|
|
2428
|
+
"pwsh": "/usr/bin/pwsh",
|
|
2429
|
+
"pses": "/home/me/.local/share/PowerShellEditorServices",
|
|
2430
|
+
"ocamlearlybird": "/home/me/.opam/default/bin/ocamlearlybird"
|
|
2431
|
+
},
|
|
2432
|
+
"default_adapters": {
|
|
2433
|
+
"cpp": "gdb",
|
|
2434
|
+
"rust": "gdb",
|
|
2435
|
+
"ocaml": "lldb-dap"
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
```
|
|
2439
|
+
|
|
2440
|
+
| Key | Values | Meaning |
|
|
2441
|
+
|-----|--------|---------|
|
|
2442
|
+
| `keybindings` | `"vim"` (default), `"emacs"`, `"default"` | Code View keybinding scheme; also set by `--keybindings` |
|
|
2443
|
+
| `theme` | `null` (default) or a Textual theme name such as `"textual-dark"`, `"textual-light"`, `"nord"`, `"gruvbox"`, `"dracula"`, `"monokai"`, `"tokyo-night"`, `"catppuccin-mocha"` | Color theme; the names offered under **Configure > Theme** are the valid ones, and an unknown name is ignored |
|
|
2444
|
+
| `step_mode` | `"statement"` (default), `"line"` | How `n`/`s` treat multi-line statements (see **Step mode** below) |
|
|
2445
|
+
| `adapters` | adapter id → executable path, or `null` | Where to find a debugger or interpreter instead of searching `PATH`. Ids: `gdb`, `lldb-dap`, `dlv` (debuggers); `perl`, `rdbg`, `bash`, `tcsh`, `pwsh` (interpreters `tdb` spawns); `pses` (the PowerShell Editor Services module directory); `ocamlearlybird`. Only list the ones you need |
|
|
2446
|
+
| `default_adapters` | language id → adapter id | Which adapter a language uses when `--adapter` isn't given. Languages with a choice: `cpp` and `rust` (`gdb` or `lldb-dap`), `ocaml` (`lldb-dap`, `gdb`, or `ocamlearlybird`) |
|
|
2447
|
+
|
|
2448
|
+
The file is plain JSON: no comments, no trailing commas. `tdb` rewrites it
|
|
2449
|
+
when you change a setting from the **Configure** menu, so hand edits made
|
|
2450
|
+
while `tdb` is running may be overwritten.
|
|
2451
|
+
|
|
1872
2452
|
Adapter-related keys in `config.json`: `adapters` maps an adapter id to an
|
|
1873
2453
|
executable path (`{"adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"}}`), and
|
|
1874
2454
|
`default_adapters` picks a language's default adapter
|
|
1875
|
-
(`{"default_adapters": {"cpp": "lldb-dap"}}`).
|
|
2455
|
+
(`{"default_adapters": {"cpp": "lldb-dap"}}`). For a one-off run,
|
|
2456
|
+
`--adapter /path/to/gdb` (or `lldb-dap`, `dlv`) overrides the `adapters` entry
|
|
2457
|
+
without editing the file; `tdb --info` reports the `gdb` and `lldb-dap`
|
|
2458
|
+
currently resolved (config override first, then `PATH`) with their versions.
|
|
2459
|
+
|
|
2460
|
+
When `tdb` creates `config.json` for the first time it seeds `adapters` with
|
|
2461
|
+
`gdb` and `lldb-dap` entries pointing at the executables found on `PATH`
|
|
2462
|
+
(`null` when one isn't installed) so the keys are there to edit. A `null`
|
|
2463
|
+
entry behaves like a missing one: `tdb` falls back to `PATH`.
|
|
1876
2464
|
|
|
1877
2465
|
**Perl is a special case:** `perl-tdb` is tdb's own bundled adapter (always
|
|
1878
2466
|
found; it's Python code, not an external executable), so
|
|
@@ -1917,6 +2505,29 @@ fires as expected.
|
|
|
1917
2505
|
|
|
1918
2506
|
MIT
|
|
1919
2507
|
|
|
2508
|
+
## Acknowledgments
|
|
2509
|
+
|
|
2510
|
+
Thank you:
|
|
2511
|
+
|
|
2512
|
+
- Will McGugan for the amazing `textual` module.
|
|
2513
|
+
`tdb` would be a pale shadow of itself had I used any other TUI framework.
|
|
2514
|
+
Fantastic work, Will.
|
|
2515
|
+
|
|
2516
|
+
- Microsoft for the Debug Adapter Protocol (DAP) and releasing
|
|
2517
|
+
its implementation in `debugpy` and the Python Debugger extension for Visual Studio Code
|
|
2518
|
+
as open source.
|
|
2519
|
+
|
|
2520
|
+
- Anthropic, for providing access to Claude Code through the
|
|
2521
|
+
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
2522
|
+
`tdb` was made almost entirely with Claude Code.
|
|
2523
|
+
|
|
2524
|
+
- OpenAI, for providing access to Codex through the
|
|
2525
|
+
[Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
|
|
2526
|
+
Codex was used to implement tcsh and Rust support.
|
|
2527
|
+
|
|
2528
|
+
This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/)
|
|
2529
|
+
Python debugger.
|
|
2530
|
+
|
|
1920
2531
|
|
|
1921
2532
|
## Known Problems
|
|
1922
2533
|
|