51 votes

Comment attacher un débogueur à un sous-processus python ?

J'ai besoin de déboguer un processus enfant généré par multiprocessing.Process() . Le pdb semble ne pas être au courant du fork et incapable de s'attacher aux processus déjà en cours d'exécution.

Existe-t-il des débogueurs Python plus intelligents pouvant être attachés à un sous-processus ?

105voto

Romuald Brunet Points 1062

J'ai cherché une solution simple à ce problème et j'ai trouvé ceci:

 import sys
import pdb

class ForkedPdb(pdb.Pdb):
    """A Pdb subclass that may be used
    from a forked multiprocessing child

    """
    def interaction(self, *args, **kwargs):
        _stdin = sys.stdin
        try:
            sys.stdin = open('/dev/stdin')
            pdb.Pdb.interaction(self, *args, **kwargs)
        finally:
            sys.stdin = _stdin

Utilisez-le de la même manière que vous pourriez utiliser le Pdb classique :

 ForkedPdb().set_trace()

13voto

memeplex Points 103

Il s'agit d'une élaboration de la réponse de Romuald qui restaure le stdin d'origine à l'aide de son descripteur de fichier. Cela permet à readline de fonctionner à l'intérieur du débogueur. En outre, la gestion spéciale pdb de KeyboardInterrupt est désactivée, afin de ne pas interférer avec le gestionnaire de signature multiprocesseur.

 class ForkablePdb(pdb.Pdb):

    _original_stdin_fd = sys.stdin.fileno()
    _original_stdin = None

    def __init__(self):
        pdb.Pdb.__init__(self, nosigint=True)

    def _cmdloop(self):
        current_stdin = sys.stdin
        try:
            if not self._original_stdin:
                self._original_stdin = os.fdopen(self._original_stdin_fd)
            sys.stdin = self._original_stdin
            self.cmdloop()
        finally:
            sys.stdin = current_stdin

3voto

brian.keng Points 96

En m'appuyant sur l'idée de @memplex, j'ai dû la modifier pour qu'elle fonctionne avec joblib en définissant le sys.stdin dans le constructeur et en le transmettant directement via joblib.

 import os
import pdb
import signal
import sys
import joblib

_original_stdin_fd = None

class ForkablePdb(pdb.Pdb):
    _original_stdin = None
    _original_pid = os.getpid()

    def __init__(self):
        pdb.Pdb.__init__(self)
        if self._original_pid != os.getpid():
            if _original_stdin_fd is None:
                raise Exception("Must set ForkablePdb._original_stdin_fd to stdin fileno")

            self.current_stdin = sys.stdin
            if not self._original_stdin:
                self._original_stdin = os.fdopen(_original_stdin_fd)
            sys.stdin = self._original_stdin

    def _cmdloop(self):
        try:
            self.cmdloop()
        finally:
            sys.stdin = self.current_stdin

def handle_pdb(sig, frame):
    ForkablePdb().set_trace(frame)

def test(i, fileno):
    global _original_stdin_fd
    _original_stdin_fd = fileno
    while True:
        pass    

if __name__ == '__main__':
    print "PID: %d" % os.getpid()
    signal.signal(signal.SIGUSR2, handle_pdb)
    ForkablePdb().set_trace()
    fileno = sys.stdin.fileno()
    joblib.Parallel(n_jobs=2)(joblib.delayed(test)(i, fileno) for i in range(10))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X