2010-04-18 12 views
39

Estoy desarrollando extensiones C desde python ad Obtengo algunos segfaults (inevitables durante el desarrollo ...).python rastreando un error de segmentación

Estoy buscando una manera de mostrar en qué línea de código ocurre la segfault (una idea es como trazar cada línea de código), ¿cómo puedo hacer eso?

Respuesta

32

Aquí está una manera para dar salida al nombre de archivo y número de línea de cada línea de Python se ejecuta el código:

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

Salida:

call, test.py:7 
line, test.py:8 
Line 8 
line, test.py:9 
Line 9 
return, test.py:9 

(lo que probablemente quiere escribir la salida de rastreo a un archivo, por supuesto.)

+0

¿Funciona esto con las extensiones C? –

+1

@MadPhysicist: No imprimirá los números de línea de su código C, si eso es lo que quiere decir. :-) Imprimirá los números de línea del código Python que llama a su código C. – RichieHindle

+0

Eso fue lo que quise decir. La pregunta original me pareció interesante porque tuve el mismo problema. El segfault resultó ser porque mi código C estaba insertando un elemento NULL en un PyList_Object. Se manifestó en el lado de Python cuando traté de iterar sobre la lista. No estoy seguro de que un depurador de Python hubiera ayudado mucho en ese caso. –

54

Si está en Linux, Python se ejecutan bajo el BGF

gdb python 
(gdb) run /path/to/script.py 
## wait for segfault ## 
(gdb) backtrace 
## stack trace of the c code 
+7

Si ya tiene un archivo core, puede usar 'gdb python core' (o lo que sea que se llame al archivo core). Si está en OSX, los volcados del núcleo (no generados por defecto, vea 'ulimit -c') se almacenan en el directorio'/cores'. –

+0

Realmente me gustaría que esta fuera la primera respuesta para mí, ya que la lectura de todas las demás me llevó un tiempo considerable que preferiría haber pasado corriendo mi error. – sage

+0

Si obtiene un segfault mientras ejecuta una prueba de unidad de Python como 'python -m unittest my.module.tests.mytest', entonces el modificador' -m' confunde 'gdb'. Invoque usando la opción '--args' de la siguiente manera:' gdb --args python -m unittest my.module.tests.mytest' –

15

Segfaults de C extensiones son muy frecuentemente el resultado de no incrementar un recuento de referencias cuando se crea una nueva referencia a un objeto. Eso hace que sea muy difícil de rastrear, ya que la segfault ocurre solo después de que la última referencia es eliminada del objeto, e incluso a menudo solo cuando se asigna otro objeto.

No dice la cantidad de código de extensión C que ha escrito hasta ahora, pero si recién está empezando, considere si puede usar cualquiera de los tipos ct o Cython. Es posible que los tipos de Ctypes no sean lo suficientemente flexibles para sus necesidades, pero debería poder vincular casi cualquier biblioteca de C con Cython y tener todos los recuentos de referencia actualizados automáticamente.

Eso no siempre es suficiente: si sus objetos Python y cualquier objeto C subyacente tienen diferentes tiempos de vida, de todos modos puede tener problemas, pero simplifica considerablemente las cosas.

+2

Además, colocando NULLs en lugares a los que no pertenecen. –

3

Existen extensiones de python algo indocumentadas para gdb.

Desde la toma de origen de Python Tools/gdb/libpython.py (no se incluye en una instalación normal).

poner esto en sys.path

continuación:

# 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 ([email protected]= 
    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), [email protected]=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): 

Como se puede ver ahora tenemos visibilidad en el conjunto Python correspondiente a la cadena de llamadas CPython.

Algunas advertencias:

  • Su versión de GDB tiene que ser mayor que 7 y tiene que haber sido compilado con --with-python
  • gdb incrusta pitón (mediante la vinculación a libpython), no se ejecuta en una subcamada Esto significa que puede no coincidir necesariamente con la versión de python que está en $PATH.
  • Debe descargar libpython.py desde cualquier versión de la fuente de Python que coincida con lo que gdb está vinculado.
  • Puede que tenga que ejecutar gdb como root; si es así, puede que necesite configurar sys.path para que coincida con el código que está depurando.

Si no puede copiar libpython.py en sys.path continuación, puede agregar su ubicación a sys.path así:

(gdb) python 
>import sys 
>sys.path.append('/path/to/containing/dir/') 
>import libpython 
> 
>end 

Esto es algo poco documentada en el python dev docs, the fedora wiki y the python wiki

Si tiene un gdb anterior o simplemente no puede conseguir que funcione allí también es un gdbinit en la fuente de Python que puede copiar a ~/.gdbinit w Que añadir una funcionalidad similar

0

Vine aquí en busca de una solución para el mismo problema, y ​​ninguna de las otras respuestas me ayudó. Lo que sí ayudó fue faulthandler, y puede instalarlo en Python 2.7 simplemente usando pip install.

faulthandler se presentó a Python solo en la versión 3.3, que se lanzó en septiembre de 2012, que fue después de que se escribieron la mayoría de las demás respuestas aquí.

Cuestiones relacionadas