【问题标题】:Debugging Python Fatal Error: GC Object already Tracked调试 Python 致命错误:已跟踪 GC 对象
【发布时间】:2014-06-04 09:48:18
【问题描述】:

我的 python 代码因错误 'GC Object already Tracked' 而崩溃。试图找出调试此崩溃的最佳方法。

操作系统:Linux。

  • 是否有适当的方法来调试此问题。

在下面的文章中有几个建议。 Python memory debugging with GDB

不确定哪种方法对作者有效。

  • 有没有办法在这种情况下生成可以分析的内存转储。就像在 Windows 世界中一样。

找到了一些关于此的文章。但不能完全回答我的问题: http://pfigue.github.io/blog/2012/12/28/where-is-my-core-dump-archlinux/

【问题讨论】:

  • 是的,可以生成转储。实际上,转储是在您上面提到的文章中描述的崩溃(段错误)时自动生成的。但是您可以通过使用kill 发送过程信号来手动强制操作。顺便说一句,你看过pyrit.wordpress.com/2010/02/18/385 吗?
  • 一旦我们设置了核心转储,你知道当进程崩溃和消失时转储文件在哪里生成吗?
  • 转储存储在进程的当前工作目录中。

标签: python crash garbage-collection


【解决方案1】:

在我的场景中找到了这个问题的原因(不一定是 GC 对象崩溃的唯一原因)。 我使用 GDB 和核心转储来调试这个问题。

我有 Python 和 C 扩展代码(在共享对象中)。 Python 代码使用 C 扩展代码注册回调例程。 在某个工作流中,来自 C 扩展代码的线程正在调用 Python 代码中注册的回调例程。

这通常工作得很好,但是当多个线程同时执行相同的操作时,会导致崩溃并显示“GC 对象已被跟踪”。

同步多个线程对 python 对象的访问确实解决了这个问题。

感谢任何对此的回应。

【讨论】:

  • 在这种情况下如何同步访问?
【解决方案2】:

当我们的 C++ 代码触发 python 回调时,我使用 boost::python 遇到了这个问题。我偶尔会收到“已跟踪 GC 对象”并且程序会终止。

我能够在触发错误之前将 GDB 附加到进程。一件有趣的事情,在 python 代码中,我们用 一个 functools 部分,它实际上掩盖了真正的错误发生的地方。用一个简单的可调用包装类替换部分后。 “GC 对象已跟踪错误”不再弹出,而是我现在只是遇到了段错误。

在我们的 boost::python 包装器中,我们有 lambda 函数来处理 C++ 回调,并且 lambda 函数捕获了 boost::python::object 回调函数。事实证明,无论出于何种原因,在 lambda 的析构函数中,在销毁导致段错误的 boost::python::object 时并不总是正确获取 GIL。

解决方法是不使用 lambda 函数,而是创建一个仿函数,确保在对 boost::python::object 调用 PyDECREF() 之前在析构函数中获取 GIL。

class callback_wrapper
{
public:
    callback_wrapper(object cb): _cb(cb), _destroyed(false) {
    }

    callback_wrapper(const callback_wrapper& other) {
        _destroyed = other._destroyed;
        Py_INCREF(other._cb.ptr());
        _cb = other._cb;
    }

    ~callback_wrapper() {
        std::lock_guard<std::recursive_mutex> guard(_mutex);
        PyGILState_STATE state = PyGILState_Ensure();
        Py_DECREF(_cb.ptr());
        PyGILState_Release(state);
        _destroyed = true;
    }

    void operator ()(topic_ptr topic) {
        std::lock_guard<std::recursive_mutex> guard(_mutex);
        if(_destroyed) {
            return;
        }
        PyGILState_STATE state = PyGILState_Ensure();
        try {
            _cb(topic);
        }
        catch(error_already_set) { PyErr_Print(); }
        PyGILState_Release(state);
    }

    object _cb;
    std::recursive_mutex _mutex;
    bool _destroyed;
};

【讨论】:

    【解决方案3】:

    问题是您尝试将对象添加到 Python 的循环垃圾收集器跟踪两次。

    查看this bug,具体来说:

    长话短说:如果您设置了Py_TPFLAGS_HAVE_GC 并且您使用的是Python 的内置内存分配(标准tp_alloc/tp_free),那么您不必手动调用PyObject_GC_Track()PyObject_GC_UnTrack() . Python 会在你背后处理这一切。

    不幸的是,目前没有很好的记录。解决问题后,请随时加入错误报告(上面链接),以更好地记录此行为。

    【讨论】:

    • 感谢克里斯提供的信息。我会调查一下并填写结果。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多