Return from C Extension is Throwing a Segmentation Fault


Joined
Dec 20, 2011
Messages
3
Reaction score
0
Hi all,

I have been working on writing a class in C for a Python game I am developing. The game uses Pygame and Numpy. Currently, I am adding a function to the class to take a 3D array (numpy/arrayobject, as is returned by pygame.surfarray.pixels3d), alter some of the rgb pixel values, and return a resulting 3D array.

Right now, this code looks like...

Code:
static PyArrayObject *
backSingle(TileTransform *self, PyArrayObject *source)
{
	PyArrayObject *result;
	int dimensions[3];
	int column, row;
	printf("Variables initialized.\n");
	
	dimensions[0] = self->width;
	dimensions[1] = self->halfLength;
	dimensions[2] = 3;
	result = (PyArrayObject *)PyArray_FromDims(3, dimensions, PyArray_INT);
	printf("Result made.\n");

	for (column = 0; column < self->width; column++)
	{
		for (row = 0; row < self->length; row++)
		{
			//Get the color.
			int red = *(int *)(source->data + column*source->strides[0] + row*source->strides[1]);
			int green = *(int *)(source->data + column*source->strides[0] + row*source->strides[1] + source->strides[2]);
			int blue = *(int *)(source->data + column*source->strides[0] + row*source->strides[1] + 2*source->strides[2]);

			//Alter the row value.
			int newRow = row/2;

			//Alter the color.

			//Save the new color.
			source->data[column*source->strides[0] + row*source->strides[1]] = red;
			source->data[column*source->strides[0] + row*source->strides[1] + source->strides[2]] = green;
			source->data[column*source->strides[0] + row*source->strides[1] + 2*source->strides[2]] = blue;
		}
	}
	return result;
}

static char TileTransform_getTransformedTile_doc[] =
"Get the transformed tile from the provided tile and topology.  The topology must be a list with 4 elements with values of 0, 1 or 2, \
the first element is the top of the tile, the second is the left corner, the third is the right corner, and the fourth is the bottom \
corner.  The provided tile is to be given as a 3D array of rgb values.";

static PyObject *
TileTransform_getTransformedTile(TileTransform *self, PyObject *args)
{
	PyObject *source;
	PyObject *final;
	PyArrayObject *sourceArray;
	PyArrayObject *resultArray;
	PyObject *topology;
	int top, left, right, bottom, topologyValue;

	//Parse the input parameters.
    if (! PyArg_ParseTuple(args, "OO", &source, &topology))
        return NULL;

	top = PyLong_AsLong(PyList_GetItem(topology, 0));
	left = PyLong_AsLong(PyList_GetItem(topology, 1));
	right = PyLong_AsLong(PyList_GetItem(topology, 2));
	bottom = PyLong_AsLong(PyList_GetItem(topology, 3));
	topologyValue = TOP_PRIME*top + LEFT_PRIME*left + RIGHT_PRIME*right + BOTTOM_PRIME*bottom;

	//Return if this tile has no changes to be made or is a cliff.
	if (topologyValue == NO_TOPOLOGY_CHANGE || top == 2 || left == 2 || right == 2 || bottom == 2)
		return source;

	//Translate the source to an array.
	printf("Translating source to array.\n");
	sourceArray = (PyArrayObject *) PyArray_ContiguousFromObject(source, PyArray_LONG, 0, 3);
	printf("Checking source array.\n");
	if (sourceArray->nd != 3 || sourceArray->descr->type_num != PyArray_LONG)
	{
		//Py_DECREF(sourceArray);
		PyErr_SetString(PyExc_ValueError, "Array must be three-dimensional and of type int");
		return NULL;
	}
	if (sourceArray->dimensions[0] != self->width || sourceArray->dimensions[1] != self->length)
	{
		//Py_DECREF(sourceArray);
		PyErr_SetString(PyExc_ValueError,
			"Array width and length must match the width and length provided to the Tile Transform constructor.");
		return NULL;
	}
	if (sourceArray->dimensions[2] != 3)
	{
		//Py_DECREF(sourceArray);
		PyErr_SetString(PyExc_ValueError,
			"Array must be three-dimensional and with the highest dimension containing 3 integers (rgb values).");
		return NULL;
	}

	//Execute the appropriate function.
	if (topologyValue == BACK_SINGLE)
	{
		printf("Back Single Transformation Call\n");
		resultArray = backSingle(self, sourceArray);
	}
	else
	{
		//Py_DECREF(sourceArray);
		return source;
	}
	
	printf("Topology: %d %d %d %d; %d\n", top, left, right, bottom, topologyValue);

	//Py_DECREF(sourceArray);
	printf("Preparing Final\n");
	final = PyArray_Return(resultArray);
	printf("Returning Final\n");
	return final;
}

I have a number of print statements to help me with the debug.

Now, to the error that I am having...
By following the debug statements, I have found that I am SOMETIMES getting a segmentation fault upon the return line (return final; ) for TileTransform_getTransformedTile. I say sometimes because there are multiple cases (at least one of them being identical) that successfully use this function without the fault.


I am also unsure of whether the Py_DECREF is necessary with the array, but that is a secondary concern until the return statement issue is solved. I have tried the program with and without the DECREF and still have the segmentation fault in the same place.

In case it helps, here is the output when I run the game....
Code:
429 1668 [25, 25, 25, 25]
429 1669 [25, 25, 25, 25]
429 1670 [25, 25, 25, 25]
429 1671 [25, 25, 25, 25]
429 1672 [25, 25, 25, 25]
429 1673 [25, 25, 25, 26]
Translating source to array.
Checking source array.
Back Single Transformation Call
Variables initialized.
Result made.
Topology: 0 0 0 1; 11
Preparing Final
Returning Final
429 1674 [25, 25, 26, 26]
Translating source to array.
Checking source array.
429 1675 [26, 26, 26, 26]
429 1676 [26, 26, 26, 26]
429 1677 [26, 26, 26, 27]
Translating source to array.
Checking source array.
Back Single Transformation Call
Variables initialized.
Result made.
Topology: 0 0 0 1; 11
Preparing Final
Returning Final
429 1678 [26, 26, 27, 26]
Translating source to array.
Checking source array.
429 1679 [27, 26, 27, 27]
Translating source to array.
Checking source array.
429 1680 [26, 27, 27, 27]
Translating source to array.
Checking source array.
429 1681 [27, 27, 27, 26]
Translating source to array.
Checking source array.
429 1682 [27, 27, 26, 27]
Translating source to array.
Checking source array.
429 1683 [26, 27, 26, 27]
Translating source to array.
Checking source array.
429 1684 [27, 27, 27, 27]
429 1685 [27, 27, 27, 27]
429 1686 [27, 27, 27, 27]
429 1687 [27, 27, 27, 27]
429 1688 [27, 27, 27, 26]
Translating source to array.
Checking source array.
429 1689 [27, 26, 26, 26]
Translating source to array.
Checking source array.
429 1690 [26, 26, 26, 26]
429 1691 [26, 26, 26, 26]
429 1692 [26, 26, 26, 26]
429 1693 [26, 26, 26, 25]
Translating source to array.
Checking source array.
429 1694 [26, 25, 25, 25]
Translating source to array.
Checking source array.
430 1668 [25, 25, 25, 25]
430 1669 [25, 25, 25, 25]
430 1670 [25, 25, 25, 25]
430 1671 [25, 25, 25, 25]
430 1672 [25, 25, 25, 25]
430 1673 [25, 25, 26, 26]
Translating source to array.
Checking source array.
430 1674 [25, 26, 26, 26]
Translating source to array.
Checking source array.
430 1675 [26, 26, 26, 26]
430 1676 [26, 26, 26, 26]
430 1677 [26, 26, 27, 27]
Translating source to array.
Checking source array.
430 1678 [26, 27, 27, 27]
Translating source to array.
Checking source array.
430 1679 [27, 27, 27, 27]
430 1680 [27, 27, 27, 27]
430 1681 [27, 27, 27, 26]
Translating source to array.
Checking source array.
430 1682 [27, 26, 26, 26]
Translating source to array.
Checking source array.
430 1683 [26, 26, 26, 27]
Translating source to array.
Checking source array.
Back Single Transformation Call
Variables initialized.
Result made.
Topology: 0 0 0 1; 11
Preparing Final
Returning Final
430 1684 [26, 27, 27, 27]
Translating source to array.
Checking source array.
430 1685 [27, 27, 27, 27]
430 1686 [27, 27, 27, 27]
430 1687 [27, 27, 27, 27]
430 1688 [27, 27, 27, 26]
Translating source to array.
Checking source array.
430 1689 [27, 26, 27, 26]
Translating source to array.
Checking source array.
430 1690 [26, 26, 26, 26]
430 1691 [26, 26, 26, 26]
430 1692 [26, 26, 26, 26]
430 1693 [26, 26, 26, 26]
430 1694 [26, 25, 26, 25]
Translating source to array.
Checking source array.
431 1668 [25, 25, 25, 25]
431 1669 [25, 25, 25, 25]
431 1670 [25, 25, 25, 25]
431 1671 [25, 25, 25, 25]
431 1672 [25, 25, 25, 26]
Translating source to array.
Checking source array.
Back Single Transformation Call
Variables initialized.
Result made.
Topology: 0 0 0 1; 11
Preparing Final
Returning Final
Fatal Python error: (pygame parachute) Segmentation Fault

This application has requested the Runtime to terminate it in an unu
Please contact the application's support team for more information.

Some of the output is from the class' debug, but the # # [#, #, #, #] is from the game itself and represents: x y [topology of corners relative to the world].

Any ideas regarding the issue are greatly appreciated (as are suggestions for cleaning-up the code... C is not my specialty ;) )

Thanks!
~Tale
 
Ad

Advertisements

Joined
Dec 20, 2011
Messages
3
Reaction score
0
Update

After more tinkering, I have found that the only C code that seems to matter is...

Code:
	PyObject *source;
    if (! PyArg_ParseTuple(args, "O", &source))
        return NULL;

	return source;

No matter what the input (with the restriction that the source is a 3D rgb array, dimension 1 has 70 elements, dimension 2 has 36 elements and dimension 3 has 3 ints), the return statement above produces a segment fault after 58 calls.

Not calling the function produces no errors, passing the exact same input in each call still produces the error.

Any suggestion would be fantastic as I am quite lost at this point...

Thanks,
~Tale
 
Ad

Advertisements

Joined
Dec 20, 2011
Messages
3
Reaction score
0
Solution

Turns out that there is an issue with returning the original input from the same address that PyArg_ParseTuple stores the parameter in.

If I convert the PyObject to a PyObjectArray (as below) and always return that array instead of the original input (earlier, my code was returning the original input when no changes were necessary), then the function does not throw the segmentation fault that I was getting earlier.

The code that works is...

Code:
static PyObject *
TileTransform_getTransformedTile(TileTransform *self, PyObject *args)
{
        PyObject *source;
	PyArrayObject *sourceArray;

	//Parse the input parameters.
        if (! PyArg_ParseTuple(args, "OO", &source))
        return NULL;

	//Translate the source to an array, (source, type, minimum dimensions, maximum dimensions).
	sourceArray = (PyArrayObject *) PyArray_ContiguousFromObject(source, PyArray_LONG, 0, 3);

        //Checks and alterations to the array here.

	return PyArray_Return(resultArray);
}

Hope this helps keep others from getting caught up on the same error.

~Tale
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top