2014-04-20 9 views
10

Il mio codice Python si è bloccato in modo anomalo con errore "Oggetto GC già tracciato". Cercando di capire l'approccio migliore per eseguire il debug di questo arresto anomalo.Debug di errore fatale di Python: oggetto GC già monitorato

SO: Linux.

  • Esiste un modo corretto di eseguire il debug del problema.

Ci sono stati un paio di suggerimenti nel seguente articolo. Python memory debugging with GDB

Non so quale approccio ha funzionato per l'autore.

  • C'è un modo per generare dump di memoria in tale scenario che potrebbe essere analizzato. Come nel mondo di Windows.

Trovato alcuni articoli su questo. Ma non risponde interamente alla mia domanda: http://pfigue.github.io/blog/2012/12/28/where-is-my-core-dump-archlinux/

+1

Sì, è possibile generare un dump. In realtà il dump viene generato automaticamente in caso di crash (segfault) come descritto nell'articolo che hai citato. Ma puoi forzare l'operazione manualmente, inviando un segnale di processo usando 'kill'. BTW Hai guardato su http://pyrit.wordpress.com/2010/02/18/385/? – user3159253

+0

Una volta impostato il core dump, sai dove viene generato il file dump quando il processo si blocca e se ne va? – Feru

+0

Il dump è memorizzato nella directory di lavoro corrente di un processo. – user3159253

risposta

3

Il problema è che si tenta di aggiungere due volte il tracciamento del garbage collector ciclico di Python.

Partenza this bug, in particolare:

Per farla breve: se si imposta Py_TPFLAGS_HAVE_GC e se si utilizza incorporato di Python in allocazione di memoria (standard tp_alloc/tp_free), non è necessario chiamare manualmente PyObject_GC_Track() o PyObject_GC_UnTrack(). Python lo gestisce tutto alle spalle.

Purtroppo, questo non è documentato molto bene, al momento. Una volta risolto il problema, sentiti libero di inserire nella segnalazione di bug (linkata sopra) una migliore documentazione di questo comportamento.

+0

Grazie per questa informazione Chris. Approfondirò questo e compilerò i risultati. – Feru

8

Ho scoperto il motivo di questo problema nel mio scenario (non necessariamente l'unico motivo per l'arresto anomalo dell'oggetto GC). Ho usato i dump GDB e Core per eseguire il debug di questo problema.

Ho codice di estensione Python e C (in oggetto condiviso). Il codice Python registra una routine di richiamata con il codice di estensione C. In un determinato flusso di lavoro una discussione dal codice di estensione C stava chiamando la routine di richiamata registrata in codice Python.

Questo di solito ha funzionato bene, ma quando più thread hanno fatto la stessa azione contemporaneamente ha provocato il crash con 'oggetto GC già tracciato'.

La sincronizzazione dell'accesso agli oggetti Python per thread multipli risolve questo problema.

Grazie a qualsiasi risposta a questo.

+1

Come si sincronizza l'accesso in questo contesto? –

2

Mi sono imbattuto in questo problema utilizzando boost :: python quando il nostro codice C++ avrebbe attivato un callback python.Occasionalmente ricevo "oggetto GC già tracciato" e il programma termina.

Sono stato in grado di collegare GDB al processo prima di attivare l'errore. Una cosa interessante, nel codice python, stavamo avvolgendo la funzione callback con a functools partial, che in realtà mascherava il punto in cui si verificava l'errore reale. Dopo aver sostituito il partial con una semplice classe wrapper chiamabile. L'errore "Oggetto GC già rintracciato" non è più spuntato, ma ora ottenevo solo un segfault.

Nel nostro involucro boost :: python, abbiamo avuto funzioni lambda per gestire un callback in C++ e la funzione lambda ha acquisito la funzione di callback boost :: python :: object. Si è scoperto, per qualsiasi ragione, nel distruttore per il lambda, non sempre stava acquisendo correttamente il GIL quando distruggeva l'oggetto boost :: python :: che stava causando il segfault.

La correzione era di non utilizzare una funzione lambda, ma invece di creare un functor che si assicurasse di acquisire GIL nel distruttore prima di chiamare PyDECREF() su 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; 
}; 
Problemi correlati