[Cython] Core dump when Python is built without debug symbols

Nikita Nemkin nikita at nemkin.ru
Thu May 30 14:08:34 CEST 2013


On Thu, 30 May 2013 17:23:39 +0600, Marin Atanasov Nikolov  
<dnaeon at gmail.com> wrote:

> Hello,
>
> First, apologies if this is not exactly the right place to post this,  
> but I
> have  tried already cython-users@ and my post is still pending somewhere
> for approval, so I've decided to give it a shot here.
>
> I'm working on Cython wrappers for a C library and everything was going
> smooth on my dev machine, until I've decided to install the wrappers on
> another machine.
>
> The only difference between these machines is that my dev machine has
> Python built with debug symbols and the other machine does not have debug
> symbols in Python.
>
> The issue I'm having and was trying to solve for the past few days is  
> that
> if I run my Cython wrappers on a machine where Python does not have debug
> symbols it core dumps ugly. If I run the same code on another machine  
> with
> Python + debug symbols then everything is running perfect.
>
> I've done some debugging and tracing as well, and noticed that the core
> dump happens at PyTuple_Size() when I have Python *without* debug  
> symbols.
> Running Python + debug symbols shows that the interpreter takes a
>  different path and does not core dump (does not even call PyTuple_Size()
> for some reason)
>
> Anyway, I've uploaded the backtrace and some gdb tracing in here:
>
> * http://users.unix-heaven.org/~dnaeon/cython-wrappers/wrappers-trace.txt
>
> The code that I have you can find in here:
>
> * http://users.unix-heaven.org/~dnaeon/cython-wrappers/src/
>
> What really puzzles me is why it core dumps when Python does not have  
> debug
> symbols and why it takes a different path (and not core dumping) when
> Python is built with debug symbols.
>
> I'd say that if it core dumps because of a NULL pointer or something
> similar, then having Python with or without debug symbols should not  
> make a
> difference, but it does for some reason.
>
> I've been working on this for the past few days and I'm out of ideas now.
>
> Any help/hints/feedback is much appreciated.

In pkg-db.pxi, this line:

         return Pkg(<object>pkg)

and in pkg-pkg.pxi this line:

     def __cinit__(self, pkg):
         self._pkg = <c_pkg.pkg *>pkg

are the problem.

You are trying to pass a C pointer to __cinit__. What actually
happens is that, because of the <object> cast, Cython treats pkg as
as Python object _itself_ and tries to do some object operations on it.

Two ways to solve this (really common) problem:

1) Pass your pointer as a real python object containing the integer value
    of the pointer:

     def __cinit__(self, pkg):
         self._pkg = <c_pkg.pkg *><Py_ssize_t>pkg  # or Py_intptr_t

     return Pkg(<object><Py_ssize_t>pkg)

     Note the <Py_ssize_t> cast. When used on a C pointer, it makes a C
     integer that Cython convert to python int object. And when <Py_ssize_t>
     is used on a python int object, Cyton extracts its integer value
     as a C-level value, so it is castable to a C-level pointer.

     Using Py_ssize_t (or Py_intptr_t or your platform intptr_t) guarantees
     that the pointer will fit.

2) Don't use __cinit__. Declare your own cdef init method and call that.
    Since it's cdef method, it can take any C-level arguments, pointerw,
    whatever without the need to convert to python objects and back.

    @cython.final  # small optimization
    cdef _init(self, c_pkg.pkg * pkg):
         self._pkg = pkg

    # cdef __cinit__ not needed. Maybe use it to set default
    # attribute values.

    cdef Pkg pkg = Pkg.__new__(Pkg)  # fast object creation. Pkg() also  
works.
    pkg._init(pkg)

    (When you use the __new__ trick, __cnit__ is called without
    arguments and __init__ is not called at all. The benefit to
    this ugly syntax is that __new__ is faster than creating the object
    with traditional Pyhton syntax.)

Also, if all your initialization is pointer assigment, you can
just do it directly:

    cdef Pkg pkg = Pkg.__new__(Pkg)  # fast object creation
    pkg._pkg = pkg


There is a problem related to cython-dev. I think the code you
wrote should have never compiled in the first place. <PyObject*>
cast must be required if you want to reinterpret a C pointer
as object or vice versa.


Best regards,
Nikita Nemkin


More information about the cython-devel mailing list