2014-04-20 17 views
10

Mój kod Pythona się zawiesza z błędem "Obiekt GC już śledzony". Próbuję znaleźć najlepsze podejście do debugowania tych awarii.Debugowanie Pythona Błąd krytyczny: Obiekt GC jest już śledzony

System operacyjny: Linux.

  • Czy istnieje właściwy sposób debugowania tego problemu.

W poniższym artykule przedstawiono kilka sugestii. Python memory debugging with GDB

Nie wiem, które podejście było skuteczne dla autora.

  • Czy istnieje sposób generowania zrzutów pamięci w takim scenariuszu, który można przeanalizować. Podobnie jak w świecie Windows.

Znaleziono artykuł na ten temat. Ale nie w pełni odpowiada na moje pytanie: http://pfigue.github.io/blog/2012/12/28/where-is-my-core-dump-archlinux/

+1

Tak, możliwe jest wygenerowanie zrzutu. Właściwie zrzut jest generowany automatycznie po awarii (segfault), jak opisano w artykule, o którym wspomniałeś. Ale możesz wymusić operację ręcznie, wysyłając sygnał procesu za pomocą 'kill'. BTW Czy obejrzałeś http://pyrit.wordpress.com/2010/02/18/385/? – user3159253

+0

Po skonfigurowaniu zrzutu pamięci, czy wiesz, gdzie generowany jest plik zrzutu, gdy proces się zawiesza i znika? – Feru

+0

Zrzut jest przechowywany w bieżącym katalogu roboczym procesu. – user3159253

Odpowiedz

3

Problem polega na tym, że dwa razy próbujesz dodać obiekt do cyklicznego śledzenia modułów do zbierania śmieci Pythona.

Wyjazd this bug, a konkretnie:

Krótko mówiąc: jeśli ustawisz Py_TPFLAGS_HAVE_GC i używasz wbudowanej Pythona w alokacji pamięci (standard tp_alloc/tp_free), nie musisz ręcznie dzwonić pod numer PyObject_GC_Track() lub PyObject_GC_UnTrack(). Python obsługuje wszystko za plecami.

Niestety, nie jest to obecnie bardzo dobrze udokumentowane. Po rozwiązaniu problemu możesz zagłosować na zgłoszenie błędu (link powyżej) na temat lepszej dokumentacji tego zachowania.

+0

Dzięki za te informacje Chris. Zajrzę do tego i wypełnię wyniki. – Feru

8

Znaleźliśmy przyczynę tego problemu w moim scenariuszu (niekoniecznie jedyny powód awarii obiektu GC). Użyłem zrzutów GDB i Core do debugowania tego problemu.

Mam Python i C Extension Code (w obiekcie udostępnionym). Kod w języku Python rejestruje procedurę oddzwaniania za pomocą kodu rozszerzenia C. W określonym przepływie pracy wątek z kodu rozszerzenia C wywoływał zarejestrowaną procedurę oddzwonienia w kodzie Pythona.

Zwykle działało dobrze, ale gdy wiele wątków wykonywało tę samą akcję jednocześnie, spowodowało awarię z "Obiektem GC już wyśledzonym".

Synchronizacja dostępu do obiektów Pythona dla wielu wątków rozwiązuje ten problem.

Dzięki każdemu odpowiedział na to.

+1

Jak synchronizujesz dostęp w tym kontekście? –

2

Wpadłem na ten problem używając boost :: python, gdy nasz kod C++ wyzwalałby wywołanie Pythona.Od czasu do czasu dostaję "obiekt GC już wyśledzony" i program się zakończy.

Udało mi się dołączyć GDB do procesu przed wyzwoleniem błędu. Jedną z interesujących rzeczy w kodzie Pythona było zawijanie wywołania zwrotnego z częściowym functools, który faktycznie maskował miejsce wystąpienia prawdziwego błędu. Po zastąpieniu częściowej prostą klasą wrappera. "Obiekt GC już wykrył błąd" już nie pojawił się, zamiast tego właśnie dostałem właśnie segfault.

W naszym dopalaczu :: python, mieliśmy funkcje lambda do obsługi wywołania zwrotnego C++, a funkcja lambda przechwyciła funkcję wywołania funkcji :: :: wywołania funkcji :: python. Z jakiegoś powodu okazało się, że w destruktorze dla lambda nie zawsze poprawnie pozyskiwał GILa podczas niszczenia obiektu boost :: python ::, który powodował uszkodzenie.

Poprawka polegała na tym, aby nie używać funkcji lambda, ale zamiast tego utworzyć funktor, który upewnia się, że GIL zostanie użyty w destruktorze przed wywołaniem PyDECREF() na obiekcie boost :: python ::.

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