Chasing down a Cython error

My python program segfaulted. When I wrote C++ programs segfaults were no big deal. I would hook into gdb mostly through my IDE’s debugger, and dance around the point the program crashed until I had an idea of where I was going wrong. Python gives a wonderful stack trace and tells you where things have gone wrong, so you can just look into the source code from the information Python spits out before it dies. But not for C-extensions.

When things go wrong in a Cython or C module that you are calling from Python you are stuck, a little bit, in no-mans land. Fortunately, from a stack-overflow thread here, you can just run Python in gdb, run your code and then debug it all as a C program (Python) operating on some data (Your Python program).

Unfortunately, I’m on Mac OS X, and though I could use homebrew to install gdb, I could not get it to run my python interpreter due to a binary compatibility issue. I decided to be brave and use LLDB (which comes with Mac OS X) to try and debug this error.

lldb python

This attaches Python to the debugger. Then you can run the Python program as follows

run /path/to/my/script arg1 arg2

My program crashed with

Fatal Python error: GC object already tracked
Process 59193 stopped

And some instructions in assembly which weren’t so helpful. However, I could do

thread backtrace

Which then led me to some candidate functions in the c-source file of the translated Cython code, like:

   frame #4: 0x000000010004d658 Python`PyTuple_New + 252
    frame #5: 0x00000001036ad6af util_cython.so`__Pyx_PyObject_CallOneArg(_object*, _object*) [inlined] __Pyx__PyObject_CallOneArg(arg=<unavailable>) + 5 at util_cython.cpp:7694
    frame #6: 0x00000001036ad6aa util_cython.so`__Pyx_PyObject_CallOneArg(func=0x000000010456f170, arg=0x0000000104c62a90) + 149 at util_cython.cpp:7712
    frame #7: 0x00000001036b1480 util_cython.so`__pyx_pw_5mitty_3lib_11util_cython_9markov_sequences(_object*, _object*, _object*) [inlined] __pyx_f_5mitty_3lib_11util_cython_markov_chain_sequence_gen(__pyx_v_first_letter='C', __pyx_v_l=<unavailable>, __pyx_v_max_len=10, __pyx_v_rng=<unavailable>) [5], unsigned char*, unsigned long*, unsigned long, _object*) + 110 at util_cython.cpp:2898
    frame #8: 0x00000001036b1412 util_cython.so`__pyx_pw_5mitty_3lib_11util_cython_9markov_sequences(_object*, _object*, _object*) [inlined] __pyx_pf_5mitty_3lib_11util_cython_8markov_sequences(__pyx_v_max_len=0x0000000100300678) + 2150 at util_cython.cpp:3710
    frame #9: 0x00000001036b0bac util_cython.so`__pyx_pw_5mitty_3lib_11util_cython_9markov_sequences(__pyx_self=<unavailable>, __pyx_args=<unavailable>, __pyx_kwds=<unavailable>) + 6070 at util_cython.cpp:3263
    frame #10: 0x000000010008614d Python`PyEval_EvalFrameEx + 8080
    frame #11: 0x0000000100084093 Python`PyEval_EvalCodeEx + 1641

Which was enough information for me to look more carefully at my markov_chain_sequence_gen function.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s