web-dev-qa-db-ja.com

デバッグPython致命的なエラー:GCオブジェクトはすでに追跡されています

私のpythonコードはエラー「GCオブジェクトはすでに追跡されています」でクラッシュしました。このクラッシュをデバッグするための最良のアプローチを見つけようとしています。

OS:Linux。

  • この問題をデバッグする適切な方法はありますか?.

次の記事にはいくつかの提案がありました。 GDBを使用したPythonメモリデバッグ

どちらのアプローチが作者にとって有効かわからない。

  • 分析できるようなシナリオでメモリダンプを生成する方法はありますか? Windowsの世界のように。

これに関するいくつかの記事を見つけました。しかし、私の質問に完全に答えているわけではありません: http://pfigue.github.io/blog/2012/12/28/where-is-my-core-dump-archlinux/

16
Feru

私のシナリオでこの問題の理由を見つけました(必ずしもGCオブジェクトがクラッシュする唯一の理由ではありません)。この問題をデバッグするために、GDBとコアダンプを使用しました。

PythonおよびC拡張コード(共有オブジェクト内))PythonコードはコールバックルーチンをC拡張コードに登録します。特定のワークフローでは、Cからのスレッド拡張コードは、Pythonコードで登録されたコールバックルーチンを呼び出していました。

これは通常は正常に機能しましたが、複数のスレッドが同じアクションを同時に実行すると、「GCオブジェクトはすでに追跡されています」というクラッシュが発生しました。

複数のスレッドのpythonオブジェクトへのアクセスを同期すると、この問題は解決します。

これに答えてくれてありがとう。

13
Feru

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;
};
9
skaught

問題は、Pythonのサイクリックガベージコレクタートラッキングにオブジェクトを2回追加しようとすることです。

チェックアウト このバグ 、具体的には:

簡単に言うと、_Py_TPFLAGS_HAVE_GC_を設定し、Pythonの組み込みメモリ割り当て(標準の_tp_alloc_/_tp_free_)を使用している場合、手動でPyObject_GC_Track()またはPyObject_GC_UnTrack()。 Pythonは、すべてを背後で処理します。

残念ながら、現時点では、これは十分に文書化されていません。問題を修正したら、この動作のより適切なドキュメントについて、バグレポート(上記のリンク)に気軽に連絡してください。

6