2008-08-11 14 views
16

Por lo tanto, necesito ayuda. Estoy trabajando en un proyecto en C++. Sin embargo, creo que de alguna manera he logrado corromper mi montón. Esto se basa en el hecho de que he añadido un std::string a una clase y asignándole un valor de otra std::string:De administración de memoria, corrupción de montón y C++

std::string hello = "Hello, world.\n"; 
/* exampleString = "Hello, world.\n" would work fine. */ 
exampleString = hello; 

accidentes en mi sistema con un volcado de pila. Así que, básicamente, necesito detener e ir a través de todo mi código y cosas de gestión de memoria y averiguar dónde he metido la pata. La base de código todavía es pequeña (alrededor de 1000 líneas), por lo que es fácilmente factible.

Aún así, estoy sobre mi cabeza con este tipo de cosas, así que pensé en tirarlo allí. Estoy en un sistema Linux y busqué con valgrind, y aunque no sabía completamente lo que estaba haciendo, sí informaba que el destructor std::string no era válido. Debo admitir que recibí el término 'Heap Corruption' de una búsqueda en Google; también se apreciarían artículos de propósito general sobre este tipo de cosas.

(En rm -rf ProjectDir antes, hacer de nuevo en C#: D)

EDIT: no he dejado claro, pero lo que estoy pidiendo son formas en que un consejo de diagnóstico de este tipo de problemas de memoria. Sé que las cosas de std :: string son correctas, así que es algo que he hecho (o un error, pero no es un problema con Select). Estoy seguro de que podría verificar el código que he escrito y ustedes muy inteligentes verían el problema en un instante, pero quiero agregar este tipo de análisis de código a mi 'caja de herramientas', por así decirlo.

Respuesta

21

Estos mecanismos son relativamente baratos para, posiblemente, la solución del problema:

  1. mantener un ojo en mi heap corruption question - Estoy actualizando con las respuestas que se dan la salida. El primero fue equilibrar new[] y delete[], pero ya lo estás haciendo.
  2. Give valgrind más de una vez; es una herramienta excelente, y solo desearía que estuviera disponible en Windows. Solo ralentizo tu programa a la mitad, lo cual es bastante bueno en comparación con los equivalentes de Windows.
  3. Piensa en usar el Google Performance Tools como un reemplazo de malloc/nuevo.
  4. ¿Ha limpiado todos sus archivos de objetos y comenzó de nuevo? Tal vez su archivo make es ... "suboptimal"
  5. No es assert() suficiente en su código. ¿Cómo sé eso sin haberlo visto? Al igual que el hilo dental, nadie assert() es suficiente en su código. Agregue una función de validación para sus objetos y llámelo en el inicio del método y el final del método.
  6. ¿Estás compiling -wall? Si no, hazlo.
  7. Búscate una herramienta de pelusa como PC-Lint. Una pequeña aplicación como la suya podría caber en la página PC-lint demo, lo que significa que no hay compra para usted.
  8. Compruebe que está anulando los punteros después de eliminarlos. A nadie le gusta un puntero colgante. Mismo concierto con punteros declarados pero no asignados.
  9. Deje de usar matrices. Use un vector en su lugar.
  10. No utilice punteros sin formato. Use un smart pointer. No use auto_ptr! Esa cosa es ... sorprendente; su semántica es muy extraña. En su lugar, elija uno de Boost smart pointers, o algo fuera de the Loki library.
+2

+1, buena lista! Sin embargo, disputaría el n. ° 8: si bien impide los accesos "malos", en realidad es un olor codificado que oculta la lógica pobre o la administración deficiente de la vida útil de los objetos en mi experiencia ... – Roddy

+0

Actualmente, C++ tiene sus propios indicadores inteligentes en el estándar biblioteca, así que no hay necesidad de Boost o Loki para eso. –

0

Por lo que puedo decir, su código es correcto. Suponiendo que exampleString es una std :: string que tiene un alcance de clase como el que describes, deberías ser capaz de inicializarlo/asignarlo de esa manera. Tal vez hay algún otro problema? Tal vez un fragmento de código real lo ayude a contextualizarlo.

Pregunta: ¿exampleString es un puntero a un objeto string creado con new?

1

Podría ser un daño en el montón, pero es muy probable que sea un daño en la pila. Jim tiene razón. Realmente necesitamos un poco más de contexto. Esas dos líneas de fuente no nos dicen mucho en forma aislada. Podría haber varias cosas que causen esto (que es la verdadera alegría de C/C++).

Si te sientes cómodo publicando tu código, incluso podrías arrojarlo todo en un servidor y publicar un enlace. Estoy seguro de que obtendrá muchos más consejos de esa manera (algunos de ellos, sin duda, sin relación con su pregunta).

1

Su código como puedo ver no tiene ningún error. Como se ha dicho, se necesita más contexto.

Si aún no lo ha intentado, instale gdb (el depurador gcc) y compile el programa con -g. Esto compilará en símbolos de depuración que puede usar gdb. Una vez que haya instalado gdb, ejecútelo con el programa (gdb). This es un cheatsheat útil para usar gdb.

Establezca un punto de interrupción para la función que produce el error y vea cuál es el valor de exampleString. Haga también lo mismo para cualquier parámetro que esté pasando a exampleString. Esto al menos debería decirle si las std :: cadenas son válidas.

Encontré la respuesta de this article para ser una buena guía sobre punteros.

1

El código era simplemente un ejemplo de dónde mi programa estaba fallando (se asignó en la pila, Jim).Realmente no estoy buscando 'qué he hecho mal', sino 'cómo diagnostico lo que he hecho mal'. Enseña a un hombre a pescar y todo eso. Aunque estoy viendo la pregunta, no lo he dejado suficientemente claro. Gracias a Dios por la función de edición. : ')

Además, realmente solucioné el problema std :: string. ¿Cómo? Sustituyéndolo por un vector, compilando, luego reemplazando la cadena de nuevo. Se fue constantemente se bloquea allí, y que se corrigió a pesar de que ... no pudo. Hay algo desagradable allí, y no estoy seguro de qué. Yo quería comprobar el tiempo asigno manualmente la memoria en el montón, sin embargo:

this->map = new Area*[largestY + 1]; 
for (int i = 0; i < largestY + 1; i++) { 
    this->map[i] = new Area[largestX + 1]; 
} 

y eliminarlo:

for (int i = 0; i < largestY + 1; i++) { 
    delete [] this->map[i]; 
} 
delete [] this->map; 

no he asignado una matriz 2D con C++ antes. Parece funcionar.

7

Ah, si quieres saber cómo depurar el problema, es simple. Primero, consigue un pollo muerto. Luego, start shaking it.

En serio, no he encontrado una forma consistente de rastrear este tipo de errores. Debido a que hay tantos problemas potenciales, no hay una lista de verificación simple para llevar a cabo. Sin embargo, recomendaría lo siguiente:

  1. Ponte cómodo en un depurador.
  2. Comience a revisar el depurador para ver si puede encontrar algo sospechoso. Compruebe especialmente para ver lo que está sucediendo durante la línea exampleString = hello;.
  3. Comprueba para asegurarse de que se está bloqueando realmente en la línea exampleString = hello;, y no al salir de algún bloque envolvente (lo que podría provocar el disparo de destructores).
  4. Compruebe cualquier magia de puntero que pueda estar haciendo. Aritmética del puntero, fundición, etc.
  5. Marque todas sus asignaciones y desasignaciones para asegurarse de que coincidan (no hay desasignaciones dobles).
  6. Asegúrese de no devolver ninguna referencia o puntero a los objetos en la pila.

Hay muchas otras cosas que probar, también. Estoy seguro de que otras personas también compartirán ideas.

1

Además, en realidad solucioné el problema de std :: string. ¿Cómo? Sustituyéndolo por un vector, compilando, luego reemplazando la cadena de nuevo. Fue chocando constantemente allí, y eso se solucionó a pesar de que ... no podía. Hay algo desagradable allí, y no estoy seguro de qué.

Parece que realmente sacudiste un pollo. Si no sabe por qué está funcionando ahora, entonces todavía está roto, y casi seguro que morderá más tarde (después de que haya agregado aún más complejidad).

3

Algunos lugares para comenzar:

Si estás en Windows, y el uso de Visual C++ 6 (espero que a Dios nadie lo usa todavía en estos días) es implementació de std :: string no está multihebra, y puede conducir a este tipo de cosas.

Here's an article I found which explains a lot of the common causes of memory leaks and corruption.

En mi lugar de trabajo anterior hemos utilizado Compuware BoundsChecker para ayudar con esto. Es comercial y muy caro, por lo que puede no ser una opción.

Aquí hay un par de librerías libres que pueden ser de alguna utilidad

http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx

Espero que ayude. ¡La corrupción de la memoria es un lugar sucky en el que estar!

1

Ejecutar Purify.

Es una herramienta casi mágica que informará cuando se está golpeando fuertemente la memoria no se debe tocar, fugas de memoria al no liberar cosas, de doble liberación, etc.

Funciona a nivel de código máquina , por lo que ni siquiera tiene que tener el código fuente.

Una de las conferencias más agradables que tuve en mi vida fue cuando Purify encontró una pérdida de memoria en su código, y pudimos preguntar, "¿es posible que no esté liberando memoria en su función foo() "y escuche el asombro en sus voces.

Pensaron que estábamos depurando a los dioses, pero luego les contamos el secreto para que pudieran ejecutar Purify antes de tener que usar su código. :-)

http://www-306.ibm.com/software/awdtools/purify/unix/

(Es bastante caro, pero tienen una eval descarga gratuita)

1

Una de las técnicas de depuración que utilizo con frecuencia (excepto en los casos de las rarezas más extrema) es dividir y conquistar Si su programa falla actualmente con algún error específico, divídalo por la mitad de alguna manera y vea si todavía tiene el mismo error. ¡Obviamente, el truco es decidir dónde dividir tu programa!

Su ejemplo como se muestra no muestra suficiente contexto para determinar dónde podría estar el error. Si alguien más intentara tu ejemplo, funcionaría bien. Por lo tanto, en su programa, intente eliminar la mayor cantidad de material extra que no nos mostró y vea si funciona en ese momento. Si es así, agregue el otro código de a poco a la vez hasta que comience a fallar. Entonces, lo que acaba de agregar es probablemente el problema.

Tenga en cuenta que si su programa es multiproceso, entonces es probable que tenga problemas más grandes. Si no, entonces deberías poder reducirlo de esta manera. ¡Buena suerte!

1

Aparte de herramientas como Boundschecker o Purify, su mejor opción para resolver problemas como este es ser realmente bueno leyendo el código y familiarizándose con el código en el que está trabajando.

La corrupción de memoria es una de las cosas más difíciles de solucionar y generalmente este tipo de problemas se resuelven gastando horas/días en un depurador y observando algo así como "¡oye, el puntero X se está utilizando después de eliminarlo!".

Si ayuda, es algo que obtienes a medida que adquieres experiencia.

Su asignación de memoria para la matriz parece correcta, pero asegúrese de verificar todos los lugares donde también accede a la matriz.

10

Una vez tuvimos un error que eludió todas las técnicas habituales, valgrind, purificar, etc. La falla solo ocurrió en máquinas con mucha memoria y solo en grandes conjuntos de datos de entrada.

Finalmente lo rastreamos utilizando puntos de vigilancia del depurador. Trataré de describir el procedimiento aquí:

1) Encuentre la causa de la falla. Desde su código de ejemplo, se ve que la memoria de "exampleString" está dañada y, por lo tanto, no se puede escribir en ella. Continuemos con esta suposición.

2) Establezca un punto de interrupción en la última ubicación conocida donde se use o modifique "exampleString" sin ningún problema.

3) Agregue un punto de observación al miembro de datos de 'exampleString'. Con mi versión de g ++, la cadena se almacena en _M_dataplus._M_p. Queremos saber cuándo cambia este miembro de datos. La técnica BGF para esto es:

(gdb) p &exampleString._M_dataplus._M_p 
$3 = (char **) 0xbfccc2d8 
(gdb) watch *$3 
Hardware watchpoint 1: *$3 

obviamente estoy usando Linux con g ++ y GDB aquí, pero creo que los puntos de reloj de memoria están disponibles en la mayoría de los depuradores.

4) Continuar hasta que se active el punto de observación:

Continuing. 
Hardware watchpoint 2: *$3 

Old value = 0xb7ec2604 "" 
New value = 0x804a014 "" 
0xb7e70a1c in std::string::_M_mutate() from /usr/lib/libstdc++.so.6 
(gdb) where 

El comando gdb where dará un rastreo hacia atrás mostrando lo que dio lugar a la modificación. Esta es una modificación perfectamente legal, en cuyo caso simplemente continúe, o si tiene suerte será la modificación debido a la corrupción de la memoria. En este último caso, ahora debería poder revisar el código que es realmente causando el problema y con suerte solucionarlo.

La causa de nuestro error era un acceso de matriz con un índice negativo. El índice fue el resultado de un molde de un puntero a un modulo 'int' del tamaño de la matriz. El error fue omitido por Valgrind et al. ya que las direcciones de memoria asignadas cuando se ejecutaban bajo esas herramientas nunca fueron "> MAX_INT" y nunca dieron como resultado un índice negativo.

+0

¡Gran discusión para Linux! Señorita en desarrollo en ese ambiente. Necesito una solución para WinDoze yo mismo ... (VS6.0 también) ... (¡no es mi culpa! Los clientes usan VS6.0 y los clientes siempre tienen razón :). –

Cuestiones relacionadas