2010-06-22 17 views
11

Ho visto post parlare di cosa potrebbe fare causa le differenze di tra le versioni di debug e release, ma non penso che nessuno abbia affrontato da un punto di vista dello sviluppo quale sia il modo più efficiente per risolvere il problema.Migliori pratiche e strumenti per il debug delle differenze tra le build di debug e release?

La prima cosa che faccio quando compare un bug nella versione Release ma non in Debug è che eseguo il mio programma attraverso valgrind nella speranza di una migliore analisi. Se questo non rivela nulla, e questo mi è successo prima, provo vari input nella speranza di far emergere il bug anche nella build di Debug. Se fallisce, allora proverei a tenere traccia delle modifiche per trovare la versione più recente per cui le due build divergono nel comportamento. E alla fine credo che ricorrere alle dichiarazioni di stampa.

Esistono migliori pratiche di ingegneria del software per il debug in modo efficiente quando le versioni di Debug e Release differiscono? Inoltre, quali strumenti ci sono che operano a un livello più fondamentale di valgrind per aiutare a eseguire il debug di questi casi?

EDIT: Ho notato molte risposte che suggeriscono alcune buone pratiche generali come test di unità e test di regressione, che sono d'accordo per trovare bug. Tuttavia, c'è qualcosa di specifico su misura per questo problema di Release vs. Debug? Ad esempio, esiste una cosa come uno strumento di analisi statico che dice "Ehi, questa macro o questo codice o questa pratica di programmazione è pericolosa perché ha il potenziale per causare differenze tra i build di Debug/Release?"

+1

Con VC, è possibile creare build di rilascio con informazioni di debug ed eseguire il debug di questi. Non c'è alcun vero divertimento nel passare attraverso il codice ottimizzato, ma hai una buona probabilità di trovare bug in questo modo. – sbi

+0

Esatto, dimentico che è possibile compilare le informazioni di debug anche nella versione di rilascio con gcc. – Eric

risposta

3

L'esistenza stessa di due configurazioni è un problema dal punto di vista del debug. Una corretta progettazione sarebbe tale che il sistema a terra e in aria si comportasse nello stesso modo, e ottenere ciò riducendo il numero di modi in cui il sistema può dire la differenza.

debug e build di rilascio si differenziano in 3 aspetti:

  • _DEBUG definiscono
  • ottimizzazioni
  • diversa versione della libreria standard

Il modo migliore in giro, il mio modo di lavoro spesso , è questo:

  • Disabilitare le ottimizzazioni laddove le prestazioni non sono critiche. Il debug è più importante. La cosa più importante è disabilitare la funzione di auto-allineamento, mantenere il frame stack standard e le ottimizzazioni del riutilizzo variabile. Questi infastidiscono di più il debug.
  • Codice monitor per dipendenza da DEBUG define. Non utilizzare mai asserimenti di debug-only o altri strumenti sensibili alla definizione di DEBUG.
  • Per impostazione predefinita, compilare e lavorare/rilasciare.
+1

Penso che questo colpisca il cuore del problema. Quando ci pensi, tecniche come test di unità e test di regressione cercano di localizzare i bug bisecando il tempo e lo spazio che sono trascorsi tra una versione funzionante e una versione non funzionante. – Eric

+0

Tuttavia, il debug della differenza tra una versione di debug e di rilascio dovrebbe avvenire in realtà dividendo le differenze tra una compilazione di debug e release perché non sono necessari test di unità se si desidera che il bug venga visualizzato nel debugger. Come hai detto, questi includono ottimizzazioni e differenze nella dipendenza. – Eric

0

Quando il debug e il rilascio differiscono significa:

  1. si codice dipende dalla _DEBUG o macro simili (definito durante la compilazione di una versione di debug - nessuna ottimizzazione)
  2. il compilatore ha un bug di ottimizzazione (ho visto questo poche volte)

Si può facilmente gestire (1) (modifica del codice) ma con (2) si dovrà isolare il bug del compilatore. Dopo aver isolato il bug, si fa un po 'di "riscrittura del codice" per far sì che il compilatore generi codice binario corretto (l'ho fatto alcune volte - la parte più difficile è isolare il bug).

Posso dire che quando si abilitano le informazioni di debug per la versione di rilascio il processo di debug funziona ... (anche se a causa delle ottimizzazioni si potrebbero vedere alcuni salti "strani" durante l'esecuzione).

È necessario disporre di alcuni test "black-box" per l'applicazione - valgrind è una soluzione in questo caso. Queste soluzioni aiutano a trovare le differenze tra release e debug (che è molto importante).

+1

Da quello che ho visto la maggior parte dei bug che emergono con le ottimizzazioni attivate non sono bug nell'ottimizzatore, ma nel codice che viene compilato. Le ottimizzazioni li fanno solo apparire. Cose come la mancanza di 'volatile' e' restrict' possono causare questo tipo di problema. I compilatori moderni e rispettabili hanno dei bug, ma anche molti test automatizzati. – nategoose

1

La causa più evidente è semplicemente l'uso di #ifdef e #ifndef direttive associati DEBUG o simbolo simile che cambiano tra i due generazioni.

Prima di scendere la strada di debug (che non è la mia idea personale di divertimento), vorrei controllare entrambe le righe di comando e controllare quali flag sono passati in una modalità e non l'altra, quindi grep il mio codice per questo flag e controllare i loro usi.

Un problema particolare, che viene in mente sono le macro:

#ifdef _DEBUG_ 
    #define CHECK(CheckSymbol) { if (!(CheckSymbol)) throw CheckException(); } 
#else 
    #define CHECK(CheckSymbol) 
#endif 

noto anche come un soft-assert.

Bene, se lo chiami con una funzione che ha un effetto collaterale, o si affida ad esso per proteggere una funzione (applicazione del contratto) e in qualche modo cattura l'eccezione, esso lancia nel debug e lo ignora ... vedrai differenze in release :)

3

Quando mi imbatto in un errore che si verifica solo nel rilascio, la prima cosa che cerco sempre è l'uso di una variabile stack non inizializzata nel codice su cui sto lavorando. Su Windows, il runtime di debug C inizializzerà automaticamente le variabili dello stack su un pattern di bit noto, 0xcdcdcdcd o qualcosa del genere. Nel rilascio, le variabili di stack conterranno il valore che è stato archiviato per l'ultima volta in quella posizione di memoria, che sarà un valore imprevisto.

In secondo luogo, proverò a identificare ciò che è diverso tra le versioni di debug e release. Osservo le impostazioni di ottimizzazione del compilatore che il compilatore è passato nelle configurazioni Debug e Release. Puoi vedere che questa è l'ultima pagina delle proprietà delle impostazioni del compilatore in Visual Studio. Inizierò con la configurazione di rilascio e cambierò gli argomenti della riga di comando passati al compilatore un elemento alla volta finché non corrispondono alla riga di comando che viene utilizzata per la compilazione in debug. Dopo ogni modifica eseguo il programma e riproduco il bug. Questo spesso mi porterà alla particolare impostazione che causa l'errore.

Una terza tecnica può essere quella di assumere una funzione che non funziona correttamente e disabilitare le ottimizzazioni intorno utilizzando il pre-processore. Questo ti permetterà di eseguire il programma in rilascio con la particolare funzione compilata in debug. Il comportamento del programma che è stato costruito in questo modo ti aiuterà a saperne di più sul bug.

#pragma optimize("", off) 
void foo() { 
    return 1; 
} 
#pragma optimize("", on) 

Per esperienza, i problemi sono di solito pila inizializzazione, memoria fregare nella allocatore di memoria, o strane #define direttive causando il codice da compilato in modo errato.

+2

Un altro problema comune a cui mi sono imbattuto è il codice che ha un effetto collaterale in una macro assert(). La macro viene compilata nel rilascio e l'effetto collaterale non si verifica mai. –

4

Un'altra "migliore pratica", o meglio una combinazione di due: avere test unitari automatizzati e dividere e conquistare.

Se si dispone di un'applicazione modulare e ogni modulo ha buoni test di unità, è possibile isolare rapidamente il pezzo errante.

+0

+1 L'unit test lo ha effettivamente fatto in due modi: (1) Non si deve fare molto (debug mode). (2) Il debug del codice di test dell'unità è spesso una scorciatoia all'origine dell'errore. –

0

La soluzione migliore consiste nell'impostare qualcosa come test di unità automatizzato per testare tutti gli aspetti dell'applicazione (non solo singoli componenti, ma veri test del mondo che utilizzano l'applicazione nello stesso modo di un normale utente con tutte le dipendenze). Questo ti permette di sapere immediatamente quando è stato introdotto un bug di sola uscita che dovrebbe darti una buona idea di dove si trova il problema.

Le buone pratiche per monitorare e ricercare attivamente i problemi superano qualsiasi strumento per aiutarti a risolverli a lungo dopo che si sono verificati.

Tuttavia, quando si ha uno di quei casi in cui è troppo tardi: troppe build sono passate, non possono essere riprodotte in modo coerente, ecc. Quindi non conosco nessuno strumento per il lavoro. A volte giocherellare con le impostazioni di rilascio può dare un po 'di informazioni sul motivo per cui il bug si sta verificando: se riesci ad eliminare le ottimizzazioni che improvvisamente fanno scomparire il bug, potresti darti qualche informazione utile su di esso.

Release-solo i bug possono rientrano in varie categorie, ma i più comuni (a parte qualcosa di simile a un uso improprio di asserzioni) è:

1) memoria non inizializzata. Uso questo termine sulle variabili non inizializzate in quanto una variabile può essere inizializzata ma punta ancora alla memoria che non è stata inizializzata correttamente. Per questo, strumenti di diagnostica della memoria come Valgrind possono aiutare.

2) Temporizzazione (es: condizioni di gara). Questi possono essere un incubo per il debug, ma ci sono alcuni profiler multithreading e strumenti diagnostici che possono aiutare. Non posso suggerire nessuno, ma c'è Coverity Integrity Manager come esempio.

Problemi correlati