it-swarm.com.de

Aus welchen Gründen wird ein Release-Build anders ausgeführt als ein Debug-Build

Ich habe ein Visual Studio 2005 C++ - Programm, das im Freigabemodus anders ausgeführt wird als im Debugmodus. Im Freigabemodus tritt ein (scheinbarer) intermittierender Absturz auf. Im Debug-Modus stürzt es nicht ab. Aus welchen Gründen funktioniert ein Release-Build anders als ein Debug-Build?

Es ist auch erwähnenswert, dass mein Programm ziemlich komplex ist und mehrere Bibliotheken von Drittanbietern für die XML-Verarbeitung, Nachrichtenvermittlung usw. verwendet.

Danke im Voraus!

55
BeachRunnerFred

Überleben der Release-Version gibt einen guten Überblick.

Dinge, auf die ich gestoßen bin - die meisten sind bereits erwähnt

Variable Initialisierung bei weitem am häufigsten. In Visual Studio initialisieren Debugbuilds den zugewiesenen Speicher explizit auf bestimmte Werte, siehe z. Speicherwerte hier. Diese Werte sind normalerweise leicht zu erkennen und verursachen einen Fehler außerhalb der Grenzen, wenn sie als Index verwendet werden, oder eine Zugriffsverletzung, wenn sie als Zeiger verwendet werden. Ein nicht initialisierter Boolescher Wert ist jedoch wahr und kann dazu führen, dass nicht initialisierte Speicherfehler jahrelang unentdeckt bleiben.

In Release-Builds, in denen der Speicher nicht explizit initialisiert wurde, werden nur die Inhalte beibehalten, die zuvor vorhanden waren. Dies führt zu "lustigen Werten" und "zufälligen" Abstürzen, aber ebenso oft zu deterministischen Abstürzen, bei denen ein scheinbar nicht verwandter Befehl ausgeführt werden muss, bevor der Befehl tatsächlich abstürzt. Dies wird dadurch verursacht, dass der erste Befehl den Speicherort mit bestimmten Werten "einrichtet". Wenn die Speicherorte wiederverwendet werden, werden sie vom zweiten Befehl als Initialisierungen angesehen. Das kommt bei nicht initialisierten Stack-Variablen häufiger vor als bei Heap, aber letzteres ist mir auch passiert.

Die Initialisierung des Raw-Speichers kann in einem Release-Build auch unterschiedlich sein, unabhängig davon, ob Sie in Visual Studio (Debugger angehängt) oder in Explorer starten. Das macht die "nettesten" Release-Build-Fehler, die unter dem Debugger niemals auftreten.

Gültige Optimierungen stehen in meiner Erfahrung an zweiter Stelle. Der C++ - Standard ermöglicht viele Optimierungen, die überraschend sein können, aber vollständig gültig sind, z. Wenn zwei Zeiger denselben Speicherort als Alias ​​haben, wird die Reihenfolge der Initialisierung nicht berücksichtigt, oder mehrere Threads ändern dieselben Speicherorte, und Sie erwarten eine bestimmte Reihenfolge, in der Thread B die von Thread A vorgenommenen Änderungen sieht. Oft wird der Compiler dafür verantwortlich gemacht diese. Nicht so schnell, junger Yedi! - siehe unten

Timing Release-Builds laufen aus verschiedenen Gründen nicht nur "schneller" (Optimierungen, Protokollierungsfunktionen, die einen Thread-Synchronisationspunkt bereitstellen, Debug-Code wie Asserts) nicht ausgeführt usw.) auch das relative Timing zwischen Operationen ändert sich dramatisch. Das häufigste Problem, das dadurch aufgedeckt wird, sind die Rennbedingungen, aber auch Deadlocks und die einfache Ausführung von Nachrichten-/Timer-/ereignisbasiertem Code in einer anderen Reihenfolge. Obwohl es sich um Timing-Probleme handelt, können sie über Builds und Plattformen hinweg überraschend stabil sein, wobei Reproduktionen "immer funktionieren, außer auf PC 23".

Guard Bytes . In Debugbuilds werden häufig (mehr) Guard-Bytes um ausgewählte Instanzen und Zuordnungen platziert, um vor Indexüberläufen und manchmal auch Unterläufen zu schützen. In den seltenen Fällen, in denen der Code auf Offsets oder Größen beruht, z. Beim Serialisieren von Rohstrukturen sind sie unterschiedlich.

Andere Codeunterschiede Einige Anweisungen - z. B. Asserts - werden in Release-Builds zu nichts ausgewertet. Manchmal haben sie unterschiedliche Nebenwirkungen. Dies ist wie beim Klassiker bei Makrotricks häufig der Fall (Warnung: mehrere Fehler).

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

if (foo)
  Log(x)
if (bar)
  Run();

Was in einem Release-Build zu if (foo && bar) ausgewertet wird. Diese Art von Fehler ist bei normalem C/C++ - Code und korrekt geschriebenen Makros sehr selten.

Compiler Bugs Das passiert wirklich nie. Nun ja, aber für den größten Teil Ihrer Karriere ist es besser, wenn Sie davon ausgehen, dass dies nicht der Fall ist. In einem Jahrzehnt der Arbeit mit VC6 habe ich einen gefunden, bei dem ich immer noch davon überzeugt bin, dass es sich um einen nicht behobenen Compiler-Fehler handelt, verglichen mit Dutzenden von Mustern (vielleicht sogar Hunderten von Instanzen) mit unzureichendem Verständnis der Schrift (a.k.a. der Standard).

122
peterchen

In der Debug-Version sind oft Assertions und/oder Debug-Symbole aktiviert. Dies kann zu einem unterschiedlichen Speicherlayout führen. Im Falle eines fehlerhaften Zeigers, eines Überlaufs eines Arrays oder eines ähnlichen Speicherzugriffs greifen Sie in einem Fall auf kritischen fehlerhaften Speicher (z. B. Funktionszeiger) und in anderen Fällen möglicherweise nur auf einen nicht kritischen Speicher zu (z. B. wird nur eine Dokumentzeichenfolge verworfen).

6
flolo

Variablen, die nicht explizit initialisiert werden, werden im Release-Build auf Null gesetzt.

5
Burkhard

Release Build (hoffentlich) würde schneller als Ihr Debug Build ausgeführt. Wenn Sie mehr als einen Thread verwenden, sehen Sie möglicherweise mehr Interleaving oder einfach einen Thread, der schneller als der andere läuft, was Sie möglicherweise beim Debugbuild nicht bemerkt haben.

2
Eugene Yokota

Release-Builds werden normalerweise mit aktivierter Optimierung im Compiler kompiliert, während dies bei Debug-Builds normalerweise nicht der Fall ist.

In einigen Sprachen oder bei Verwendung vieler verschiedener Bibliotheken kann dies zu zeitweiligen Abstürzen führen, insbesondere wenn der gewählte Optimierungsgrad sehr hoch ist.

Ich weiß, dass dies beim C++ - Compiler gcc der Fall ist, aber ich bin mir nicht sicher, ob es sich um einen Compiler von Microsoft handelt.

2
fluffels

Ich hatte ein ähnliches Problem vor nicht allzu langer Zeit , das dadurch verursacht wurde, dass der Stapel in Release-Builds unterschiedlich behandelt wurde. Andere Dinge, die sich unterscheiden können:

  • Die Speicherzuordnung wird bei Debugbuilds im VS-Compiler unterschiedlich behandelt (z. B. Schreiben von 0xcc über gelöschten Speicher usw.).
  • Loop-Abwicklung und andere Compiler-Optimierungen
  • Alignment von Zeigern
2
Nik Reiman

Dies hängt sowohl vom Hersteller des Compilers als auch von den Bibliotheken ab, die Sie mit den DEBUG-Flags kompilieren. Während DEBUG-Code niemals den laufenden Code beeinflussen sollte (sollte keine Nebenwirkungen haben), ist dies manchmal der Fall.

Insbesondere können Variablen nur im DEBUG-Modus initialisiert und im RELEASE-Modus nicht initialisiert werden. Die STL in Visual Studio-Compilern unterscheiden sich in den Modi DEBUG und RELEASE. Die Idee ist, dass Iteratoren in DEBUG vollständig auf mögliche Fehler geprüft werden (bei Verwendung ungültiger Iteratoren wird beispielsweise ein Iterator in einen Vektor ungültig gemacht, wenn eine Einfügung erfolgt, nachdem der Iterator abgerufen wurde).

Das Gleiche passiert bei Bibliotheken von Drittanbietern. Die erste, die ich mir vorstellen kann, ist QT4. Sie beendet Ihr Programm mit einer Assertion, wenn ein anderer Thread als der, der das grafische Objekt erstellt hat, Maloperationen ausführt.

Bei allen Änderungen unterscheiden sich Code und Speicherbedarf in beiden Modi. Ein Zeigerproblem (das Lesen der Position ein Ende des Arrays durchläuft) wird möglicherweise nicht erkannt, wenn diese Position lesbar ist.

Assertions sollen die Anwendung während DEBUG beenden und aus den RELEASE-Builds verschwinden, daher würde ich Assertions nicht als Ihr Problem betrachten. Ein Schurkenzeiger oder der Zugriff auf einen über das Ende hinausgehenden würde mein erster Verdächtiger sein.

Vor einiger Zeit gab es Probleme mit einigen Compiler-Optimierungen, die Code brechen, aber ich habe in letzter Zeit keine Beschwerden gelesen. Dort könnte es ein Optimierungsproblem geben, aber das wäre nicht mein erster Verdächtiger.

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

Aufgrund der Tatsache, dass in Debug-Builds Guard-Bytes hinzugefügt werden, können Sie möglicherweise "sicher" auf Speicher zugreifen, der für ein Array (insbesondere dynamische Arrays) außerhalb des zulässigen Bereichs liegt. Dies führt jedoch zu einer Zugriffsverletzung im Release-Build . Dieser Fehler kann unbemerkt bleiben und einen beschädigten Heap und möglicherweise eine Zugriffsverletzung an einem Ort verursachen, der nicht mit dem ursprünglichen Fehler zusammenhängt.

Verwenden Sie PageHeap (oder, falls Sie Debugging-Tools installiert haben, könnten Sie gflags verwenden), um Fehler im Zusammenhang mit beschädigten Heaps zu entdecken.

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

1
DCX2

Nach meiner Erfahrung scheint der häufigste Grund zu sein, dass Konfigurationen sich in mehr als den Release-/Build-Einstellungen unterscheiden. Z.B. Andere Bibliotheken sind enthalten, oder der Debug-Build hat eine andere Stackgröße als der Release-Build.

Um dies in unseren Visual Studio 2005-Projekten zu vermeiden, verwenden wir häufig Eigenschaftsblätter. Auf diese Weise können die Release- und Debug-Konfigurationen gemeinsame Einstellungen verwenden.

0
Tobias Furuholm

Unter den Optimierungen, die im Release-Modus (und nicht im Debug-Modus) durchgeführt werden können, kann die Kopierentfernung zu unterschiedlichen Ergebnissen führen. Insbesondere RVO (Rückgabewertoptimierung), je nachdem, wie Ihre Konstruktoren entworfen wurden.

Was sind Kopieroptimierung und Rückgabewertoptimierung?

0
brahmin

Dieser Beitrag zusammen mit den bereitgestellten Links ist sehr hilfreich bei der Behebung des zugehörigen Fehlers. Neben der obigen Liste können auch Unterschiede in den Aufrufkonventionen zu diesem Verhalten führen. Es ist beim Release-Build nur mit Optimierung für mich fehlgeschlagen. [seltsamerweise wird diese Warnung auch in Warnstufe 4 MSVC nicht ausgewählt?] 

0
FL4SOF