Adición del seguimiento de eventos a controladores en modo kernel
En esta sección se describe cómo usar la API de modo kernel de Seguimiento de eventos para Windows (ETW) para agregar seguimiento de eventos a controladores en modo kernel. La API del modo kernel de ETW se introdujo con Windows Vista y no se admite en sistemas operativos anteriores. Usa el seguimiento de software de WPP o el seguimiento de eventos WMI si el controlador necesita admitir la funcionalidad de seguimiento en Windows 2000 y versiones posteriores.
Sugerencia
Para ver el código de ejemplo que muestra cómo implementar ETW mediante el Kit de controladores de Windows (WDK) y Visual Studio, consulte el ejemplo eventdrv.
En esta sección:
Flujo de trabajo: adición de seguimiento de eventos a controladores en modo kernel
1. Decidir el tipo de eventos que se van a generar y dónde publicarlos
2. Crear un manifiesto de instrumentación que defina el proveedor, los eventos y los canales
3. Compile el manifiesto de instrumentación mediante el compilador de mensajes (Mc.exe)
5. Compilación del controlador
7. Pruebe el controlador para comprobar la compatibilidad con ETW.
Flujo de trabajo: adición de seguimiento de eventos a controladores en modo kernel
1. Decidir el tipo de eventos que se van a generar y dónde publicarlos
Antes de empezar a codificar, debe decidir qué tipo de eventos desea que el controlador registre a través del seguimiento de eventos para Windows (ETW). Por ejemplo, puede que quiera registrar eventos que pueden ayudarle a diagnosticar problemas después de distribuir el controlador o eventos que pueden ayudarle a desarrollar el controlador. Para obtener información, consulte Referencia del registro de eventos de Windows.
Los tipos de eventos se identifican con canales. Un canal es una secuencia con nombre de eventos de tipo Admin, Operational, Analytical o Debug dirigidos a una audiencia específica, similar a un canal de televisión. Un canal entrega los eventos del proveedor de eventos a los registros de eventos y a los consumidores de eventos. Para obtener información, consulte Definición de canales.
Durante el desarrollo, lo más probable es que le interesen los eventos de seguimiento que le ayudarán a depurar el código. Este mismo canal se puede usar en el código de producción para ayudar a solucionar problemas que pueden aparecer después de implementar el controlador. Es posible que también desee realizar un seguimiento de eventos que se puedan usar para medir el rendimiento; estos eventos pueden ayudar a los profesionales de TI a ajustar el rendimiento del servidor y pueden ayudar a identificar cuellos de botella de red.
2. Crear un manifiesto de instrumentación que defina el proveedor, los eventos y los canales
El manifiesto de instrumentación es un archivo XML que proporciona una descripción formal de los eventos que generará un proveedor. El manifiesto de instrumentación identifica el proveedor de eventos, especifica el canal o los canales (hasta ocho) y describe los eventos y las plantillas que usan los eventos. Además, el manifiesto de instrumentación permite la localización de cadenas, por lo que puede localizar los mensajes de seguimiento. El sistema de eventos y los consumidores de eventos pueden usar los datos XML estructurados proporcionados en el manifiesto para realizar consultas y análisis.
Para obtener información sobre el manifiesto de instrumentación, vea Escritura de un manifiesto de instrumentación (Windows), Esquema EventManifest (Windows) y Uso del registro de eventos de Windows (Windows).
El siguiente manifiesto de instrumentación muestra un proveedor de eventos que usa el nombre "Controlador de ejemplo". Tenga en cuenta que este nombre no tiene que ser el mismo que el nombre del archivo binario del controlador. El manifiesto también especifica un GUID para el proveedor y las rutas de acceso a los archivos de mensajes y recursos. Los archivos de mensajes y recursos permiten a ETW saber dónde localizar los recursos necesarios para descodificar e informar de los eventos. Estas rutas de acceso apuntan a la ubicación del archivo del controlador (.sys). El controlador debe instalarse en el directorio especificado en el equipo de destino.
En el ejemplo se usa el canal con nombre System, un canal para eventos de tipo Admin. Este canal se define en el archivo Winmeta.xml que se proporciona con el Kit de controladores de Windows (WDK) en el directorio%WindowsSdkDir%\include\mmm. El canal del sistema está protegido para las aplicaciones que se ejecutan en cuentas de servicio del sistema. El manifiesto incluye las plantillas de evento que describen los tipos de datos proporcionados con los eventos cuando se publican, junto con su contenido estático y dinámico. Este manifiesto de ejemplo define tres eventos: StartEvent
, SampleEventA
y UnloadEvent
.
Además de los canales, puede asociar eventos con niveles y palabras clave. Las palabras clave y los niveles proporcionan una manera de habilitar eventos y proporcionar un mecanismo para filtrar eventos cuando se publican. Las palabras clave se pueden usar para agrupar eventos relacionados lógicamente. Se puede usar un nivel para indicar la gravedad o detalle de un evento, por ejemplo, crítico, error, advertencia o informativo. El archivo Winmeta.xml contiene valores predefinidos para los atributos de evento.
Al crear una plantilla para la carga del evento (mensaje de evento y datos), debe especificar los tipos de entrada y salida. Los tipos admitidos se describen en la sección Comentarios del tipo complejo InputType (Windows).
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<instrumentationManifest
xmlns="http://schemas.microsoft.com/win/2004/08/events"
xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.microsoft.com/win/2004/08/events eventman.xsd"
>
<instrumentation>
<events>
<provider
guid="{b5a0bda9-50fe-4d0e-a83d-bae3f58c94d6}"
messageFileName="%SystemDrive%\ETWDriverSample\Eventdrv.sys"
name="Sample Driver"
resourceFileName="%SystemDrive%\ETWDriverSample\Eventdrv.sys"
symbol="DriverControlGuid"
>
<channels>
<importChannel
chid="SYSTEM"
name="System"
/>
</channels>
<templates>
<template tid="tid_load_template">
<data
inType="win:UInt16"
name="DeviceNameLength"
outType="xs:unsignedShort"
/>
<data
inType="win:UnicodeString"
name="name"
outType="xs:string"
/>
<data
inType="win:UInt32"
name="Status"
outType="xs:unsignedInt"
/>
</template>
<template tid="tid_unload_template">
<data
inType="win:Pointer"
name="DeviceObjPtr"
outType="win:HexInt64"
/>
</template>
</templates>
<events>
<event
channel="SYSTEM"
level="win:Informational"
message="$(string.StartEvent.EventMessage)"
opcode="win:Start"
symbol="StartEvent"
template="tid_load_template"
value="1"
/>
<event
channel="SYSTEM"
level="win:Informational"
message="$(string.SampleEventA.EventMessage)"
opcode="win:Info"
symbol="SampleEventA"
value="2"
/>
<event
channel="SYSTEM"
level="win:Informational"
message="$(string.UnloadEvent.EventMessage)"
opcode="win:Stop"
symbol="UnloadEvent"
template="tid_unload_template"
value="3"
/>
</events>
</provider>
</events>
</instrumentation>
<localization xmlns="http://schemas.microsoft.com/win/2004/08/events">
<resources culture="en-US">
<stringTable>
<string
id="StartEvent.EventMessage"
value="Driver Loaded"
/>
<string
id="SampleEventA.EventMessage"
value="IRP A Occurred"
/>
<string
id="UnloadEvent.EventMessage"
value="Driver Unloaded"
/>
</stringTable>
</resources>
</localization>
</instrumentationManifest>
3. Compile el manifiesto de instrumentación mediante el compilador de mensajes (Mc.exe)
El compilador de mensajes (Mc.exe) debe ejecutarse antes de compilar el código fuente. El compilador de mensajes se incluye en el Kit de controladores de Windows (WDK). El compilador de mensajes crea un archivo de encabezado que contiene definiciones para el proveedor de eventos, los atributos de evento, los canales y los eventos. Debe incluir este archivo de encabezado en el código fuente. El compilador de mensajes también coloca el script del compilador de recursos generado (*.rc) y los archivos de .bin generados (recursos binarios) que incluye el script del compilador de recursos.
Puede incluir este paso como parte del proceso de compilación de dos maneras:
Agregar la tarea del compilador de mensajes al archivo de proyecto del controlador (como se muestra en el ejemplo eventdrv).
Usar Visual Studio para agregar el manifiesto de instrumentación y configurar las propiedades del compilador de mensajes.
Agregar una tarea del compilador de mensajes al archivo de proyecto Para obtener un ejemplo de cómo puede incluir el compilador de mensajes en el proceso de compilación, examine el archivo de proyecto para el ejemplo Eventdrv. En el archivo Eventdrv.vcxproj, hay una <sección MessageCompile> que llama al compilador de mensajes. El compilador de mensajes usa el archivo de manifiesto (evntdrv.xml) como entrada para generar el archivo de encabezado evntdrvEvents.h. En esta sección también se especifican las rutas de acceso de los archivos RC generados y se habilitan las macros de registro del modo kernel. Puede copiar esta sección y agregarla al archivo de proyecto de controlador (.vcxproj).
<MessageCompile Include="evntdrv.xml">
<GenerateKernelModeLoggingMacros>true</GenerateKernelModeLoggingMacros>
<HeaderFilePath>.\$(IntDir)</HeaderFilePath>
<GeneratedHeaderPath>true</GeneratedHeaderPath>
<WinmetaPath>"$(SDK_INC_PATH)\winmeta.xml"</WinmetaPath>
<RCFilePath>.\$(IntDir)</RCFilePath>
<GeneratedRCAndMessagesPath>true</GeneratedRCAndMessagesPath>
<GeneratedFilesBaseName>evntdrvEvents</GeneratedFilesBaseName>
<UseBaseNameOfInput>true</UseBaseNameOfInput>
</MessageCompile>
Al compilar el ejemplo de Eventdrv.sys, Visual Studio crea los archivos necesarios para el seguimiento de eventos. También agrega el manifiesto evntdrv.xml a la lista de archivos de recursos para el proyecto de controlador. Puede seleccionar y mantener presionado (o haga clic con el botón derecho) en el manifiesto para ver las páginas de propiedades del compilador de mensajes.
Uso de Visual Studio para agregar el manifiesto de instrumentación
Puede agregar el manifiesto de instrumentación al proyecto de controlador y, a continuación, configurar las propiedades del compilador de mensajes para compilar los archivos de encabezado y recursos necesarios.
Para agregar el manifiesto de instrumentación al proyecto mediante Visual Studio
En el Explorador de soluciones, agregue el archivo de manifiesto al proyecto de controlador. Seleccione y mantenga presionado (o haga clic con el botón derecho) Archivos > de recursos Agregar > elemento existente (por ejemplo, evntdrv.xml o mydriver.man).
Seleccione y mantenga presionado (o haga clic con el botón derecho) en el archivo que acaba de agregar y use las páginas de propiedades para cambiar el tipo de elemento a MessageCompile y seleccione Aplicar.
Aparecen las propiedades del compilador de mensajes. En Configuración general , establezca las opciones siguientes y, a continuación, seleccione Aplicar.
General Configuración Generar macros de registro en modo kernel Sí (-km) Usar el nombre base de la entrada Sí (-b) En Opciones de archivo, establezca las siguientes opciones y, a continuación, seleccione Aplicar.
Opciones de archivo Configuración Generación de un archivo de encabezado para el contador contenedor Sí Ruta de acceso del archivo de encabezado $(IntDir) Ruta de acceso de archivos de mensajes rc y binarios generados Sí Ruta de acceso del archivo RC $(IntDir) Nombre base de archivos generados $(Filename)
De forma predeterminada, el compilador de mensajes usa el nombre base del archivo de entrada como nombre base de los archivos que genera. Para especificar un nombre base, establezca el campo Nombre base de archivos generados (-z). En el ejemplo de Eventdr.sys, el nombre base se establece en evntdrvEvents para que coincida con el nombre del archivo de encabezado evntdrvEvents.h, que se incluye en evntdrv.c.
Nota:
Si no incluye el archivo .rc generado en el proyecto de Visual Studio, es posible que reciba mensajes de error sobre los recursos que no se encuentran al instalar el archivo de manifiesto.
4. Agregue el código generado para generar (publicar) los eventos (registrar, anular el registro y escribir eventos)
En el manifiesto de instrumentación, ha definido los nombres del proveedor de eventos y los descriptores de eventos. Al compilar el manifiesto de instrumentación con el compilador de mensajes, el compilador de mensajes genera un archivo de encabezado que describe los recursos y también define macros para los eventos. Ahora, debe agregar el código generado al controlador para generar estos eventos.
En el archivo de origen, incluya el archivo de encabezado de evento generado por el compilador de mensajes (MC.exe). Por ejemplo, en el ejemplo Eventdrv, el archivo de origen Evntdrv.c incluye el archivo de encabezado (evntdrvEvents.h) que se generó en el paso anterior:
#include "evntdrvEvents.h"
Agregue las macros que registran y anulan el registro del controlador como proveedor de eventos. Por ejemplo, en el archivo de encabezado del ejemplo Eventdrv (evntdrvEvents.h), el compilador de mensajes crea macros basadas en el nombre del proveedor. En el manifiesto, el ejemplo Eventdrv usa el nombre "Controlador de ejemplo" como nombre del proveedor. El compilador de mensajes combina el nombre del proveedor con la macro de evento para registrar el proveedor, en este caso, EventRegisterSample_Driver.
// This is the generated header file envtdrvEvents.h // // ... // // // Register with ETW Vista + // #ifndef EventRegisterSample_Driver #define EventRegisterSample_Driver() McGenEventRegister(&DriverControlGuid, McGenControlCallbackV2, &DriverControlGuid_Context, &Sample_DriverHandle) #endif
Agregue la macro del proveedor> EventRegister<a la función DriverEntry. Agregue esta función después del código que crea e inicializa el objeto de dispositivo. Tenga en cuenta que debe coincidir con la llamada a la función de proveedor> EventRegister<con una llamada al proveedor> EventUnregister.< Puede anular el registro del controlador en la rutina De descarga* del controlador.
// DriverEntry function // ... // Register with ETW // EventRegisterSample_Driver();
Agregue el código generado a los archivos de código fuente del controlador para escribir (generar) los eventos especificados en el manifiesto. El archivo de encabezado que ha compilado a partir del manifiesto contiene el código generado para el controlador. Use las funciones de evento> EventWrite<definidas en el archivo de encabezado para publicar mensajes de seguimiento en ETW. Por ejemplo, el código siguiente muestra las macros de los eventos definidos en evntdrvEvents.h para el ejemplo eventdrv.
Las macros para escribir estos eventos se denominan:
EventWriteStartEvent
,EventWriteSampleEventA
yEventWriteUnloadEvent
. Como puede ver en la definición de estas macros, la definición de macro incluye automáticamente una macro de evento> EventEnabled<que comprueba si el evento está habilitado. La comprobación elimina la necesidad de compilar la carga si el evento no está habilitado./// // This is the generated header file envtdrvEvents.h // // ... // // Enablement check macro for StartEvent // #define EventEnabledStartEvent() ((Sample_DriverEnableBits[0] & 0x00000001) != 0) // // Event Macro for StartEvent // #define EventWriteStartEvent(Activity, DeviceNameLength, name, Status)\ EventEnabledStartEvent() ?\ Template_hzq(Sample_DriverHandle, &StartEvent, Activity, DeviceNameLength, name, Status)\ : STATUS_SUCCESS\ // // Enablement check macro for SampleEventA // #define EventEnabledSampleEventA() ((Sample_DriverEnableBits[0] & 0x00000001) != 0) // // Event Macro for SampleEventA // #define EventWriteSampleEventA(Activity)\ EventEnabledSampleEventA() ?\ TemplateEventDescriptor(Sample_DriverHandle, &SampleEventA, Activity)\ : STATUS_SUCCESS\ // // Enablement check macro for UnloadEvent // #define EventEnabledUnloadEvent() ((Sample_DriverEnableBits[0] & 0x00000001) != 0) // // Event Macro for UnloadEvent // #define EventWriteUnloadEvent(Activity, DeviceObjPtr)\ EventEnabledUnloadEvent() ?\ Template_p(Sample_DriverHandle, &UnloadEvent, Activity, DeviceObjPtr)\ : STATUS_SUCCESS\
Agregue las macros de evento> EventWrite<al código fuente de los eventos que está generando. Por ejemplo, el siguiente fragmento de código muestra la rutina DriverEntry del ejemplo Eventdrv. DriverEntry incluye las macros para registrar el controlador con ETW (EventRegisterSample_Driver) y la macro para escribir el evento del controlador en ETW (EventWriteStartEvent).
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Installable driver initialization entry point. This entry point is called directly by the I/O system. Arguments: DriverObject - pointer to the driver object RegistryPath - pointer to a unicode string representing the path to driver-specific key in the registry Return Value: STATUS_SUCCESS if successful STATUS_UNSUCCESSFUL otherwise --*/ { NTSTATUS Status = STATUS_SUCCESS; UNICODE_STRING DeviceName; UNICODE_STRING LinkName; PDEVICE_OBJECT EventDrvDeviceObject; WCHAR DeviceNameString[128]; ULONG LengthToCopy = 128 * sizeof(WCHAR); UNREFERENCED_PARAMETER (RegistryPath); KdPrint(("EventDrv: DriverEntry\n")); // // Create Dispatch Entry Points. // DriverObject->DriverUnload = EventDrvDriverUnload; DriverObject->MajorFunction[ IRP_MJ_CREATE ] = EventDrvDispatchOpenClose; DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = EventDrvDispatchOpenClose; DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = EventDrvDispatchDeviceControl; RtlInitUnicodeString( &DeviceName, EventDrv_NT_DEVICE_NAME ); // // Create the Device object // Status = IoCreateDevice( DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &EventDrvDeviceObject); if (!NT_SUCCESS(Status)) { return Status; } RtlInitUnicodeString( &LinkName, EventDrv_WIN32_DEVICE_NAME ); Status = IoCreateSymbolicLink( &LinkName, &DeviceName ); if ( !NT_SUCCESS( Status )) { IoDeleteDevice( EventDrvDeviceObject ); return Status; } // // Choose a buffering mechanism // EventDrvDeviceObject->Flags |= DO_BUFFERED_IO; // // Register with ETW // EventRegisterSample_Driver(); // // Log an Event with : DeviceNameLength // DeviceName // Status // // Copy the device name into the WCHAR local buffer in order // to place a NULL character at the end, since this field is // defined in the manifest as a NULL-terminated string if (DeviceName.Length <= 128 * sizeof(WCHAR)) { LengthToCopy = DeviceName.Length; } RtlCopyMemory(DeviceNameString, DeviceName.Buffer, LengthToCopy); DeviceNameString[LengthToCopy/sizeof(WCHAR)] = L'\0'; EventWriteStartEvent(NULL, DeviceName.Length, DeviceNameString, Status); return STATUS_SUCCESS; }
Agregue todas las macros de evento> EventWrite<al código fuente de los eventos que está generando. Puede examinar el ejemplo eventdrv para ver cómo se llaman las otras dos macros para los eventos del código fuente del controlador.
Anule el registro del controlador como proveedor de eventos mediante la macro del proveedor> EventUnregister<del archivo de encabezado generado.
Coloque esta llamada de función en la rutina de descarga del controlador. No se debe realizar ninguna llamada de seguimiento después de llamar a la macro del proveedor> EventUnregister.< Si no se anula el registro del proveedor de eventos, se pueden producir errores cuando se descarga el proceso porque las funciones de devolución de llamada asociadas al proceso ya no son válidas.
// DriverUnload function // ... // // Unregister the driver as an ETW provider // EventUnregisterSample_Driver();
5. Compilación del controlador
Si ha agregado el manifiesto de instrumento al proyecto y ha configurado las propiedades del compilador de mensajes (MC.exe), puede compilar el proyecto de controlador o la solución mediante Visual Studio y MSBuild.
Abra la solución de controlador en Visual Studio.
Compile el ejemplo en el menú Compilar; para ello, seleccione Compilar solución. Para obtener más información sobre la creación de soluciones, consulte Building a Driver (Creación de un controlador).
6. Instalar el manifiesto
Debe instalar el manifiesto en el sistema de destino para que los consumidores de eventos (por ejemplo, el registro de eventos) puedan encontrar la ubicación del binario que contiene los metadatos del evento. Si agregó la tarea del compilador de mensajes al proyecto de controlador en el paso 3, el manifiesto de instrumentación se compiló y los archivos de recursos se generaron al compilar el controlador. Usa la Utilidad de línea de comandos de eventos de Windows (Wevtutil.exe) para instalar el manifiesto. La sintaxis para instalar el manifiesto es la siguiente:
wevtutil.exe im drivermanifest
Por ejemplo, para instalar el manifiesto para el controlador de ejemplo de Evntdrv.sys, abra una ventana del símbolo del sistema con privilegios elevados (Ejecutar como administrador) vaya al directorio donde se encuentra el archivo evntdrv.xml y escriba el siguiente comando:
Wevtutil.exe im evntdrv.xml
Una vez completada la sesión de seguimiento, desinstale el manifiesto con la sintaxis siguiente.
drivermanifest de wevtutil.exe mmm
Por ejemplo, para desinstalar el manifiesto del ejemplo Eventdrv:
Wevtutil.exe um evntdrv.xml
7. Pruebe el controlador para comprobar la compatibilidad con ETW.
Instale el controlador. Ejercicio del controlador para generar la actividad de seguimiento. Vea los resultados en el Visor de eventos. También puede ejecutar Tracelog y, a continuación, ejecutar Tracerpt, una herramienta para procesar registros de seguimiento de eventos, controlar, recopilar y ver los registros de seguimiento de eventos.