Slide 1

Slide 1 text

Va debugger ton par Stéphane Wirtel PyCon FR 2017 - Toulouse - 23/09/2017 1 / 107

Slide 2

Slide 2 text

Salut, je suis Stéphane par ma fille 1 / 107

Slide 3

Slide 3 text

Qui aime developper... ? 2 / 107

Slide 4

Slide 4 text

Debugger c'est Hard! 3 / 107

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

La sacro-sainte fonction print() 6 / 107

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

Le module logging 9 / 107

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

Cool mais Modification du code Execution Oubli de supprimer le print en production... 15 / 107

Slide 17

Slide 17 text

que faire, quoi utiliser... 16 / 107

Slide 18

Slide 18 text

Un debugger! 17 / 107

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Eh oui, Python vient avec un debugger! 19 / 107

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

Comment faire On peut aussi charger pdb comme un module à la ligne de commande. $ python -m pdb $ python -m pdb -c "command" 25 / 107

Slide 27

Slide 27 text

Prenons un exemple lisant un .csv 26 / 107

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Je débugge $ python -m pdb events.py > events.py(1)() -> import pendulum (Pdb) 30 / 107

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

J'avance d'une ligne (Pdb) next > events.py(2)() -> 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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Breakpoints Je reprends le code de base 36 / 107

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

Breakpoints Lister > events.py(1)() -> import pendulum (Pdb) break (Pdb) 38 / 107

Slide 40

Slide 40 text

Breakpoints Ajouter > events.py(1)() -> 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

Slide 41

Slide 41 text

Breakpoints Ajouter > events.py(1)() -> 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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Je continue (Pdb) continue > events.py(9)__init__() -> self.date = pendulum.parse(date) 45 / 107

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

Nouvelles commandes avec alias alias d pp dir(%1) 48 / 107

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

Nouvelles commandes avec alias alias loc !list(locals().keys()) 51 / 107

Slide 53

Slide 53 text

Nouvelles commandes avec alias alias loc !list(locals().keys()) (Pdb) loc dict_keys(['date', 'name', 'self']) 52 / 107

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

Réutilisation d'alias alias pd printdict %1 55 / 107

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Démonstration 58 / 107

Slide 60

Slide 60 text

PDB Récapitulatif des commandes 59 / 107

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

Breakpoints Commande Description b(reak) Montre tous les breakpoints 63 / 107

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

Il y a un truc amusant 68 / 107

Slide 70

Slide 70 text

Ça fonctionne avec ipdb 69 / 107

Slide 71

Slide 71 text

Les alternatives - ipdb Auto-completion Coloration syntaxique Meilleurs tracebacks Meilleur introspection Commandes supplémentaires https://github.com/gotcha/ipdb 70 / 107

Slide 72

Slide 72 text

Les alternatives - pudb https://github.com/inducer/pudb 71 / 107

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

Les alternatives - wdb https://github.com/Kozea/wdb 75 / 107

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

GNU Debugger gdb >= 7 , support natif de Python 79 / 107

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

GNU Debugger Python + gdb = ? 82 / 107

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

A cher la traceback (gdb) py-bt Traceback (most recent call first): File "ex_loop1.py", line 6, in next time.sleep(10) File "ex_loop1.py", line 11, in next(i) 90 / 107

Slide 92

Slide 92 text

A cher la traceback avec plus d'infos (gdb) py-bt-full #3 #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 () next(i) 91 / 107

Slide 93

Slide 93 text

A cher la traceback avec plus d'infos (gdb) py-bt-full #3 #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 () next(i) (gdb) select-frame 7 (gdb) info locals tstate = 0x55c8907a3cf0 (gdb) select-frame 3 (gdb) info locals func = 0x7f40d5974418 meth = 0x7f40dd57c6c6 self = result = 'sleep' flags = 8 __PRETTY_FUNCTION__ = "_PyCFunction_FastCallDict" 92 / 107

Slide 94

Slide 94 text

Examiner une frame (gdb) py-bt-full #3 #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 () 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

Slide 95

Slide 95 text

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 94 / 107

Slide 96

Slide 96 text

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=, obj= at /usr/src/debug/Python-3.6.2/Modules/timemodule.c:235 #3 0x00007f40dd39a9af in _PyCFunction_FastCallDict ( func_obj=, args=0x55c8908048e0, nargs=1, kwargs=0x0) at /usr/src/debug/Python-3.6.2/Objects/methodobject.c:209 #4 0x00007f40dd39ada6 in _PyCFunction_FastCallKeywords ( func=, 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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

Dumper les stack traces des threads (gdb) thread apply all py-bt Thread 1 (Thread 0x7f40ddaef700 (LWP 10619)): Traceback (most recent call first): File "ex_loop1.py", line 6, in next time.sleep(10) File "ex_loop1.py", line 11, in next(i) (gdb) 97 / 107

Slide 99

Slide 99 text

Débugger à distance 98 / 107

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

Extensible ? 102 / 107

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

Alternatives 104 / 107

Slide 106

Slide 106 text

gdbgui pip install gdbgui 105 / 107

Slide 107

Slide 107 text

Prochaines talks pour 2018 SystemTap et DTrace pour mon Python Que fait mon débuggeur Python? GDB en profondeur 106 / 107

Slide 108

Slide 108 text

Questions ? [email protected] 107 / 107