2014-04-18 17 views
5

Próbuję debugować skrypt wielowątkowy. Po Wyjątkiem jest podniesiony chcę:Post mortem debugowanie skryptów wielowątkowych

  1. Zgłoś go do systemu monitoringu (tylko drukowanie w poniższym przykładzie)
  2. zatrzymać cały skrypt (w tym wszystkich innych wątków)
  3. rozmowę wiersz debugera poubojowe w perspektywa podniesiona wyjątek

przygotować dość skomplikowany przykład, aby pokazać jak próbowałem go rozwiązać:

#!/usr/bin/env python 

import threading 
import inspect 
import traceback 
import sys 
import os 
import time 


def POST_PORTEM_DEBUGGER(type, value, tb): 
    traceback.print_exception(type, value, tb) 
    print 
    if hasattr(sys, 'ps1') or not sys.stderr.isatty(): 
     import rpdb 
     rpdb.pdb.pm() 
    else: 
     import pdb 
     pdb.pm() 

sys.excepthook = POST_PORTEM_DEBUGGER 



class MyThread(threading.Thread): 

    def __init__(self): 

     threading.Thread.__init__(self) 
     self.exception = None 
     self.info = None 
     self.the_calling_script_name = os.path.abspath(inspect.currentframe().f_back.f_code.co_filename) 

    def main(self): 
     "Virtual method to be implemented by inherited worker" 
     return self 

    def run(self): 
     try: 
      self.main() 
     except Exception as exception: 
      self.exception = exception 
      self.info = traceback.extract_tb(sys.exc_info()[2])[-1] 
      # because of bug http://bugs.python.org/issue1230540 
      # I cannot use just "raise" under threading.Thread 
      sys.excepthook(*sys.exc_info()) 

    def __del__(self): 
     print 'MyThread via {} catch "{}: {}" in {}() from {}:{}: {}'.format(self.the_calling_script_name, type(self.exception).__name__, str(self.exception), self.info[2], os.path.basename(self.info[0]), self.info[1], self.info[3]) 




class Worker(MyThread): 

    def __init__(self): 
     super(Worker, self).__init__() 

    def main(self): 
     """ worker job """ 
     counter = 0 
     while True: 
      counter += 1 
      print self 
      time.sleep(1.0) 
      if counter == 3: 
       pass # print 1/0 


def main(): 

    Worker().start() 

    counter = 1 
    while True: 
     counter += 1 
     time.sleep(1.0) 
     if counter == 3: 
      pass # print 1/0 

if __name__ == '__main__': 
    main() 

Sztuczka z

sys.excepthook = POST_PORTEM_DEBUGGER 

działa perfekcyjnie jeśli są zaangażowane żadne nitki. Okazało się, że w przypadku skryptu wielowątkowego mogę używać rpdb dla debuggig pod numerem:

import rpdb; rpdb.set_trace() 

To działa doskonale na określonym przerwania ale chcę debugowania wielowątkowego skrypt pośmiertnie (po uncatched wyjątkiem jest podniesiony ). Kiedy próbuję użyć rpdb w funkcji POST_PORTEM_DEBUGGER przy zastosowaniu wielowątkowego dostaję następujący:

Exception in thread Thread-1: 
Traceback (most recent call last): 
    File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner 
     self.run() 
    File "./demo.py", line 49, in run 
     sys.excepthook(*sys.exc_info()) 
    File "./demo.py", line 22, in POST_PORTEM_DEBUGGER 
     pdb.pm() 
    File "/usr/lib/python2.7/pdb.py", line 1270, in pm 
     post_mortem(sys.last_traceback) 
AttributeError: 'module' object has no attribute 'last_traceback' 

I wygląda

sys.excepthook(*sys.exc_info()) 

nie założyć wszystko co robi komenda raise. Chcę tego samego zachowania, jeśli wyjątek jest podniesiony w main(), nawet pod uruchomionym wątku.

Odpowiedz

0

(nie testowałem moją odpowiedź, ale wydaje mi się, że ...)

Wezwanie do pdb.pm (pm = „post mortem”) nie tylko dlatego, że nie było „mortem "przed tym. To znaczy. program nadal działa.

Patrząc na kod pdb źródłowego, można znaleźć realizację pdb.pm:

def pm(): 
    post_mortem(sys.last_traceback) 

co sprawia, że ​​zgadnę, co rzeczywiście chcesz zrobić to zadzwonić pdb.post_mortem() bez args. Wygląda na to, że domyślne zachowanie robi dokładnie to, czego potrzebujesz.

Niektóre bardziej kod źródłowy (zauważ linię t = sys.exc_info()[2]):

def post_mortem(t=None): 
    # handling the default 
    if t is None: 
     # sys.exc_info() returns (type, value, traceback) if an exception is 
     # being handled, otherwise it returns None 
     t = sys.exc_info()[2] 
     if t is None: 
      raise ValueError("A valid traceback must be passed if no " 
               "exception is being handled") 

    p = Pdb() 
    p.reset() 
    p.interaction(None, t) 
+0

Dzięki za cynk. Kończy się jednak wyjątkiem ValeError ('t is None'). Wygląda na to, że wywołanie 'sys.excepthook (* sys.exc_info())' nie ustawia, co robi 'raise'. –

0

To może pomóc:

import sys 
from IPython.core import ultratb 

sys.excepthook = ultratb.FormattedTB(mode='Verbose', color_scheme='Linux', 
    call_pdb=True, ostream=sys.__stdout__)