Quali sono alcuni motivi per cui una build di rilascio potrebbe essere eseguita in modo diverso rispetto a una build di debug

Ho un programma Visual Studio 2005 C ++ che viene eseguito in modo diverso rispetto alla modalità Debug. Nella modalità di rilascio, si verifica un arresto anomalo (apparente) intermittente. In modalità di debug, non si blocca. Quali sono alcuni motivi per cui una build Release funzionerebbe diversamente da una build Debug?

Vale anche la pena ricordare che il mio programma è abbastanza complesso e utilizza diverse librerie di terze parti per l’elaborazione XML, il brokeraggio di messaggi, ecc …

Grazie in anticipo!

Sopravvivere alla versione di rilascio offre una buona panoramica.

Le cose che ho incontrato – la maggior parte sono già menzionate

Inizializzazione variabile di gran lunga la più comune. In Visual Studio, le build di debug inizializzano in modo esplicito la memoria allocata ai valori dati, vedere ad esempio i valori di memoria qui. Questi valori sono in genere facili da individuare, causano un errore di fuori limite quando utilizzati come indice o una violazione di accesso quando utilizzati come puntatore. Un booleano non inizializzato è vero, tuttavia, e può causare bug di memoria non inizializzati che non vengono rilevati per anni.

Nelle versioni di Release in cui la memoria non è inizializzata in modo esplicito, mantiene solo i contenuti che aveva in precedenza. Questo porta a “crash divertenti” e “casuali”, ma spesso a crash deterministici che richiedono un comando apparentemente non correlato per essere eseguito prima del comando che si blocca effettivamente. Ciò è causato dal primo comando che “imposta” la posizione di memoria con valori specifici e, quando le posizioni di memoria vengono riciclate, il secondo comando le vede come inizializzazioni. Questo è più comune con le variabili di stack non inizializzate rispetto all’heap, ma anche quest’ultimo è successo a me.

L’inizializzazione della memoria grezza può anche essere diversa in un rilascio di build, sia che si parta da Visual Studio (debugger allegato) che a partire da explorer. Questo rende il tipo “più bello” di bug di build di generazione che non appare mai sotto il debugger.

Le ottimizzazioni valide arrivano seconde nella mia exeprience. Lo standard C ++ consente di eseguire molte ottimizzazioni che possono essere sorprendenti, ma sono interamente valide, ad esempio quando due puntatori alias la stessa posizione di memoria, l’ordine di inizializzazione non è considerato, o più thread modificano le stesse posizioni di memoria e ci si aspetta un certo ordine in quale thread B vede le modifiche apportate dal thread A. Spesso, il compilatore viene incolpato per questi. Non così veloce, giovane yedi! – vedi sotto

Le build di Timing Release non solo “eseguono più velocemente”, per una serie di motivi (ottimizzazioni, funzioni di registrazione che forniscono un punto di sincronizzazione del thread, codice di debug come asserzioni non eseguite ecc.) Anche il tempo relativo tra le operazioni cambia drasticamente. Il problema più comune scoperto da questo è le condizioni di gara, ma anche deadlock e la semplice esecuzione di “diversi ordini” di messaggi / timer / codice basato sugli eventi. Anche se sono problemi di temporizzazione, possono essere sorprendentemente stabili su build e piattaforms, con riproduzioni che “funzionano sempre, tranne che sul PC 23”.

Byte di guardia . I build di debug spesso mettono (di più) i byte di guardia attorno alle istanze e alle allocazioni selezionate, per proteggerli dagli overflow dell’indice e talvolta dai underflow. Nei rari casi in cui il codice si basa su offset o dimensioni, ad esempio la serializzazione di strutture raw, sono diversi.

Altre differenze di codice Alcune istruzioni, ad esempio asserzioni, non valutano nulla nelle versioni di rilascio. A volte hanno effetti collaterali diversi. Questo è prevalente con macro trucco, come nel classico (attenzione: errori multipli)

#ifdef DEBUG #define Log(x) cout << #x << x << "\n"; #else #define Log(x) #endif if (foo) Log(x) if (bar) Run(); 

Che, in una build di rilascio, valuta if (foo && bar) Questo tipo di errore è molto raro con il normale codice C / C ++ e le macro che sono state scritte correttamente.

Bug del compilatore Questo in realtà non succede mai. Bene - lo fa, ma tu sei per la maggior parte della tua carriera meglio assumendo che non lo faccia. In un decennio di lavoro con VC6, ne ho trovato uno in cui sono ancora convinto che si tratti di un bug del compilatore non risolto, rispetto a dozzine di pattern (forse anche centinaia di istanze) con una comprensione insufficiente delle Scritture (ovvero lo standard).

Nella versione di debug vengono spesso triggerste le asserzioni e / oi simboli di debug. Questo può portare a un diverso layout di memoria. In caso di un puntatore non valido, l’overflow di un array o di un accesso alla memoria simile accede in una memoria ctriggers di un caso (ad es. Il puntatore di funzione) e in altri casi solo una memoria non critica (ad es. Solo una stringa doc viene spostata nel cestino)

Le variabili che non sono inizializzate in modo esplicito saranno o non saranno azzerate in Release build.

La build di rilascio (si spera) potrebbe essere eseguita più velocemente della build di debug. Se si utilizza più di un thread, è ansible visualizzare più interfoliazioni o semplicemente un thread più veloce rispetto agli altri, che potrebbe non essere stato notato nella creazione di debug.

Ho avuto un problema simile non molto tempo fa , che è stato causato dallo stack trattato in modo diverso nelle versioni di rilascio. Altre cose che possono essere diverse:

  • L’allocazione della memoria viene gestita in modo diverso con le compilazioni di debug nel compilatore VS (ad esempio, scrivendo 0xcc su memoria cancellata, ecc.)
  • Loop di svolgimento e altre ottimizzazioni del compilatore
  • Accensione dei puntatori

Dipende sia dal venditore del compilatore che dalle librerie compilate con i flag DEBUG. Mentre il codice DEBUG non dovrebbe mai influenzare il codice in esecuzione (non dovrebbe avere effetti collaterali) a volte lo fa.

In particolare, le variabili possono essere inizializzate solo in modalità DEBUG e lasciate non inizializzate in modalità RELEASE. I compilatori STL in Visual Studio sono diversi nelle modalità DEBUG e RELEASE. L’idea è che gli iteratori siano completamente controllati in DEBUG per rilevare possibili errori (utilizzando iteratori invalidati, ad esempio un iteratore in un vettore viene invalidato se si verifica un inserimento dopo che l’iteratore è stato recuperato).

Lo stesso accade con le librerie di terze parti, il primo che posso pensare è QT4, che terminerà il tuo programma con un assert se un thread diverso da quello che ha creato l’object grafico esegue operazioni di pittura.

Con tutte le modifiche, il codice e l’impronta della memoria saranno diversi in entrambe le modalità. Un puntatore (leggendo la posizione che si passa alla fine di un array) può passare inosservato se questa posizione è leggibile.

Le asserzioni hanno lo scopo di uccidere l’applicazione durante il DEBUG e spariscono dai build di RELEASE, quindi non penserei che le asserzioni siano il tuo problema. Un puntatore canaglia o l’accesso a uno oltre la fine sarebbero i miei primi sospetti.

Qualche tempo fa c’erano problemi con il codice di errore di alcuni compilatori, ma ultimamente non ho letto reclami. Potrebbe esserci un problema di ottimizzazione lì, ma questo non sarebbe il mio primo sospetto.

Generalmente le build di rilascio vengono compilate con l’ottimizzazione abilitata nel compilatore, mentre le build di debug di solito non lo sono.

In alcune lingue o quando si usano molte librerie diverse questo può causare arresti intermittenti, specialmente quando il livello di ottimizzazione scelto è molto alto.

So che questo è il caso del compilatore gcc C ++, ma non sono sicuro del compilatore di Microsoft.

http://www.debuginfo.com/tips/userbpntdll.html

A causa del fatto che i byte di guardia vengono aggiunti nei build di debug, è ansible accedere in modo sicuro alla memoria che è fuori limite per un array (in particolare gli array dinamici), ma ciò causerà una violazione di accesso nella build di rilascio . Questo errore potrebbe passare inosservato, causando un heap danneggiato e probabilmente una violazione di accesso in un luogo non correlato all’errore originale.

Usa PageHeap (o, se hai installato gli strumenti di debug, potresti usare gflags) per scoprire bug relativi a heap corrotti.

http://support.microsoft.com/?id=286470

Nella mia esperienza, la ragione più comune sembra essere che le configurazioni differiscono in più modi rispetto alle impostazioni di rilascio / generazione. Ad esempio, sono incluse librerie diverse o la build di debug ha una dimensione di stack diversa rispetto alla build di rilascio.

Per evitare ciò nei nostri progetti di Visual Studio 2005 utilizziamo estesamente i fogli di proprietà. In questo modo le configurazioni di rilascio e di debug possono condividere le impostazioni comuni.

Questo post insieme ai link forniti è molto utile per correggere bug correlati. aggiungendo alla lista sopra, la differenza nelle convenzioni di chiamata può anche portare a questo comportamento – Ha fallito nel rilascio di build solo con l’ottimizzazione per me. ho dichiarato come __stdcall e definito come __cdecl (di default). [stranamente questo avviso non viene selezionato anche nel livello di avviso 4 MSVC?]