简体   繁体   English

第二次调用PyObject_CallObject()时出现分段错误

[英]Segmentation fault when calling PyObject_CallObject() the second time

I am writing a c++ class calling a python script. 我正在写一个调用python脚本的c ++类。 But when I call the method a second it stops working. 但是,当我每秒调用该方法时,它将停止工作。 I googled everywhere and finds no clue. 我到处搜索,没有发现任何线索。 I thought it is because of ref count problem, so I removed all the Py_DECREF call, but still no help. 我以为是因为引用计数问题,所以我删除了所有Py_DECREF调用,但仍然没有帮助。

Here is the c++ code (In the original code, I call PyObject_GetAttrString every time in RunModel , but in the second call gdb indicates the second func is <Py_NoneStruct> . So I changed to this): 这是c ++代码(在原始代码中,我每次在RunModel都调用PyObject_GetAttrString ,但是在第二次调用中,gdb表示第二个func<Py_NoneStruct> 。因此,我将其更改为<Py_NoneStruct>

#include <Python.h>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <thread>

using namespace std;

class PyRecognitionContext {

  PyObject *module, *func, *result, *args;

  public:

  PyRecognitionContext(int argc, char **argv) {
    Py_Initialize();
    PySys_SetArgv(argc, argv);
    module = PyImport_Import(PyString_FromString("model.t"));
    if (module == NULL) {
      PyErr_Print();
      throw std::invalid_argument("fails to import the module");
    }
  }

  ~PyRecognitionContext() {
    Py_Finalize();
  }

  void LoadModel(std::string model) {
    func = PyObject_GetAttrString(module, "load");
    args = PyTuple_New(1);
    PyTuple_SetItem(args, 0, PyString_FromString(model.c_str()));
    PyObject_CallObject(func, args);
    func = PyObject_GetAttrString(module, "run");
  }

  char* RunModel(const char *imageBytes, size_t size) {
    args = PyTuple_New(1);
    PyTuple_SetItem(args, 0, PyByteArray_FromStringAndSize(imageBytes, size));
    result = PyObject_CallObject(func, args);
    char* resultStr = PyString_AsString(result);
    return resultStr;
  }

};

int task(PyRecognitionContext pr, string fileName) {
  cout << "//";
  cout << fileName << endl;
  std::ifstream ifs(fileName, ios::binary|ios::ate);
  size_t imgSize = ifs.tellg();
  char *result = new char[imgSize];
  ifs.seekg(0, ios::beg);
  ifs.read(result, imgSize);
  ifs.close();
  std::cout << pr.RunModel(result, imgSize) << endl;
  return 0;
}

int main(int argc, char *argv[]) {
  PyRecognitionContext pr(argc, argv);
  pr.LoadModel("/home/dickzhou/data/sex_256/cp/");
  cout << "//loaded" << endl;

  task(pr, "/home/dickzhou/data/face/male/24-28/24/135.JPG");
  task(pr, "/home/dickzhou/data/face/female/24-28/24/138.JPG");

  //thread t1(task, pr, "/home/dickzhou/data/face/male/24-28/24/135.JPG");
  //thread t2(task, pr, "/home/dickzhou/data/face/female/24-28/24/125.JPG");

  //t1.join();
  //t2.join();

  cout << "//done" << endl;
  return 0;
}

I compiled it using: 我使用以下代码进行编译:

g++ -I/usr/include/python2.7 -I/usr/include/python2.7 -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O1 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -lpthread -ldl -lutil -lm -lpython2.7 -Xlinker -export-dynamic -std=c++11 pdr.cpp

and this is my gdb output: 这是我的gdb输出:

(gdb) break 40
Breakpoint 1 at 0x401f47: file pdr.cpp, line 40.
(gdb) run
Starting program: /home/dickzhou/data/sex_256/c++/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
//loaded
///home/dickzhou/data/face/male/24-28/24/135.JPG

Breakpoint 1, RunModel (size=14637, imageBytes=0x6e5270 "\377\330\377", <incomplete sequence \340>, this=0x7fffffffe040) at pdr.cpp:40
40          result = PyObject_CallObject(func, args);
Missing separate debuginfos, use: debuginfo-install glibc-2.17-105.el7.x86_64 libgcc-4.8.5-16.el7.x86_64 libstdc++-4.8.5-4.el7.x86_64 python-libs-2.7.5-34.el7.x86_64
(gdb) p func
$1 = (PyObject *) 0x2aaaaabfa9b0
(gdb) p args
$2 = (PyObject *) 0x2aaaaabf2d10
(gdb) c
Continuing.
a
///home/dickzhou/data/face/female/24-28/24/138.JPG

Breakpoint 1, RunModel (size=13572, imageBytes=0x68d9f0 "\377\330\377", <incomplete sequence \340>, this=0x7fffffffe040) at pdr.cpp:40
40          result = PyObject_CallObject(func, args);
(gdb) p func
$3 = (PyObject *) 0x2aaaaabfa9b0
(gdb) p args
$4 = (PyObject *) 0x2aaaaab8bbd0
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x00002aaaab33c09a in PyObject_Call () from /lib64/libpython2.7.so.1.0
(gdb)

a minimal python code would be: 最小的python代码是:

def load(str):
    pass

def run(bytes):
    return "a"

Your actual problem is not following Rule of Zero . 您的实际问题不是遵循零规则 You have a class with resources (a resource is anything that needs freeing later) managed manually. 您有一个具有手动管理的资源的类(资源是以后需要释放的任何东西)。 That won't do. 那不会。

Instances of PyRecognitionContext are copied, and Py_Finalize() is called twice, and also prematurely. 复制PyRecognitionContext实例,并两次并且过早地调用Py_Finalize()

The global Python context needs initialization and finalization. 全局Python上下文需要初始化和完成。 We can delegate that to a separate class to facilitate Single Responsibility Principle. 我们可以将其委托给一个单独的类,以促进“单一责任原则”。

struct PyContext
{
    PyContext()
    {
        Py_Initialize();
    }

    ~PyContext()
    {
        Py_Finalize();
    }

    PyContext& operator=(const PyContext&) = delete;
    PyContext(const PyContext&) = delete;
    PyContext& operator=(PyContext&&) = delete;
    PyContext(PyContext&&) = delete;
};

Now you can put a member of that class in your PyRecognitionContext 现在,您可以在PyRecognitionContext中放置该类的成员

class PyRecognitionContext {
    PyContext pyContext;

    // ... rest of the code
};

This moves the run time error to the compile time. 这会将运行时错误移至编译时。

The other Python objects are resources too because they need to be freed. 其他Python对象也是资源,因为它们需要被释放。 We can use std::unique_ptr with a custom deleter to manage these. 我们可以将std::unique_ptr与自定义删除器一起使用来管理这些。

Thanks for all your help, I have solved this question. 感谢您的所有帮助,我已经解决了这个问题。

The problem was the deconstruct function being called twice because the task function is pass-by-value. 问题在于解构函数被调用两次,因为task函数是按值传递的。 I changed it to pass-by-reference: 我将其更改为通过引用:

int task(PyRecognitionContext *pr, string fileName) {
  cout << "//";
  cout << fileName << endl;
  std::ifstream ifs(fileName.c_str(), ios::binary|ios::ate);
  size_t imgSize = ifs.tellg();
  char *result = new char[imgSize];
  ifs.seekg(0, ios::beg);
  ifs.read(result, imgSize);
  ifs.close();
  std::cout << pr->RunModel(result, imgSize) << endl;
  return 0;
}

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

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