简体   繁体   English

在C ++中嵌入python:奇怪的分段错误

[英]Embedding python in C++ : strange segmentation faults

(Sorry for the vague title but it shows how dumbfounded I am by the issue). (对不起这个模糊的标题,但它显示我对这个问题有多傻眼)。

So I'm running Python code from a C++ program, following the approach described here : https://docs.python.org/2/extending/embedding.html . 所以我按照这里描述的方法运行C ++程序中的Python代码: https//docs.python.org/2/extending/embedding.html

Here is the C++ code: 这是C ++代码:

#include <Python.h>
#include <iostream>

int main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pDict, *pFunc;
    PyObject *pArgs, *pValue;
    int i;

    if (argc < 3) {
        fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
        return 1;
    }

    Py_SetProgramName(argv[0]);
    Py_Initialize();
    PySys_SetArgv(argc, argv); 
    PyObject *sys = PyImport_ImportModule("sys");
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyString_FromString("."));
    pName = PyString_FromString((char*)argv[1]);
    /* Error checking of pName left out */

    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) {
        pFunc = PyObject_GetAttrString(pModule, argv[2]);
        /* pFunc is a new reference */

        if (pFunc && PyCallable_Check(pFunc)) {   
            PyObject *pArgs = PyList_New(4);
            PyList_SetItem(pArgs,0,PyString_FromString("H-SAMPLE1-OH"));
            PyList_SetItem(pArgs,1,PyInt_FromLong(2));
            PyList_SetItem(pArgs,2,PyString_FromString("H-SAMPLE2-OH"));
            PyList_SetItem(pArgs,3,PyInt_FromLong(3));
            PyObject *arglist = Py_BuildValue("(O)", pArgs);
            Py_DECREF(pArgs);

            for(int run = 0; run < 2; run++)
            { 
                std::cout << "begin" << std::endl;

                pValue = PyObject_CallObject(pFunc, arglist);
                //Py_DECREF(arglist);
                if (pValue != NULL) 
                {
                    int py_list_size = PyList_Size(pValue);
                    printf("list size = %d\n",py_list_size);
                    int sub_list_size = 0;
                    for(Py_ssize_t i = 0; i < py_list_size; ++i)
                    {
                        PyObject *pList = PyList_GetItem(pValue, i);
                        sub_list_size = PyList_Size(pList);  
                        if(PyList_Check(pList))
                        {
                            for(Py_ssize_t j = 0; j < sub_list_size; ++j)
                            {
                                PyObject *pListItem = PyList_GetItem(pList, j);
                                double pyNumber = PyFloat_AsDouble(pListItem);
                                std::cout << "pynumber ok" << std::endl;
                                Py_DECREF(pListItem);
                                printf("Result of call: %f\n", pyNumber);
                            }
                        }
                        else
                        {
                            printf("Not list!\n");
                        }  
                        Py_DECREF(pList);
                    }
                    Py_DECREF(pValue);
                }
                else {
                    std::cout << "Else" << std::endl;
                    Py_DECREF(pFunc);
                    Py_DECREF(pModule);
                    PyErr_Print();
                    fprintf(stderr,"Call failed\n");
                    return 1;
                }
            }
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
        return 1;
    }
    Py_Finalize();
    return 0;
}

and here is the toy Python code: 这是玩具Python代码:

def test(a):
    print a
    return [[1.2,2.6],[4.7,5.6]]

Notice the main loop in the C++ code, iterating over the variable "run". 注意C ++代码中的主循环,迭代变量“run”。 It works like a charm when the code inside the loop is executed only once. 当循环内的代码只执行一次时,它就像一个魅力。 If I try to run it more, only twice for example, it goes wrong, I get a segmentation fault. 如果我试图运行它更多,例如只有两次,它出错了,我得到一个分段错误。 Apparently, the fault happens at line 61, when trying to perform 显然,当试图执行时,故障发生在第61行

double pyNumber = PyFloat_AsDouble(pListItem);

I find it really strange. 我觉得很奇怪。 It works fine during the first execution, and then suddenly if doesn't manage to properly get something from pListItem anymore (although it does receive something it recognizes as a list of size 2 and seems to handle all other pyObject pointers correctly). 它在第一次执行时工作正常,然后突然如果没有设法从pListItem正确获取某些东西(尽管它确实接收到它识别为大小为2的列表并且似乎正确处理所有其他pyObject指针的东西)。 Any idea about what is happening? 关于发生了什么的任何想法?

To reproduce: 重现:

I compiled as follow: 我编译如下:

g++ -L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -I/usr/include/python2.7 -o ms2pip ms2pip.c -lpthread -ldl  -lutil -lm  -lpython2.7 -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions

And then executed as follow: 然后执行如下:

$ ./ms2pip python_code test
(so ./executable < python_file_without_py_extension > < function_name >)

I think your problem is that PyList_GetItem() returns a borrowed references. 我认为你的问题是PyList_GetItem()返回一个借来的引用。 So the problem is with calling Py_DECREF() with both pList and pListItem : 所以问题是用pListpListItem调用Py_DECREF()

PyObject *pList = PyList_GetItem(pValue, i);
// ...
if(PyList_Check(pList))
{
    for(Py_ssize_t j = 0; j < sub_list_size; ++j)
    {
        PyObject *pListItem = PyList_GetItem(pList, j);
        double pyNumber = PyFloat_AsDouble(pListItem); // <-- Segfault in second iteration after released from first iteration.
        // ...
        Py_DECREF(pListItem); // <-- Bad, released in first iteration.
        // ...
    }
}
//...
Py_DECREF(pList); // <-- Bad, released in first iteration.

pList is a borrowed reference which you are not responsible for releasing with Py_DECREF() . pList是一个借用的引用,您不负责使用Py_DECREF()进行释放。 Also, pListItem is also a borrowed reference. 此外, pListItem也是借用的引用。 So on the first iteration, you release pList as well as each pListItem which is bad. 所以在第一次迭代中,你释放pList以及每个坏的pListItem On the second iteration, you grab pList and each pListItem which have all been released and treat them as though they are still stable which is not the case. 在第二次迭代中,您获取已释放的pList和每个pListItem ,并将它们视为仍然稳定,而不是这种情况。 Because you are accessing freed objects, the program could really fail or give bad results at any of the function calls involving them (eg, PyList_Size(pList) , PyList_GetItem(pList, j) , PyFloat_AsDouble(pListItem) , Py_DECREF(pListItem) , Py_DECREF(pList) ). 因为您正在访问释放的对象,该计划可能真的失败或在任何一个函数调用的给予不好的结果让他们参与(例如, PyList_Size(pList) PyList_GetItem(pList, j) PyFloat_AsDouble(pListItem) Py_DECREF(pListItem) Py_DECREF(pList) )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM