Отладка Python Fatal Error: объект GC уже отслеживается

Мой код python сбой с ошибкой "Объект GC уже отслеживается". Попытка выяснить лучший подход к отладке этих сбоев.

ОС: Linux.

  • Есть ли способ отладить эту проблему.

В следующей статье было несколько предложений. Отладка памяти Python с помощью GDB

Не уверен, какой подход работал для автора.

  • Есть ли способ генерации дампов памяти в таком сценарии, который можно было бы проанализировать. Как в мире Windows.

Нашел некоторую статью об этом. Но не полностью отвечает на мой вопрос: http://pfigue.github.io/blog/2012/12/28/where-is-my-core-dump-archlinux/

Ответ 1

Узнал причину этой проблемы в моем сценарии (не обязательно единственная причина сбоя объекта GC). Я использовал отвалы GDB и Core для отладки этой проблемы.

У меня есть код расширения Python и C (в общем объекте). Код Python регистрирует процедуру обратного вызова с кодом расширения C. В определенном рабочем потоке поток из кода расширения C вызывал зарегистрированную процедуру обратного вызова в коде Python.

Обычно это работало нормально, но когда несколько потоков выполняли одно и то же действие одновременно, это привело к сбою с "GC Object уже отслеживаемым".

Синхронизация доступа к объектам python для нескольких потоков устраняет эту проблему.

Спасибо, кто-то ответил на это.

Ответ 2

Проблема заключается в том, что вы дважды пытаетесь добавить объект в циклическое отслеживание сборщика мусора Python.

Отметьте эту ошибку, в частности:

Короче говоря: если вы установили Py_TPFLAGS_HAVE_GC, и вы используете встроенное выделение памяти Python (стандартное tp_alloc/tp_free), вам никогда не придется вручную вызывать PyObject_GC_Track() или PyObject_GC_UnTrack(), Python обрабатывает все это за спиной.

К сожалению, на данный момент это не очень хорошо документировано. Как только вы устраните проблему, не стесняйтесь записывать отчет об ошибке (связанный выше) о лучшей документации по этому поведению.

Ответ 3

Я столкнулся с этой проблемой, используя boost:: python, когда наш код на С++ запускает обратный вызов python. Иногда я получаю "отслеживаемый объект GC", и программа завершится.

Я смог подключить GDB к процессу до запуска ошибки. Одна интересная вещь, в коде python мы обернули обратный вызов с помощью functools partial, который фактически маскировал, где происходила настоящая ошибка. После замены частичного класса класс вызываемых оболочек. "Объект GC уже отслеживал ошибку" больше не появлялся, вместо этого теперь я просто получил segfault.

В нашей оболочке boost:: python у нас были функции лямбда для обработки обратного вызова С++, и функция лямбда захватила функцию обратного вызова boost:: python:: object. По какой-то причине оказалось, что в деструкторе лямбды он не всегда надлежащим образом приобретает GIL при уничтожении объекта boost:: python::, вызывающего segfault.

Исправить было не использование функции лямбда, а вместо этого создать функтор, который обязательно приобретет GIL в деструкторе до вызова PyDECREF() в boost:: python:: object.

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