Je développe des extensions C à partir de python ad j'obtiens quelques segfaults (inévitables lors du développement ...).
Je cherche un moyen d'afficher à quelle ligne de code le segfault se produit (une idée est comme tracer chaque ligne de code), comment puis-je faire cela?
Voici un moyen de sortir le nom de fichier et le numéro de ligne de chaque ligne de Python votre code s'exécute:
import sys
def trace(frame, event, arg):
print "%s, %s:%d" % (event, frame.f_code.co_filename, frame.f_lineno)
return trace
def test():
print "Line 8"
print "Line 9"
sys.settrace(trace)
test()
Production:
call, test.py:7
line, test.py:8
Line 8
line, test.py:9
Line 9
return, test.py:9
(Vous voudrez probablement écrire la sortie de trace dans un fichier, bien sûr.)
Si vous êtes sous Linux, exécutez python sous gdb
gdb python
(gdb) run /path/to/script.py
## wait for segfault ##
(gdb) backtrace
## stack trace of the c code
Les erreurs de segmentation des extensions C sont très souvent le résultat de l'incrémentation d'un nombre de références lorsque vous créez une nouvelle référence à un objet. Cela les rend très difficiles à localiser, car la faute de segmentation ne se produit qu'après la suppression de la dernière référence de l'objet, et même souvent uniquement lorsqu'un autre objet est alloué.
Vous ne dites pas combien de code d'extension C vous avez écrit jusqu'à présent, mais si vous débutez, demandez-vous si vous pouvez utiliser les types ctypes ou Cython . Les types C ne sont peut-être pas assez flexibles pour vos besoins, mais vous devriez pouvoir vous lier à n'importe quelle bibliothèque C avec Cython et avoir automatiquement tous les décomptes de références pour vous.
Ce n'est pas toujours suffisant: si vos objets Python et tous les objets C sous-jacents ont des durées de vie différentes, vous pouvez toujours avoir des problèmes, mais cela simplifie considérablement les choses.
Je suis venu ici à la recherche d'une solution au même problème, et aucune des autres réponses ne m'a aidé. Ce qui a aidé était faulthandler
, et vous pouvez l'installer dans Python 2.7 en utilisant simplement pip install
.
faulthandler
a été introduit dans Python uniquement dans la version 3.3, qui a été publiée en septembre 2012, après que la plupart des autres réponses aient été écrites ici.
Il existe des extensions python pour gdb non documentées).
Depuis la source Python source Tools/gdb/libpython.py
(il n'est pas inclus dans une installation normale).
Mettez ceci dans sys.path
Ensuite:
# gdb /gps/python2.7_x64/bin/python coredump
...
Core was generated by `/usr/bin/python script.py'.
Program terminated with signal 11, Segmentation fault.
#0 call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037
...
(gdb) python
>import libpython
>
>end
(gdb) bt
#0 call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037
#1 PyEval_EvalFrameEx (f=f@entry=
Frame 0x7f9084d20ad0,
for file /usr/lib/python2.7/site-packages/librabbitmq/__init__.py, line 220,
in drain_events (self=<Connection(channels={1: <Channel(channel_id=1, connection=<...>, is_open=True, connect_timeout=4, _default_channel=<....(truncated), throwflag=throwflag@entry=0) at Python/ceval.c:2681
...
(gdb) py-list
218 else:
219 timeout = float(timeout)
>220 self._basic_recv(timeout)
221
222 def channel(self, channel_id=None):
Comme vous pouvez le voir, nous avons maintenant une visibilité sur la pile Python correspondant à la chaîne d'appel CPython.
Quelques mises en garde:
--with-python
gdb
embeds python (en créant un lien vers libpython
), il ne l'exécute pas en sous-shell. Cela signifie qu'il ne correspond pas nécessairement à la version de python qui est sur $PATH
.libpython.py
à partir de la version de la source Python qui correspond à ce que gdb
est lié à.sys.path
pour correspondre à celui du code que vous déboguez.Si vous ne pouvez pas copier libpython.py
en sys.path
alors vous pouvez ajouter son emplacement à sys.path
comme ça:
(gdb) python
>import sys
>sys.path.append('/path/to/containing/dir/')
>import libpython
>
>end
Ceci est quelque peu mal documenté dans les documents de développement python , le wiki Fedora et le python)
Si vous avez une ancienne gdb
ou si vous ne pouvez tout simplement pas faire fonctionner cela, il y a aussi un gdbinit dans la source Python que vous pouvez copier vers ~/.gdbinit
qui ajoutent des fonctionnalités similaires