2008-11-23 5 views
52

Tengo un programa Visual Studio 2005 C++ que se ejecuta de manera diferente en modo Release que en el modo Debug. En el modo de lanzamiento, se produce un bloqueo intermitente (aparente). En modo de depuración, no falla. ¿Cuáles son algunas de las razones por las que una versión de Release funcionaría de forma diferente a una versión de Debug?Cuáles son algunos de los motivos por los que una versión de Release se ejecutará de forma diferente a una versión de Debug

También vale la pena mencionar que mi programa es bastante compleja y utiliza varias bibliotecas 3 ª parte para el procesamiento de XML, mensaje de intermediación, etc ...

Gracias de antemano!

+1

Mientras traté de responder a su pregunta, tal vez eso no ayude a resolver el problema. Primero, consigue una buena reproducción. En segundo lugar, habilite la información de depuración en la versión de lanzamiento. Crash todavía allí? no -> puede ser temporización o inicialización. Sí -> use el depurador (remoto) – peterchen

Respuesta

116

Surviving the Release Version le da una buena visión general.

cosas que he encontrado - la mayoría ya se mencionan

inicialización de variables con mucho el más común. En Visual Studio, las compilaciones de depuración inicializan explícitamente la memoria asignada a los valores dados, ver p. Memory Values aquí. Estos valores generalmente son fáciles de detectar, causan un error fuera de límites cuando se usan como índice o una infracción de acceso cuando se usan como punteros. Un booleano no inicializado es cierto, sin embargo, y puede causar errores de memoria no inicializados que pasan desapercibidos durante años.

En las versiones de lanzamiento donde la memoria no se ha inicializado explícitamente que sólo mantiene el contenido que tenía antes. Esto conduce a bloqueos "aleatorios" y "valores divertidos", pero a menudo a bloqueos determinísticos que requieren que se ejecute un comando aparentemente no relacionado antes del comando que realmente falla. Esto se debe a que el primer comando "configura" la ubicación de la memoria con valores específicos, y cuando las ubicaciones de memoria se reciclan, el segundo comando las ve como inicializaciones. Eso es más común con las variables de pila no inicializadas que con Heap, pero también me ha pasado a mí.

La inicialización de la memoria bruta también puede ser diferente en una compilación de lanzamiento, ya sea que se inicie desde el estudio visual (depurador conectado) o desde el explorador. Eso hace que los errores de compilación de versión "más bonitos" que nunca aparecen bajo el depurador.

Optimizaciones válidas vienen en segundo lugar en mi exeperiencia. El estándar C++ permite que ocurran muchas optimizaciones que pueden ser sorprendentes, pero que son completamente válidas, p. cuando dos punteros alias la misma ubicación de memoria, no se considera el orden de inicialización o múltiples subprocesos modifican las mismas ubicaciones de memoria, y se espera un cierto orden en el que el subproceso B ve los cambios realizados por el subproceso A. A menudo, se culpa al compilador estas. ¡No tan rápido, joven yedi! - véase más adelante

sincronización versión se basa no sólo hacer "correr más rápido", para una variedad de razones (optimizaciones, registrando las funciones que proporcionan un punto de sincronización hilo, código de depuración similares no ejecutado según afirma etc.) También el tiempo relativo entre las operaciones cambian dramáticamente. El problema más común descubierto por eso son las condiciones de carrera, pero también los bloqueos y la simple ejecución de "orden diferente" de código de mensaje/temporizador/evento. A pesar de que son problemas de sincronización, pueden ser pueden ser sorprendentemente estables en las construcciones y plataformas, con reproducciones que "funcionan siempre, excepto en PC 23".

Guard Bytes. Las compilaciones de depuración a menudo ponen (más) bytes de protección alrededor de instancias y asignaciones seleccionadas, para proteger contra desbordamientos de índices y, a veces, subdesbordamientos. En los casos poco frecuentes en los que el código se basa en desplazamientos o tamaños, p. serializar estructuras crudas, son diferentes.

Otras diferencias de código Algunas instrucciones - e.g afirma: no evalúa nada en las versiones de lanzamiento. A veces tienen diferentes efectos secundarios. Esto es frecuente con engaños macro, como en el clásico (advertencia: múltiples errores)

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

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

Lo cual, en una versión de lanzamiento, se evalúa como si (foo & & bar) Este tipo de error es muy, muy raro con el código C/C++ normal y macros que están escritos correctamente.

Errores del compilador Esto realmente nunca sucede. Bueno, lo hace, pero es mejor para la mayor parte de su carrera asumir que no es así. En una década de trabajo con VC6, encontré uno en el que todavía estoy convencido de que este es un error de compilación no fijo, en comparación con decenas de patrones (tal vez incluso cientos de instancias) con una comprensión insuficiente de las Escrituras (por ejemplo, el estándar).

+1

¡Excelente respuesta +1! Sugerir aceptado –

+1

Fabuloso, pero por favor agregue algo acerca de los efectos secundarios de assertion. La expresión en una afirmación puede tener un efecto secundario y no se ejecutará en la versión de lanzamiento. p.ej. caso patológico: assert (n ++ == 2); A veces hay una macro alternativa que siempre evalúa la expresión, p. VERIFICAR en VC++. –

+0

Oh, veo que el artículo vinculado menciona que ... –

5

Las variables que no se inicializan explícitamente se pondrán a cero o no en la compilación Release.

6

En la versión de depuración a menudo se habilitan las aserciones y/o los símbolos de depuración. Esto puede conducir a un diseño de memoria diferente. En caso de un puntero malo, desbordamiento de una matriz o acceso a memoria similar, puede acceder a una mala memoria crítica (por ejemplo, un puntero de función) y en otro caso a una memoria no crítica (p. Ej., Solo una cadena de documentación está en la papelera)

2

La compilación de versiones (con suerte) se ejecutaría más rápido que la compilación de depuración. Si está utilizando más de un hilo, es posible que vea más entrelazado, o simplemente un hilo que se ejecuta más rápido que los otros, que puede que no haya notado en la compilación de depuración.

2

Las compilaciones de versión normalmente se compilan con la optimización habilitada en el compilador, mientras que las compilaciones de depuración generalmente no lo son.

En algunos idiomas o cuando se usa muchas bibliotecas diferentes esto puede causar fallos intermitentes - sobre todo cuando el nivel de optimización elegido es muy alta.

sé que este es el caso con el compilador gcC++ C, pero no estoy seguro sobre el compilador de Microsoft.

2

tuve a similar problem not long ago, que terminó siendo causados ​​por la pila que está siendo tratado de manera diferente en las versiones de lanzamiento. Otras cosas que pueden diferir:

  • asignación de memoria se maneja de manera diferente con versiones de depuración en el compilador VS (es decir, escribiendo sobre 0xcc memoria borrada, etc.)
  • Loop desenrollar y otras optimizaciones del compilador
  • Alightment de punteros
2

Depende tanto del proveedor del compilador como de las bibliotecas que compila con las banderas DEBUG. Mientras que el código DEBUG nunca debería afectar el código de ejecución (no debería tener efectos secundarios) a veces lo hace.

En particular, las variables pueden inicializarse sólo en el modo de depuración y dejaron sin inicializar en modo de lanzamiento. El STL en los compiladores de Visual Studio son diferentes en los modos DEBUG y RELEASE. La idea es que los iteradores estén completamente controlados en DEPURACIÓN para detectar posibles errores (utilizando iteradores invalidados, por ejemplo, un iterador en un vector se invalida si ocurre una inserción después de recuperar el iterador).

Lo mismo sucede con las bibliotecas de terceros, la primera que se me ocurre es QT4, que terminará su programa con una afirmación si un hilo diferente al que creó el objeto gráfico realiza operaciones de pintura.

Con todos los cambios, el código y la huella de memoria serán diferentes en ambos modos.Un problema de puntero (leer la posición de una pasada al final de una matriz) puede pasar desapercibido si esa posición es legible.

Las afirmaciones están destinadas a matar la aplicación durante DEPURACIÓN y desaparecen de las compilaciones de LIBERACIÓN, por lo que no pensaría en las aserciones como su problema. Un puntero deshonesto o acceder a uno pasado el final serían mis primeros sospechosos.

Hace algún tiempo hubo problemas con algunas compilaciones optimizaciones rompiendo código, pero no he leído quejas últimamente. Podría haber un problema de optimización allí, pero este no sería mi primer sospechoso.

0

En mi experiencia, la razón más común parece ser que las configuraciones difieren en más formas que las configuraciones de lanzamiento/compilación. P.ej. se incluyen diferentes libs, o la compilación de depuración tiene un tamaño de pila diferente al de la versión de lanzamiento.

Para evitar esto en nuestros proyectos de Visual Studio 2005 utilizamos hojas de propiedades de manera exhaustiva. De esta forma, las configuraciones de liberación y depuración pueden compartir configuraciones comunes.

1

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

Debido al hecho de que los bytes de protección se añaden en versiones de depuración, puede ser capaz de "seguridad" memoria de acceso que está fuera de los límites de una matriz (en particular matrices dinámicas), pero esto causará una infracción de acceso en la versión de lanzamiento. Este error puede pasar desapercibido, causando un montón corrupto y posiblemente una violación de acceso en un lugar no relacionado con el error original.

Use PageHeap (o, si tiene Herramientas de depuración instaladas puede usar gflags) para descubrir errores relacionados con montones corruptos.

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

0

Este post junto con los enlaces que aparecen es muy útil en la corrección de errores relacionados. agregando a la lista anterior, la diferencia en las convenciones de llamadas también puede llevar a este comportamiento: Falló en la compilación de lanzamiento solo con la optimización para mí. declaro como __stdcall y definido como __cdecl (por defecto). [extrañamente esta advertencia no se recogió ni siquiera en advertir nivel 4 MSVC?]

Cuestiones relacionadas