Descripción general del análisis de excepciones de software de C++

       Una gran parte del trabajo en los últimos años es solucionar varias anomalías encontradas durante la operación del software.Ya sea el módulo de red subyacente, el módulo de protocolo y el módulo de componentes, o el módulo de interfaz de usuario de nivel superior, se han tratado muchas veces y he visto varias excepciones o bloqueos de C++ han acumulado mucha experiencia práctica, y la compartiré con ustedes aquí. Este artículo describirá en detalle la clasificación de las excepciones de software C++ en el sistema Windows y los métodos de solución de problemas comúnmente utilizados , para brindarle una referencia y una referencia.

1. Clasificación de las excepciones de software

       Las excepciones comunes de software incluyen memoria fuera de los límites , violación de acceso a la memoria , desbordamiento de pila, desbordamiento de pila de subprocesos , puntero nulo y puntero salvaje , bucle infinito , interbloqueo , fuga de memoria, fuga de objetos GDI , desequilibrio de pila causado por convenciones de llamada de funciones inconsistentes , etc. .

      Algunas excepciones hacen que el software se bloquee inmediatamente. Algunas excepciones provocarán bloqueos después de ejecutarse durante un período de tiempo prolongado, como fugas de memoria y fugas de objetos GDI. Algunas excepciones no provocan bloqueos, sino que solo provocan que el software se bloquee o se congele, como bucles infinitos y puntos muertos.

       Hay otro tipo de problema que puede causar excepciones en la ejecución del código de negocios.Dichos problemas no causarán bloqueos del software, pero harán que el código de negocios no se ejecute de acuerdo con la lógica normal o las bifurcaciones, lo que resultará en una lógica de negocios anormal. Por ejemplo , una función arroja una excepción y hace que se salte parte del código , es decir, el código ejecutado no se ejecuta, lo que genera una excepción lógica cuando se ejecuta el código comercial posterior. En circunstancias normales, estos códigos omitidos realizarán algunos juicios y establecerán los valores de algunas variables, lo que afectará directamente el juicio y la lógica de ejecución del código subsiguiente, por lo que provocará excepciones de lógica comercial cuando se ejecute el código subsiguiente. Nos hemos encontrado con este problema varias veces antes.

      Otro ejemplo es que se sobrescribe el último error de la función API del sistema , lo que da como resultado un error lógico en el posterior juicio condicional del valor del último error, es decir, se produce un error de juicio. Hemos encontrado este problema antes. Agregamos una impresión al código fuente abierto de la biblioteca libjingle. Debido a que la encapsulación de la interfaz del código fuente abierto es relativamente profunda, no vimos que el valor del último error se sobrescribiría al agregar el código para impresión de registro pregunta. Específicamente, la función API del sistema se llama en el código que imprime el registro.Después de ejecutar el código para imprimir el registro, se sobrescribirá el último valor de error generado por el código de la biblioteca de fuente abierta en la oración anterior. La interfaz en el código fuente abierto en la siguiente oración debajo del registro de impresión determinará el valor del último error después de la ejecución del código fuente abierto en la oración anterior. en códigos comerciales subsiguientes Ha ocurrido una excepción lógica.

       Para la fuga del objeto GDI , se debe al uso del objeto GDI para dibujar la ventana, y el objeto GDI no se libera después de realizar la operación de dibujo. Los objetos GDI incluyen pincel de pluma , pincel de pincel , mapa de bits de mapa de bits , fuente de fuente , contexto de dispositivo DC , área de región , etc. Si hay una fuga de objetos GDI en el programa, no provocará una excepción ni se bloqueará inmediatamente. Cuando el número total de objetos GDI en el proceso del programa llegue a unos 10.000 , se producirá una excepción y se producirá un bloqueo de flashback. En el sistema de Windows, el límite superior de objetos GDI para un solo proceso es 10 000. Cuando los objetos GDI de un proceso se acercan a 10 000, se producirá una excepción de dibujo de función GDI y, a continuación, se producirá un bloqueo. De hecho, la investigación de las fugas de objetos GDI es mucho más simple que las fugas de memoria. Solo necesita usar la herramienta de software GDIView para averiguar qué objeto GDI tiene fugas, y puede averiguarlo rápidamente con el código. La interfaz de la herramienta GDIView es la siguiente:

 2. Use windbg para analizar las excepciones de software

       windbg es una de las herramientas de análisis y depuración de software más potentes y generales en la plataforma Windows. Es utilizada principalmente por la plataforma Windows para analizar diversas anomalías de software.

       La mayoría de los problemas de fallas anormales pueden ser capturados por el módulo de captura de excepciones en el software (todos usan la biblioteca de captura de excepciones carshreport de código abierto), y el contexto cuando ocurre la excepción se guarda en el archivo de volcado , y se puede usar windbg para analizar estáticamente estos volcados posteriormente.

       Para algunas excepciones que no provocan bloqueos del software , como interbloqueos, bucles infinitos y fugas de memoria, necesitamos montar windbg en el proceso de destino para el análisis dinámico.

       Para algunas excepciones que no pueden ser capturadas por el módulo de captura de excepciones , como el programa falla durante el proceso en ejecución, es necesario adjuntar windbg al proceso de destino para ejecutarlo, es decir, windbg y su proceso de destino adjunto están obligados a ejecutarse. juntos, una vez que el proceso de destino es anormal, windbg puede detectar e interrumpir inmediatamente. Después de adjuntar windbg al proceso de destino, necesitamos encontrar una forma de reproducir la excepción. Después de que ocurra la excepción, windbg la capturará y la interrumpirá. En este momento, podemos usar directamente el comando windbg para analizarlo, o podemos usar el comando .dump para eliminar la excepción El contexto se exporta a un archivo de volcado para el análisis post-mortem. Puede llevar mucho tiempo analizar el problema. La computadora en cuestión puede pertenecer a un colega o líder, y no puede ocupar las computadoras de otras personas todo el tiempo. En este momento, puede optar por exportar el archivo de volcado para su posterior análisis con windbg .

       Para algunos errores de cuadro emergente o excepciones atascadas de software , el software se ha atascado en este punto (el proceso de destino todavía está allí) y puede bloquear directamente windbg en este momento. Se ha producido una excepción en este momento, pero el momento de montar windbg No es demasiado tarde, y también se puede obtener la información de contexto de la excepción. Para este tipo de problema, no haga clic en el botón Aceptar del informe de errores, o no se apresure a eliminar el proceso de destino a través del administrador de recursos y mantenga el proceso de destino. En este momento, es el momento adecuado para colgar up windbg, y también puede obtener la información completa del contexto de la excepción. . Estas excepciones pueden ser difíciles de reproducir después, debemos aprovechar esta oportunidad y montar directamente windbg en el proceso en cuestión para su análisis. Si pierde esta oportunidad, puede ser difícil reproducirlo la próxima vez, lo que deja muchos peligros ocultos en el software.

3. Métodos comunes de solución de problemas de excepción distintos de windbg

       Por supuesto, además del análisis estático y la depuración dinámica de windbg , existen otros métodos comunes.Estos métodos también son muy importantes y deben dominarse, como usar VS para depurar directamente (Debug o Release debugging), adjuntar a el proceso para depurar , agregar registros de impresión , método de comparación de versiones históricas (descubrir el momento en que comenzó el problema), bloquear código comentado , establecer puntos de interrupción de datos (monitoreo en tiempo real de la memoria), etc. A veces, necesitamos usar una combinación de métodos.

       Para algunas excepciones lógicas en el negocio, generalmente es necesario agregar la impresión de registros para solucionar problemas. Otra excepción típica es que se encuentra un error durante la ejecución del software, y el software lo juzga por sí mismo y lo considera un error fatal, y llamará directamente a abortar o salir para terminar el proceso por la fuerza. Por ejemplo, en la biblioteca jsoncpp de código abierto, si se produce un error al analizar un nodo json anómalo, se llamará directamente a abort para finalizar por la fuerza todo el proceso. Para otro ejemplo, en la biblioteca webrtc de código abierto, cuando falla la solicitud de memoria de almacenamiento dinámico con new, la biblioteca webrtc pensará que se ha producido un error fatal y también llamará a abort para finalizar el proceso a la fuerza. Este tipo de forma activa finaliza el proceso a la fuerza, el módulo de captura de excepción es una excepción que no se puede capturar, por lo que no se generará un archivo de volcado.

       En este caso, windbg se puede montar en el proceso de destino para que se ejecute. Una vez que se llama a la interfaz de cancelación, windbg se interrumpirá. En este momento, al observar la pila de llamadas de la función, puede ver dónde el código desencadena el problema. . ¿Por qué windbg puede percibir que el módulo de captura de excepciones instalado en el software no puede capturarlo? Debido a que el software en sí no tiene RaiseException , el software finaliza activamente el proceso. Entonces, ¿por qué windbg puede percibirlo? Debido a que se genera una notificación de señal de finalización SIGABRT en la interfaz de cancelación , el depurador puede percibirla, por lo que windbg genera una interrupción. En este punto, use el comando kn para ver la pila de llamadas de función y podrá ver qué interfaces desencadenan el problema. Puede ir directamente a la implementación de la función de cancelación en Visual Studio para ver la implementación interna de la función de cancelación, de la siguiente manera:

/***
*void abort() - abort the current program by raising SIGABRT
*
*Purpose:
*   print out an abort message and raise the SIGABRT signal.  If the user
*   hasn't defined an abort handler routine, terminate the program
*   with exit status of 3 without cleaning up.
*
*   Multi-thread version does not raise SIGABRT -- this isn't supported
*   under multi-thread.
*
*Entry:
*   None.
*
*Exit:
*   Does not return.
*
*Uses:
*
*Exceptions:
*
*******************************************************************************/

void __cdecl abort (
        void
        )
{
    _PHNDLR sigabrt_act = SIG_DFL;

#ifdef _DEBUG
    if (__abort_behavior & _WRITE_ABORT_MSG)
    {
        /* write the abort message */
        _NMSG_WRITE(_RT_ABORT);
    }
#endif  /* _DEBUG */


    /* Check if the user installed a handler for SIGABRT.
     * We need to read the user handler atomically in the case
     * another thread is aborting while we change the signal
     * handler.
     */
    sigabrt_act = __get_sigabrt();
    if (sigabrt_act != SIG_DFL)
    {
        raise(SIGABRT);
    }

    /* If there is no user handler for SIGABRT or if the user
     * handler returns, then exit from the program anyway
     */

    if (__abort_behavior & _CALL_REPORTFAULT)
    {
        _call_reportfault(_CRT_DEBUGGER_ABORT, STATUS_FATAL_APP_EXIT, EXCEPTION_NONCONTINUABLE);
    }


    /* If we don't want to call ReportFault, then we call _exit(3), which is the
     * same as invoking the default handler for SIGABRT
     */


    _exit(3);
}

Como puede verse en el código, se genera una notificación de señal de terminación  SIGABRT .

       Cuando algunos módulos detectan una excepción, también llamarán a la función API DebugBreak , lo que hará que el depurador se rompa. La descripción de la función DebugBreak es la siguiente:

Por ejemplo, cuando una falla al solicitar memoria en la biblioteca webrtc de código abierto provocó un problema de flashback, la biblioteca webrtc creyó internamente que la falla de la aplicación de memoria era un error fatal, y se llamó a la función de cancelación para finalizar el proceso por la fuerza. Antes de llamar a la función abortar , primero se llamará a la función DebugBreak . Si el windbg actual está pendiente del proceso del problema, la llamada de la función DebugBreak interrumpirá el windbg, para que el depurador pueda percibir el problema. Mirando la llamada a la función pila en este punto, puede determinar qué operación desencadenó el problema. 

4. Problemas existentes en la biblioteca CrashReport de código abierto

      Muchos fabricantes utilizan la biblioteca de captura de excepción de CrashReport de código abierto , pero la biblioteca nativa de CrashReport es defectuosa. Muchos grandes fabricantes deberían usar la biblioteca de informes de fallas profundamente mejorada.

    La biblioteca de captura de excepción de informe de fallas de código abierto convierte dinámicamente la función API CreateThread HOOK de creación de hilos en la tabla de importación de la biblioteca cargada en nuestra función personalizada MyCreateThread (sin importar a qué interfaz se llame para crear un hilo, eventualmente irá a la interfaz CreateThread ), de modo que se pueda llamar a la función de la API del sistema SetUnhandledExceptionFilter montar un controlador de excepciones para cada subproceso creado.

     Sin embargo, este mecanismo es defectuoso. Las funciones de manejo de excepciones no se pueden montar en los subprocesos de todos los módulos del software. Las funciones de manejo de excepciones solo se pueden montar en bibliotecas cargadas antes del informe de bloqueo. Las bibliotecas cargadas después del informe de bloqueo no se pueden enganchar. Esto provocará que se produzcan excepciones. en aquellas bibliotecas que no realizan operaciones de enganche para ser capturadas. Cuando se inicia el exe, todas las bibliotecas dependientes se cargarán en el espacio de proceso. No podemos controlar que todas las bibliotecas se carguen antes que la biblioteca de informes de bloqueo, lo que también provoca algunos bloqueos anormales que no se pueden capturar cuando se genera un informe de bloqueo.

       Más tarde, mejoramos la biblioteca de informes de fallas y usamos el código en el proyecto de desvíos de código abierto de Microsoft para ENGANCHAR la interfaz UnhandledExceptionFilter en la biblioteca del sistema de Windows . Debido a que básicamente todas las excepciones eventualmente ingresarán a esta función, conectamos la interfaz UnhandledExceptionFilter a nuestra interfaz personalizada y podemos percibir casi todas las excepciones en la interfaz personalizada. Una vez que se detecta una excepción, se puede generar un archivo de volcado que contiene el contexto de la excepción. De esta manera, el problema de que la versión anterior de crashreport no se puede cargar después del gancho puede resolverse bien.La nueva versión de crashreport puede actuar en todos los módulos del proceso actual y, básicamente, todas las excepciones del proceso pueden ser capturadas.

       Por supuesto, el informe de fallas mejorado no puede detectar el 100 % de las excepciones, pero puede detectar más del 90 % de las excepciones. Para escenarios que no se pueden capturar, debe montar windbg en el proceso de destino y dejar que windbg lo capture.

Supongo que te gusta

Origin blog.csdn.net/chenlycly/article/details/123991269
Recomendado
Clasificación