Va debugger ton Python!

Va debugger ton Python!

Cette presentation vous explique les bases de Pdb ainsi que GDB, afin de debugger plus facilement vos scripts Python.

8987d77750701f93bb78228c86d2c205?s=128

Stéphane Wirtel

September 27, 2017
Tweet

Transcript

  1. Va debugger ton par Stéphane Wirtel PyCon FR 2017 -

    Toulouse - 23/09/2017 1 / 107
  2. Salut, je suis Stéphane par ma fille 1 / 107

  3. Qui aime developper... ? 2 / 107

  4. Debugger c'est Hard! 3 / 107

  5. Que fait ce code ? # Example 1 def main():

    for i in range(1, 4): i /= 4 if i == 4: print('Salut PyConFR 2017') if __name__ == '__main__': main() 4 / 107
  6. Que fait ce code ? # Example 1 def main():

    for i in range(1, 4): i /= 4 if i == 4: print('Salut PyConFR 2017') if __name__ == '__main__': main() $ python example1.py $ 5 / 107
  7. La sacro-sainte fonction print() 6 / 107

  8. La sacro-sainte fonction print() # Example 2 def main(): for

    i in range(1, 4): i /= 4 print(i) if i == 4: print('Salut PyConFR 2017') 7 / 107
  9. La sacro-sainte fonction print() # Example 2 def main(): for

    i in range(1, 4): i /= 4 print(i) if i == 4: print('Salut PyConFR 2017') $ python example2.py 0 0 0 $ 8 / 107
  10. Le module logging 9 / 107

  11. Utilisons logging # Example 3 import logging def main(): logging.basicConfig(level=logging.INFO)

    logging.info('start program') logging.debug("before loop") for i in range(1, 4): i /= 4 logging.debug(i) logging.debug("after loop") if i == 4: print("Hello PyConFR 2017") logging.debug("Hello PyConFR 2017") logging.info('end program') if __name__ == '__main__': main() 10 / 107
  12. Utilisons logging # Example 3 import logging def main(): logging.basicConfig(level=logging.INFO)

    logging.info('start program') logging.debug("before loop") for i in range(1, 4): i /= 4 logging.debug(i) logging.debug("after loop") if i == 4: print("Hello PyConFR 2017") logging.debug("Hello PyConFR 2017") logging.info('end program') if __name__ == '__main__': main() INFO:root:start program INFO:root:end program 11 / 107
  13. Utilisons logging # Example 4 import logging def main(): logging.basicConfig(level=logging.DEBUG)

    logging.info('start program') logging.debug("before loop") for i in range(1, 4): i /= 4 logging.debug(i) logging.debug("after loop") if i == 4: print("Hello PyConFR 2017") logging.debug("Hello PyConFR 2017") logging.info('end program') if __name__ == '__main__': main() 12 / 107
  14. Utilisons logging INFO:root:start program DEBUG:root:before loop DEBUG:root:0 DEBUG:root:0 DEBUG:root:0 DEBUG:root:after

    loop INFO:root:end program 13 / 107
  15. Utilisons logging INFO:root:start program DEBUG:root:before loop DEBUG:root:0 DEBUG:root:0 DEBUG:root:0 DEBUG:root:after

    loop INFO:root:end program Obligé de modifier le niveau de log pour voir ce qu'il se passe. 14 / 107
  16. Cool mais Modification du code Execution Oubli de supprimer le

    print en production... 15 / 107
  17. que faire, quoi utiliser... 16 / 107

  18. Un debugger! 17 / 107

  19. PDB Python Debugger est l'outil natif pour aider les développeurs

    à trouver et corriger les bugs de leur logiciel fétiche (ou celui que l'on récupère d'un ancien collègue ou prestataire...) 18 / 107
  20. Eh oui, Python vient avec un debugger! 19 / 107

  21. Comment faire import pdb; pdb.set_trace() 20 / 107

  22. Comment faire import pdb; pdb.set_trace() # Example 5 import pdb

    def main(): for i in range(1, 4): i /= 4 if i == 4: print("Hello PyConFR 2017") if __name__ == '__main__': pdb.set_trace() main() 21 / 107
  23. Comment faire import pdb; pdb.set_trace() # Example 5 import pdb

    def main(): for i in range(1, 4): i /= 4 if i == 4: print("Hello PyConFR 2017") if __name__ == '__main__': pdb.set_trace() main() $ python example5.py > example5.py(7)main() -> for i in range(1, 4): 22 / 107
  24. Comment faire Example 6 import pdb def do_something(): raise Exception("bug")

    def main(): try: do_something() except Exception: pdb.post_mortem() if __name__ == '__main__': main() 23 / 107
  25. Comment faire Example 6 import pdb def do_something(): raise Exception("bug")

    def main(): try: do_something() except Exception: pdb.post_mortem() if __name__ == '__main__': main() > example6.py(4)do_something() -> raise Exception("bug") (Pdb) bt example6.py(8)main() -> do_something() > example6.py(4)do_something() -> raise Exception("bug") (Pdb) ll 3 def do_something(): 4 -> raise Exception("bug") (Pdb) 24 / 107
  26. Comment faire On peut aussi charger pdb comme un module

    à la ligne de commande. $ python -m pdb <file.py> $ python -m pdb -c "command" <file.py> 25 / 107
  27. Prenons un exemple lisant un .csv 26 / 107

  28. Prenons un exemple lisant un .csv import pendulum import csv

    class Event: def __init__(self, name, date): self.name = name self.date = pendulum.parse(date) def get_events(stream): for row in csv.reader(stream): yield Event(*row) def main(): with open('events.csv') as stream: for event in get_events(stream): print(event.name, event.date) if __name__ == '__main__': main() 27 / 107
  29. Prenons un exemple lisant un .csv import pendulum import csv

    class Event: def __init__(self, name, date): self.name = name self.date = pendulum.parse(date) def get_events(stream): for row in csv.reader(stream): yield Event(*row) def main(): with open('events.csv') as stream: for event in get_events(stream): print(event.name, event.date) if __name__ == '__main__': main() PyCon France,2017-09-21 PyCon Canada,2017-11-18 PyCon US,2017-05-17 Python FOSDEM,2018-02-03 Python TOTO,x 28 / 107
  30. Prenons un exemple lisant un .csv $ python events.py Non

    d'un petit Python, CRASH avec deux exceptions :/ PyCon France 2017-09-21T00:00:00+00:00 PyCon Canada 2017-11-18T00:00:00+00:00 PyCon US 2017-05-17T00:00:00+00:00 Python FOSDEM 2018-02-03T00:00:00+00:00 File "/home/stephane/.virtualenvs/flask/lib/python3.6/site-packages/dateutil/par raise ValueError("Unknown string format") ValueError: Unknown string format During handling of the above exception, another exception occurred: File "/home/stephane/.virtualenvs/flask/lib/python3.6/site-packages/pendulum/par raise ParserError('Invalid date string: {}'.format(text)) pendulum.parsing.exceptions.ParserError: Invalid date string: x 29 / 107
  31. Je débugge $ python -m pdb events.py > events.py(1)<module>() ->

    import pendulum (Pdb) 30 / 107
  32. Je liste mon code avec l ou ll (Pdb) ll

    1 -> import pendulum 2 import csv 3 4 class Event: 5 def __init__(self, name, date): 6 self.name = name 7 self.date = pendulum.parse(date) 8 9 def get_events(stream): 10 for row in csv.reader(stream): 11 yield Event(*row) 12 13 def main(): 14 with open('events.csv') as stream: 15 for event in get_events(stream): 16 print(event.name, event.date) 17 18 if __name__ == '__main__': 19 main() 31 / 107
  33. J'avance d'une ligne (Pdb) next > events.py(2)<module>() -> import csv

    32 / 107
  34. J'avance d'une ligne (Pdb) next > events.py(2)<module>() -> import csv

    (Pdb) ll 1 import pendulum 2 -> import csv 3 4 class Event: 5 def __init__(self, name, date): 6 self.name = name 7 self.date = pendulum.parse(date) 8 9 def get_events(stream): 10 for row in csv.reader(stream): 11 yield Event(*row) 12 13 def main(): 14 with open('events.csv') as stream: 15 for event in get_events(stream): 16 print(event.name, event.date) 17 18 if __name__ == '__main__': 19 main() 33 / 107
  35. On saute dans le code -> def main(): (Pdb) ll

    15 -> def main(): 16 with open('events.csv') as stream: 17 for event in get_events(stream): 18 print(event.name, event.date) 19 print(Event.id) (Pdb) step > events.py(16)main() -> with open('events.csv') as stream: (Pdb) ll 15 def main(): 16 -> with open('events.csv') as stream: 17 for event in get_events(stream): 18 print(event.name, event.date) 19 print(Event.id) 34 / 107
  36. On saute dans le code (Pdb) next > events.py(17)main() ->

    for event in get_events(stream): (Pdb) ll 15 def main(): 16 with open('events.csv') as stream: 17 -> for event in get_events(stream): 18 print(event.name, event.date) 19 print(Event.id) (Pdb) step --Call-- > events.py(11)get_events() -> def get_events(stream): (Pdb) ll 11 -> def get_events(stream): 12 for row in csv.reader(stream): 13 yield Event(*row) (Pdb) next > events.py(12)get_events() -> for row in csv.reader(stream): (Pdb) ll 11 def get_events(stream): 12 -> for row in csv.reader(stream): 13 yield Event(*row) (Pdb) 35 / 107
  37. Breakpoints Je reprends le code de base 36 / 107

  38. Breakpoints (Pdb) ll 1 import pendulum 2 import csv 3

    4 class Event: 5 def __init__(self, name, date): 6 self.name = name 7 self.date = pendulum.parse(date) 8 9 def get_events(stream): 10 for row in csv.reader(stream): 11 yield Event(*row) 12 13 def main(): 14 with open('events.csv') as stream: 15 for event in get_events(stream): 16 print(event.name, event.date) 17 18 if __name__ == '__main__': 19 main() 37 / 107
  39. Breakpoints Lister > events.py(1)<module>() -> import pendulum (Pdb) break (Pdb)

    38 / 107
  40. Breakpoints Ajouter > events.py(1)<module>() -> import pendulum (Pdb) break 9

    Breakpoint 1 at events.py:9 (Pdb) l 4 class Event: 5 id = 0 6 def __init__(self, name, date): 7 self.id += 1 8 self.name = name 9 B-> self.date = pendulum.parse(date) 10 11 def get_events(stream): 12 for row in csv.reader(stream): 13 yield Event(*row) 39 / 107
  41. Breakpoints Ajouter > events.py(1)<module>() -> import pendulum (Pdb) break 9

    Breakpoint 1 at events.py:9 (Pdb) l 4 class Event: 5 id = 0 6 def __init__(self, name, date): 7 self.id += 1 8 self.name = name 9 B-> self.date = pendulum.parse(date) 10 11 def get_events(stream): 12 for row in csv.reader(stream): 13 yield Event(*row) (Pdb) break Num Type Disp Enb Where 1 breakpoint keep yes at events.py:9 40 / 107
  42. Breakpoints Désactiver un breakpoint (Pdb) disable 1 Disabled breakpoint 1

    at events.py:9 41 / 107
  43. Breakpoints Désactiver un breakpoint (Pdb) disable 1 Disabled breakpoint 1

    at events.py:9 (Pdb) break Num Type Disp Enb Where 1 breakpoint keep no at events.py:9 42 / 107
  44. Breakpoints Activer un breakpoint (Pdb) enable 1 Enabled breakpoint 1

    at events.py:9 43 / 107
  45. Breakpoints Activer un breakpoint (Pdb) enable 1 Enabled breakpoint 1

    at events.py:9 (Pdb) break Num Type Disp Enb Where 1 breakpoint keep yes at events.py:9 44 / 107
  46. Je continue (Pdb) continue > events.py(9)__init__() -> self.date = pendulum.parse(date)

    45 / 107
  47. J'examine (Pdb) args self = <__main__.Event object at 0x7f23cf8cc908> name

    = 'PyCon France' date = '2017-09-21' 46 / 107
  48. J'examine (Pdb) args self = <__main__.Event object at 0x7f23cf8cc908> name

    = 'PyCon France' date = '2017-09-21' (Pdb) whatis name <class 'str'> (Pdb) whatis self <class '__main__.Event'> 47 / 107
  49. Nouvelles commandes avec alias alias d pp dir(%1) 48 /

    107
  50. Nouvelles commandes avec alias alias d pp dir(%1) (Pdb) b

    9 Breakpoint 1 at events.py:9 (Pdb) c > events.py(9)__init__() -> self.date = pendulum.parse(date) 49 / 107
  51. Nouvelles commandes avec alias alias d pp dir(%1) (Pdb) b

    9 Breakpoint 1 at events.py:9 (Pdb) c > events.py(9)__init__() -> self.date = pendulum.parse(date) (Pdb) d self.__dict__ ['__class__', '__contains__', '__delattr__', ... ] 50 / 107
  52. Nouvelles commandes avec alias alias loc !list(locals().keys()) 51 / 107

  53. Nouvelles commandes avec alias alias loc !list(locals().keys()) (Pdb) loc dict_keys(['date',

    'name', 'self']) 52 / 107
  54. Nouvelles commandes avec alias alias printdict for key, value in

    %1.items(): print(f"{key}:{value}") 53 / 107
  55. Nouvelles commandes avec alias alias printdict for key, value in

    %1.items(): print(f"{key}:{value}") (Pdb) printdict self.__dict__ id:1 name:PyCon France date:2017-09-21T00:00:00+00:00 54 / 107
  56. Réutilisation d'alias alias pd printdict %1 55 / 107

  57. Réutilisation d'alias alias pd printdict %1 (Pdb) pd self.__dict__ id:1

    name:PyCon France date:2017-09-21T00:00:00+00:00 56 / 107
  58. Breakpoint et une action Possibilité de définir une action lors

    d'un breakpoint commands [bpnumber] p self.__dict__ printdict self.__dict__ end 57 / 107
  59. Démonstration 58 / 107

  60. PDB Récapitulatif des commandes 59 / 107

  61. Les importantes Commande Description h(elp) Affiche l'aide h(elp) command Affiche

    l'aide d'une commande q(uit) Quitte le debuggeur 60 / 107
  62. Examiner Commande Description p(rint) expr Affiche la valeur de l'expression

    pp expr Pretty-print de la valeur de l'expression l(ist) Affiche une partie du code ll Affiche toute le code de la fonction a(rgs) Affiche la liste des arguments de la fonction whatis arg Affiche le type de l'argument where Mais ou se trouve l'exception source Affiche la source d'une fonction 61 / 107
  63. Naviguer Commande Description n(ext) Exécute l'instruction s(tep) Exécute et rentre

    dans la fonction/méthode r(eturn) Continue l'exécution jusqu'au return c(ontinue) Continue l'exécution jusqu'à un breakpoint u(p) Remonte d'un niveau dans la stack trace d(own) Descend d'un niveau dans la stack trace j(ump) lineno Saute a la ligne 62 / 107
  64. Breakpoints Commande Description b(reak) Montre tous les breakpoints 63 /

    107
  65. Breakpoints Commande Description b(reak) lineno Place un breakpoint à lineno

    b(reak) lineno, cond Place un breakpoint à lineo si cond est True b(reak) file:lineno Place un breakpoint dans le fichier file à la ligne lineno b(reak) func Place un breakpoint à la première ligne de la fonction func tbreak lineno Place un breakpoint et l'enleve dès que l'on y est 64 / 107
  66. Breakpoints Commande Description clear bpnumber Supprime un breakpoint disable bpnumber

    Désactive un breakpoint enable bpnumber Active un breakpoint ignore bpnumber count Ignore le breakpoint X fois condition bpnumber condition Permet de modifer le breakpoint 65 / 107
  67. Celles que vous ne connaissez pas ;-) Commande Description !expr

    Execute une expression dans l'interpreteur Python alias Créer une nouvelle commande PDB unalias Supprime un alias commands [bpnumber] Assigne des commandes à un breakpoint restart or run Redémarre le script 66 / 107
  68. Fichier de con guration alias d pp dir(%1) alias ps

    d self.__dict__ alias loc !list(locals().keys()) alias printdict for key, value in %1.items(): print(f"{key}:{value}") Voir $HOME/.pdbrc ou ./.pdbrc 67 / 107
  69. Il y a un truc amusant 68 / 107

  70. Ça fonctionne avec ipdb 69 / 107

  71. Les alternatives - ipdb Auto-completion Coloration syntaxique Meilleurs tracebacks Meilleur

    introspection Commandes supplémentaires https://github.com/gotcha/ipdb 70 / 107
  72. Les alternatives - pudb https://github.com/inducer/pudb 71 / 107

  73. Les alternatives - rdb import pdb, socket, sys # noqa

    --> Pas PEP8 class Rdb(pdb.Pdb): def __init__(self, port=4444): self.old_stdout = sys.stdout self.old_stdin = sys.stdin self.skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.skt.bind(('localhost', port)) self.skt.listen(1) (clientsocket, address) = self.skt.accept() handle = clientsocket.makefile('rw') pdb.Pdb.__init__(self, completekey='tab', stdin=handle, stdout=handle) sys.stdout = sys.stdin = handle def do_continue(self, arg): sys.stdout = self.old_stdout sys.stdin = self.old_stdin self.skt.close() self.set_continue() return 1 do_c = do_cont = do_continue https://dzone.com/articles/remote-debugging-python-using 72 / 107
  74. Les alternatives - rdb (2) # Example usage - connect

    with 'telnet 4444' if __name__=='__main__': def buggy_method(): x = 3 remote_debug = Rdb() remote_debug.set_trace() print(x) buggy_method() https://dzone.com/articles/remote-debugging-python-using 73 / 107
  75. Les alternatives - rdb (3) telnet localhost 4444 Trying 127.0.0.1...

    Connected to localhost. Escape character is '^]'. > rdb.py(32)buggy_method() -> print(x) (Pdb) ll 28 def buggy_method(): 29 x = 3 30 remote_debug = Rdb() 31 remote_debug.set_trace() 32 -> print(x) (Pdb) p x 3 (Pdb) quit Connection closed by foreign host. Meilleure implémentation dans https://github.com/celery/celery/blob/master/celery/contrib/rdb.py https://dzone.com/articles/remote-debugging-python-using 74 / 107
  76. Les alternatives - wdb https://github.com/Kozea/wdb 75 / 107

  77. Les alternatives - pdbpp https://github.com/antocuni/pdb 76 / 107

  78. pdb ou ipdb avec du dev Web Par exemple, Django

    django-pdb avec --ipdb ou --pdb ou simplement python -m pdb ./manage.py test python -m ipdb ./manage.py test python -m pudb ./manage.py test 77 / 107
  79. GNU Debugger Également nommé gdb, GNU Debugger, est le débuggeur

    standard du projet GNU, portable et fonctionne pour plusieurs langages, comme le C, le C++ et aussi le Python ;-) 78 / 107
  80. GNU Debugger gdb >= 7 , support natif de Python

    79 / 107
  81. GNU Debugger Avoir les symboles de debug de Python $

    dnf install python3-debug 80 / 107
  82. GNU Debugger Avoir les symboles de debug de Python $

    dnf install python3-debug ou $ ./configure --prefix=$PWD-build --with-pydebug $ make $ make install 81 / 107
  83. GNU Debugger Python + gdb = ? 82 / 107

  84. GNU Debugger Python + gdb = ? 1. il y

    a une librairie pour debugger votre script Python depuis gdb 83 / 107
  85. GNU Debugger Python + gdb = ? 1. il y

    a une librairie pour debugger votre script Python depuis gdb 2. gdb est extensible via Python 84 / 107
  86. Que faire avec gdb ? Commande Description py-bt Montre la

    stack trace Python py-bt-full py-bt mais en plus verbeux py-list Liste le code Python py-print Affiche le contenu d'une variable Python py-locals Affiche les locals py-up Remonte d'une frame py-down Descend d'une frame 85 / 107
  87. Le code import time import os def next(i): print(f"{os.getpid()} {time.strftime('%H:%M:%S')}")

    time.sleep(10) i = 1 - i i = 1 while True: next(i) 86 / 107
  88. Le code import time import os def next(i): print(f"{os.getpid()} {time.strftime('%H:%M:%S')}")

    time.sleep(10) i = 1 - i i = 1 while True: next(i) $ python ex_loop1.py 10619 06:14:24 10619 06:14:34 10619 06:14:44 10619 06:14:54 10619 06:15:04 10619 06:15:14 PID 10619 87 / 107
  89. Comment faire ? $ gdb -p 10619 GNU gdb (GDB)

    Fedora 8.0.1-26.fc26 ... For help, type "help". Type "apropos word" to search for commands related to "word". Attaching to process 10619 Reading symbols from /usr/bin/python3.6dm...Reading symbols from /usr/lib/debug/us Reading symbols from /lib64/libpython3.6dm.so.1.0...Reading symbols from /usr/lib/d Reading symbols from /lib64/libpthread.so.0...Reading symbols from /usr/lib/debug/ [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Reading symbols from /lib64/libdl.so.2...Reading symbols from /usr/lib/debug/usr/li Reading symbols from /lib64/libutil.so.1...Reading symbols from /usr/lib/debug/usr Reading symbols from /lib64/libm.so.6...Reading symbols from /usr/lib/debug/usr/li Reading symbols from /lib64/libc.so.6...Reading symbols from /usr/lib/debug/usr/li Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/de 0x00007f40dc696ce3 in __select_nocancel () at ../sysdeps/unix/syscall-template.S:84 84 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) (gdb) 88 / 107
  90. A cher le code (gdb) py-list 1 import time 2

    import os 3 4 def next(i): 5 print(f"{os.getpid()} {time.strftime('%H:%M:%S')}") >6 time.sleep(10) 7 i = 1 - i 8 9 i = 1 10 while True: 11 next(i) (gdb) 89 / 107
  91. A cher la traceback (gdb) py-bt Traceback (most recent call

    first): <built-in method sleep of module object at remote 0x7f40d590fd58> File "ex_loop1.py", line 6, in next time.sleep(10) File "ex_loop1.py", line 11, in <module> next(i) 90 / 107
  92. A cher la traceback avec plus d'infos (gdb) py-bt-full #3

    <built-in method sleep of module object at remote 0x7f40d590fd58> #7 Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1) time.sleep(10) #12 Frame 0x55c890804458, for file ex_loop1.py, line 11, in <module> () next(i) 91 / 107
  93. A cher la traceback avec plus d'infos (gdb) py-bt-full #3

    <built-in method sleep of module object at remote 0x7f40d590fd58> #7 Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1) time.sleep(10) #12 Frame 0x55c890804458, for file ex_loop1.py, line 11, in <module> () next(i) (gdb) select-frame 7 (gdb) info locals tstate = 0x55c8907a3cf0 (gdb) select-frame 3 (gdb) info locals func = 0x7f40d5974418 meth = 0x7f40dd57c6c6 <time_sleep> self = <module at remote 0x7f40d590fd58> result = 'sleep' flags = 8 __PRETTY_FUNCTION__ = "_PyCFunction_FastCallDict" 92 / 107
  94. Examiner une frame (gdb) py-bt-full #3 <built-in method sleep of

    module object at remote 0x7fc8e71bad58> #7 Frame 0x55706036f748, for file ex_loop1.py, line 6, in next (i=1) time.sleep(10) #12 Frame 0x55706036f458, for file ex_loop1.py, line 11, in <module> () next(i) (gdb) select-frame 7 (gdb) py-list 1 import time 2 import os 3 4 def next(i): 5 print(f"{os.getpid()} {time.strftime('%H:%M:%S')}") >6 time.sleep(10) 7 i = 1 - i 8 9 i = 1 10 while True: 11 next(i) (gdb) py-print i local 'i' = 1 (gdb) py-locals i = 1 93 / 107
  95. Navigation (gdb) py-down Unable to find a newer python frame

    (gdb) py-up #7 Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1) time.sleep(10) (gdb) py-down #3 <built-in method sleep of module object at remote 0x7f40d590fd58> 94 / 107
  96. Backtrace de CPython (gdb) bt #0 0x00007f40dc696ce3 in __select_nocancel ()

    at ../sysdeps/unix/syscall-template #1 0x00007f40dd57e7f8 in pysleep (secs=10000000000) at /usr/src/debug/Python-3.6.2/Modules/timemodule.c:1438 #2 0x00007f40dd57c73d in time_sleep (self=<module at remote 0x7f40d590fd58>, obj= at /usr/src/debug/Python-3.6.2/Modules/timemodule.c:235 #3 0x00007f40dd39a9af in _PyCFunction_FastCallDict ( func_obj=<built-in method sleep of module object at remote 0x7f40d590fd58>, args=0x55c8908048e0, nargs=1, kwargs=0x0) at /usr/src/debug/Python-3.6.2/Objects/methodobject.c:209 #4 0x00007f40dd39ada6 in _PyCFunction_FastCallKeywords ( func=<built-in method sleep of module object at remote 0x7f40d590fd58>, stack=0x55c8908048e0, nargs=1, kwnames=0x0) at /usr/src/debug/Python-3.6.2/Objects/methodobject.c:294 #5 0x00007f40dd4b046e in call_function (pp_stack=0x7ffd7bcab7b8, oparg=1, kwnames= at /usr/src/debug/Python-3.6.2/Python/ceval.c:4809 #6 0x00007f40dd4a8046 in _PyEval_EvalFrameDefault ( f=Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1), throwflag= at /usr/src/debug/Python-3.6.2/Python/ceval.c:3295 #7 0x00007f40dd494118 in PyEval_EvalFrameEx ( f=Frame 0x55c890804748, for file ex_loop1.py, line 6, in next (i=1), throwflag= at /usr/src/debug/Python-3.6.2/Python/ceval.c:718 95 / 107
  97. Threads (gdb) info threads Id Target Id Frame 1 Thread

    0x7f40ddaef700 (LWP 10619) "python3-debug" 0x00007f40dd39a9af in _PyC args=0x55c8908048e0, nargs=1, kwargs=0x0) at /usr/src/debug/Python-3.6.2/Objects/methodobject.c:209 96 / 107
  98. Dumper les stack traces des threads (gdb) thread apply all

    py-bt Thread 1 (Thread 0x7f40ddaef700 (LWP 10619)): Traceback (most recent call first): <built-in method sleep of module object at remote 0x7f40d590fd58> File "ex_loop1.py", line 6, in next time.sleep(10) File "ex_loop1.py", line 11, in <module> next(i) (gdb) 97 / 107
  99. Débugger à distance 98 / 107

  100. Mode Remote - gdbserver $ gdbserver --multi host:2345 # #

    ou ces modes-ci # $ gdbserver host:2345 process_name $ gdbserver host:2345 --attach PID 99 / 107
  101. Mode Remote - gdbserver $ gdbserver --multi host:2345 # #

    ou ces modes-ci # $ gdbserver host:2345 process_name $ gdbserver host:2345 --attach PID $ gdb (gdb) target extended-remote host:2345 (gdb) attach PID 100 / 107
  102. Mode Remote - gdbserver $ gdbserver --multi host:2345 # #

    ou ces modes-ci # $ gdbserver host:2345 process_name $ gdbserver host:2345 --attach PID $ gdb (gdb) target extended-remote host:2345 (gdb) attach PID (gdb) py-list 1 import time 2 import os 3 4 def next(i): 5 print(f"{os.getpid()} {time.strftime('%H:%M:%S')}") >6 time.sleep(10) 7 i = 1 - i 8 9 i = 1 10 while True: 11 next(i) (gdb) 101 / 107
  103. Extensible ? 102 / 107

  104. Extensible ? - Oui https://github.com/cyrus-and/gdb-dashboardOui 103 / 107

  105. Alternatives 104 / 107

  106. gdbgui pip install gdbgui 105 / 107

  107. Prochaines talks pour 2018 SystemTap et DTrace pour mon Python

    Que fait mon débuggeur Python? GDB en profondeur 106 / 107
  108. Questions ? stephane@mgx.io 107 / 107