私のpythonコードはエラー「GCオブジェクトはすでに追跡されています」でクラッシュしました。このクラッシュをデバッグするための最良のアプローチを見つけようとしています。
OS:Linux。
次の記事にはいくつかの提案がありました。 GDBを使用したPythonメモリデバッグ
どちらのアプローチが作者にとって有効かわからない。
これに関するいくつかの記事を見つけました。しかし、私の質問に完全に答えているわけではありません: http://pfigue.github.io/blog/2012/12/28/where-is-my-core-dump-archlinux/
私のシナリオでこの問題の理由を見つけました(必ずしもGCオブジェクトがクラッシュする唯一の理由ではありません)。この問題をデバッグするために、GDBとコアダンプを使用しました。
PythonおよびC拡張コード(共有オブジェクト内))PythonコードはコールバックルーチンをC拡張コードに登録します。特定のワークフローでは、Cからのスレッド拡張コードは、Pythonコードで登録されたコールバックルーチンを呼び出していました。
これは通常は正常に機能しましたが、複数のスレッドが同じアクションを同時に実行すると、「GCオブジェクトはすでに追跡されています」というクラッシュが発生しました。
複数のスレッドのpythonオブジェクトへのアクセスを同期すると、この問題は解決します。
これに答えてくれてありがとう。
C++コードがpythonコールバックをトリガーするときに、boost :: pythonを使用してこの問題に遭遇しました。「GCオブジェクトはすでに追跡されています」と表示され、プログラムが終了することがありました。
エラーをトリガーする前に、GDBをプロセスにアタッチすることができました。興味深いことに、pythonコードでは、コールバックをfunctoolsパーシャルでラップしていました。これは、実際にエラーが発生している場所をマスクしていました。パーシャルを単純な呼び出し可能なラッパークラスに置き換えた後。 「GCオブジェクトはすでにエラーを追跡しています」がポップアップしなくなり、代わりにセグメンテーション違反が発生しました。
Boost :: pythonラッパーには、C++コールバックを処理するラムダ関数があり、ラムダ関数はboost :: python :: objectコールバック関数をキャプチャしました。何らかの理由で、ラムダのデストラクタで、セグメンテーション違反を引き起こしていたboost :: python :: objectを破棄するときにGILを常に適切に取得しているとは限りませんでした。
修正は、ラムダ関数を使用せず、代わりに、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;
};
問題は、Pythonのサイクリックガベージコレクタートラッキングにオブジェクトを2回追加しようとすることです。
チェックアウト このバグ 、具体的には:
簡単に言うと、_Py_TPFLAGS_HAVE_GC
_を設定し、Pythonの組み込みメモリ割り当て(標準の_tp_alloc
_/_tp_free
_)を使用している場合、手動でPyObject_GC_Track()
またはPyObject_GC_UnTrack()
。 Pythonは、すべてを背後で処理します。
残念ながら、現時点では、これは十分に文書化されていません。問題を修正したら、この動作のより適切なドキュメントについて、バグレポート(上記のリンク)に気軽に連絡してください。