textual-debugger 0.2.12__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.
Files changed (170) hide show
  1. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/PKG-INFO +579 -55
  2. {textual_debugger-0.2.12/src/tdb → textual_debugger-0.2.14}/README.md +576 -54
  3. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/pyproject.toml +2 -1
  4. {textual_debugger-0.2.12 → textual_debugger-0.2.14/src/tdb}/README.md +576 -54
  5. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/__init__.py +1 -1
  6. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/_timeouts.py +7 -0
  7. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/bash/server.py +20 -0
  8. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/bash/session.py +63 -5
  9. textual_debugger-0.2.14/src/tdb/adapters/native/__init__.py +1 -0
  10. textual_debugger-0.2.14/src/tdb/adapters/native/tdb.h +269 -0
  11. textual_debugger-0.2.14/src/tdb/adapters/perl/Devel/TdbRemote.pm +363 -0
  12. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/padwalker.py +10 -3
  13. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/server.py +109 -10
  14. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/session.py +111 -8
  15. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/server.py +65 -2
  16. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/ruby/server.py +210 -16
  17. textual_debugger-0.2.14/src/tdb/adapters/ruby/tdb.rb +216 -0
  18. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/inspect.py +11 -0
  19. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/server.py +21 -0
  20. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/session.py +86 -2
  21. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/app.py +372 -15
  22. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/app_handlers/dap_events.py +31 -12
  23. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/app_helpers.py +9 -21
  24. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/cli.py +96 -37
  25. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/dap/client.py +106 -5
  26. textual_debugger-0.2.14/src/tdb/info.py +197 -0
  27. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/keybindings/__init__.py +86 -3
  28. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/base.py +126 -0
  29. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/bash.py +11 -1
  30. textual_debugger-0.2.14/src/tdb/languages/cpp.py +451 -0
  31. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/go.py +25 -2
  32. textual_debugger-0.2.14/src/tdb/languages/native_tools.py +187 -0
  33. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/ocaml.py +76 -6
  34. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/perl.py +11 -2
  35. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/powershell.py +8 -2
  36. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/python.py +13 -1
  37. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/registry.py +68 -2
  38. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/ruby.py +8 -1
  39. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/rust.py +54 -2
  40. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/tcsh.py +10 -1
  41. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/persist.py +44 -9
  42. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/replay_tui.py +21 -0
  43. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/run_mode.py +11 -2
  44. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/gdb_script.py +9 -1
  45. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/server/handlers.py +26 -0
  46. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/server/runner.py +10 -0
  47. textual_debugger-0.2.14/src/tdb/session/breakpoint_sync.py +158 -0
  48. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/controller.py +384 -18
  49. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/state.py +27 -0
  50. textual_debugger-0.2.14/src/tdb/session/terminal.py +268 -0
  51. textual_debugger-0.2.14/src/tdb/source_edit.py +141 -0
  52. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/breakpoint_view.py +11 -0
  53. textual_debugger-0.2.14/src/tdb/widgets/code_editor.py +738 -0
  54. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/code_view.py +422 -20
  55. textual_debugger-0.2.14/src/tdb/widgets/console_view.py +125 -0
  56. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/modals.py +13 -4
  57. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/variable_view.py +52 -2
  58. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/PKG-INFO +579 -55
  59. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/SOURCES.txt +8 -0
  60. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/requires.txt +3 -0
  61. textual_debugger-0.2.12/src/tdb/adapters/perl/Devel/TdbRemote.pm +0 -93
  62. textual_debugger-0.2.12/src/tdb/languages/cpp.py +0 -204
  63. textual_debugger-0.2.12/src/tdb/session/terminal.py +0 -121
  64. textual_debugger-0.2.12/src/tdb/widgets/console_view.py +0 -35
  65. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/LICENSE +0 -0
  66. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/setup.cfg +0 -0
  67. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/__main__.py +0 -0
  68. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/__init__.py +0 -0
  69. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/bash/__init__.py +0 -0
  70. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/bash/__main__.py +0 -0
  71. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/bash/declares.py +0 -0
  72. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/bash/tdb_harness.sh +0 -0
  73. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/ocaml/__init__.py +0 -0
  74. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/ocaml/lldb_formatters.py +0 -0
  75. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/Devel/TdbCompile.pm +0 -0
  76. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/__init__.py +0 -0
  77. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/__main__.py +0 -0
  78. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/helpers.pl +0 -0
  79. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/padwalker_src/Makefile.PL +0 -0
  80. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/padwalker_src/PadWalker.pm +0 -0
  81. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/padwalker_src/PadWalker.xs +0 -0
  82. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/protocol.py +0 -0
  83. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/perl/refs.py +0 -0
  84. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/__init__.py +0 -0
  85. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/__main__.py +0 -0
  86. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/locate.py +0 -0
  87. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/output.py +0 -0
  88. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/powershell/tdb_launch.ps1 +0 -0
  89. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/ruby/__init__.py +0 -0
  90. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/ruby/__main__.py +0 -0
  91. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/seqs.py +0 -0
  92. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/__init__.py +0 -0
  93. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/__main__.py +0 -0
  94. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/breakpoints.py +0 -0
  95. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/cli.py +0 -0
  96. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/guardian.py +0 -0
  97. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/instrumenter.py +0 -0
  98. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/models.py +0 -0
  99. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/protocol.py +0 -0
  100. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/runtime.py +0 -0
  101. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/scanner.py +0 -0
  102. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/adapters/tcsh/transport.py +0 -0
  103. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/app_handlers/__init__.py +0 -0
  104. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/app_handlers/inspection.py +0 -0
  105. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/app_handlers/routing.py +0 -0
  106. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/app_handlers/ui_panels.py +0 -0
  107. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/breakpoint_hook.py +0 -0
  108. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/dap/__init__.py +0 -0
  109. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/dap/messages.py +0 -0
  110. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/dap/protocol.py +0 -0
  111. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/dap/reverse.py +0 -0
  112. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/dap/types.py +0 -0
  113. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/eval_mode.py +0 -0
  114. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/examine.py +0 -0
  115. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/go_concurrency/__init__.py +0 -0
  116. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/go_concurrency/analyzer.py +0 -0
  117. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/go_concurrency/classifier.py +0 -0
  118. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/go_concurrency/collector.py +0 -0
  119. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/go_concurrency/models.py +0 -0
  120. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/inspection.py +0 -0
  121. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/inspection_full.py +0 -0
  122. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/__init__.py +0 -0
  123. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/languages/errors.py +0 -0
  124. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/mcp/__init__.py +0 -0
  125. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/mcp/__main__.py +0 -0
  126. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/mcp/server.py +0 -0
  127. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/mcp/session.py +0 -0
  128. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/post_mortem.py +0 -0
  129. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/processes_cache.py +0 -0
  130. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/replay.py +0 -0
  131. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/__init__.py +0 -0
  132. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/analyzer.py +0 -0
  133. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/classifier.py +0 -0
  134. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/collector.py +0 -0
  135. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/models.py +0 -0
  136. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/__init__.py +0 -0
  137. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/base.py +0 -0
  138. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/gdb.py +0 -0
  139. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/lldb.py +0 -0
  140. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/rust_concurrency/probes/lldb_script.py +0 -0
  141. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/server/__init__.py +0 -0
  142. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/server/app.py +0 -0
  143. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/server/event_handler.py +0 -0
  144. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/server/rpc_types.py +0 -0
  145. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/__init__.py +0 -0
  146. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/child_processes.py +0 -0
  147. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/errors.py +0 -0
  148. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/event_bus.py +0 -0
  149. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/inspect_service.py +0 -0
  150. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/messages.py +0 -0
  151. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/post_mortem_loader.py +0 -0
  152. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/recorder.py +0 -0
  153. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/statement_stepper.py +0 -0
  154. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/session/textual_handler.py +0 -0
  155. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/source_analysis.py +0 -0
  156. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/__init__.py +0 -0
  157. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/_inspection_modal.py +0 -0
  158. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/async_tasks_modal.py +0 -0
  159. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/evaluate_console.py +0 -0
  160. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/full_contents_modal.py +0 -0
  161. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/goroutines_modal.py +0 -0
  162. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/menu_bar.py +0 -0
  163. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/processes_modal.py +0 -0
  164. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/rust_concurrency_modal.py +0 -0
  165. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/stack_view.py +0 -0
  166. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/status_bar.py +0 -0
  167. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/tdb/widgets/threads_modal.py +0 -0
  168. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
  169. {textual_debugger-0.2.12 → textual_debugger-0.2.14}/src/textual_debugger.egg-info/entry_points.txt +0 -0
  170. {textual_debugger-0.2.12 → 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.12
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:
@@ -54,6 +54,8 @@ Requires-Dist: pygments>=2.17.0
54
54
  Requires-Dist: fastapi>=0.115.0
55
55
  Requires-Dist: uvicorn>=0.30.0
56
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"
@@ -123,7 +125,11 @@ 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
@@ -168,11 +174,13 @@ uvx --from textual-debugger tdb my_program.py
168
174
  # show comprehensive documentation in a terminal-based Markdown viewer
169
175
  tdb --doc
170
176
 
171
- # show where tdb is installed, where its bundled Perl PadWalker module
172
- # lives, and the Help > About text
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)
173
181
  tdb --info
174
182
 
175
- # debug a script (stops at first line by default)
183
+ # debug a Python program (stops at first line by default)
176
184
  tdb my_program.py
177
185
 
178
186
  # debug with arguments
@@ -207,7 +215,7 @@ tdb --python /path/to/venv/bin/python my_program.py
207
215
  # step into, or stop at tracebacks in library code
208
216
  tdb --no-just-my-code --python /path/to/venv/bin/python my_program.py
209
217
 
210
- # run until first breakpoint or exit
218
+ # run until first breakpoint (persisted during a prior run) or exit
211
219
  tdb --no-stop-on-entry my_program.py
212
220
 
213
221
  # run the debuggee in an external terminal
@@ -232,7 +240,7 @@ tdb -r remotehost:5678 --local-root /my/code/dir --remote-root /app -k my_progra
232
240
  # attach, which can leave it suspended after tdb quits
233
241
  tdb -r remotehost:5678 --no-pause-on-attach
234
242
 
235
- # separate tdb arguments from debuggee arguments with `--`
243
+ # separate tdb arguments from debuggee arguments with `--`
236
244
  tdb --python /path/to/venv/bin/python -- my_program.py -k 17 --max 23.3
237
245
  ```
238
246
 
@@ -250,7 +258,7 @@ languages are supported out of the box:
250
258
  | Language | Adapter(s) | Dependencies | Feature level |
251
259
  |----------|------------|------------------------|---------------|
252
260
  | Python | `debugpy` (default) | Python ≥ 3.11 | everything in this README |
253
- | 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) |
254
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 |
255
263
  | Perl | perl-tdb (bundled) | perl ≥ 5.18 on PATH | core debugging + remote attach |
256
264
  | Bash | bash-tdb (bundled) | bash ≥ 4.4 on PATH | core debugging (no remote attach) |
@@ -297,6 +305,23 @@ The language is auto-detected from the debug target:
297
305
  it (`tdb --lang cpp --adapter lldb-dap ./myprog`). Rust always requires the
298
306
  explicit language selection (`tdb --lang rust target/debug/app`).
299
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
+
300
325
  > **Migration note:** extensionless Python scripts without a `python` shebang
301
326
  > were previously assumed to be Python; they now require `--lang python`.
302
327
 
@@ -341,7 +366,10 @@ language — Python, Perl, Bash, Tcsh, Ruby, and C/C++, OCaml native, or Rust
341
366
  sessions via `--adapter lldb-dap` — see
342
367
  [External Terminal Support](#external-terminal-support). Go and PowerShell do
343
368
  not support `--terminal` yet (`dlv dap` and PSES have no terminal-routing
344
- 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).
345
373
 
346
374
  **Bash limitations (v1):** the bash adapter uses bash's own `DEBUG` trap and
347
375
  has a smaller feature set than Python:
@@ -407,10 +435,16 @@ picture:
407
435
  backtrace instead.
408
436
  - No Windows support, no remote attach, and a stripped native binary that
409
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.
410
441
 
411
442
  **Go limitations (v1):**
412
443
 
413
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.
414
448
  - The Goroutines workspace's wait graph shows channel-send/-receive,
415
449
  mutex, and `WaitGroup` edges; a goroutine parked in a `select` is
416
450
  classified but contributes no wait edge (Delve can't tell which of the
@@ -437,9 +471,17 @@ See [Go](#go) below for launch details.
437
471
  If no breakpoint in a file can be
438
472
  bound, `tdb` prints a console warning suggesting the program may lack
439
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.
440
478
  - Stack frames pointing into system libraries often have no source on disk;
441
479
  the Code View shows a `<Could not read …>` placeholder while the stack,
442
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).
443
485
  - GDB (the default adapter) has the most complete libstdc++
444
486
  pretty-printing. `lldb-dap` (via `--adapter lldb-dap`) also debugs
445
487
  GCC-built binaries fine. DWARF is compiler-neutral.
@@ -450,6 +492,80 @@ See [Go](#go) below for launch details.
450
492
  - `--terminal` (see [External Terminal Support](#external-terminal-support))
451
493
  requires `--adapter lldb-dap`; GDB's DAP mode has no terminal integration
452
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`.
453
569
 
454
570
  ### Rust
455
571
 
@@ -471,7 +587,9 @@ cargo rustc -- -C debuginfo=2 -C opt-level=0
471
587
  # Equivalent direct rustc settings: rustc -C debuginfo=2 -C opt-level=0 src/main.rs
472
588
  ```
473
589
 
474
- A panic that terminates the debuggee opens the error modal with the parsed
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
475
593
  panic message and backtrace; `tdb` injects `RUST_BACKTRACE=1` into launched
476
594
  programs (your own value wins if you set one) so the frames are present.
477
595
 
@@ -502,6 +620,50 @@ When source paths differ, pair `--local-root` with `--remote-root`. Do not
502
620
  expose the remote debug port to an untrusted network: prefer an SSH tunnel,
503
621
  for example `ssh -L 2345:127.0.0.1:2345 host`, then attach to `127.0.0.1:2345`.
504
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
+
505
667
  In a Rust session the Threads action (`Alt+T`, or the Threads menu entry)
506
668
  opens the Rust Concurrency workspace while the debuggee is stopped — a
507
669
  fresh, bounded snapshot of threads, wait edges, and findings. The graph is
@@ -637,11 +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
- **Pause is unavailable in attach mode.** Launch-mode sessions (`tdb
641
- script.pl`) support pausing a running program at any time. Remote-attach
642
- sessions don't. Asynchronous pause (as Python gets via `debugpy`) needs a control channel
643
- `Devel::TdbRemote` doesn't implement yet; `pause` in attach mode returns a
644
- "not available" error instead of hanging.
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
+ ```
825
+
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).
645
844
 
646
845
  ### Bash
647
846
 
@@ -731,6 +930,33 @@ exec` integration yet.
731
930
  > race on launch; tdb detects a stalled/corrupted handshake and retries
732
931
  > once automatically with a fresh `rdbg`, so this is usually invisible.
733
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
+
734
960
  ### OCaml
735
961
 
736
962
  `tdb` debugs OCaml through two independent adapters, chosen automatically
@@ -754,7 +980,7 @@ ocamlc -g -o my_program.byte "$PWD/my_program.ml"
754
980
  ```
755
981
 
756
982
  Then, to ensure transition from bytecode to OCaml source in the
757
- Code view, provide an explicit temporary breakpoint (`-t`)
983
+ Code view, provide an explicit temporary breakpoint (`-t`)
758
984
  at the first line of execution:
759
985
 
760
986
  ```
@@ -834,6 +1060,52 @@ sessions can't `pause` a running program (so `--run` is unavailable for
834
1060
  bytecode. Native sessions support `--run` normally); `Unix.fork` and Eio
835
1061
  fibers are out of scope (only OCaml 5 domains are presented as threads).
836
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
+
837
1109
  ### Go
838
1110
 
839
1111
  `tdb` debugs Go through [Delve](https://github.com/go-delve/delve)'s own
@@ -859,10 +1131,53 @@ tdb -r host:port --lang go # attach to a `dlv dap --listen` already running remo
859
1131
 
860
1132
  `tdb --test ./pkg` debugs the package's tests under Delve's `test` mode;
861
1133
  pass test-binary flags after `--`, e.g. `tdb --test ./pkg -- -run TestFoo`.
862
- `-a`/`--attach` currently supports Go only; on Linux, and only on Linux,
863
- `tdb` can identify the target as Go from `/proc/PID/exe`'s buildinfo
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`
864
1136
  without `--lang` (elsewhere pass `--lang go` alongside `-a`). Attaching
865
- 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/`.
866
1181
 
867
1182
  **Remote attach:** start Delve's own DAP server against your program
868
1183
  first —
@@ -964,6 +1279,12 @@ Notes:
964
1279
  blocks attributed to tdb's own `tdb_launch.ps1` are skipped, so a bare
965
1280
  `Write-Error` before the `exit` opens no modal at all.
966
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.
967
1288
  - Windows PowerShell 5.1 is not supported (pwsh 7 only). Running tdb *on*
968
1289
  Windows against pwsh is designed for but not yet verified (experimental).
969
1290
 
@@ -973,7 +1294,7 @@ Notes:
973
1294
  ┌─ Header ──────────────────────────────────────────────┐
974
1295
  ├─ Menu Bar (File / Configure / Help)───────────────────┤
975
1296
  │ │ │
976
- │ Code View │ Console View (stdout) │
1297
+ │ Code View │ Console View (stdio) │
977
1298
  │ (source + breakpoints) ├───────────────────────────┤
978
1299
  │ │ Variable View (tree) │
979
1300
  │ ├───────────────────────────┤
@@ -1016,6 +1337,7 @@ A cursor line (blue) tracks your position; the current execution line is highlig
1016
1337
  | Key | Menu |
1017
1338
  |-----|------|
1018
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) |
1019
1341
  | `Alt+C` | Configure (Color Theme, Keybindings, Step Mode) |
1020
1342
  | `Alt+T` | Threads |
1021
1343
  | `Alt+P` | Processes |
@@ -1029,7 +1351,7 @@ written in the same language as the one currently being debugged.
1029
1351
 
1030
1352
  **Navigation (vim-style by default):**
1031
1353
 
1032
- By default the Code View is in Debug mode. Hit `Escape` to switch to Navigate mode
1354
+ By default the Code View is in Debug mode. `Escape` cycles Debug → Navigate → Edit → Debug.
1033
1355
  In Navigate mode, you can move around the file with the following keys:
1034
1356
 
1035
1357
  | Key | Action |
@@ -1043,7 +1365,43 @@ In Navigate mode, you can move around the file with the following keys:
1043
1365
  | `n` / `N` | Next / previous search result |
1044
1366
  | `PageUp` / `PageDown` | Scroll by page |
1045
1367
 
1046
- Switch from Navigate back to Debug mode with `Escape`.
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).
1047
1405
 
1048
1406
  > **Note:** Many terminals send the byte sequence `ESC+f` for `Alt+F`, which Textual's
1049
1407
  ANSI parser rewrites to `Ctrl+Right` (the readline "forward-word" convention).
@@ -1115,6 +1473,15 @@ Set a Python expression (e.g., `x > 10`) and/or a hit count (pause on the Nth hi
1115
1473
 
1116
1474
  Breakpoints persist across session restarts.
1117
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
+
1118
1485
  ### Variable Inspection
1119
1486
 
1120
1487
  The Variable View shows a tree of scopes with all variables in the current frame. The scopes
@@ -1128,6 +1495,39 @@ the Variables View and press `Enter`
1128
1495
  to display that variable in a modal. This simplifies inspection of
1129
1496
  large or deeply nested data structures.
1130
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
+
1131
1531
  ### Call Stack
1132
1532
 
1133
1533
  The Stack View shows the full call stack. Click a frame to navigate to its source
@@ -1155,6 +1555,45 @@ interactive evaluation of expressions in the current scope:
1155
1555
  ```
1156
1556
 
1157
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>`.
1158
1597
 
1159
1598
  ### Cut / Paste
1160
1599
 
@@ -1165,12 +1604,35 @@ capture mouse events for their own use.
1165
1604
  Instead, hold the `Shift` key while performing your conventional cut/paste keystrokes or mouse
1166
1605
  operation to get the expected behavior.
1167
1606
 
1168
- ### Console Output
1607
+ ### Console Output and Input
1169
1608
 
1170
1609
  The Console View captures stdout (normal text) and stderr (red text) from the debuggee
1171
1610
  in real time.
1172
1611
 
1173
- If your program prints a lot, or prompts for input, or uses colors or
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
1174
1636
  terminal control codes, run the program in an external terminal
1175
1637
  with `--terminal` for a better experience.
1176
1638
  The `--terminal` switch requires a graphical environment and a compatible
@@ -1282,6 +1744,12 @@ This behavior matches hitting `c` while in a conventional (that is, the Python
1282
1744
  standard library's) `breakpoint()` session.
1283
1745
  If you want to kill the program instead, use `Ctrl+c` in the terminal running the debuggee.
1284
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
+
1285
1753
  ### Async Task Inspector
1286
1754
 
1287
1755
  For programs using `asyncio`, the menu bar shows an **Async Tasks (N)** label with the count of
@@ -1470,7 +1938,8 @@ terminal around.
1470
1938
  The debuggee runs in a separate window of the specified terminal, including
1471
1939
  all of its stdin/stdout/stderr. Keyboard input, program output, and
1472
1940
  anything the program itself draws to the terminal all happen in that
1473
- window, not in `tdb`'s Console View. Supported choices:
1941
+ window, not in `tdb`'s Console View (whose stdin line is hidden in this
1942
+ mode). Supported choices:
1474
1943
  `xterm`, `konsole`, `gnome-terminal`, `ghostty`, `kitty`, `iterm2`, `warp`,
1475
1944
  `wezterm`, `terminator`. The selected terminal must be on `PATH`. Debugging
1476
1945
  (breakpoints, stepping, variable inspection, the evaluate console) proceeds
@@ -1480,39 +1949,31 @@ This feature only works in graphical environments where external terminals are a
1480
1949
 
1481
1950
  ### Run Mode (`--run`)
1482
1951
 
1483
- `tdb`'s run mode invokes an unmodified program nearly at full speed, without bringing up the TUI.
1484
- At any point afterwards, hitting `Ctrl-C` in that terminal pauses the program
1485
- and drops you into the full TUI.
1486
- From there you can inspect the program's state, navigate the stack, set and continue
1487
- to breakpoints, and so on. After you finish inspecting, hitting `q` to quit brings up
1488
- a modal that lets you either detach from the TUI and let the program resume running,
1489
- 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:
1490
1955
 
1491
- The primary use cases for run mode are: (1) allowing inspection of a program that
1492
- appears hung, and (2) finding where a program execution path lands after it
1493
- receives a problematic input.
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.
1494
1962
 
1495
1963
  ```bash
1496
1964
  tdb --run my_program.py args...
1497
1965
  ```
1498
1966
 
1499
- The debuggee's stdout/stderr stream straight to the terminal. If the program exits on
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
1500
1971
  its own, `tdb` exits with the same code:
1501
1972
 
1502
1973
  ```bash
1503
1974
  tdb --run my_program.py; echo $? # my_program.py's own exit code
1504
1975
  ```
1505
1976
 
1506
- **Interrupting:** press Ctrl-C in the terminal (any platform), or, on Unix, send
1507
- `SIGUSR1` to `tdb`'s pid from another terminal: `kill -USR1 <tdb pid>`. Either one pauses
1508
- the debuggee at its currently executing line and opens the TUI with full debugging
1509
- available (breakpoints, stepping, variable inspection, the evaluate console). The
1510
- debuggee itself never receives the signal (`tdb`'s adapter runs in its own process
1511
- group), so its own `SIGINT` handling is undisturbed.
1512
-
1513
- **Examining without opening the TUI:** press `Ctrl-\` in the terminal (`Ctrl-Break` on
1514
- Windows), or, on Unix, send `SIGUSR2` to `tdb`'s pid: `kill -USR2 <tdb pid>`.
1515
-
1516
1977
  **Windows caveat:** the Ctrl-Break trigger is untested. Unlike Ctrl-C, a console
1517
1978
  Ctrl-Break is delivered to every process attached to the console, including the debug
1518
1979
  adapter and the program itself, so it may terminate the program instead of snapshotting
@@ -1587,9 +2048,14 @@ blocking external call or syscall, the pause can't land until that call returns
1587
2048
 
1588
2049
  ### Eval Mode (`-e`/`--eval`)
1589
2050
 
1590
- Eval mode automates a common debugging loop--set a breakpoint, run to it,
1591
- evaluate an expression in the Evaluate console, continue--without
1592
- opening the TUI. `-e` takes two arguments, a location and an expression:
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:
1593
2059
 
1594
2060
  ```bash
1595
2061
  tdb --eval examples/digits_of_pi.py:25 "print(f'{nr=}')" examples/digits_of_pi.py
@@ -1609,7 +2075,7 @@ evaluated in each child process that reaches it.
1609
2075
  - `-e` may be repeated to plant several eval points in one run.
1610
2076
  - The expression is evaluated through the debug adapter (DAP `evaluate`,
1611
2077
  context `repl`), just like the Evaluate console: side effects are real
1612
- and persist in the running program, for example
2078
+ and persist in the running program, for example
1613
2079
  `-e my_program.py:25 "limit = 10"` changes `limit` from line 25 onward.
1614
2080
  - A bare expression's result is printed to stdout; statement-style
1615
2081
  expressions (`print(...)`, assignments) print nothing beyond their own
@@ -1632,7 +2098,7 @@ evaluated in each child process that reaches it.
1632
2098
  ```bash
1633
2099
  tdb --keybindings vim my_program.py # default
1634
2100
  tdb --keybindings emacs my_program.py
1635
- tdb --keybindings default my_program.py
2101
+ tdb --keybindings default my_program.py # Notepad-style editing
1636
2102
  ```
1637
2103
 
1638
2104
  The keybinding choice is saved to `~/.config/tdb/config.json` and remembered for subsequent
@@ -1714,6 +2180,8 @@ curl -s -X POST http://127.0.0.1:8150/rpc \
1714
2180
  | `stack_down` | `[]` | Move down the call stack |
1715
2181
  | `get_stack_trace` | `[]` | Full call stack |
1716
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) |
1717
2185
  | `get_source` | `["file_path"]` | Read a source file |
1718
2186
  | `list_threads` | `[]` | List all threads |
1719
2187
  | `inspect_thread` | `[thread_id]` | Inspect a specific thread |
@@ -1741,7 +2209,8 @@ Each is JSON with `event`, `data`, and `timestamp` fields.
1741
2209
 
1742
2210
  `tdb --record session.jsonl prog.py` runs a normal TUI session and captures
1743
2211
  your debugging actions including breakpoints (including `-k`/`-t` and persisted
1744
- ones), stepping, continue/pause, Evaluate-console entries, stack-frame
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
1745
2214
  navigation, variable expansion, restart, quit. The session is
1746
2215
  written to `session.jsonl` as
1747
2216
  JSON-RPC commands. Works with launch mode (any language) and `-r`
@@ -1894,6 +2363,7 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
1894
2363
  | Flag | Description |
1895
2364
  |------|-------------|
1896
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. |
1897
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. |
1898
2368
  | `--remote-root PATH` | Remote directory matched to `--local-root` (same CLI position via `zip()`). |
1899
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. |
@@ -1903,14 +2373,14 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
1903
2373
  | `--python PATH` | Python interpreter for the debuggee (Python targets only) |
1904
2374
  | `--pv` | Shorthand for --python .venv/bin/python |
1905
2375
  | `--lang LANGUAGE` | Debuggee language (`python`, `cpp`, `perl`); default: auto-detect from the target |
1906
- | `--adapter ADAPTER` | Debug adapter within the language (e.g. `--lang cpp --adapter lldb-dap`); default: the language's standard adapter |
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 |
1907
2377
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
1908
2378
  (default: skipped). On uncaught exceptions, the crash modal always shows the full traceback
1909
2379
  including library frames, regardless of this flag. |
1910
2380
  | `--no-subprocess` | Disable debugpy's subprocess tracking (use when debugging `tdb` itself) |
1911
2381
  | `--terminal TERM` | Run debuggee in the named external terminal: `xterm`, `konsole`,
1912
2382
  `gnome-terminal`, `ghostty`, `kitty`, `iterm2`, `warp`, `wezterm`, or `terminator` |
1913
- | `--keybindings SCHEME` | `default`, `vim`, or `emacs` (saved to config) |
2383
+ | `--keybindings SCHEME` | `vim`, `emacs`, or `default` (Notepad-style editing); saved to config |
1914
2384
  | `--server` | Enable JSON-RPC server alongside TUI |
1915
2385
  | `--headless` | JSON-RPC server only, no TUI |
1916
2386
  | `--server-port PORT` | Server port (default: 8150) |
@@ -1934,10 +2404,63 @@ On Windows, it uses `%APPDATA%\tdb\`.
1934
2404
  | `config.json` | User preferences (keybinding scheme, color theme, step mode, adapter overrides) |
1935
2405
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
1936
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
+
1937
2452
  Adapter-related keys in `config.json`: `adapters` maps an adapter id to an
1938
2453
  executable path (`{"adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"}}`), and
1939
2454
  `default_adapters` picks a language's default adapter
1940
- (`{"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`.
1941
2464
 
1942
2465
  **Perl is a special case:** `perl-tdb` is tdb's own bundled adapter (always
1943
2466
  found; it's Python code, not an external executable), so
@@ -2000,6 +2523,7 @@ as open source.
2000
2523
 
2001
2524
  - OpenAI, for providing access to Codex through the
2002
2525
  [Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
2526
+ Codex was used to implement tcsh and Rust support.
2003
2527
 
2004
2528
  This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/)
2005
2529
  Python debugger.