Pregunta

Actualmente estoy trabajando para agregar excepciones y manejo de excepciones a mi aplicación OSS. Las excepciones han sido la idea general desde el principio, pero quería encontrar un buen marco de excepción y, con toda honestidad, comprender las convenciones de manejo de excepciones de C ++ un poco mejor antes de comenzar a usarlos. Tengo mucha experiencia con C#/. Net, Python y otros idiomas que usan excepciones. No soy ajeno a la idea (pero lejos de ser un maestro).

En C# y Python, cuando ocurre una excepción no controlada, el usuario obtiene un buen rastro de pila y en general mucho de muy útil Información de depuración invaluable. Si está trabajando en una aplicación OSS, hacer que los usuarios peguen esa información en informes de problemas es ... bueno, digamos que me resulta difícil vivir sin eso. Para este proyecto C ++, obtengo "la aplicación bloqueada", o de usuarios más informados, "Hice X, Y y Z, y luego se bloqueó". ¡Pero también quiero esa información de depuración!

Ya (y con gran dificultad) hice las paces con el hecho de que nunca veré una forma de plataforma cruzada y compiladora de obtener un rastro de la pila de excepciones C ++, pero sé que puedo obtener el nombre de la función y otros informacion relevante.

Y ahora quiero eso para mis excepciones no controladas. Estoy usando Boost :: Excepción, y tienen esto muy bonito diagnóstico_información ThingAmajig que puede imprimir el nombre, el archivo, la línea, la línea y, lo más importante, la información específica de la excepción que el programador agregó a esa excepción.

Naturalmente, manejaré excepciones dentro del código siempre que pueda, pero no soy tan ingenuo al pensar que no dejaré pasar un par (involuntariamente, por supuesto).

Entonces, lo que quiero hacer es envolver mi punto de entrada principal dentro de un try bloquear con un catch Eso crea un diálogo especial que informa al usuario que se ha producido un error en la aplicación, con información más detallada presentada cuando el usuario hace clic "Más" o "Información de depuración" o lo que sea. Esto contendría la cadena de Diagnostic_Information. Luego podría instruir a los usuarios que peguen esta información en informes de problemas.

Pero un instinto persistente me dice que envolver todo en un bloque de intentos es una muy mala idea. ¿Es lo que estoy a punto de hacer estúpido? Si es (e incluso si no lo es), ¿cuál es la mejor manera de lograr lo que quiero?

¿Fue útil?

Solución

Envolver todo su código en uno try/catch El bloque es A-OK. No reducirá la ejecución de nada dentro de él, por ejemplo. De hecho, todos mis programas tienen (código similar a) este marco:

int execute(int pArgc, char *pArgv[])
{
    // do stuff
}

int main(int pArgc, char *pArgv[])
{
    // maybe setup some debug stuff,
    // like splitting cerr to log.txt

    try
    {
        return execute(pArgc, pArgv);
    }
    catch (const std::exception& e)
    {
        std::cerr << "Unhandled exception:\n" << e.what() << std::endl;
        // or other methods of displaying an error

        return EXIT_FAILURE;
    }
    catch (...)
    {
        std::cerr << "Unknown exception!" << std::endl;

        return EXIT_FAILURE;
    }
}

Otros consejos

Poner un bloque de prueba/captura en main () está bien, no causa ningún problema. El programa está muerto en una excepción no controlada de todos modos. Sin embargo, no será útil en su búsqueda para obtener el rastro de pila tan importante. Esa información es Gonzo cuando el bloque de captura atrapa la excepción.

La captura de una excepción de C ++ tampoco será muy útil. Las probabilidades de que el programa muera con una excepción derivada de STD :: Exception son bastante escasas. Aunque podría suceder. Es mucho más probable que en una aplicación C/C ++ sea la muerte debido a las excepciones de hardware, AccessViolation es Numero Uno. Capturarlas requiere las palabras clave __try y __except en su método Main (). Nuevamente, hay muy poco contexto disponible, básicamente solo tiene un código de excepción. Un AV también le dice qué ubicación exacta de memoria causó la excepción.

Este no es solo un problema multiplataforma por cierto, no puede obtener un buen rastro de pila en ninguna plataforma. No hay una forma confiable de caminar por la pila, hay demasiadas optimizaciones (como la omisión de FramePointer) que hacen de este un viaje peligroso. Es la forma C/C ++: hágalo lo más rápido posible, no deje ni idea de lo que sucedió cuando explota.

Lo que debe hacer es depurar este tipo de problemas de la manera C/C ++. Necesitas crear un minidcup. Es aproximadamente análogo al "vertedero de núcleo" de antaño, una instantánea de la imagen del proceso en el momento en que ocurre la excepción. En aquel entonces, en realidad tienes un volcado completo del núcleo. Ha habido progreso, hoy en día es "mini", algo necesario porque un vertedero de núcleo completo tomaría cerca de 2 gigabytes. En realidad, funciona bastante bien diagnosticar el estado del programa.

En Windows, eso comienza llamando a SetUnHandledExceptionFilter (), proporciona un puntero de función de devolución de llamada a una función que se ejecutará cuando su programa muera con una excepción no controlada. Cualquier excepción, C ++ y SEH. Su próximo recurso es dbghelp.dll, disponible en las herramientas de depuración para la descarga de Windows. Tiene un punto de entrada llamado minidumpwritedump (), crea una minidump.

Una vez que obtenga el archivo creado por minidumpwritedump (), es bastante dorado. Puede cargar el archivo .dmp en Visual Studio, casi como si fuera un proyecto. Presione F5 y VS muelle por un tiempo intentando cargar archivos .pdb para las DLL cargadas en el proceso. Querrás configurar el servidor de símbolos, eso es muy importante para obtener buenas trazas de pila. Si todo funciona, obtendrá un "descanso de depuración" en el lugar exacto donde se lanzó la excepción ". Con un rastro de pila.

Cosas que debe hacer para que esto funcione sin problemas:

  • Use un servidor de compilación para crear los binarios. Necesita empujar los símbolos de depuración (archivos .pdb) a un servidor de símbolos para que estén fácilmente disponibles cuando depure el minidump.
  • Configure el depurador para que pueda encontrar los símbolos de depuración para todos los módulos. Puede obtener los símbolos de depuración para Windows de Microsoft, los símbolos para su código deben provenir del servidor de símbolos mencionado anteriormente.
  • Escriba el código para atrapar la excepción no controlada y crear el Minidump. Mencioné setUnhandledExceptionFilter () pero el código que crea el minidump no debe estar en el programa que se bloqueó. Las probabilidades de que pueda escribir el Minidump con éxito son bastante delgadas, el estado del programa no es determinado. Lo mejor que puede hacer es ejecutar un proceso de "guardia" que vigile a un mutex llamado. Su filtro de excepción puede establecer el mutex, el guardia puede crear el minidump.
  • Cree una forma para que el Minidump se transfiera de la máquina del cliente a la suya. Utilizamos el servicio S3 de Amazon para eso, terabytes a un ritmo razonable.
  • Cablee el manejador minidcomado en su base de datos de depuración. Usamos JIRA, tiene un servicio web que nos permite verificar el cubo de bloqueo en una base de datos de bloqueos anteriores con la misma "firma". Cuando es único o no tiene suficientes golpes, le pedimos al código de Crash Manager que cargue el Minidump en Amazon y cree la entrada de la base de datos de errores.

Bueno, eso es lo que hice por la compañía para la que trabajo. Funcionó muy bien, redujo la frecuencia de cubos de choque de miles a docenas. Mensaje personal a los creadores del componente de código abierto ffdshow: te odio con pasión. ¡Pero ya no estás bloqueando nuestra aplicación! Buggers.

No, no es estúpido. Es una muy buena idea, y no cuesta prácticamente nada en tiempo de ejecución hasta que llegues a una excepción no controlada, por supuesto.

Tenga en cuenta que ya hay un manejador de excepción que envuelve su hilo, proporcionado por el sistema operativo (y otro por el runtime C, creo). Es posible que deba pasar ciertas excepciones a estos manejadores para obtener un comportamiento correcto. En algunas arquitecturas, el acceso a datos mal alineados se maneja por un controlador de excepción. Entonces es posible que desee un caso especial EXCEPTION_DATATYPE_MISALIGNMENT Y déjelo pasar al controlador de excepciones de nivel superior.

Incluyo los registros, la versión de la aplicación y el número de compilación, el tipo de excepción y un volcado de pila en hexadecimal anotado con nombres de módulos y compensaciones para valores hexadecimales que podrían ser direcciones para codificar. Asegúrese de incluir el número de versión y el número/fecha de compilación de su EXE.

También puedes usar VirtualQuery Para convertir los valores de la pila en "Modulename+Offset" con bastante facilidad. Y eso, combinado con un archivo .map a menudo le dirá exactamente dónde se bloqueó.

Descubrí que podía entrenar a los beta testers para enviar mi texto con bastante facilidad, pero en los primeros días lo que obtuve fue una imagen del diálogo de error en lugar del texto. Creo que se debe a que muchos usuarios no saben que puede hacer clic derecho en cualquier control de edición para obtener un menú con "Seleccionar todo" y "Copiar". Si lo volviera a hacer, agregaría un botón que copiara ese texto al portapapeles para que pueda pegar fácilmente en un correo electrónico.

Aún mejor si desea tomarse la molestia de tener un botón 'Enviar informe de error', pero simplemente dar a los usuarios una forma de llevar el texto a sus propios correos electrónicos lo lleva la mayor parte del camino, y no eleva ninguna bandera roja sobre "¿Qué información estoy compartiendo con ellos?"

De hecho, Boost :: Diagnostic_Information se ha diseñado específicamente para ser utilizado en un bloque de captura "global" (...), para mostrar información sobre excepciones que no deberían haber alcanzadolo. Sin embargo, tenga en cuenta que la cadena devuelta por Boost :: Diagnostic_Information no es fácil de usar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top