Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Numpy C++ program always gives segfault (most likely misuse of syntax or types)

Tags:

c++

python

numpy

I'm working on my first C++ extension for a python program. I have been trying to debug this particular piece of code for hours and I am out of ideas.

The segfault seems to have something to do with the PyArrayObject old_simplices_array that is getting passed to the C++ code. That object is a 2d numpy array of type uint32.

This code was modified directly from what scipy.weave puts together. Everything works fine when the code is formatted for and used by scipy.weave.inline. This seems to eliminate the python portion of my program and the algorithm itself from being possible culprits.

That just leaves the syntax and types. Does anyone see any incorrect syntax or type casting the code?

static PyObject* exterior(PyObject* self,
                          PyArrayObject* old_simplices_array)
{
    const short unsigned int step = old_simplices_array->dimensions[1];
    const short unsigned int j_max = step - 1;
    const long unsigned int col_max = 
        old_simplices_array->dimensions[0] * step;
    short unsigned int j, k, face_index;
    long unsigned int col;
    unsigned int num_simplices = 0;

    PyObject* indices = PyList_New(0);
    PyObject* indptr =  PyList_New(0);
    PyObject* data =  PyList_New(0);
    PyObject* simplices = PyList_New(0);
    PyList_Append(indptr, PyLong_FromLong(0));
    PyObject* simplex_to_index = PyDict_New();

    for(col = 0; col < col_max; col+=step)
    {
        for(j = 0; j <= j_max; j++)
        {
            face_index = 0;
            PyObject* face = PyTuple_New(j_max);
            for(k = 0; k <= j_max; k++)
            {
                if(j != k)
                {
                    PyTuple_SetItem(face, face_index, 
                        PyLong_FromLong(old_simplices_array->data[col + k]));
                    face_index++;
                }
            }

            if(PyDict_Contains(simplex_to_index, face))
            {
                PyList_Append(indices, 
                    PyDict_GetItem(simplex_to_index, face));
            }
            else
            {
                PyDict_SetItem(simplex_to_index, face, 
                    PyLong_FromLong(num_simplices));
                PyList_Append(simplices, face);
                num_simplices++;
            }
            PyList_Append(data, PyLong_FromLong(1 - 2 * (j % 2)));
        }
        PyList_Append(indptr, PyLong_FromLong(col + j));
    }
    return PyTuple_Pack(3, PyTuple_Pack(3, data, indices, indptr), simplices,
        simplex_to_index);
}                                

------UPDATE------

gdb indicates

const short unsigned int step = old_simplices_array->dimensions[1];

causes a segfault. Did I misuse types?

------UPDATE------

Despite GDB telling me,

const short unsigned int step = old_simplices_array->dimensions[1];

causes the segfault, if I return from the program just before the for loop, I get no segfault (just an error on the python side complaining about returning a NoneType).

This is the full backtrace:

Program received signal SIGSEGV, Segmentation fault.
exterior (self=<optimized out>, old_simplices_array=0xec0a50)
    at src/_alto.cpp:39
warning: Source file is more recent than executable.
39      const short unsigned int step = old_simplices_array->dimensions[1];
(gdb) bt
exterior (self=<optimized out>, old_simplices_array=0xec0a50)
    at src/_alto.cpp:39
0x00007ffff7aedad2 in PyEval_EvalFrameEx ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7aeddc9 in PyEval_EvalFrameEx ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7aee902 in PyEval_EvalCodeEx ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7a70ad6 in ?? ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7a4565e in PyObject_Call ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7a53b80 in ?? ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7a4565e in PyObject_Call ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7aaaea0 in ?? ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7aa68bc in ?? ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7a4565e in PyObject_Call ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7ae9bce in PyEval_EvalFrameEx ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7aee902 in PyEval_EvalCodeEx ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7aeea32 in PyEval_EvalCode ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7b103fa in PyRun_FileExFlags ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7b10e3d in PyRun_SimpleFileExFlags ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff7b26972 in Py_Main ()
   from /usr/lib/sagemath/local/lib/libpython2.7.so.1.0
0x00007ffff6d29ea5 in __libc_start_main ()
   from /lib/x86_64-linux-gnu/libc.so.6
0x00000000004006d1 in _start ()
like image 605
Alex Eftimiades Avatar asked Sep 12 '13 18:09

Alex Eftimiades


People also ask

What causes a segfault?

A segfault occurs when a reference to a variable falls outside the segment where that variable resides, or when a write is attempted to a location that is in a read-only segment.

What causes a segmentation fault in C?

Segmentation faults are a common class of error in programs written in languages like C that provide low-level memory access and few to no safety checks. They arise primarily due to errors in use of pointers for virtual memory addressing, particularly illegal access.

What is segmentation fault in C with example?

Core Dump (Segmentation fault) in C/C++ Core Dump/Segmentation fault is a specific kind of error caused by accessing memory that “does not belong to you.” When a piece of code tries to do read and write operation in a read only location in memory or freed block of memory, it is known as core dump.


1 Answers

In general, the signature of a method in a C module is PyObject* f(PyObject* self, PyObject* args), where args is intended to be parsed by PyArg_ParseTuple. You can see this in the code scipy.weave generates: http://docs.scipy.org/doc/scipy/reference/tutorial/weave.html#a-quick-look-at-the-code). Unless there's some wrapper function you haven't posted that calls PyArg_ParseTuple for you, your exterior method must call it to extract the PyArrayObject from the generic PyObject* args.

like image 181
Ben Darnell Avatar answered Oct 24 '22 11:10

Ben Darnell