Knifflige STL Fehler in Release in Visual Studio
-
08-07-2019 - |
Frage
Ich habe, was ich bin mir ziemlich sicher, dass ein Fehler in den Optimierer in Visual Studio 2005. Das Problem ist, mit einem STL Karte.
Hier ist der relevante Code:
MyMapIterator myIt = m_myMap.find(otherID);
if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second.foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second.foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
funktioniert einwandfrei im Debug, verwendet in Version zum Absturz zu bringen, und deaktivieren globale Optimierungen „fixed it“ Nun, nichts funktioniert. Ich erhalte ein:
Microsoft Visual Studio C Runtime Library has detected a fatal error in [...]
Press Break to debug the program or Continue to terminate the program.
Dies geschieht auf der Seite ersten MyMapIterator :: operator-> der letzten else if
Die Karte leer ist, weiß ich, dass zurück Ende haben finden soll () Die ersten beiden Vergleiche in diesem Sinne arbeiten. Aber irgendwie, das dritte Mal 'Myit! = M_myMap.end ()' gibt true zurück, und die rechte Seite des && ausgeführt wird.
Verschiedene andere Orte nicht wie diese mit einer Variante von ‚Myit! = M_myMap.end ()‘ in derselben Datei wahr Rückkehr, aber das ist für mich derjenige, der die meisten anderen Möglichkeiten ausgeschlossen ist. Früher dachte ich, es ist ein Pufferüberlauf war, die auf meiner Karte wurde stampfen, aber einen Blick zurück auf den Code nehmen. Ich bin sicher, dass kein anderer Thread auf es stampft, und das ist 100% reproduzierbar.
Also, was soll ich tun, von hier. Dies ist nicht die Leistung empfindlich im geringsten. Ich brauche es nur zu arbeiten, wie es sollte. Jede Option ist akzeptabel. Ja, ich weiß, ich könnte die ganze Sache mit der Iterator Gleichheitsprüfung umgeben und es ist nicht der schönste Code. Der Punkt ist, es sollte immer noch funktionieren, und wenn das nicht funktioniert, Sonstigem.
Bearbeiten
Die letzte else-if erzeugt keinen Sprung!
if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_A)
009270BE mov ecx,dword ptr [this]
009270C4 add ecx,0Ch
009270C7 lea eax,[ebp-90h]
009270CD call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::end (8A21E0h)
009270D2 mov esi,eax
009270D4 lea edi,[myIt]
009270D7 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::const_iterator::operator!= (8A2220h)
009270DC movzx ecx,al
009270DF test ecx,ecx
009270E1 je lux::Bar::DoStuff+0E4h (927154h)
009270E3 lea esi,[myIt]
009270E6 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::iterator::operator-> (8A21F0h)
009270EB cmp dword ptr [eax+8],1
009270EF jne lux::Bar::DoStuff+0E4h (927154h)
{
Stuff
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B)
00927154 mov ecx,dword ptr [this]
0092715A add ecx,0Ch
0092715D lea eax,[ebp-98h]
00927163 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::end (8A21E0h)
00927168 mov esi,eax
0092716A lea edi,[myIt]
0092716D call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::const_iterator::operator!= (8A2220h)
00927172 movzx edx,al
00927175 test edx,edx
00927177 je lux::Bar::DoStuff+17Ah (9271EAh)
00927179 lea esi,[myIt]
0092717C call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::iterator::operator-> (8A21F0h)
00927181 cmp dword ptr [eax+8],2
00927185 jne lux::Bar::DoStuff+17Ah (9271EAh)
{
//Stuff
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_C)
009271EA mov ecx,dword ptr [this]
009271F0 add ecx,0Ch
009271F3 lea eax,[ebp-0A0h]
009271F9 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::end (8A21E0h)
009271FE mov esi,eax
00927200 lea edi,[myIt]
00927203 call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::const_iterator::operator!= (8A2220h)
00927208 lea esi,[myIt]
0092720B call std::_Tree<std::_Tmap_traits<unsigned __int64,lux::Foo,std::less<unsigned __int64>,std::allocator<std::pair<unsigned __int64 const ,lux::Foo> >,0> >::iterator::operator-> (8A21F0h)
{
//Stuff in the condition and after
Lösung 2
Nun Jungs. Nach viel Schmerz und Tränen. Um es zu beheben zumindest zeitweise hatte ich den Code ein wenig auf die Form von den meisten Menschen vorgeschlagen zu überarbeiten, das ist, wie es im ersten Fall sein sollte:
if (myIt != m_myMap.end())
{
if (myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
}
Allerdings war der Fehler immer noch vorhanden. Dieses Juwel es fest:
if (myIt != m_myMap.end())
if (myIt != m_myMap.end())
{
if (myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
}
Ja. Verdoppeln des resultiert, wenn in einem Sprungbefehl nach dem Test emittiert wird.
Andere Tipps
Ich vermute, Sie Code an anderer Stelle haben, die einen Puffer über Lauf in sich hat.
Das andere Code ist die Verwaltung des Speichers zu zerstören, die der entsandte Code verwendet wird.
Im Debug-Modus der zusätzliche Speicher padding das Verhalten ändern.
anderen Zufallscode ändert, wird auch das Verhalten ändern, da es die relative Position der Dinge verschieben.
Im Grunde müssen Sie eine Menge Code oder verwenden Sie ein Tool wie Bounds überprüfen, die Fehler zu finden.
Konzentrieren Sie sich auf Arrays oder irgendwelche Rohzeiger Mathematik. Auch eine Verwendung von gelöschten Zeigern.
Bugs wie diese können für eine lange Zeit verstecken, wenn sie Ort schreiben, die nicht zu einem Absturz verursacht. Aus dem gleichen Grund verschwinden sie oft auf mysteriöse Weise.
Es scheint wahrscheinlich, dass Sie eine andere Bedingung im Code haben Sie das nicht gezeigt haben, ist die Karte verursacht bei einigen ungültigen Daten gefüllt werden.
Versuchen Sie den Inhalt der Karte, bevor Sie Ihren Anruf Dumping, um zu find()
, und hat einen Blick für mögliche nicht initialisierte Werte in dem Rest des Codes.
offensichtlich kann alles Fehler, auch die VC-Compiler. so, wenn es einen Fehler in diesem Abschnitt des Codes ist, könnten Sie es wickeln in einem # Pragma drehen Optimierungen aus. Wenn es immer noch danach abstürzt, dann ist etwas anderes in Ihrem Code Ihre Karte Wegwerfen (und dann müssen Sie auf DrWatson drehen Dumps zu erhalten, so dass Sie den Absturz in windbg überprüfen können, um zu versuchen und herausfinden, was es passiert ist).
So deaktivieren Sie Optimierung Verwendung:
#pragma optimize("g", off)
, um ihn wieder auf, entweder ausgeschaltet oder auf den speziellen Fall verwenden ändern
#pragma optimize("", on)
, die Ihre Optimierungseinstellungen auf die Projektvorgaben zurücksetzt.
Der Optimierer vermasselt wirklich Ihre Debugging-Informationen auf. Es gibt eine sehr gute Chance, dass die Linie, die ist wirklich auf einer andere, in der Nähe Linie versagt. Auch wenn der Debugger sagt Ihnen, dass die Ausnahme auf der zweiten Hälfte des &&
Betreiber auftritt, ist es wahrscheinlich nicht.
das eigentliche Problem zu diagnostizieren, können Sie die Pause wollen die letzte if-Anweisung in etwas nach oben wie:
else if (myIt != m_myMap.end())
{
printf("TEST 1\n");
if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
}
printf("TEST 2\n");
Wenn Sie eine Ausgabe von „TEST 1“ erhalten, dann gibt es etwas falsch mit Ihrem Iterator. Wenn Sie eine Ausgabe von „TEST 2“ und dann einem Absturz zu bekommen, dann ist offensichtlich der Fehler in dem nachfolgenden Code auftritt. Wenn weder "TEST 1" noch "TEST 2" druckt und stürzt immer noch da, dann ist etwas wirklich falsch.
Ich glaube, ich in diese genau das gleiche Problem lief.
scheint nur in x64 Release passieren (vollständige Optimierung). Meine STL Karte leer bestätigt (size () == 0), aber diese Zeile Code nicht erkennt, die leere Karte und stattdessen geht in die for-Schleife:
typedef std::map<std::string,std::string> KeyValueMap;
printf( "size: %d\n", ExtraHeaders.size() ); // prints "size: 0"
for( KeyValueMap::iterator it= ExtraHeaders.begin(); it != ExtraHeaders.end(); it++ )
{
// this line is executed
printf( "%s\t%s\n", (*it).first.c_str(), (*it).second.c_str() );
}
Man fühlt sich wie ein Compiler Fehler zu mir.
Ja, ich weiß, ich könnte die ganze Sache mit der Iterator Gleichheitsprüfung umgeben und es ist nicht der schönste Code. Der Punkt ist, es sollte immer noch funktionieren, und wenn das nicht funktioniert, etwas anderes kann.
Natürlich mit dieser Logik haben Sie keine Optionen ... Alles, was Sie tun, eine Abhilfe ist.
Ich glaube, mein erster Ansatz wäre, die Sachen in einem Block zu wickeln, die überprüft haben, dass myIt
gilt:
if (myIt != m_myMap.end())
{
if (myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
}
Ich denke, man eine Annahme stellen, für die wahrscheinlich falsch ist: dass der Absturz passiert, nachdem erfolgreich myIt != m_myMap.end() && myIt->second.userStatus
in der gleichen Iteration geprüft. Ich würde wetten, dass der Absturz sofort das erste Mal, folgt diese Werte untersucht werden. Es ist das Optimierungsprogramm, das es habe die ersten beiden if
Bedingungen gelöscht aussehen wie Sie macht.
Versuchen Sie, einen völlig anderen Code-Block in die Schleife setzen und sehen, ob es dort stürzt ab (meine Wette ist, dass es würde - ich glaube nicht, dass Sie die Ausgabe von cerr
sehen werden sofort zu einem Absturz vorhergehenden.)
// New code block
MyMapIterator yourIt = m_myMap.find(otherID);
if (yourIt != m_myMap.end() && yourIt->second.userStatus == STATUS_A ||
yourIt->second.userStatus == STATUS_B)
{
cerr << "Hey, I can print something! " << std::ios::hex << this << endl;
}
// Original code
MyMapIterator myIt = m_myMap.find(otherID);
if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
{
//Prints stuff. No side-effects whatsoever.
}
else if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_B && /*other meaningless conditions */)
{
//Prints stuff. No side-effects whatsoever.
}
if (myIt != m_myMap.end() && myIt->second.userStatus == STATUS_B && myIt->second->foo == FOO_A)
Warum zugreifen Sie Userstatus mit. und foo mit ->