Ich habe hier ein Problem vom Typ "Schroedinger's Cat" - mein Programm (eigentlich die Testsuite für mein Programm, aber dennoch ein Programm) stürzt ab, aber nur, wenn es im Release-Modus erstellt wurde und nur, wenn es über die Befehlszeile gestartet wird . Durch das Debuggen von Höhlenmenschen (dh böse printf () - Nachrichten überall) habe ich die Testmethode ermittelt, bei der der Code abstürzt, obwohl der tatsächliche Absturz leider in einem Destruktor zu passieren scheint, da sich die letzten Trace-Nachrichten befinden, die ich sehe andere Destruktoren, die sauber ausgeführt werden.
Wenn ich versuche, dieses Programm in Visual Studio auszuführen, stürzt es nicht ab. Gleiches gilt beim Starten von WinDbg.exe. Der Absturz tritt nur beim Starten über die Befehlszeile auf. Dies geschieht übrigens unter Windows Vista, und leider habe ich momentan keinen Zugriff auf einen XP-Computer, auf dem ich testen kann.
Es wäre wirklich schön, wenn ich Windows dazu bringen könnte, einen Stack-Trace auszudrucken, oder etwas anderes, als das Programm einfach so zu beenden, als ob es sauber beendet worden wäre. Hat jemand einen Rat, wie ich hier aussagekräftigere Informationen erhalten und hoffentlich diesen Fehler beheben kann?
Bearbeiten: Das Problem wurde in der Tat durch ein außerhalb der Grenzen liegendes Array verursacht, das ich in diesem Beitrag ausführlicher beschreibe . Vielen Dank an alle für Ihre Hilfe bei der Suche nach diesem Problem!
Antworten:
In 100% der Fälle, von denen ich gesehen oder gehört habe, dass ein C- oder C ++ - Programm im Debugger einwandfrei ausgeführt wird, bei Ausführung im Freien jedoch fehlschlägt, wurde die Ursache über das Ende eines lokalen Funktionsarrays hinaus geschrieben. (Der Debugger legt mehr auf den Stapel, sodass Sie weniger wahrscheinlich etwas Wichtiges überschreiben.)
quelle
Wenn ich zuvor auf solche Probleme gestoßen bin, war dies im Allgemeinen auf die Initialisierung von Variablen zurückzuführen. Im Debug-Modus werden Variablen und Zeiger automatisch auf Null initialisiert, im Release-Modus jedoch nicht. Daher, wenn Sie Code wie diesen haben
Im Debug-Modus enthält der Code im if nicht ausgeführt, aber im Release-Modus p enthält einen undefinierten Wert, der wahrscheinlich nicht 0 ist, sodass der Code häufig ausgeführt wird und einen Absturz verursacht.
Ich würde Ihren Code auf nicht initialisierte Variablen überprüfen. Dies kann auch für den Inhalt von Arrays gelten.
quelle
Bisher hat keine Antwort versucht, einen ernsthaften Überblick über die verfügbaren Techniken zum Debuggen von Release-Anwendungen zu geben:
Release- und Debug-Builds verhalten sich aus vielen Gründen unterschiedlich. Hier ist eine hervorragende Übersicht. Jeder dieser Unterschiede kann einen Fehler im Release-Build verursachen, der im Debug-Build nicht vorhanden ist.
Das Vorhandensein eines Debuggers kann auch das Verhalten eines Programms ändern , sowohl für Release- als auch für Debug-Builds. Siehe diese Antwort. Kurz gesagt, zumindest der Visual Studio-Debugger verwendet den Debug-Heap automatisch, wenn er an ein Programm angehängt wird. Sie können den Debug-Heap mithilfe der Umgebungsvariablen _NO_DEBUG_HEAP deaktivieren. Sie können dies entweder in Ihren Computereigenschaften oder in den Projekteinstellungen in Visual Studio angeben. Dies könnte den Absturz mit dem angeschlossenen Debugger reproduzierbar machen.
Mehr zum Debuggen von Heap-Korruption hier.
Wenn die vorherige Lösung nicht funktioniert, müssen Sie die nicht behandelte Ausnahme abfangen und einen Post-Mortem-Debugger anhängen , wenn der Absturz auftritt. Sie können hierfür zB WinDbg, Details zu den verfügbaren Post-Mortem-Debuggern und deren Installation bei MSDN verwenden
Sie können Ihren Code für die Ausnahmebehandlung verbessern. Wenn es sich um eine Produktionsanwendung handelt, sollten Sie:
ein. Installieren Sie einen benutzerdefinierten Terminierungshandler mit
std::set_terminate
Wenn Sie dieses Problem lokal debuggen möchten, können Sie eine Endlosschleife im Terminierungshandler ausführen und Text an die Konsole ausgeben, um Sie
std::terminate
über den Aufruf zu informieren . Schließen Sie dann den Debugger an und überprüfen Sie den Aufrufstapel. Oder Sie drucken den Stack-Trace wie in dieser Antwort beschrieben.In einer Produktionsanwendung möchten Sie möglicherweise einen Fehlerbericht nach Hause senden, idealerweise zusammen mit einem kleinen Speicherauszug, mit dem Sie das hier beschriebene Problem analysieren können.
b. Verwenden Sie den strukturierten Ausnahmebehandlungsmechanismus von Microsoft , mit dem Sie sowohl Hardware- als auch Software-Ausnahmen abfangen können. Siehe MSDN . Sie können Teile Ihres Codes mit SEH schützen und den gleichen Ansatz wie in a) verwenden, um das Problem zu debuggen. SEH enthält weitere Informationen zu der aufgetretenen Ausnahme, die Sie beim Senden eines Fehlerberichts über eine Produktions-App verwenden können.
quelle
Dinge, auf die Sie achten sollten:
Array-Überläufe - Der Visual Studio-Debugger fügt Polster ein, die Abstürze stoppen können.
Race-Bedingungen - Sind mehrere Threads beteiligt, wenn dies der Fall ist? Viele Race-Bedingungen werden nur angezeigt, wenn eine Anwendung direkt ausgeführt wird.
Verknüpfen - Ist Ihr Release-Build, der die richtigen Bibliotheken abruft?
Dinge zu versuchen:
Minidump - sehr einfach zu bedienen (einfach in msdn nachschlagen) gibt Ihnen einen vollständigen Crash-Dump für jeden Thread. Sie laden die Ausgabe einfach in Visual Studio und es ist, als würden Sie zum Zeitpunkt des Absturzes debuggen.
quelle
Sie können WinDbg als Ihren Postmortem-Debugger festlegen. Dadurch wird der Debugger gestartet und beim Absturz an den Prozess angehängt. Verwenden Sie die Option / I, um WinDbg für das Postmortem-Debugging zu installieren (beachten Sie, dass es groß geschrieben wird ):
Weitere Details hier .
Was die Ursache betrifft, handelt es sich höchstwahrscheinlich um eine einheitliche Variable, wie die anderen Antworten vermuten lassen.
quelle
Nach vielen Stunden des Debuggens fand ich schließlich die Ursache des Problems, das tatsächlich durch einen Pufferüberlauf verursacht wurde und einen einzelnen Byte-Unterschied verursachte:
Dies ist ein Zaunpfostenfehler (Off-by-One-Fehler) und wurde behoben durch:
Das Seltsame war, dass ich _CrtCheckMemory () mehrmals um verschiedene Teile meines Codes aufgerufen habe und sie immer 1 zurückgegeben haben. Ich konnte die Ursache des Problems finden, indem ich "return false" platzierte. ruft im Testfall auf und ermittelt schließlich durch Ausprobieren, wo der Fehler lag.
Vielen Dank an alle für Ihre Kommentare - ich habe heute viel über windbg.exe gelernt! :) :)
quelle
Auch wenn Sie Ihre Exe als Release-Version erstellt haben, können Sie dennoch PDB-Dateien (Program Database) generieren, mit denen Sie Trace verfolgen und eine begrenzte Anzahl von Variablenprüfungen durchführen können. In Ihren Build-Einstellungen gibt es eine Option zum Erstellen der PDB-Dateien. Schalten Sie dies ein und verknüpfen Sie es erneut. Versuchen Sie dann zuerst, von der IDE aus zu laufen, um festzustellen, ob der Absturz auftritt. Wenn ja, dann großartig - Sie sind bereit, sich die Dinge anzuschauen. Wenn nicht, können Sie beim Ausführen über die Befehlszeile eines von zwei Dingen tun:
Wenn Sie aufgefordert werden, auf PDB-Dateien zu verweisen, suchen Sie nach diesen. Wenn die PDBs im selben Ausgabeordner wie Ihre EXE- oder DLL-Dateien abgelegt wurden, werden sie wahrscheinlich automatisch abgerufen.
Die PDBs bieten einen Link zur Quelle mit genügend Symbolinformationen, damit Stapelspuren, Variablen usw. angezeigt werden können. Sie können die Werte wie gewohnt überprüfen. Beachten Sie jedoch, dass Sie falsche Messwerte erhalten können, da der Optimierungsdurchlauf möglicherweise nur die Bedeutung hat erscheinen in Registern oder Dinge passieren in einer anderen Reihenfolge als Sie erwarten.
NB: Ich gehe hier von einer Windows / Visual Studio-Umgebung aus.
quelle
Abstürze wie diese werden fast immer verursacht, weil eine IDE normalerweise den Inhalt einer nicht initialisierten Variablen auf Nullen, Null oder einen anderen solchen "vernünftigen" Wert setzt, während Sie bei nativer Ausführung den zufälligen Müll erhalten, den das System aufnimmt.
Ihr Fehler ist daher mit ziemlicher Sicherheit, dass Sie so etwas wie einen Zeiger verwenden, bevor er ordnungsgemäß initialisiert wurde, und dass Sie damit in der IDE davonkommen, weil er nirgendwo gefährlich zeigt - oder der Wert von Ihnen verarbeitet wird Fehlerprüfung - aber im Release-Modus macht es etwas Böses.
quelle
Um einen Absturzspeicherauszug zu erhalten, den Sie analysieren können:
Sie sollten auch die Tools unter Debugging-Tools für Windows überprüfen . Sie können die Anwendung überwachen und alle Ausnahmen der ersten Chance anzeigen, die vor Ihrer Ausnahme der zweiten Chance aufgetreten sind.
Ich hoffe es hilft...
quelle
Eine gute Möglichkeit, einen solchen Fehler zu debuggen, besteht darin, Optimierungen für Ihren Debug-Build zu aktivieren.
quelle
Einmal hatte ich ein Problem, als sich die App ähnlich wie deine verhielt. Es stellte sich heraus, dass es sich bei Sprintf um einen unangenehmen Pufferüberlauf handelte. Natürlich funktionierte es, wenn ein Debugger angeschlossen war. Was ich getan habe, war, einen nicht behandelten Ausnahmefilter zu installieren ( SetUnhandledExceptionFilter) installiert ) in dem ich einfach unendlich blockiert habe (mit WaitForSingleObject auf einem falschen Handle mit dem Timeout-Wert INFINITE).
Sie könnten also etwas in der Art von:
Ich habe dann den Debugger angehängt, nachdem sich der Fehler manifestiert hatte (das GUI-Programm reagiert nicht mehr).
Dann können Sie entweder einen Dump nehmen und später damit arbeiten:
Oder debuggen Sie es sofort. Der einfachste Weg besteht darin, zu verfolgen, wo der Prozessorkontext von der Laufzeit-Ausnahmebehandlungsmaschine gespeichert wurde:
Der Befehl durchsucht den Adressraum des Stapels nach CONTEXT-Datensätzen, sofern die Suchdauer eingehalten wird. Normalerweise benutze ich so etwas wie 'l? 10000' . Beachten Sie, dass Sie keine ungewöhnlich großen Zahlen als Datensatz verwenden, nach dem Sie normalerweise in der Nähe des nicht übergebenen Ausnahmefilterrahmens suchen. 1003f ist die Kombination von Flags (ich glaube, sie entspricht CONTEXT_FULL), die zum Erfassen des Prozessorstatus verwendet wird. Ihre Suche würde ungefähr so aussehen:
Wenn Sie die Ergebnisse zurückerhalten, verwenden Sie die Adresse im Befehl cxr:
Dies bringt Sie genau zum Zeitpunkt des Absturzes zu diesem neuen KONTEXT (Sie erhalten genau den Stack-Trace zum Zeitpunkt des Absturzes Ihrer App). Verwenden Sie außerdem:
um genau herauszufinden, welche Ausnahme aufgetreten war.
Ich hoffe es hilft.
quelle
Manchmal geschieht dies, weil Sie wichtige Operationen in das Makro "assert" eingewickelt haben. Wie Sie vielleicht wissen, wertet "assert" Ausdrücke nur im Debug-Modus aus.
quelle
Haben Sie im Hinblick auf Ihre Probleme beim Abrufen von Diagnoseinformationen versucht, adplus.vbs als Alternative zu WinDbg.exe zu verwenden? Verwenden Sie zum Anhängen an einen laufenden Prozess
Oder um die Anwendung zu starten, falls der Absturz schnell passiert:
Ausführliche Informationen zu adplus.vbs finden Sie unter: http://support.microsoft.com/kb/286350
quelle
Ntdll.dll mit angehängtem Debugger
Ein wenig bekannter Unterschied zwischen dem Starten eines Programms von der IDE oder WinDbg im Gegensatz zum Starten von einem Befehl über die Befehlszeile / den Desktop besteht darin, dass ntdll.dll beim Starten mit einem angeschlossenen Debugger (dh IDE oder WinDbg) eine andere Heap-Implementierung verwendet, die eine kleine Validierung durchführt auf die Speicherzuordnung / Freigabe.
Sie können einige relevante Informationen im unerwarteten Benutzer-Haltepunkt in ntdll.dll lesen . Ein Tool, mit dem Sie das Problem möglicherweise identifizieren können, ist PageHeap.exe .
Crash-Analyse
Sie haben nicht geschrieben, was der "Absturz" ist, den Sie erleben. Sobald das Programm abstürzt und Ihnen anbietet, die Fehlerinformationen an Microsoft zu senden, sollten Sie in der Lage sein, auf die technischen Informationen zu klicken und zumindest den Ausnahmecode zu überprüfen. Mit etwas Aufwand können Sie sogar eine Post-Mortem-Analyse durchführen (siehe Heisenbug) : WinApi Programm stürzt auf einigen Computern) für weitere Anweisungen)
quelle
Vista SP1 hat tatsächlich einen wirklich schönen Crash-Dump-Generator im System eingebaut. Leider ist es nicht standardmäßig aktiviert!
Siehe diesen Artikel: http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx
Der Vorteil dieses Ansatzes besteht darin, dass keine zusätzliche Software auf dem betroffenen System installiert werden muss. Griff es und zerreiß es, Baby!
quelle
Nach meiner Erfahrung handelt es sich dabei hauptsächlich um Probleme mit der Speicherbeschädigung.
Zum Beispiel :
Es ist sehr gut möglich, im Debug-Modus normal zu sein, wenn man den Code ausführt.
Aber in der Veröffentlichung wäre / könnte das ein Absturz sein.
Für mich ist es zu mühsam, dort zu stöbern, wo die Erinnerung nicht gebunden ist.
Verwenden Sie einige Tools wie Visual Leak Detector (Windows) oder Valgrind (Linux).
quelle
Ich habe viele richtige Antworten gesehen. Es gibt jedoch keine, die mir geholfen haben. In meinem Fall wurden die SSE-Anweisungen mit dem nicht ausgerichteten Speicher falsch verwendet . Sehen Sie sich Ihre Mathematikbibliothek an (falls Sie eine verwenden) und versuchen Sie, die SIMD-Unterstützung zu deaktivieren, den Absturz neu zu kompilieren und zu reproduzieren.
Beispiel:
Ein Projekt enthält mathfu und verwendet die Klassen mit dem STL-Vektor: std :: vector <mathfu :: vec2> . Eine solche Verwendung wird wahrscheinlich zum Zeitpunkt der Erstellung des Elements mathfu :: vec2 zum Absturz führen , da der STL-Standardzuweiser die erforderliche 16-Byte-Ausrichtung nicht garantiert. In diesem Fall kann man, um die Idee zu beweisen,
#define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT 1
vor jedem Include der mathfu definieren , in der Release-Konfiguration neu kompilieren und erneut prüfen.Die Debug und RelWithDebInfo Konfigurationen funktionierten gut für mein Projekt, aber nicht die Veröffentlichung ein. Der Grund für dieses Verhalten liegt wahrscheinlich darin, dass der Debugger Zuordnungs- / Freigabeanforderungen verarbeitet und eine Speicherbuchhaltung durchführt, um die Zugriffe auf den Speicher zu überprüfen und zu verifizieren.
Ich habe die Situation in Visual Studio 2015- und 2017-Umgebungen erlebt.
quelle
Ähnliches passierte mir einmal mit GCC. Es stellte sich als zu aggressive Optimierung heraus, die nur beim Erstellen der endgültigen Version und nicht während des Entwicklungsprozesses aktiviert wurde.
Um die Wahrheit zu sagen, war es meine Schuld, nicht die von gcc, da ich nicht bemerkte, dass mein Code auf der Tatsache beruhte, dass diese bestimmte Optimierung nicht durchgeführt worden wäre.
Ich habe viel Zeit gebraucht, um es aufzuspüren, und bin nur dazu gekommen, weil ich in einer Newsgroup gefragt habe und jemand mich dazu gebracht hat, darüber nachzudenken. Lassen Sie mich den Gefallen erwidern, falls dies auch Ihnen passiert.
quelle
Ich fand diesen Artikel nützlich für Ihr Szenario. ISTR die Compiler-Optionen waren etwas veraltet. Schauen Sie sich in Ihren Visual Studio-Projektoptionen um, um zu sehen, wie Sie PDF-Dateien für Ihren Release-Build usw. generieren.
quelle
Es ist verdächtig, dass es außerhalb des Debuggers und nicht innerhalb des Debuggers passieren würde. Das Ausführen im Debugger ändert normalerweise nicht das Anwendungsverhalten. Ich würde die Umgebungsunterschiede zwischen der Konsole und der IDE überprüfen. Kompilieren Sie die Version natürlich auch ohne Optimierungen und mit Debug-Informationen und prüfen Sie, ob sich dies auf das Verhalten auswirkt. Schauen Sie sich zum Schluss die Post-Mortem-Debugging-Tools an, die andere Leute hier vorgeschlagen haben. Normalerweise können Sie einen Hinweis von ihnen erhalten.
quelle
Das Debuggen von Release-Builds kann aufgrund von Optimierungen, die die Reihenfolge ändern, in der Zeilen Ihres Codes ausgeführt zu werden scheinen, schmerzhaft sein. Es kann wirklich verwirrend werden!
Eine Technik, um das Problem zumindest einzugrenzen, besteht darin, MessageBox () zu verwenden, um schnelle Anweisungen anzuzeigen, die angeben, zu welchem Teil des Programms Ihr Code gehören muss ("Starting Foo ()", "Starting Foo2 ()"). Setzen Sie sie an die Spitze der Funktionen im Bereich Ihres Codes, den Sie vermuten (was haben Sie zu dem Zeitpunkt getan, als er abstürzte?). Wenn Sie feststellen können, welche Funktion verwendet wird, ändern Sie die Meldungsfelder in Codeblöcke oder sogar in einzelne Zeilen innerhalb dieser Funktion, bis Sie sie auf einige Zeilen eingrenzen. Anschließend können Sie den Wert der Variablen ausdrucken, um festzustellen, in welchem Zustand sie sich zum Zeitpunkt des Absturzes befinden.
quelle
Versuchen Sie, _CrtCheckMemory () zu verwenden, um festzustellen, in welchem Status sich der zugewiesene Speicher befindet. Wenn alles gut geht, _CrtCheckMemory liefert TRUE , sonst FALSE .
quelle
Sie können Ihre Software mit aktivierten globalen Flags ausführen (siehe Debugging-Tools für Windows). Es wird sehr oft helfen, das Problem zu lösen.
quelle
Lassen Sie Ihr Programm einen Mini-Dump generieren, wenn die Ausnahme auftritt, und öffnen Sie ihn dann in einem Debugger (z. B. in WinDbg). Die wichtigsten Funktionen: MiniDumpWriteDump, SetUnhandledExceptionFilter
quelle
Hier ist ein Fall, den ich hatte, den jemand als lehrreich empfinden könnte. Es stürzte nur bei der Veröffentlichung in Qt Creator ab - nicht beim Debuggen. Ich habe INI-Dateien verwendet (da ich Apps bevorzuge, die auf andere Laufwerke kopiert werden können, im Vergleich zu Apps, die ihre Einstellungen verlieren, wenn die Registrierung beschädigt wird). Dies gilt für alle Apps, deren Einstellungen im Verzeichnisbaum der Apps gespeichert sind. Wenn sich die Debug- und Release-Builds in verschiedenen Verzeichnissen befinden, können Sie auch eine andere Einstellung festlegen. Ich hatte eine Präferenz eingecheckt, die in der anderen nicht eingecheckt war. Es stellte sich heraus, dass dies die Quelle meines Absturzes war. Gut, dass ich es gefunden habe.
Ich hasse es, es zu sagen, aber ich habe den Absturz nur in MS Visual Studio Community Edition diagnostiziert. Nachdem Sie VS installiert haben, lassen Sie meine App in Qt Creator abstürzen und öffnen Sie sie im Debugger von Visual Studio . Während meine Qt-App keine Symbolinformationen hatte, stellte sich heraus, dass die Qt-Bibliotheken einige hatten. Es führte mich zur beleidigenden Linie; da konnte ich sehen, welche Methode aufgerufen wurde. (Dennoch denke ich, dass Qt ein praktisches, leistungsstarkes und plattformübergreifendes LGPL-Framework ist.)
quelle
Ich hatte diesen Fehler und vs stürzte sogar beim Versuch! Reinigen! Mein Projekt. Also habe ich die obj-Dateien manuell aus dem Release-Verzeichnis gelöscht und danach hat es gut funktioniert.
quelle
Ich stimme Rolf zu. Da die Reproduzierbarkeit so wichtig ist, sollten Sie keinen Nicht-Debug-Modus verwenden. Alle Ihre Builds sollten debuggbar sein. Wenn Sie zwei Ziele zum Debuggen haben, verdoppelt sich Ihre Debugging-Last mehr als. Versenden Sie einfach die "Debug-Modus" -Version, es sei denn, sie ist unbrauchbar. In diesem Fall machen Sie es verwendbar.
quelle