Compartir por


Introducción a la generación de perfiles

Un generador de perfiles es una herramienta que supervisa la ejecución de otra aplicación. Un generador de perfiles de Common Language Runtime (CLR) es una biblioteca de vínculos dinámicos (DLL) que consta de funciones que reciben mensajes de y envían mensajes a CLR mediante la API de generación de perfiles. ClR carga el archivo DLL del generador de perfiles en tiempo de ejecución.

Las herramientas de generación de perfiles tradicionales se centran en medir la ejecución de la aplicación. Es decir, miden el tiempo dedicado a cada función o al uso de memoria de la aplicación a lo largo del tiempo. La API de generación de perfiles tiene como destino una clase más amplia de herramientas de diagnóstico, como utilidades de cobertura de código e incluso ayudas avanzadas de depuración. Estos usos son todos diagnósticos por naturaleza. La API de generación de perfiles no solo mide, sino que también supervisa la ejecución de una aplicación. Por este motivo, la propia aplicación nunca debe usar la API de generación de perfiles y la ejecución de la aplicación no debe depender (o verse afectada por) del generador de perfiles.

La generación de perfiles de una aplicación CLR requiere más compatibilidad que generar perfiles de código de máquina compilado convencionalmente. Esto se debe a que CLR presenta conceptos como dominios de aplicación, recolección de elementos no utilizados, control de excepciones administrados, compilación Just-In-Time (JIT) de código (conversión de lenguaje intermedio común o CIL, código en código de máquina nativo) y características similares. Los mecanismos de generación de perfiles convencionales no pueden identificar ni proporcionar información útil sobre estas características. La API de generación de perfiles proporciona esta información que falta de forma eficaz, con un efecto mínimo en el rendimiento de CLR y la aplicación con perfiles.

La compilación JIT en tiempo de ejecución proporciona buenas oportunidades para la generación de perfiles. La API de generación de perfiles permite a un generador de perfiles cambiar el flujo de código de la CIL en memoria para una rutina antes de compilar JIT. De esta manera, el generador de perfiles puede agregar dinámicamente código de instrumentación a rutinas concretas que necesitan una investigación más profunda. Aunque este enfoque es posible en escenarios convencionales, es mucho más fácil implementar para CLR mediante la API de generación de perfiles.

Api de generación de perfiles

Normalmente, la API de generación de perfiles se usa para escribir un generador de perfiles de código, que es un programa que supervisa la ejecución de una aplicación administrada.

Una DLL del generador de perfiles usa la API de generación de perfiles, que se carga en el mismo proceso que la aplicación que se está generando el perfil. El archivo DLL del generador de perfiles implementa una interfaz de devolución de llamada (ICorProfilerCallback en .NET Framework versión 1.0 y 1.1, ICorProfilerCallback2 en la versión 2.0 y posteriores). CLR llama a los métodos de esa interfaz para notificar al generador de perfiles de eventos en el proceso de generación de perfiles. El generador de perfiles puede volver a llamar al entorno de ejecución mediante los métodos de las interfaces ICorProfilerInfo e ICorProfilerInfo2 para obtener información sobre el estado de la aplicación con perfiles.

Nota:

Solo la parte de recopilación de datos de la solución profiler debe ejecutarse en el mismo proceso que la aplicación con perfiles. Toda la interfaz de usuario y el análisis de datos deben realizarse en un proceso independiente.

En la ilustración siguiente se muestra cómo interactúa el archivo DLL del generador de perfiles con la aplicación que se está generando perfiles y CLR.

Captura de pantalla que muestra la arquitectura de generación de perfiles.

Las interfaces de notificación

ICorProfilerCallback e ICorProfilerCallback2 se pueden considerar interfaces de notificación. Estas interfaces constan de métodos como ClassLoadStarted, ClassLoadFinished y JITCompilationStarted. Cada vez que CLR carga o descarga una clase, compila una función, etc., llama al método correspondiente en la interfaz o ICorProfilerCallback2 del ICorProfilerCallback generador de perfiles.

Por ejemplo, un generador de perfiles podría medir el rendimiento del código a través de dos funciones de notificación: FunctionEnter2 y FunctionLeave2. Solo marca la hora de cada notificación, acumula los resultados y genera una lista que indica qué funciones consumieron la mayor cantidad de CPU o tiempo de reloj durante la ejecución de la aplicación.

Las interfaces de recuperación de información

Las otras interfaces principales implicadas en la generación de perfiles son ICorProfilerInfo e ICorProfilerInfo2. El generador de perfiles llama a estas interfaces según sea necesario para obtener más información para ayudar a su análisis. Por ejemplo, cada vez que CLR llama a la función FunctionEnter2 , proporciona un identificador de función. El generador de perfiles puede obtener más información sobre esa función llamando al método ICorProfilerInfo2::GetFunctionInfo2 para detectar la clase primaria de la función, su nombre, etc.

Características admitidas

La API de generación de perfiles proporciona información sobre una variedad de eventos y acciones que se producen en Common Language Runtime. Puede usar esta información para supervisar el funcionamiento interno de los procesos y analizar el rendimiento de la aplicación de .NET Framework.

La API de generación de perfiles recupera información sobre las siguientes acciones y eventos que se producen en CLR:

  • Eventos de inicio y apagado de CLR.

  • Eventos de creación y apagado del dominio de aplicación.

  • Eventos de carga y descarga de ensamblados.

  • Eventos de carga y descarga de módulos.

  • Eventos de creación y destrucción de vtable COM.

  • Compilación Just-In-Time (JIT) y eventos de lanzamiento de código.

  • Eventos de carga y descarga de clases.

  • Eventos de creación y destrucción de subprocesos.

  • Eventos de entrada y salida de la función.

  • Excepciones.

  • Realiza transiciones entre la ejecución de código administrada y no administrada.

  • Transiciones entre distintos contextos de tiempo de ejecución.

  • Información sobre las suspensiones en tiempo de ejecución.

  • Información sobre el montón de memoria en tiempo de ejecución y la actividad de recolección de elementos no utilizados.

Se puede llamar a la API de generación de perfiles desde cualquier lenguaje compatible con COM (no administrado).

La API es eficaz con respecto al consumo de CPU y memoria. La generación de perfiles no implica cambios en la aplicación con perfiles que son lo suficientemente importantes como para provocar resultados engañosos.

La API de generación de perfiles es útil para los generadores de perfiles de muestreo y no de muestreo. Un generador de perfiles de muestreo inspecciona el perfil en tics de reloj normales, por ejemplo, a 5 milisegundos separados. Un generador de perfiles que no es de muestreo se informa de un evento de forma sincrónica con el subproceso que provoca el evento.

Funcionalidad no admitida

La API de generación de perfiles no admite la siguiente funcionalidad:

  • Código no administrado, que debe generar perfiles mediante métodos Win32 convencionales. Sin embargo, clR profiler incluye eventos de transición para determinar los límites entre código administrado y no administrado.

  • Modificar automáticamente las aplicaciones que modifican su propio código con fines como la programación orientada a aspectos.

  • La comprobación de límites, porque la API de generación de perfiles no proporciona esta información. CLR proporciona compatibilidad intrínseca con la comprobación de límites de todo el código administrado.

  • Generación de perfiles remota, que no se admite por los siguientes motivos:

    • La generación de perfiles remota amplía el tiempo de ejecución. Al usar las interfaces de generación de perfiles, debe minimizar el tiempo de ejecución para que los resultados de la generación de perfiles no se vean afectados indebidamente. Esto es especialmente cierto cuando se supervisa el rendimiento de la ejecución. Sin embargo, la generación de perfiles remotas no es una limitación cuando las interfaces de generación de perfiles se usan para supervisar el uso de memoria o para obtener información en tiempo de ejecución sobre marcos de pila, objetos, etc.

    • El generador de perfiles de código CLR debe registrar una o varias interfaces de devolución de llamada con el tiempo de ejecución en el equipo local en el que se ejecuta la aplicación con perfiles. Esto limita la capacidad de crear un generador de perfiles de código remoto.

Subprocesos de notificación

En la mayoría de los casos, el subproceso que genera un evento también ejecuta notificaciones. Estas notificaciones (por ejemplo, FunctionEnter y FunctionLeave) no necesitan proporcionar el elemento explícito ThreadID. Además, el generador de perfiles podría decidir usar el almacenamiento local de subprocesos para almacenar y actualizar sus bloques de análisis en lugar de indexar los bloques de análisis en el almacenamiento global, en ThreadID función del subproceso afectado.

Tenga en cuenta que estas devoluciones de llamada no se serializan. Los usuarios deben proteger su código creando estructuras de datos seguras para subprocesos y bloqueando el código del generador de perfiles cuando sea necesario para evitar el acceso paralelo desde varios subprocesos. Por lo tanto, en determinados casos puede recibir una secuencia inusual de devoluciones de llamada. Por ejemplo, supongamos que una aplicación administrada genera dos subprocesos que ejecutan código idéntico. En este caso, es posible recibir un evento ICorProfilerCallback::JITCompilationStarted para alguna función de un subproceso y una FunctionEnter devolución de llamada del otro subproceso antes de recibir la devolución de llamada ICorProfilerCallback::JITCompilationFinished . En este caso, el usuario recibirá una FunctionEnter devolución de llamada para una función que puede que no se haya compilado completamente Just-In-Time (JIT).

Security

Un archivo DLL del generador de perfiles es un archivo DLL no administrado que se ejecuta como parte del motor de ejecución de Common Language Runtime. Como resultado, el código del archivo DLL del generador de perfiles no está sujeto a restricciones de seguridad de acceso de código administrado. Las únicas limitaciones en el archivo DLL del generador de perfiles son las impuestas por el sistema operativo en el usuario que ejecuta la aplicación con perfiles.

Los autores de generadores de perfiles deben tomar las precauciones adecuadas para evitar problemas relacionados con la seguridad. Por ejemplo, durante la instalación, se debe agregar un archivo DLL del generador de perfiles a una lista de control de acceso (ACL) para que un usuario malintencionado no pueda modificarlo.

Combinación de código administrado y no administrado en un generador de perfiles de código

Un generador de perfiles escrito incorrectamente puede provocar referencias circulares a sí mismas, lo que da lugar a un comportamiento imprevisible.

Una revisión de la API de generación de perfiles de CLR puede crear la impresión de que puede escribir un generador de perfiles que contenga componentes administrados y no administrados que se llamen entre sí a través de la interoperabilidad COM o llamadas indirectas.

Aunque esto es posible desde una perspectiva de diseño, la API de generación de perfiles no admite componentes administrados. Un generador de perfiles CLR debe no administrarse completamente. Los intentos de combinar código administrado y no administrado en un generador de perfiles CLR pueden provocar infracciones de acceso, errores de programa o interbloqueos. Los componentes administrados del generador de perfiles activarán los eventos a sus componentes no administrados, lo que posteriormente llamaría a los componentes administrados de nuevo, lo que provocaría referencias circulares.

La única ubicación en la que un generador de perfiles CLR puede llamar al código administrado de forma segura está en el cuerpo común del lenguaje intermedio (CIL) de un método. La práctica recomendada para modificar el cuerpo de la CIL es usar los métodos de recompilación JIT en la interfaz ICorProfilerCallback4 .

También es posible usar los métodos de instrumentación más antiguos para modificar la CIL. Antes de completar la compilación Just-In-Time (JIT) de una función, el generador de perfiles puede insertar llamadas administradas en el cuerpo de la CIL de un método y, a continuación, COMPILAR JIT (vea el método ICorProfilerInfo::GetILFunctionBody ). Esta técnica se puede usar correctamente para la instrumentación selectiva del código administrado o para recopilar estadísticas y datos de rendimiento sobre el JIT.

Como alternativa, un generador de perfiles de código puede insertar enlaces nativos en el cuerpo de la CIL de cada función administrada que llama al código no administrado. Esta técnica se puede usar para instrumentación y cobertura. Por ejemplo, un generador de perfiles de código podría insertar enlaces de instrumentación después de cada bloque CIL para asegurarse de que el bloque se ha ejecutado. La modificación del cuerpo de la CIL de un método es una operación muy delicada y hay muchos factores que se deben tener en cuenta.

Generación de perfiles de código no administrado

La API de generación de perfiles de Common Language Runtime (CLR) proporciona una compatibilidad mínima para generar perfiles de código no administrado. Se proporciona la siguiente funcionalidad:

  • Enumeración de cadenas de pila. Esta característica permite a un generador de perfiles de código determinar el límite entre el código administrado y el código no administrado.

  • Determinación de si una cadena de pila corresponde al código administrado o al código nativo.

En las versiones 1.0 y 1.1 de .NET Framework, estos métodos están disponibles a través del subconjunto en proceso de la API de depuración de CLR. Se definen en el archivo CorDebug.idl.

En .NET Framework 2.0 y versiones posteriores, puede usar el método ICorProfilerInfo2::D oStackSnapshot para esta funcionalidad.

Uso de COM

Aunque las interfaces de generación de perfiles se definen como interfaces COM, Common Language Runtime (CLR) realmente no inicializa COM para usar estas interfaces. El motivo es evitar tener que establecer el modelo de subprocesos mediante la función CoInitialize antes de que la aplicación administrada haya tenido la oportunidad de especificar su modelo de subproceso deseado. Del mismo modo, el propio generador de perfiles no debe llamar a CoInitialize, ya que puede elegir un modelo de subprocesos que no es compatible con la aplicación que se está generando el perfil y puede provocar un error en la aplicación.

Pilas de llamadas

La API de generación de perfiles proporciona dos maneras de obtener pilas de llamadas: un método de instantánea de pila, que permite la recopilación dispersa de pilas de llamadas y un método de pila de sombras, que realiza un seguimiento de la pila de llamadas en cada instante.

Instantánea de pila

Una instantánea de pila es un seguimiento de la pila de un subproceso en un instante en el tiempo. La API de generación de perfiles admite el seguimiento de funciones administradas en la pila, pero deja el seguimiento de funciones no administradas al propio caminador de pila del generador de perfiles.

Para obtener más información sobre cómo programar el generador de perfiles para recorrer las pilas administradas, consulte el método ICorProfilerInfo2::D oStackSnapshot en este conjunto de documentación y Profiler Stack Walking en .NET Framework 2.0: Conceptos básicos y posteriores.

Pila de sombras

El uso del método de instantánea con demasiada frecuencia puede crear rápidamente un problema de rendimiento. Si desea realizar seguimientos de pila con frecuencia, el generador de perfiles debe crear una pila de sombras mediante las devoluciones de llamada de excepción FunctionEnter2, FunctionLeave2, FunctionTailcall2 e ICorProfilerCallback2 . La pila de sombras siempre es actual y se puede copiar rápidamente en el almacenamiento siempre que se necesite una instantánea de pila.

Una pila de sombras puede obtener argumentos de función, valores devueltos e información sobre las instancias genéricas. Esta información solo está disponible a través de la pila de sombras y se puede obtener cuando el control se entrega a una función. Sin embargo, es posible que esta información no esté disponible más adelante durante la ejecución de la función.

Devoluciones de llamada y profundidad de pila

Las devoluciones de llamada del generador de perfiles se pueden emitir en circunstancias muy restringidas de pila y un desbordamiento de pila en una devolución de llamada del generador de perfiles provocará una salida de proceso inmediata. Un generador de perfiles debe asegurarse de usar la menor pila posible en respuesta a las devoluciones de llamada. Si el generador de perfiles está diseñado para su uso en procesos que son sólidos frente al desbordamiento de la pila, el propio generador de perfiles también debe evitar desencadenar el desbordamiento de la pila.

Title Description
Configuración de un entorno de generación de perfiles Explica cómo inicializar un generador de perfiles, establecer notificaciones de eventos y generar perfiles de un servicio de Windows.
Interfaces de generación de perfiles Describe las interfaces no administradas que usa la API de generación de perfiles.
Generación de perfiles de funciones estáticas globales Describe las funciones estáticas globales no administradas que usa la API de generación de perfiles.
Enumeraciones de generación de perfiles Describe las enumeraciones no administradas que usa la API de generación de perfiles.
Estructuras de generación de perfiles Describe las estructuras no administradas que usa la API de generación de perfiles.