web-dev-qa-db-fra.com

python traçage d'une erreur de segmentation

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?

50
pygabriel

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.)

38
RichieHindle

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
67
Mark

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.

17
Duncan

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.

7
mariotomo

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:

  • Votre version de gdb doit être supérieure à 7 et elle doit avoir été compilée avec --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.
  • Vous devez télécharger libpython.py à partir de la version de la source Python qui correspond à ce que gdb est lié à.
  • Vous devrez peut-être exécuter gdb en tant que root - dans ce cas, vous devrez peut-être configurer 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

3
scytale