Error STL desconcertante en la versión en Visual Studio
-
08-07-2019 - |
Pregunta
Tengo lo que estoy bastante seguro es un error en el optimizador en Visual Studio 2005. El problema es con un mapa STL.
Aquí está el código relevante:
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.
}
Funciona perfectamente en la depuración, solía bloquearse en el lanzamiento y deshabilita las optimizaciones globales "lo arregló" Ahora nada funciona. Me sale un:
Microsoft Visual Studio C Runtime Library has detected a fatal error in [...]
Press Break to debug the program or Continue to terminate the program.
Esto sucede en el primer MyMapIterator :: operator- > del último si
El mapa está vacío, sé que find debería haber devuelto end (). Las dos primeras comparaciones con ese efecto funcionan. Pero de alguna manera, la tercera vez 'myIt! = M_myMap.end ()' devuelve verdadero, y el lado derecho de & amp; & amp; se ejecuta.
Varios otros lugares fallan como este con una variante de 'myIt! = m_myMap.end ()' que devuelve verdadero en el mismo archivo, pero este es, para mí, el que descarta la mayoría de las otras posibilidades. Solía ??pensar que era un desbordamiento de búfer que estaba pisando fuerte en mi mapa, pero eche un vistazo al código. Estoy seguro de que ningún otro hilo está pisando fuerte, y esto es 100% reproducible.
Entonces, ¿qué hago desde aquí? Esto no es sensible al rendimiento en lo más mínimo. Solo necesito que funcione como debería. Cualquier opción es aceptable. Sí, sé que podría rodear todo con la verificación de igualdad del iterador y no es el código más agradable. El punto es que aún debería funcionar, y si eso falla, cualquier otra cosa puede hacerlo.
EDIT
¡El último si no genera ningún salto!
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
Solución 2
Bueno chicos. Después de mucho dolor y lágrimas. Para solucionarlo, al menos temporalmente, tuve que modificar el código un poco al formulario sugerido por la mayoría de las personas, que es como debería haber sido en el primer caso:
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.
}
}
Sin embargo, el error todavía estaba presente. Esta gema lo arregló:
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.
}
}
Sí Doblar el if resultó en una instrucción de salto emitida después de la prueba.
Otros consejos
Sospecho que tiene código en otro lugar que tiene un buffer sobre ejecutado en él.
Este otro código está logrando destruir la memoria que está usando el código publicado.
En modo de depuración, el relleno de memoria adicional cambiará el comportamiento.
Cambiar otro código aleatorio también cambiará el comportamiento, ya que cambiará la posición relativa de las cosas.
Básicamente, deberá revisar una gran cantidad de código o utilizar una herramienta como BoundsChecker para encontrar el error.
Concéntrese en matrices o cualquier matemática de puntero sin formato. También cualquier uso de punteros eliminados.
Los errores como este pueden ocultarse durante mucho tiempo si escriben en un lugar que no provoque un bloqueo. Por la misma razón, a menudo desaparecen misteriosamente.
Parece probable que tenga alguna otra condición en el código que no haya mostrado que esté causando que el mapa se complete con algunos datos no válidos.
Intente deshacerse del contenido del mapa antes de su llamada a find ()
, y busque posibles valores no inicializados en el resto del código.
obviamente cualquier cosa puede tener errores, incluso el compilador de VC. así que si hay un error en esta sección del código, puede envolverlo en un # pragma para desactivar las optimizaciones. SI todavía se bloquea después de eso, entonces algo más en su código está tirando a la basura su mapa (y luego necesita encender DrWatson para obtener volcados para que pueda inspeccionar el bloqueo en windbg para tratar de averiguar qué le sucedió).
Para desactivar la optimización use:
#pragma optimize("g", off)
para volver a encenderlo, ya sea apagarlo o usar el caso especial
#pragma optimize("", on)
que restablece la configuración de optimización a los valores predeterminados del proyecto.
El optimizador realmente desordena su información de depuración. Existe una muy buena posibilidad de que la línea que en realidad falla en una línea cercana diferente. Incluso si el depurador le dice que la excepción está ocurriendo en la segunda mitad del operador & amp; & amp;
, probablemente no lo sea.
Para diagnosticar el problema real, es posible que desee dividir la última declaración if en algo como:
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");
Si obtiene una salida de "PRUEBA 1", entonces hay algo mal con su iterador. Si obtiene una salida de "PRUEBA 2" y luego un bloqueo, entonces obviamente el error está ocurriendo en el código posterior. Si ninguno " PRUEBA 1 " ni "PRUEBA 2" se imprime y todavía se bloquea allí, entonces algo está realmente mal.
Creo que me encontré exactamente con el mismo problema.
Solo parece suceder en la versión x64 (optimización completa). Mi mapa STL se confirma vacío (tamaño () == 0), pero esta línea de código no detecta el mapa vacío y en su lugar entra en el bucle for:
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() );
}
Me parece un error de compilación.
Sí, sé que podría rodear todo con la verificación de igualdad del iterador y no es el código más agradable. El punto es que aún debería funcionar, y si eso falla, cualquier otra cosa puede hacerlo.
Por supuesto, con esa lógica no tienes opciones ... Cualquier cosa que hagas es una solución alternativa.
Creo que mi primer enfoque sería envolver las cosas en un bloque que verifique que myIt
es válido:
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.
}
}
Creo que está asumiendo que probablemente sea falso: que el bloqueo ocurre después de haber exitosamente verificado myIt! = m_myMap.end () & amp; & amp; myIt- > second.userStatus
en la misma iteración. Apuesto a que el bloqueo ocurre inmediatamente la primera vez que se examinan estos valores. Es el optimizador lo que hace que parezca que ha borrado las dos primeras condiciones if
.
Intente poner un bloque de código completamente diferente en el bucle y vea si se bloquea allí (mi apuesta es que lo haría, no creo que vea la salida de cerr
inmediatamente anterior un accidente.)
// 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)
¿Por qué estás accediendo a userStatus? y foo con - > ?