Corrupción montón bajo Win32; cómo localizar?


Estoy trabajando en una multithreaded aplicación C++ que está corrompiendo el montón. Las herramientas habituales para localizar esta corrupción parecen inaplicables. Las compilaciones antiguas (18 meses) del código fuente exhiben el mismo comportamiento que la versión más reciente, por lo que esto ha existido durante mucho tiempo y simplemente no se notó; en el lado negativo, los deltas de código fuente no se pueden usar para identificar cuándo se introdujo el error: hay muchos de cambios de código en repositorio.

La solicitud para el comportamiento de bloqueo es generar el rendimiento en esta transferencia de datos de socket del sistema que se munged en una representación interna. Tengo un conjunto de datos de prueba que periódicamente hará que la aplicación de excepción (varios lugares, varias causas-incluyendo montón alloc falla, por lo tanto: corrupción montón).

El comportamiento parece relacionado con la potencia de la CPU o el ancho de banda de la memoria; cuanto más de cada máquina tiene, más fácil es bloquearse. Desactivación de a el núcleo hyper-threading o un núcleo de doble núcleo reduce la tasa de corrupción (pero no elimina). Esto sugiere un problema relacionado con el tiempo.

Ahora aquí está el problema:
Cuando se ejecuta en un entorno de depuración ligero (digamos Visual Studio 98 / AKA MSVC6), la corrupción del montón es razonablemente fácil de reproducir: pasan diez o quince minutos antes de que algo falle horriblemente y las excepciones, como un alloc; cuando se ejecuta en un entorno de depuración sofisticado (Rational Purify, VS2008/MSVC9 o incluso una aplicación de Microsoft Verificador) el sistema se convierte en la memoria de velocidad limitada y no se bloquea (Memory-bound: CPU is not getting above 50%, disk light is not on, the program's going as fast it can, box consuming 1.3G of 2G of RAM). Por lo tanto, Tengo una opción entre ser capaz de reproducir el problema (pero no identificar la causa) o ser capaz de identificar la causa o un problema que no puedo reproducir.

Mis mejores conjeturas actuales en cuanto a dónde está a continuación:

  1. Obtener una caja increíblemente grunty (para reemplace el cuadro de desarrollo actual: 2 Gb de RAM en un E6550 Core2 Duo); esto hará posible volver a repetir el bloqueo que causa un mal comportamiento cuando se ejecuta en un entorno de depuración potente; o
  2. Reescribe los operadores new y delete para usar VirtualAlloc y VirtualProtect para marcar la memoria como de solo lectura tan pronto como termine. Corre bajo MSVC6 y haz que el sistema operativo atrape al malo que está escribiendo en freed memory. Sí, esto es un signo de desesperación: ¿quién demonios reescribe new y delete?! Me pregunto si esto va a hacer que sea como lento como bajo Purify et al.

Y, no: El envío con la instrumentación Purify incorporada no es una opción.

Un colega acaba de pasar y preguntó " Stack Overflow? Estamos recibiendo desbordamientos de pila ahora?!?"

Y ahora, la pregunta: ¿Cómo puedo localizar al corruptor del montón?


Actualización: equilibrar new[] y delete[] parece haber avanzado mucho hacia la solución del problema. En lugar de 15 minutos, la aplicación ahora va unas dos horas antes estrellar. Todavía no. ¿Alguna otra sugerencia? La corrupción persiste.

Actualización: una versión compilada bajo Visual Studio 2008 parece dramáticamente mejor; la sospecha actual se basa en la implementación STL que se incluye con VS98.


  1. Reproducir el problema. Dr Watson producirá un volcado que podría ser útil en un análisis posterior.

Tomaré nota de eso, pero me preocupa que el Dr. Watson solo se tropiece después el hecho, no cuando el montón está siendo pisoteado.

Otro intento podría ser usar WinDebug como una herramienta de depuración que es bastante poderosa y al mismo tiempo liviana.

Lo tengo en marcha en este momento, de nuevo: no hay mucha ayuda hasta que algo sale mal. Quiero atrapar al vándalo en el acto.

Tal vez estas herramientas le permitirán al menos reducir el problema a cierto componente.

No tengo mucha esperanza, pero estoy desesperado los tiempos lo exigen...

Y ¿está seguro de que todos los componentes del proyecto tienen la configuración correcta de la biblioteca de tiempo de ejecución (C/C++ tab, categoría de generación de código en la configuración del proyecto VS 6.0)?

No, no lo estoy, y pasaré un par de horas mañana revisando el espacio de trabajo (58 proyectos en él) y comprobando que todos están compilando y enlazando con los indicadores apropiados.


Actualización: Esto tomó 30 segundos. Seleccione todos los proyectos en el diálogo Settings, deseleccione hasta encuentra los proyectos que no tienen la configuración correcta (todos tenían la configuración correcta).
Author: Josh, 2008-08-04

15 answers

Mi primera opción sería una herramienta de montón dedicada como pageheap.exe .

Reescribir new y delete puede ser útil, pero eso no captura los allocs confirmados por código de nivel inferior. Si esto es lo que quieres, es mejor desviarse de los low-level alloc API usando Microsoft Detours.

También comprobaciones de cordura tales como: verifique que sus bibliotecas de tiempo de ejecución coincidan (liberación vs. depuración, subproceso múltiple vs. subproceso único, dll vs. lib estática), busque eliminaciones incorrectas (por ejemplo, eliminar donde debería eliminar [] se utiliza), asegúrese de no mezclar y combinar sus asignaciones.

También intente desactivar selectivamente los hilos y vea cuándo/si el problema desaparece.

¿Cómo se ve la pila de llamadas, etc. en el momento de la primera excepción?

 27
Author: Michael Kelley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-01 17:32:32

Tengo los mismos problemas en mi trabajo (también usamos VC6 a veces). Y no hay una solución fácil para ello. Solo tengo algunos consejos:

  • Pruebe con volcados automáticos en la máquina de producción (ver Volcador de procesos). Mi experiencia dice que el Dr. Watson no es perfecto para verter.
  • Remove all catch(...) de su código. A menudo esconden serias excepciones de memoria.
  • Comprobar Depuración avanzada de Windows - hay un montón de grandes consejos para problemas como el tuyo. Recomiendo esto con todo mi corazón.
  • Si usas STL prueba STLPort y comprueba las compilaciones. El iterador inválido es el infierno.

Buena suerte. Problemas como el tuyo nos llevan meses solucionarlos. Prepárate para esto...

 11
Author: Michal Sznajder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-07 12:30:58

Ejecute la aplicación original con ADplus -crash -pn appnename.exe Cuando el problema de memoria pop-up obtendrá un gran vertedero agradable.

Puede analizar el volcado para averiguar qué ubicación de memoria estaba dañada. Si tienes suerte de que la memoria de sobrescritura sea una cadena única, puedes averiguar de dónde vino. Si no tienes suerte, tendrás que indagar en win32 heap y averiguar cuáles eran las características de la memoria original. (heap-x podría ayudar)

Después de saber lo que estaba en mal estado, puede restringir appverifier uso con ajustes especiales de montón. es decir, puede especificar qué DLL supervisa, o qué tamaño de asignación debe supervisar.

Esperemos que esto acelere el monitoreo lo suficiente como para atrapar al culpable.

En mi experiencia, nunca necesité el modo de verificador de montón completo, pero pasé mucho tiempo analizando los volcados de bloqueo y explorando las fuentes.

P. S: Puede usar DebugDiag para analizar los volcados. Puede señalar el DLL dueño de la pila corrupta, y darle otros detalles útiles.

 8
Author: Tal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-07 12:33:55

Hemos tenido bastante buena suerte al escribir nuestro propio malloc y funciones libres. En producción, solo llaman al estándar malloc y gratis, pero en debug, pueden hacer lo que quieras. También tenemos una clase base simple que no hace más que anular los operadores new y delete para usar estas funciones, entonces cualquier clase que escriba puede simplemente heredar de esa clase. Si tiene un montón de código, puede ser un gran trabajo reemplazar las llamadas a malloc y free al nuevo malloc y free (¡no se olvide de realloc!), pero a largo plazo es muy útil.

En el libro de Steve Maguire Writing Solid Code (muy recomendable), hay ejemplos de cosas de depuración que puedes hacer en estas rutinas, como:

  • Realizar un seguimiento de las asignaciones para encontrar fugas
  • Asigne más memoria de la necesaria y coloque marcadores al principio y al final de la memoria {durante la rutina libre, puede asegurarse de que estos marcadores sigan ahí
  • memset la memoria con un marcador en la asignación (para encontrar uso de la memoria no inicializada) y en free (para encontrar el uso de la memoria free)

Otra buena idea es nunca a usar cosas como strcpy, strcat, o sprintf -- siempre uso strncpy, strncat, y snprintf. También hemos escrito nuestras propias versiones de estos, para asegurarnos de que no escribimos el final de un búfer, y estos también han atrapado muchos problemas.

 7
Author: Graeme Perrow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-07 12:32:29

Debe atacar este problema tanto con el tiempo de ejecución como con el análisis estático.

Para el análisis estático considere compilar con PREfast (cl.exe /analyze). Detecta desajustes delete y delete[], desbordamientos de búfer y una serie de otros problemas. Esté preparado, sin embargo, para vadear a través de muchos kilobytes de advertencia L6, especialmente si su proyecto todavía tiene L4 no arreglado.

PREfast está disponible con Visual Studio Team System y, aparentemente, como parte del SDK de Windows.

 4
Author: Constantin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-07 12:32:50

La aparente aleatoriedad de la corrupción de memoria suena muy parecido a un problema de sincronización de hilo - un error se reproduce dependiendo de la velocidad de la máquina. Si los objetos (trozos de memoria) se comparten entre subprocesos y las primitivas de sincronización (sección crítica, mutex, semáforo, otros) no están por clase (por objeto, por clase), entonces es posible llegar a una situación en la que la clase (trozo de memoria) se elimina / libera mientras está en uso, o se usa después de eliminado / liberado.

Como a prueba para eso, puedes agregar primitivas de sincronización a cada clase y método. Esto hará que su código sea más lento porque muchos objetos tendrán que esperar unos por otros, pero si esto elimina la corrupción del montón, su problema de corrupción del montón se convertirá en un problema de optimización de código.

 3
Author: Ignas Limanauskas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-25 19:55:09

¿Es esto en condiciones de baja memoria? Si es así, podría ser que new está regresando NULL en lugar de lanzar std::bad_alloc. Los compiladores VC++ más antiguos no implementaron esto correctamente. Hay un artículo sobre Fallas en la asignación de memoria heredada bloqueos STL aplicaciones creadas con VC6.

 3
Author: Steve Steiner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-07 12:34:24

Probaste compilaciones antiguas, pero ¿hay alguna razón por la que no puedas ir más atrás en el historial del repositorio y ver exactamente cuándo se introdujo el error?

De lo contrario, sugeriría agregar un registro simple de algún tipo para ayudar a rastrear el problema, aunque estoy en una pérdida de lo que específicamente podría querer registrar.

Si puede averiguar qué es exactamente lo que puede causar este problema, a través de Google y la documentación de las excepciones que está recibiendo, tal vez eso le dará más información sobre qué buscar en el código.

 1
Author: Mike Stone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-04 07:48:51

Mi primera acción sería la siguiente:

  1. Construya los binarios en la versión "Release" pero creando un archivo de información de depuración (encontrará esta posibilidad en la configuración del proyecto).
  2. Utilice Dr Watson como depurador defualt (DrWtsn32-I) en una máquina en la que desea reproducir el problema.
  3. Reproduzca el problema. El Dr. Watson producirá un vertedero que podría ser útil para un análisis posterior.

Otro intento podría ser usar WinDebug como una herramienta de depuración que es bastante potente ser al mismo tiempo también ligero.

Tal vez estas herramientas le permitirán al menos reducir el problema a cierto componente.

¿Y está seguro de que todos los componentes del proyecto tienen la configuración correcta de la biblioteca de tiempo de ejecución (pestaña C/C++, categoría de generación de código en la configuración del proyecto VS 6.0)?

 1
Author: Piotr Tyburski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-04 08:26:55

Así que de la información limitada que tiene, esto puede ser una combinación de una o más cosas:

  • Mal uso del montón, es decir, doble libera, leer después de libre, escribir después de libre, estableciendo la bandera HEAP_NO_SERIALIZE con allocs y libera de múltiples hilos en el mismo montón
  • Fuera de memoria
  • Código incorrecto (es decir, desbordamientos de búfer, desbordamientos de búfer, etc.)
  • Cuestiones de"Tiempo"

Si se trata de los dos primeros pero no de los últimos, ya deberías haberlo cogido. con cualquier pageheap.exe.

Lo que probablemente significa que se debe a cómo el código está accediendo a la memoria compartida. Desafortunadamente, rastrear eso va a ser bastante doloroso. El acceso no sincronizado a la memoria compartida a menudo se manifiesta como extraños problemas de "sincronización". Cosas como no usar semántica de adquisición / liberación para sincronizar el acceso a la memoria compartida con una bandera, no usar bloqueos apropiadamente, etc.

Por lo menos, sería útil poder rastrear las asignaciones de alguna manera, como lo fue sugerido antes. Al menos entonces se puede ver lo que realmente sucedió hasta la corrupción montón e intentar diagnosticar a partir de eso.

También, si puede redirigir fácilmente las asignaciones a varios montones, es posible que desee probar eso para ver si eso corrige el problema o resulta en un comportamiento de errores más reproducible.

Cuando estaba probando con VS2008, ¿se ejecutó con HeapVerifier con Conserve Memory establecido en Sí? Esto podría reducir el impacto en el rendimiento del asignador de montones. (Además, tienes que ejecutar con él Debug- > Start with Application Verifier, pero es posible que ya lo sepas.)

También puede probar la depuración con Windbg y varios usos de la !comando heap.

MSN

 1
Author: Mat Noguchi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-22 16:51:06

Si elige reescribir nuevo / eliminar, he hecho esto y tengo un código fuente simple en:

http://gandolf.homelinux.org/~smhanov/blog/?id=10

Esto captura fugas de memoria y también inserta datos de guardia antes y después del bloque de memoria para capturar la corrupción del montón. Puedes integrarte con él poniendo # include " debug.h " en la parte superior de cada archivo CPP, y definiendo DEBUG y DEBUG_MEM.

 1
Author: Steve Hanov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 13:40:35

La sugerencia de Graeme de malloc/free personalizado es una buena idea. Vea si usted puede caracterizar algún patrón sobre la corrupción para darle una manija para apalancar.

Por ejemplo, si está siempre en un bloque del mismo tamaño (digamos 64 bytes) entonces cambie su par malloc/free para asignar siempre 64 bytes en su propia página. Cuando libere un fragmento de 64 bytes, establezca los bits de protección de memoria en esa página para evitar lecturas e ingenio (usando VirtualQuery). Entonces cualquiera que intente acceder a esto la memoria generará una excepción en lugar de corromper el montón.

Esto supone que el número de fragmentos de 64 bytes pendientes es solo moderado o que tiene mucha memoria para grabar en la caja.

 0
Author: Rob Walker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-02 04:23:34

El poco tiempo que tuve para resolver un problema similar. Si el problema todavía existe te sugiero que hagas esto : Monitorea todas las llamadas a nuevo / borrar y malloc / calloc / realloc / gratis. Hago una sola DLL exportando una función para registrar todas las llamadas. Esta función recibe el parámetro para identificar su fuente de código, el puntero al área asignada y el tipo de llamada que guarda esta información en una tabla. Se elimina todo par asignado / liberado. Al final o después de que necesite hacer una llamada a otra función para crear informe para los datos de la izquierda. Con esto puede identificar llamadas incorrectas (nuevo / libre o malloc / eliminar) o faltantes. Si tiene algún caso de buffer sobrescrito en su código, la información guardada puede ser incorrecta, pero cada prueba puede detectar / descubrir / incluir una solución de falla identificada. Muchas corridas para ayudar a identificar los errores. Buena suerte.

 0
Author: lsalamon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-19 11:52:50

¿Crees que esto es una condición de carrera? ¿Varios hilos comparten un montón? Puedes darle a cada hilo un heap privado con HeapCreate, entonces pueden correr rápido con HEAP_NO_SERIALIZE. De lo contrario, un montón debería ser seguro para subprocesos, si está utilizando la versión multiproceso de las bibliotecas del sistema.

 0
Author: Don,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-30 13:48:40

Un par de sugerencias. Usted menciona las copiosas advertencias en W4-Yo sugeriría tomarse el tiempo para arreglar su código para compilar limpiamente en el nivel de advertencia 4 - esto ayudará mucho a evitar errores sutiles difíciles de encontrar.

Segundo - para el conmutador /analyze - de hecho genera advertencias copiosas. Para usar este interruptor en mi propio proyecto, lo que hice fue crear un nuevo archivo de encabezado que usara # pragma warning para desactivar todas las advertencias adicionales generadas por / analyze. Luego más lejos abajo en el archivo, enciendo solo las advertencias que me importan. Luego use el conmutador / FI compiler para forzar que este archivo de cabecera se incluya primero en todas sus unidades de compilación. Esto debería permitirle usar el interruptor / analyze mientras controla la salida

 0
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-03 16:48:57