Laboratorio 1.3 Solución de problemas de bloqueo: capturar volcados de memoria con la herramienta createdump

Se aplica a:   .NET Core 2.1, .NET Core 3.1, .NET 5

En este artículo se describe cómo usar la herramienta createdump para capturar archivos de volcado de memoria de .NET Core en Linux y, a continuación, usar lldb para diagnosticar el problema de bloqueo.

Requisitos previos

El requisito mínimo para seguir estos laboratorios de solución de problemas es tener una aplicación ASP.NET Core para demostrar problemas de bajo rendimiento de CPU y CPU alta.

Puede encontrar varias aplicaciones de ejemplo para lograr este objetivo en Internet. Por ejemplo, puede descargar y configurar el ejemplo de webapi simple de Microsoft para demostrar un comportamiento no deseado. O bien, puede usar la aplicación ASP.NET Core BuggyAmb como proyecto de ejemplo.

Si ha seguido las partes anteriores de esta serie, debe tener lista la siguiente configuración:

  • Nginx está configurado para hospedar dos sitios web:
    • El primero escucha las solicitudes mediante el encabezado de host myfirstwebsite ( ) y las solicitudes de enrutamiento a la aplicación de demostración ASP.NET Core que escucha en el puerto http://myfirstwebsite 5000.
    • El segundo escucha las solicitudes mediante el encabezado de host de buggyamb ( ) y las solicitudes de enrutamiento ASP.NET Core la segunda aplicación de ejemplo de errores que escucha en el puerto http://buggyamb 5001.
  • Ambas ASP.NET Core deben ejecutarse como servicios que se reinician automáticamente cuando se reinicia el servidor o la aplicación deja de responder.
  • El firewall local de Linux está habilitado y configurado para permitir el tráfico SSH y HTTP.

Nota

Si la configuración no está lista, vaya a "Part 2 Create and run ASP.NET Core apps."

Para continuar con este laboratorio, debe tener al menos una aplicación ASP.NET Core web problemática que se ejecute detrás de Nginx.

Objetivo de este laboratorio

Los archivos de volcado de datos principales generados automáticamente no son útiles porque no contienen toda la información de estado administrado. Se crea la herramienta recomendada para capturar archivos de volcado de memoria de .NET Core.

En esta parte, aprenderá a capturar un archivo de volcado de memoria mediante createdump y a abrir el archivo en lldb para diagnosticar el problema de bloqueo.

Configurar createdump para que se ejecute en la finalización del proceso

Createdump se instala automáticamente junto con cada tiempo de ejecución de .NET Core.

Como se explica en la documentación de la directiva de configuración createdump, puede establecer opciones de configuración que tengan variables de entorno. Estos se pasan al comando createdump como parámetros. Estas son las variables de entorno admitidas:

  • COMPlus_DbgEnableMiniDump: Si se establece en 1, habilita la generación automática de volcado de núcleo al finalizar. El valor predeterminado es 0.
  • COMPlus_DbgMiniDumpType: este es el tipo del archivo de mini volcado de datos que se creará. El valor predeterminado para esto es 2 (o, un tipo de enumeración de MiniDumpWithPrivateReadWriteMemory ). Esto significa que el archivo de volcado de datos generado incluirá los montón de GC y la información necesaria para capturar seguimientos de pila para todos los subprocesos existentes en un proceso.
  • COMPlus_DbgMiniDumpName: si se establece, use como plantilla para crear la ruta de acceso del archivo de volcado y el nombre de archivo. El PID se puede colocar en el nombre mediante el %d parámetro. La plantilla predeterminada es /tmp/coredump.%d . Con esta variable de entorno, puede configurar el directorio de salida.
  • COMPlus_CreateDumpDiagnostics: Si se establece en 1, habilita los mensajes de diagnóstico de la herramienta createdump (macro TRACE). Esta configuración puede ser útil si createdump no funciona como se esperaba y no genera un archivo de volcado de memoria.

Puede encontrar detalles sobre estas variables en createdump configuration policy.

La variable importante aquí es COMPlus_DbgEnableMiniDump . Debe establecer esta variable de entorno en 1. Hay varios métodos para establecer este entorno:

  • Establa en el archivo de configuración de la aplicación.
  • Use el export COMPlus_DbgEnableMiniDump=1 comando para establecerlo. Esta configuración no persistirá después de reiniciar el sistema operativo. Por lo tanto, debe establecerlo como persistente si desea mantener la configuración habilitada después de un reinicio.
  • Establa en el archivo ASP.NET Core unidad de servicio.

Establecer esta variable en ASP.NET Core de unidad de servicio es el método más sencillo. El inconveniente es que el servicio debe reiniciarse. En esta sección de solución de problemas, esta será la opción que se muestra.

Abra el archivo de servicio de la aplicación de errores y agregue la COMPlus_DbgEnableMiniDump=1 variable de entorno. Esto es lo mismo que ha hecho varias veces en capítulos anteriores de este aprendizaje.

Captura de pantalla del comando de errores.

Debe volver a cargar la configuración del servicio porque se cambió la configuración. A continuación, reinicie el servicio Desamb.

Captura de pantalla del comando sudo.

Después de realizar estos cambios, reproduce el problema de bloqueo. Si createdump funciona, el archivo de volcado debe escribirse en el directorio /tmp/ como coredump. <PID>. Siga los mismos pasos para reproducir el problema:

  1. Seleccione Bloqueo 3. La página se carga correctamente, pero devuelve un mensaje de error que sugiere que el proceso debería haber sido un bloqueo.
  2. Seleccione Lento. Esto generará un código de respuesta "HTTP 502" (error de puerta de enlace mal) en lugar de la tabla de productos.
  3. Después de que se produzca el problema, ninguna de las páginas se representará y recibirá el mismo mensaje de error durante 10-15 segundos.
  4. Después de 10-15 segundos, la aplicación comienza a funcionar correctamente.

Ahora debería tener un archivo de volcado de memoria en el directorio /tmp.

Captura de pantalla del comando ll.

Si no tiene un archivo de volcado de memoria, asegúrese de configurar el buggyamb.service archivo correctamente. También debe volver a cargar la configuración del servicio y reiniciar el servicio.

Abrir el archivo de volcado principal en lldb

Se recomienda mover el archivo de volcado a la carpeta para seguir el análisis ~/dumps/ de ejemplo. Para abrir el archivo de volcado, ejecute lldb --core ~/dumps/coredump.<10354> . En este comando, reemplace el marcador de posición 10354 por el PID del proceso.

Nota

Si ha abierto previamente un archivo de volcado de datos y ha trabajado con lldb, ya ha configurado símbolos e instalado SOS. Puede abrir el mismo archivo de volcado de versión de .NET Core sin tener que volver a descargar los símbolos. Sin embargo, si abre un archivo de volcado de versión de .NET Core diferente para el que aún no se descargan los símbolos, tendrá que descargar los símbolos de esa versión antes de poder iniciar el análisis.

Ejecute el comando SOS clrstack para mostrar la pila de llamadas administradas. Recuerde que estaba viendo un error al ejecutar el mismo comando mediante un archivo de volcado de memoria generado por el sistema. Esta vez, debería ver la pila de llamadas administradas correcta.

Captura de pantalla del comando lldb.

Este es un buen comienzo. Sin embargo, la pila de llamadas que se muestra pertenece al subproceso principal de nuestro proceso de depuración. No es el subproceso donde se produce la excepción.

Nota

Si abrimos un archivo de volcado de memoria en WinDbg en Windows, WinDbg seleccionaría directamente el subproceso que provocó el bloqueo. Sin embargo, este no es el caso de lldb. En lldb, WinDbg no selecciona automáticamente el subproceso que desencadenó el depurador para generar el volcado de memoria.

Aunque este comportamiento de WinDbg es útil al depurar, la falta de esta característica en lldb no es el fin del mundo. En su lugar, puede examinar todos los subprocesos para intentar determinar dónde se podría lanzar la excepción. Comience examinando los subprocesos nativos mediante el thread list comando.

Siempre es una buena idea empezar ejecutando una inspección rápida de todas las pilas de llamadas de subproceso para que pueda comprender lo que se estaba ejecutando en el momento en que se generó el archivo de volcado. Vea primero la lista de subprocesos nativos que tiene el thread list comando.

Nota

El asterisco (*) cerca del primer subproceso de la lista ( ) indica que thread #1 es el subproceso activo.

Captura de pantalla del comando list.

La inspección de subprocesos nativos no revela mucho. Dado que se trata de una aplicación .NET Core, examine la lista de subprocesos CLR ejecutando el comando clrthreads SOS. Este comando enumera los subprocesos administrados que se ejecutan en la aplicación.

Captura de pantalla del comando clrthreads.

Esta captura de pantalla no muestra todos los subprocesos administrados. Sin embargo, el detalle en el que debes centrarte aparece en la captura de pantalla. Thread #15 tiene una excepción que hemos visto en los registros del sistema.

Captura de pantalla de información de thread15.

Examine la pila de llamadas de ese subproceso. Para ello, primero debe seleccionar el subproceso en cuestión. En el análisis de volcado de memoria que va a ejecutar, lo más probable es que el número de subproceso sea diferente. Para seleccionar otro subproceso como subproceso activo, use el comando y pase el id. de thread select subproceso dbg de lldb. Por ejemplo, ejecute thread select 15 para cambiar al subproceso 15. A continuación, todos los comandos sucesivos que ejecute estarán en el contexto de ese subproceso. Para ver la pila de llamadas nativas, ejecute el bt comando (seguimiento posterior).

Captura de pantalla del comando select.

Como puede ver en esta captura de pantalla, este subproceso es sin duda el subproceso que desencadenó el bloqueo.

  • PROCEndProcess y PROCAbort() se llama después de una excepción no controlada.
  • POCCreateCrashDump indica que .NET Core escribe un volcado de memoria.

Puede examinar la pila de llamadas administradas ejecutando el clrstack comando. Sin embargo, esto no revelará mucho. Ejecute el pe comando para obtener los detalles de la excepción.

Captura de pantalla del comando pe.

Esta información indica: A System.Net.HttpWebRequest se desencadena en la página Crash3 del LogTheRequest() método. Esta es una información importante para ayudar a localizar el problema. Pero, ¿qué ocurre si desea encontrar la dirección URL de la solicitud HTTP? Para continuar, intente inspeccionar los objetos a los que se hace referencia en la pila para ver si puede recopilar más información de esta lista. Para mostrar todos los objetos administrados que se encuentran dentro de los límites de la pila actual, ejecute dso .

Captura de pantalla del comando dso.

Esto no es útil. No debería ver ninguna System.Net.HttpWebRequest instancia. Hay instancias de la excepción y ya la ha inspeccionado. Por lo tanto, este comando no produjo nueva información relacionada con la causa.

Todos los objetos administrados se almacenan en un montón administrado y podemos ver el montón administrado ejecutando dumpheap . No se ejecute sin ningún parámetro porque, a continuación, el comando enumerará todos los objetos dentro del montón administrado dumpheap (una lista grande). En su lugar, puede obtener las estadísticas del montón mediante el dumpheap -stat comando.

Puede usar una táctica más para restringir las estadísticas ejecutando el comando en el siguiente formato:

dumpheap -stat -type System.Net.HttpWebRequest

En la siguiente captura de pantalla se muestran las estadísticas de los objetos administrados, que contienen la cadena System.Net.HttpWebRequest en su nombre.

Captura de pantalla de la información de la solicitud.

En la aplicación de ejemplo, solo hay un System.Net.HttpWebRequest objeto en el montón administrado. En la lista anterior, la dirección que se ve junto a la entrada no es la dirección de ese objeto HttpWebRequest en la memoria. En su lugar, es la dirección que corresponde a la "tabla de métodos" de objetos de tipo System.Net.HttpWebRequest . Para obtener la lista real de los objetos, puede pasar esa dirección de tabla de métodos (MT) al dumpheap comando de la siguiente manera:

dumpheap -mt <address>

Por ejemplo, ejecute dumpheap -mt 00007f53623cb640 para buscar la dirección del objeto.

Captura de pantalla del comando dumpheap.

Ahora, puede identificar la dirección del objeto problemático. En este ejemplo, es 00007f51300c0868 . Puede investigar las propiedades del objeto pasando esa dirección al dumpobj comando. Esto enumerará las propiedades de ese objeto. En este ejemplo, ejecute dumpobj 00007f51300c0868 para examinar las propiedades del objeto.

Captura de pantalla del comando dumpobj.

Nota

Está investigando un System.Net.HttpWebRequest objeto y que una de sus propiedades es _requestUri . Se trata de un objeto del System.Uri tipo. Desea determinar el URI. Por lo tanto, pase la dirección de la _requestUri propiedad al dumpobj comando

Copie la dirección del System.Uri objeto e investigue usando de dumpobj nuevo. Ejecute dumpobj 00007f51300bfbb8. La dirección del objeto en el archivo de volcado de memoria que generó será, sin duda, diferente. La lista mostrará la _string propiedad System.Uri de .

Captura de pantalla del comando dumpobj2.

Copie la dirección _string de y, a continuación, vuelva a ejecutar el dumpobj comando en él. Ejecute dumpobj 00007f51300bfb40. El resultado se muestra en la siguiente captura de pantalla.

Captura de pantalla del comando dumpobj3.

Por último, puede encontrar la dirección URL de HttpWebRequest: http://buggyamb/Problem/Api/NotExistingLoggingApi . Como su nombre sugiere, probablemente no se trata de una página existente dentro de la aplicación.

Para concluir, la teoría sobre cómo se produjo el bloqueo es la siguiente:

  • HttpWebRequest se realiza en una dirección URL no existente en el LogTheRequest() método de la página web crash3.
  • En una aplicación real, la solución para solucionar este problema sería controlar los errores cuando HttpWebRequest se realiza. Sin embargo, en este caso, la solución es mucho más sencilla: no realice una solicitud HttpWebRequest a una página no existente.

En este punto, probablemente debería tener más preguntas sobre lo que causó el bloqueo. Por ejemplo, ¿por qué se desencadenó el bloqueo después de seleccionar el vínculo Lento?

No dude en continuar la investigación por su cuenta. El siguiente paso sugerido sería ejecutar el comando usando la dirección del objeto para averiguar gcroot HttpWebRequest dónde está arraigado. Esto puede ayudarle a desarrollar una imagen de cómo se produjo el bloqueo.

Esto concluye el laboratorio. Presione Ctrl+C o use el q comando para salir del depurador de lldb.

Siguientes pasos

Laboratorio 2.1 Solución de problemas de rendimiento mediante createdump en Linux