Domanda

Sto eseguendo il debug di un'applicazione C ++ multi-thread (nativa) in Visual & nbsp; Studio & nbsp; 2008. In occasioni apparentemente casuali, ottengo un "Windows ha attivato un punto di interruzione ..." errore con una nota che ciò potrebbe essere dovuto a una corruzione nell'heap. Questi errori non causano sempre l'arresto anomalo dell'applicazione immediatamente, anche se è probabile che si arresti in modo anomalo poco dopo.

Il grosso problema di questi errori è che compaiono solo dopo che si è effettivamente verificata la corruzione, il che li rende molto difficili da rintracciare ed eseguire il debug, specialmente su un'applicazione multi-thread.

  • Che tipo di cose possono causare questi errori?

  • Come eseguo il debug?

Suggerimenti, strumenti, metodi, illuminazioni ... sono i benvenuti.

È stato utile?

Soluzione

Application Verifier combinato con Strumenti di debug per Windows è una configurazione straordinaria. Puoi ottenere entrambi come parte del Windows Driver Kit o il più leggero Windows SDK . (Scopri Application Verifier durante la ricerca di un domanda precedente su un problema di corruzione dell'heap .) Ho usato anche BoundsChecker e Insure ++ (menzionato in altre risposte) in passato, anche se sono rimasto sorpreso dalla funzionalità di Application Verifier.

Electric Fence (aka " efence "), dmalloc , valgrind e così via sono tutti degni di nota, ma la maggior parte di questi sono molto più facili da eseguire con * nix di Windows. Valgrind è incredibilmente flessibile: ho eseguito il debug di software server di grandi dimensioni con molti problemi di heap che lo utilizzano.

Quando tutto il resto fallisce, puoi fornire al tuo operatore globale sovraccarichi new / delete e malloc / calloc / realloc - il modo per farlo varierà leggermente a seconda del compilatore e della piattaforma - e questo sarà un po 'un investimento - ma potrebbe ripagare nel lungo periodo. L'elenco delle caratteristiche desiderabili dovrebbe apparire familiare da dmalloc ed electricfence e il libro sorprendentemente eccellente Scrivere Solid Code :

  • valori di sentinella : concedi un po 'più di spazio prima e dopo ogni allocazione, rispettando il requisito di allineamento massimo; riempire con numeri magici (aiuta a catturare overflow e underflow del buffer e il puntatore "selvaggio" occasionale)
  • riempimento alloc : riempi nuove allocazioni con un valore magico diverso da 0: Visual C ++ lo farà già per te nelle build di debug (aiuta a catturare l'utilizzo di variabili non inizializzate)
  • riempimento libero : riempi la memoria liberata con un valore magico diverso da 0, progettato per attivare un segfault nella maggior parte dei casi (aiuta a catturare puntatori penzolanti)
  • ritardato libero : non rimettere la memoria libera nell'heap per un po ', tenerlo libero ma non disponibile (aiuta a catturare più puntatori penzolanti, cattura doppi liberaggi vicini)
  • tracciamento : essere in grado di registrare dove è stata effettuata un'allocazione a volte può essere utile

Nota che nel nostro sistema homebrew locale (per un target incorporato) manteniamo il tracciamento separato dalla maggior parte delle altre cose, perché il sovraccarico di runtime è molto più alto.


Se sei interessato a più motivi per sovraccaricare queste funzioni / operatori di allocazione, dai un'occhiata a la mia risposta a " Qualsiasi motivo sovraccaricare l'operatore globale nuovo ed eliminare? " ; autopromozione spudorata a parte, elenca altre tecniche che sono utili nel tracciare gli errori di corruzione dell'heap, così come altri strumenti applicabili.


Poiché continuo a trovare la mia risposta qui quando cerco valori alloc / free / fence utilizzati da MS, ecco un'altra risposta che copre Microsoft dbgheap riempire i valori .

Altri suggerimenti

Puoi rilevare molti problemi di corruzione dell'heap abilitando l'heap della pagina per la tua applicazione. Per fare ciò è necessario utilizzare gflags.exe incluso in Strumenti di debug per Windows

Esegui Gflags.exe e nelle opzioni del file immagine per il tuo eseguibile, seleziona " Abilita heap di pagine " opzione.

Ora riavvia il tuo exe e collegalo a un debugger. Con Page Heap abilitato, l'applicazione si interromperà nel debugger ogni volta che si verifica un danneggiamento dell'heap.

Per rallentare veramente le cose ed eseguire molti controlli di runtime, prova ad aggiungere quanto segue all'inizio del tuo main () o equivalente in Microsoft Visual Studio C ++

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
  

Che tipo di cose possono causare questi errori?

Fare cose cattive con la memoria, ad es. scrivere dopo la fine di un buffer o scrivere su un buffer dopo che è stato liberato di nuovo nell'heap.

  

Come posso eseguirne il debug?

Usa uno strumento che aggiunge il controllo automatico dei limiti al tuo eseguibile: vale a dire valgrind su Unix o uno strumento come BoundsChecker (Wikipedia suggerisce anche Purify and Insure ++) su Windows.

Attenzione che rallenteranno la tua applicazione, quindi potrebbero essere inutilizzabili se la tua è un'applicazione soft-real-time.

Un altro possibile strumento / aiuto per il debug potrebbe essere HeapAgent di MicroQuill.

Un breve suggerimento, che ho ricevuto da Rilevamento dell'accesso alla memoria liberata è questo:

  

Se si desidera individuare l'errore   rapidamente, senza controllare tutti   istruzione che accede alla memoria   blocco, è possibile impostare il puntatore di memoria   su un valore non valido dopo aver liberato il file   block:

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif

Lo strumento migliore che ho trovato utile e lavorato ogni volta è la revisione del codice (con buoni revisori del codice).

Oltre alla revisione del codice, proverei prima Pagina Heap. L'archiviazione della pagina richiede alcuni secondi e, per fortuna, potrebbe individuare il tuo problema.

Se non hai fortuna con Page Heap, scarica Strumenti di debug per Windows da Microsoft e impara ad usare WinDbg. Spiacenti, non posso darti un aiuto più specifico, ma il debug della corruzione dell'heap multi-thread è più un'arte che una scienza. Google per " corruzione heap WinDbg " e dovresti trovare molti articoli sull'argomento.

Puoi anche verificare se stai collegando la libreria di runtime C dinamica o statica. Se i tuoi file DLL si collegano alla libreria di runtime C statica, i file DLL hanno heap separati.

Quindi, se dovessi creare un oggetto in una DLL e provare a liberarlo in un'altra DLL, otterrai lo stesso messaggio che vedi sopra. Questo problema è indicato in un'altra domanda StackTranslate.it, Liberare memoria allocata in una DLL diversa .

Che tipo di funzioni di allocazione stai usando? Recentemente ho riscontrato un errore simile utilizzando le funzioni di allocazione dello stile Heap *.

Si è scoperto che stavo creando erroneamente l'heap con l'opzione HEAP_NO_SERIALIZE . Questo essenzialmente fa funzionare le funzioni Heap senza sicurezza del thread. È un miglioramento delle prestazioni se usato correttamente ma non dovrebbe mai essere usato se si utilizza HeapAlloc in un programma multi-thread [1]. Lo dico solo perché il tuo post menziona che hai un'app multi-thread. Se stai usando HEAP_NO_SERIALIZE ovunque, eliminalo e probabilmente risolverà il tuo problema.

[1] Ci sono alcune situazioni in cui ciò è legale, ma richiede di serializzare le chiamate a Heap * e in genere non è il caso dei programmi multi-thread.

Se questi errori si verificano casualmente, è molto probabile che si siano verificate gare di dati. Per favore, controlla: modifichi i puntatori di memoria condivisa da thread diversi? Intel Thread Checker può aiutare a rilevare tali problemi nel programma multithread.

Oltre a cercare strumenti, considera la ricerca di un probabile colpevole. C'è qualche componente che stai utilizzando, forse non scritto da te, che potrebbe non essere stato progettato e testato per funzionare in un ambiente multithread? O semplicemente uno che non conosci è stato eseguito in tale ambiente.

L'ultima volta che mi è successo, era un pacchetto nativo che era stato usato con successo da lavori batch per anni. Ma era la prima volta in questa azienda che era stato utilizzato da un servizio Web .NET (che è multithread). Ecco fatto: avevano mentito sul fatto che il codice fosse thread-safe.

Puoi utilizzare le macro Heap-Check di VC CRT per _CrtSetDbgFlag : _CRTDBG_CHECK_ALWAYS_DF o _CRTDBG_CHECK_EVERY_16_DF .. _CRTDBG_CHECK_EVERY_1024>

Vorrei aggiungere la mia esperienza. Negli ultimi giorni, ho risolto un'istanza di questo errore nella mia applicazione. Nel mio caso particolare, gli errori nel codice erano:

  • Rimozione di elementi da una raccolta STL durante l'iterazione su di essa (credo che ci siano flag di debug in Visual Studio per rilevare queste cose; l'ho rilevato durante la revisione del codice)
  • Questo è più complesso, lo dividerò per passaggi:
    • Da un thread C ++ nativo, richiamare nel codice gestito
    • Nella terra gestita, chiamare Control.Invoke e disporre un oggetto gestito che avvolge l'oggetto nativo a cui appartiene il callback.
    • Poiché l'oggetto è ancora attivo all'interno del thread nativo (rimarrà bloccato nella chiamata di richiamata fino alla fine di Control.Invoke ). Dovrei chiarire che uso boost :: thread , quindi uso una funzione membro come funzione thread.
    • Soluzione : usa Control.BeginInvoke (la mia GUI è realizzata con Winforms) invece in modo che il thread nativo possa terminare prima che l'oggetto venga distrutto (lo scopo del callback è precisamente notificando che il thread è terminato e l'oggetto può essere distrutto).

Ho avuto un problema simile - ed è apparso in modo abbastanza casuale. Forse qualcosa era corrotto nei file di build, ma ho finito per risolverlo pulendo prima il progetto e poi la ricostruzione.

Quindi, oltre alle altre risposte fornite:

Che tipo di cose possono causare questi errori? Qualcosa di corrotto nel file di build.

Come eseguo il debug? Pulizia del progetto e ricostruzione. Se è stato risolto, questo era probabilmente il problema.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top