From f54a2bbd203230a92fba8fb740c73b395285d8d2 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 15 Nov 2021 19:15:38 -0600 Subject: [PATCH 01/14] Move management of current debuggers into Debugger class --- pudb/__init__.py | 12 +++++------- pudb/debugger.py | 10 ++++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pudb/__init__.py b/pudb/__init__.py index c504bfed..cca005b9 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -58,9 +58,6 @@ def go(self): builtins.__dict__["pu"] = PudbShortcuts() -CURRENT_DEBUGGER = [] - - def _tty_override(): import os return os.environ.get("PUDB_TTY") @@ -76,7 +73,8 @@ def _open_tty(tty_path): def _get_debugger(**kwargs): - if not CURRENT_DEBUGGER: + from pudb.debugger import Debugger + if not Debugger._current_debugger: tty_path = _tty_override() if tty_path and ("stdin" not in kwargs or "stdout" not in kwargs): tty_file, term_size = _open_tty(tty_path) @@ -87,14 +85,14 @@ def _get_debugger(**kwargs): from pudb.debugger import Debugger dbg = Debugger(**kwargs) - CURRENT_DEBUGGER.append(dbg) return dbg else: - return CURRENT_DEBUGGER[0] + return Debugger._current_debugger[0] def _have_debugger(): - return len(CURRENT_DEBUGGER) > 0 + from pudb.debugger import Debugger + return bool(Debugger._current_debugger) import signal # noqa diff --git a/pudb/debugger.py b/pudb/debugger.py index 126c43f1..ce7682cd 100644 --- a/pudb/debugger.py +++ b/pudb/debugger.py @@ -180,9 +180,15 @@ # {{{ debugger interface class Debugger(bdb.Bdb): + _current_debugger = [] + def __init__(self, stdin=None, stdout=None, term_size=None, steal_output=False, **kwargs): + if Debugger._current_debugger: + raise ValueError("a Debugger instance already exists") + self._current_debugger.append(self) + # Pass remaining kwargs to python debugger framework bdb.Bdb.__init__(self, **kwargs) self.ui = DebuggerUI(self, stdin=stdin, stdout=stdout, term_size=term_size) @@ -200,6 +206,10 @@ def __init__(self, stdin=None, stdout=None, term_size=None, steal_output=False, for bpoint_descr in load_breakpoints(): self.set_break(*bpoint_descr) + def __del__(self): + assert self._current_debugger == [self] + self._current_debugger.pop() + # These (dispatch_line and set_continue) are copied from bdb with the # patch from https://bugs.python.org/issue16482 applied. See # https://github.com/inducer/pudb/pull/90. From 4a77bd8805ff43f6f4807197565f972eaebe344b Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 15 Nov 2021 19:16:20 -0600 Subject: [PATCH 02/14] Move term_size heuristic to RemoteDebugger constructor, document better (gh-485) --- doc/starting.rst | 7 ++++--- pudb/remote.py | 34 +++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/doc/starting.rst b/doc/starting.rst index d17fcc6a..58e2f8dd 100644 --- a/doc/starting.rst +++ b/doc/starting.rst @@ -1,5 +1,5 @@ -Getting Started ---------------- +Starting the debugger +--------------------- To start debugging, simply insert:: @@ -81,6 +81,8 @@ connection:: pudb:6899: Please telnet into 127.0.0.1 6899. pudb:6899: Waiting for client... +.. automodule:: pudb.remote + "Reverse" remote debugging ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +108,6 @@ Then watch the debugger connect to netcat:: pudb:9999: Now in session with 127.0.0.1:6899. - Using the debugger after forking ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/pudb/remote.py b/pudb/remote.py index e588fa79..876124e3 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -1,3 +1,10 @@ +""" +.. autoclass:: RemoteDebugger + +.. autofunction:: set_trace +.. autofunction:: debugger +""" + __copyright__ = """ Copyright (C) 2009-2017 Andreas Kloeckner Copyright (C) 2014-2017 Aaron Meurer @@ -77,6 +84,10 @@ class RemoteDebugger(Debugger): + """ + .. automethod:: __init__ + """ + me = "pudb" _prev_outs = None _sock = None @@ -90,8 +101,24 @@ def __init__( term_size=None, reverse=False, ): + """ + :arg term_size: A two-tuple ``(columns, rows)``, or *None*. If *None*, + try to determine the terminal size automatically. + + Currently, this uses a heuristic: It uses the terminal size of the + debuggee as that for the debugger. The idea is that you might be + running both in two tabs of the same terminal window, hence using + terminals of the same size. + """ self.out = out + if term_size is None: + try: + s = struct.unpack("hh", fcntl.ioctl(1, termios.TIOCGWINSZ, "1234")) + term_size = (s[1], s[0]) + except Exception: + term_size = (80, 24) + self._prev_handles = sys.stdin, sys.stdout self._client, (address, port) = self.get_client( host=host, port=port, search_limit=port_search_limit, reverse=reverse @@ -205,13 +232,6 @@ def set_trace( """Set breakpoint at current location, or a specified frame""" if frame is None: frame = _frame().f_back - if term_size is None: - try: - # Getting terminal size - s = struct.unpack("hh", fcntl.ioctl(1, termios.TIOCGWINSZ, "1234")) - term_size = (s[1], s[0]) - except Exception: - term_size = (80, 24) return debugger( term_size=term_size, host=host, port=port, reverse=reverse From 552f56a828d149a14a9b0f5124d3969daf656be4 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Mon, 15 Nov 2021 19:17:30 -0600 Subject: [PATCH 03/14] Fix post-mortem debugging for Py3 --- pudb/__init__.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pudb/__init__.py b/pudb/__init__.py index cca005b9..47d3ab99 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -312,19 +312,17 @@ def post_mortem(tb=None, e_type=None, e_value=None): dbg = _get_debugger() dbg.reset() - dbg.interaction(tb.tb_frame, exc_info) + dbg.interaction(None, (exc_info[0], exc_info[1], tb)) def pm(): import sys - try: - e_type = sys.last_type - e_value = sys.last_value - tb = sys.last_traceback - except AttributeError: + exc_type, exc_val, tb = sys.exc_info() + + if exc_type is None: # No exception on record. Do nothing. return - post_mortem(tb, e_type, e_value) + post_mortem() if __name__ == "__main__": From 9a8e660a842b07738dfce68ffcbe3f5861e1b07d Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 17 Nov 2021 17:07:23 -0600 Subject: [PATCH 04/14] Remove unexplained stack-eating loop from post_mortem --- pudb/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pudb/__init__.py b/pudb/__init__.py index 47d3ab99..b3cc88fb 100644 --- a/pudb/__init__.py +++ b/pudb/__init__.py @@ -306,13 +306,9 @@ def post_mortem(tb=None, e_type=None, e_value=None): else: exc_info = (e_type, e_value, tb) - tb = exc_info[2] - while tb.tb_next is not None: - tb = tb.tb_next - dbg = _get_debugger() dbg.reset() - dbg.interaction(None, (exc_info[0], exc_info[1], tb)) + dbg.interaction(None, exc_info) def pm(): From 4b9451341b1fd1f36e4081928ca304a7efb8646d Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Thu, 16 Dec 2021 18:30:02 +0100 Subject: [PATCH 05/14] add debug_remote_on_single_rank --- pudb/remote.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/pudb/remote.py b/pudb/remote.py index 876124e3..e296d25e 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -3,6 +3,7 @@ .. autofunction:: set_trace .. autofunction:: debugger +.. autofunction:: debug_remote_on_single_rank """ __copyright__ = """ @@ -43,10 +44,11 @@ import termios import struct import atexit +from typing import Callable, Any from pudb.debugger import Debugger -__all__ = ["PUDB_RDB_HOST", "PUDB_RDB_PORT", "default_port", "debugger", "set_trace"] +__all__ = ["PUDB_RDB_HOST", "PUDB_RDB_PORT", "default_port", "debugger", "set_trace", "debug_remote_on_single_rank"] default_port = 6899 @@ -236,3 +238,22 @@ def set_trace( return debugger( term_size=term_size, host=host, port=port, reverse=reverse ).set_trace(frame) + + +def debug_remote_on_single_rank(function: Callable, comm: Any, rank: int = 0): + """Run a remote debugger on a single rank of an :mod:`mpi4py` application.""" + if comm.rank == rank: + dbg = debugger() + try: + dbg.runcall(function) + except Exception: + from pudb import pm + pm() + + else: + try: + function() + finally: + from time import sleep + while True: + sleep(1) \ No newline at end of file From c1b68a39812408a293b12da67afa3777ebfb56f7 Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Thu, 16 Dec 2021 18:38:16 +0100 Subject: [PATCH 06/14] flake8 --- pudb/remote.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pudb/remote.py b/pudb/remote.py index e296d25e..cbfb0353 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -48,7 +48,8 @@ from pudb.debugger import Debugger -__all__ = ["PUDB_RDB_HOST", "PUDB_RDB_PORT", "default_port", "debugger", "set_trace", "debug_remote_on_single_rank"] +__all__ = ["PUDB_RDB_HOST", "PUDB_RDB_PORT", "default_port", "debugger", "set_trace", + "debug_remote_on_single_rank"] default_port = 6899 @@ -240,20 +241,20 @@ def set_trace( ).set_trace(frame) -def debug_remote_on_single_rank(function: Callable, comm: Any, rank: int = 0): +def debug_remote_on_single_rank(func: Callable, comm: Any, rank: int = 0) -> None: """Run a remote debugger on a single rank of an :mod:`mpi4py` application.""" if comm.rank == rank: dbg = debugger() try: - dbg.runcall(function) + dbg.runcall(func) except Exception: from pudb import pm pm() else: try: - function() + func() finally: from time import sleep while True: - sleep(1) \ No newline at end of file + sleep(1) From cc98e275fb47b8789d561129d692d9265f8eed3e Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Wed, 29 Dec 2021 12:28:54 +0100 Subject: [PATCH 07/14] better doc --- pudb/remote.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pudb/remote.py b/pudb/remote.py index 87796213..a3138824 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -243,7 +243,12 @@ def set_trace( def debug_remote_on_single_rank(func: Callable, comm: Any, rank: int = 0) -> None: - """Run a remote debugger on a single rank of an :mod:`mpi4py` application.""" + """Run a remote debugger on a single rank of an `mpi4py` application. + + :param func: the function to debug. + :param comm: An `mpi4py` communicator. + :param rank: The rank to debug. All other ranks will spin until this rank exits. + """ if comm.rank == rank: dbg = debugger() try: From 3663cecd342fa7bb465386f5bef2aa2de54172d7 Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Wed, 29 Dec 2021 12:35:19 +0100 Subject: [PATCH 08/14] a bit more text --- pudb/remote.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pudb/remote.py b/pudb/remote.py index a3138824..e4ae36b9 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -244,10 +244,12 @@ def set_trace( def debug_remote_on_single_rank(func: Callable, comm: Any, rank: int = 0) -> None: """Run a remote debugger on a single rank of an `mpi4py` application. + `func` will be called on rank `rank` running in a :class:`RemoteDebugger`, + and will be called normally on all other ranks. :param func: the function to debug. - :param comm: An `mpi4py` communicator. - :param rank: The rank to debug. All other ranks will spin until this rank exits. + :param comm: an `mpi4py` communicator. + :param rank: the rank to debug. All other ranks will spin until this rank exits. """ if comm.rank == rank: dbg = debugger() From d00cfd52f63f70012ee107fa37e95d4ae873d957 Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Wed, 29 Dec 2021 19:10:04 +0100 Subject: [PATCH 09/14] add an mpi4py example --- examples/mpi4py-debug.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 examples/mpi4py-debug.py diff --git a/examples/mpi4py-debug.py b/examples/mpi4py-debug.py new file mode 100644 index 00000000..57521f7a --- /dev/null +++ b/examples/mpi4py-debug.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +from mpi4py import MPI +from pudb.remote import debug_remote_on_single_rank + + +def debugged_function(x): + y = x + fail # noqa: F821 + return y + + +# debug this application on rank 0 +debug_remote_on_single_rank(MPI.COMM_WORLD, 0, debugged_function, 42) From 063f7ad181b07ddb41ca94d46195e8a15972ba37 Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Wed, 29 Dec 2021 19:11:28 +0100 Subject: [PATCH 10/14] allow arguments to func, better doc, simplify --- pudb/remote.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pudb/remote.py b/pudb/remote.py index e4ae36b9..e29741c7 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -242,23 +242,19 @@ def set_trace( ).set_trace(frame) -def debug_remote_on_single_rank(func: Callable, comm: Any, rank: int = 0) -> None: - """Run a remote debugger on a single rank of an `mpi4py` application. - `func` will be called on rank `rank` running in a :class:`RemoteDebugger`, +def debug_remote_on_single_rank(comm: Any, rank: int, func: Callable, *args, **kwargs) -> None: + """Run a remote debugger on a single rank of an ``mpi4py`` application. + *func* will be called on rank *rank* running in a :class:`RemoteDebugger`, and will be called normally on all other ranks. - :param func: the function to debug. - :param comm: an `mpi4py` communicator. + :param comm: an ``mpi4py`` ``Comm`` object. :param rank: the rank to debug. All other ranks will spin until this rank exits. + :param func: the callable to debug. + :param args: the arguments passed to ``func``. + :param kwargs: the kwargs passed to ``func``. """ if comm.rank == rank: - dbg = debugger() - try: - dbg.runcall(func) - except Exception: - from pudb import pm - pm() - + debugger().runcall(func, *args, **kwargs) else: try: func() From 0db05a9c83594a61e442998a77f8bce9cc119a74 Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Wed, 29 Dec 2021 19:14:36 +0100 Subject: [PATCH 11/14] clarify comment --- examples/mpi4py-debug.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mpi4py-debug.py b/examples/mpi4py-debug.py index 57521f7a..7ac4880b 100644 --- a/examples/mpi4py-debug.py +++ b/examples/mpi4py-debug.py @@ -9,5 +9,5 @@ def debugged_function(x): return y -# debug this application on rank 0 +# debug 'debugged_function' on rank 0 debug_remote_on_single_rank(MPI.COMM_WORLD, 0, debugged_function, 42) From d1716c9d783f199ff45d9c1356af533d638cc40b Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Wed, 29 Dec 2021 19:17:55 +0100 Subject: [PATCH 12/14] more doc --- examples/mpi4py-debug.py | 5 +++++ pudb/remote.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/mpi4py-debug.py b/examples/mpi4py-debug.py index 7ac4880b..87395d0c 100644 --- a/examples/mpi4py-debug.py +++ b/examples/mpi4py-debug.py @@ -1,5 +1,10 @@ #!/usr/bin/env python +# Demonstrate on how to debug an mpi4py application. +# Run this with 'mpirun -n 2 python mpi4py-debug.py'. +# You can then attach to the debugger by running 'telnet 127.0.0.1 6899' +# in another terminal. + from mpi4py import MPI from pudb.remote import debug_remote_on_single_rank diff --git a/pudb/remote.py b/pudb/remote.py index e29741c7..e8b16894 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -242,7 +242,8 @@ def set_trace( ).set_trace(frame) -def debug_remote_on_single_rank(comm: Any, rank: int, func: Callable, *args, **kwargs) -> None: +def debug_remote_on_single_rank(comm: Any, rank: int, func: Callable, + *args: Any, **kwargs: Any) -> None: """Run a remote debugger on a single rank of an ``mpi4py`` application. *func* will be called on rank *rank* running in a :class:`RemoteDebugger`, and will be called normally on all other ranks. From 58e2d21f65bdcb99ff89beb8620c6bb4c16284c8 Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Wed, 29 Dec 2021 19:19:33 +0100 Subject: [PATCH 13/14] clarify comment --- examples/mpi4py-debug.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/mpi4py-debug.py b/examples/mpi4py-debug.py index 87395d0c..f3c7b396 100644 --- a/examples/mpi4py-debug.py +++ b/examples/mpi4py-debug.py @@ -1,9 +1,9 @@ #!/usr/bin/env python -# Demonstrate on how to debug an mpi4py application. +# This example demonstrates how to debug an mpi4py application. # Run this with 'mpirun -n 2 python mpi4py-debug.py'. # You can then attach to the debugger by running 'telnet 127.0.0.1 6899' -# in another terminal. +# (when using the default pudb configuration) in another terminal. from mpi4py import MPI from pudb.remote import debug_remote_on_single_rank From ff7f8e304e781fd30949eb49e019b2186b2c9b50 Mon Sep 17 00:00:00 2001 From: Matthias Diener Date: Wed, 29 Dec 2021 19:28:30 +0100 Subject: [PATCH 14/14] add missing args --- pudb/remote.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pudb/remote.py b/pudb/remote.py index e8b16894..87c59246 100644 --- a/pudb/remote.py +++ b/pudb/remote.py @@ -258,7 +258,7 @@ def debug_remote_on_single_rank(comm: Any, rank: int, func: Callable, debugger().runcall(func, *args, **kwargs) else: try: - func() + func(*args, **kwargs) finally: from time import sleep while True: