c++ debugging crash lua mingw

c++ - Resolviendo accidentes aleatorios



debugging crash (17)

  1. Comience a registrar. Ponga declaraciones de registro en lugares donde cree que el código es escamoso. enfóquese en probar el código, y repita hasta que reduzca el problema a un módulo o a una función.

  2. ¡Poner afirma en todas partes!

  3. Mientras estás en eso, solo pon una expresión en una afirmación.

  4. Escriba una prueba unitaria para el código que cree que está fallando. De esta forma, puedes ejercitar el código de manera aislada del resto de tu entorno de tiempo de ejecución.

  5. Escriba más pruebas automatizadas que ejerzan el código problemático.

  6. No agregue más código encima del código incorrecto que está fallando. Esa es solo una idea tonta.

  7. Aprenda a escribir mini-volcados y realizar la depuración post mortem. Parece que otros aquí lo han explicado bastante bien.

  8. Ejercite el código incorrecto de tantas maneras posibles como sea posible para que pueda aislar el error.

  9. Use una compilación de depuración. Ejecute la compilación de depuración debajo del depurador si es posible.

  10. Recorte su aplicación eliminando binarios, módulos, etc ... si es posible para que le resulte más fácil intentar reproducir el error.

Recibo bloqueos aleatorios en mi aplicación C ++, es posible que no se bloquee durante un mes y luego se cuelgue 10 veces en una hora, y que a veces se bloquee al iniciarse, mientras que a veces puede fallar después de varias horas de funcionamiento (o no todas).

Utilizo GCC en GNU / Linux y MingW en Windows, por lo que no puedo usar Visual Studio JIT Debug ...

No tengo idea de cómo proceder, mirar al azar el código no funcionaría, el código es ENORME (y buena parte no fue mi trabajo, también tiene una buena cantidad de material heredado), y tampoco tener una pista sobre cómo reproducir el bloqueo.

EDITAR: Mucha gente mencionó que ... ¿cómo hago un volcado de memoria, minivolcado o cualquier otro duplicado? Esta es la primera vez que necesito una depuración post mortem.

EDIT2: En realidad, DrMingw capturó una pila de llamadas, no hay información de memoria ... Desafortunadamente, la pila de llamadas no me ayudó mucho, porque cerca del final de repente entra una biblioteca (o algo) que no tengo información de depuración , lo que resulta solo en algunos números hexadecimales ... Así que todavía necesito un volcado decente que brinde más información (especialmente sobre lo que estaba en la memoria ... específicamente, lo que estaba en el lugar que dio el error de "violación de acceso")

Además, mi aplicación usa Lua y Luabind, tal vez el error está causado por un script .lua, pero no tengo idea de cómo depurar eso.


Aquí hay muchas buenas respuestas, pero nadie ha tocado el ángulo de Lua.

Lua generalmente se comporta bien, pero aún es posible que cause daños en la memoria o fallas si, por ejemplo, la pila Lua se desborda o desborda, o si se ejecuta un bytecode incorrecto.

Una cosa fácil que puede hacer para detectar muchos de estos errores es definir la macro lua_assert en luaconf.h. Definir esto (por ejemplo, la afirmación de C estándar) permitirá una variedad de verificaciones de cordura dentro del núcleo de Lua.


Donde trabajo, los programas bloqueados generalmente generan un archivo de volcado del núcleo que se puede cargar en windbg.

Entonces tenemos una imagen de la memoria en el momento en que el programa se bloqueó. No hay mucho que puedas hacer con eso, pero al menos te da la última pila de llamadas. Una vez que conozca la función que se colgó, es posible que pueda rastrear el problema, al menos puede reducir el problema a un caso de prueba más reproducible.


Dos punteros / ideas más (además de core dump y valgrind en Linux):

1) Pruebe el "Creador de Qt" de Nokia. Es compatible con mingw y puede actuar como depurador post mortem.

2) Si es factible, ¿tal vez solo ejecute la aplicación en gdb constantemente?


En primer lugar, tiene suerte de que su proceso falle varias veces en un corto período de tiempo. Eso debería facilitar el proceder.

Esta es la forma de proceder.

  • Obtener un volcado de emergencia
  • Aislar un conjunto de posibles funciones sospechosas
  • Ajustar la comprobación del estado
  • Repetir

Obtener un volcado de emergencia

Primero, realmente necesitas obtener un volcado de emergencia.

Si no obtiene volcados de emergencia cuando se bloquea, comience por escribir una prueba que produzca volcados de emergencia confiables.

Vuelva a compilar el binario con símbolos de depuración o asegúrese de que puede analizar el volcado de bloqueo con símbolos de depuración.

Encuentra funciones sospechosas

Dado que tienes un volcado de emergencia, míralo en gdb o en tu depurador favorito y recuerda mostrar todos los hilos. Puede que no sea el hilo que ves en gdb que tiene errores.

Si observa dónde gdb dice que su binario se bloqueó, aísle algunas funciones que cree que pueden causar el problema.

Ver los bloqueos múltiples y las secciones de código de aislamiento que comúnmente están activas en todos los bloqueos es un ahorro de tiempo real.

Ajustar la comprobación del estado

Por lo general, se produce un bloqueo debido a un estado incoherente. La mejor manera de proceder es a menudo ajustar los requisitos del estado. Haz esto de la siguiente manera.

Para cada función que crea que podría causar el problema, documente qué estado legal debe tener la entrada o el objeto al ingresar a la función. (Haga lo mismo con el estado legal que debe tener al salir de la función, pero eso no es demasiado importante).

Si la función contiene un bucle, documente el estado legal que necesita tener al comienzo de cada iteración de bucle.

Agregue afirmaciones para todas esas expresiones de estado legal.

Repetir

Luego repite el proceso. Si aún se bloquea fuera de sus afirmaciones, apriete las afirmaciones aún más. En algún momento, el proceso se bloqueará en una afirmación y no debido a un bloqueo aleatorio. En este punto puede concentrarse en tratar de averiguar qué hizo que su programa pasara de un estado legal al ingresar a la función a un estado ilegal en el punto donde ocurrió la afirmación.

Si empareja los asertos con el registro detallado, debería ser más fácil seguir lo que hace el programa.


Eso suena como algo complicado como una condición de carrera.

Te sugiero que crees una compilación de depuración y la uses. También debe asegurarse de que se cree un volcado del núcleo cuando el programa falla.

La próxima vez que el programa falle, puede ejecutar gdb en el núcleo y ver dónde se encuentra el problema. Probablemente será una falla consecutiva, pero esto debería comenzar.


Este tipo de errores siempre son complicados: a menos que pueda reproducir el error, entonces su única opción es realizar cambios en su aplicación para que se registre información adicional, y luego esperar hasta que el error vuelva a suceder en la naturaleza.

Existe una herramienta excelente llamada Descargador de procesos que puede usar para obtener un volcado de emergencia de un proceso que experimenta una excepción o se cierra inesperadamente: puede solicitar a los usuarios que instalen eso y configuren las reglas para su aplicación.

De forma alternativa, si no desea solicitar a los usuarios que instalen otras aplicaciones, podría hacer que su aplicación supervise las excepciones y crear un volcado llamando a MiniDumpWriteDump .

La otra opción es mejorar el registro, sin embargo, averiguar qué información registrar (sin solo registrar todo) puede ser complicado, por lo que puede llevar varias iteraciones de bloqueo - cambiar el registro para buscar el problema.

Como dije, este tipo de errores siempre son difíciles de diagnosticar; en mi experiencia, en general, implica horas y horas de mirar a través de registros y volcados hasta que repentinamente obtienes ese momento eureka donde todo tiene sentido, la clave es recopilar la información correcta.


Inicie el programa bajo depurador (estoy seguro de que hay un depurador junto con GCC y MingW) y espere hasta que se bloquee en el depurador. En el momento de la falla, podrá ver qué acción específica está fallando, examinar el código ensamblador, los registros, el estado de la memoria, esto a menudo lo ayudará a encontrar la causa del problema.


Lo primero que haría es depurar el volcado del núcleo con gdb (tanto Windows como Linux). El segundo sería ejecutar un programa como Lint, Prefast (Windows), Clang Analyzer o algún otro programa de análisis estático (prepárese para muchos falsos positivos). La tercera cosa sería algún tipo de verificación en tiempo de ejecución, como Valgrind (o sus variantes cercanas), Microsoft Application Verifier o Google Perftools .

Y registrando. Lo cual no tiene que ir al disco. Podría, por ejemplo, iniciar sesión en una std::list<std::string> global, que se reduciría a las últimas 100 entradas. Cuando se captura una excepción, muestra el contenido de esa lista.


Otra verificación básica: asegúrese de hacer una reconstrucción completa de su proyecto. Si ha estado retocando varios archivos (especialmente archivos de encabezado) y haciendo compilaciones parciales, las cosas pueden volverse complicadas si las dependencias de su compilación no son perfectas. Una reconstrucción completa solo elimina esa posibilidad.

También para Windows, consulte las herramientas de depuración de Microsoft para Windows , y particularmente su herramienta gflags .


Parece que su programa sufre daños en la memoria. Como ya dije, tu mejor opción en Linux es probablemente valgrind. Pero aquí hay otras dos opciones:

  • En primer lugar, use un depurador malloc . Casi todas las bibliotecas de C ofrecen una implementación de depuración de malloc que inicializa la memoria (el malloc normal mantiene el contenido "antiguo" en la memoria), verifica los límites de un bloque asignado para detectar corrupción, etc. Y si eso no es suficiente, existe una gran variedad de implementaciones de terceros.

  • Es posible que desee echar un vistazo a la estación de trabajo VMWare. No lo he configurado de esa manera, pero a partir de sus materiales de marketing admiten una forma bastante interesante de depuración: ejecutar el debugee en una máquina virtual de "grabación". Cuando se produce un daño en la memoria, establezca un punto de interrupción de memoria en la dirección dañada y luego vuelva el tiempo de la máquina virtual al momento exacto en que se sobrescribió esa parte de la memoria. Vea este PDF sobre cómo configurar la depuración de la repetición con Linux / gdb. Creo que hay una demo de 15 o 30 días para Workstation 7, que podría ser suficiente para eliminar esos errores de tu código.


Probablemente haya cometido un error de memoria donde puso algunos valores en el espacio no asignado de alguna manera, es una buena razón para bloqueos aleatorios, durante mucho tiempo nadie intenta usar esa memoria para que no haya errores, puede echar un vistazo al lugares donde asigna memoria y compruebe dónde utiliza los punteros. Aparte de esto, como otros señalaron, debe usar un registro extenso, tanto en pantalla como en archivos.


Pruebe Valgrind (es gratis, de código abierto):

La distribución de Valgrind actualmente incluye seis herramientas de calidad de producción: un detector de errores de memoria, dos detectores de errores de subprocesos, un generador de perfiles de caché y predicción de ramificaciones, un generador de perfiles de caché de generación de gráficos y un generador de perfiles de montículos. También incluye dos herramientas experimentales: un detector de desbordamiento de matriz global / pila / global y un generador de vector de bloque básico SimPoint. Se ejecuta en las siguientes plataformas: X86 / Linux, AMD64 / Linux, PPC32 / Linux, PPC64 / Linux y X86 / Darwin (Mac OS X).

Valgrind hizo con frecuencia preguntas

La parte de Memcheck del paquete es probablemente el lugar para comenzar:

Memcheck es un detector de errores de memoria. Puede detectar los siguientes problemas que son comunes en los programas C y C ++.

  • Acceder a la memoria no debería, por ejemplo, desbordar y subutilizar bloques de montón, sobrepasar la parte superior de la pila y acceder a la memoria después de que se haya liberado.

  • Usar valores indefinidos, es decir, valores que no se han inicializado o que se han derivado de otros valores indefinidos.

  • Liberación incorrecta de la memoria del montón, como bloques de bloques de doble liberación, o uso no coincidente de malloc / nuevo / nuevo [] versus libre / eliminar / eliminar []

  • Superposición de punteros src y dst en memcpy y funciones relacionadas.

  • Pérdidas de memoria.


Si su aplicación no es específica de Windows, puede intentar compilar y ejecutar su programa en otras plataformas como Linux (distribución diferente, 32/64 bits, ... si tiene el lujo). Eso puede ayudar a desencadenar los errores de su programa. Por supuesto, debe usar las herramientas mencionadas en otras publicaciones como gdb, valgrind, etc.


Si todo lo demás falla (particularmente si el rendimiento bajo el depurador es inaceptable), loggear extensamente. Comience con los puntos de entrada: ¿es la aplicación transaccional? Registre cada transacción a medida que entra. Registre todas las llamadas al constructor para sus objetos clave. Como el bloqueo es tan intermitente, registra las llamadas a todas las funciones que pueden no ser llamadas todos los días.

Al menos comenzarás a reducir el lugar donde podría estar el bloqueo.


Ya has escuchado cómo manejar esto en Linux: inspecciona los volcados del núcleo y ejecuta tu código en valgrind. Así que su primer paso podría ser encontrar los errores en Linux y luego verificar si desaparecen bajo mingw. Como nadie mencionó mudflap aquí, lo haré: use mudflap si su distribución de Linux lo proporciona. mudflap lo ayuda a detectar el mal uso del puntero y los desbordamientos del búfer al rastrear la información a la que un puntero realmente puede apuntar:

Y para Windows: hay un depurador JIT para mingw, llamado DrMingw:


Ejecute la aplicación en Linux bajo valgrind para buscar errores de memoria. Los bloqueos aleatorios generalmente se reducen a la corrupción de la memoria.

Repare todos los errores que encuentre con la herramienta de validación de valgrind, y con suerte la falla desaparecerá.

Si todo el programa tarda demasiado en ejecutarse bajo valgrind, entonces divida la funcionalidad en pruebas unitarias y ejecútelas en valgrind, con suerte encontrará los errores de memoria que están causando los problemas.

Si no lo hace, asegúrese de que los núcleos estén habilitados ( ulimit -a ) y luego, cuando se bloquee, podrá averiguar dónde está el gdb .