调试 Python 致命错误:已跟踪 GC 对象
Posted
技术标签:
【中文标题】调试 Python 致命错误:已跟踪 GC 对象【英文标题】:Debugging Python Fatal Error: GC Object already Tracked 【发布时间】: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 吗?
一旦我们设置了核心转储,你知道当进程崩溃和消失时转储文件在哪里生成吗?
转储存储在进程的当前工作目录中。
【参考方案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,具体来说:
Python 中的documentation for supporting cyclic garbage collection My documentation patch for the issue 和 My explanation in the bug report itself长话短说:如果您设置了Py_TPFLAGS_HAVE_GC
并且您使用的是Python 的内置内存分配(标准tp_alloc
/tp_free
),那么您不必手动调用PyObject_GC_Track()
或PyObject_GC_UnTrack()
. Python 会在你背后处理这一切。
不幸的是,目前没有很好的记录。解决问题后,请随时加入错误报告(上面链接),以更好地记录此行为。
【讨论】:
感谢克里斯提供的信息。我会调查一下并填写结果。以上是关于调试 Python 致命错误:已跟踪 GC 对象的主要内容,如果未能解决你的问题,请参考以下文章