Поделиться через


Добавление трассировки событий в драйверы режима ядра

В этом разделе описывается использование API трассировки событий для режима ядра Windows (ETW) для добавления трассировки событий в драйверы режима ядра. API режима ядра ETW появился в Windows Vista и не поддерживается в более ранних операционных системах. Используйте трассировку программного обеспечения WPP или трассировку событий WMI, если драйвер должен поддерживать возможности трассировки в Windows 2000 и более поздних версиях.

Совет

Чтобы просмотреть пример кода, показывающий, как реализовать ETW с помощью комплекта драйверов Windows (WDK) и Visual Studio, см . пример Eventdrv.

В этом разделе рассматриваются следующие вопросы.

Рабочий процесс. Добавление трассировки событий в драйверы режима ядра

1. Определите тип событий для создания и публикации.

2. Создание манифеста инструментирования, определяющего поставщика, события и каналы

3. Компиляция манифеста инструментирования с помощью компилятора сообщений (Mc.exe)

4. Добавьте созданный код для создания (публикации) событий (регистрация, отмена регистрации и запись событий)

5. Создание драйвера

6. Установка манифеста

7. Проверка драйвера для проверки поддержки ETW

Рабочий процесс. Добавление трассировки событий в драйверы режима ядра

Блок-схема, демонстрирующая процесс добавления трассировки событий в драйверы режима ядра.

1. Определите тип событий для создания и публикации.

Прежде чем приступить к написанию кода, необходимо решить, какой тип событий требуется выполнить вход с помощью трассировки событий для Windows (ETW). Например, может потребоваться регистрировать события, которые помогут вам диагностировать проблемы после распространения драйвера или события, которые могут помочь вам при разработке драйвера. Дополнительные сведения см. в справочнике по журналу событий Windows.

Типы событий определяются каналами. Канал — это именованный поток событий типа "Администратор", "Операционный", "Аналитический" или "Отладка", направленный на определенную аудиторию, аналогичную телевизионному каналу. Канал передает события от поставщика событий к журналам событий и потребителям событий. Дополнительные сведения см. в разделе "Определение каналов".

Во время разработки вы, скорее всего, заинтересованы в событиях трассировки, которые помогают выполнить отладку кода. Этот же канал можно использовать в рабочем коде для устранения неполадок, которые могут возникнуть после развертывания драйвера. Также может потребоваться отслеживать события, которые можно использовать для измерения производительности; Эти события могут помочь ИТ-специалистам точно настроить производительность сервера и помочь определить узкие места сети.

2. Создание манифеста инструментирования, определяющего поставщика, события и каналы

Манифест инструментирования — это XML-файл, который предоставляет официальное описание событий, которые будет вызывать поставщик. Манифест инструментирования определяет поставщика событий, задает канал или каналы (до восьми), а также описывает события и шаблоны используемых событий. Кроме того, манифест инструментирования позволяет локализации строк, поэтому можно локализовать сообщения трассировки. Система событий и потребители событий могут использовать структурированные XML-данные, предоставленные в манифесте для выполнения запросов и анализа.

Сведения о манифесте инструментирования см. в статье "Написание манифеста инструментирования(Windows)", схемы EventManifest (Windows) и использование журнала событий Windows (Windows).

В следующем манифесте инструментирования показан поставщик событий, использующий имя "Пример драйвера". Обратите внимание, что это имя не должно совпадать с именем двоичного файла драйвера. Манифест также задает GUID поставщика и пути к файлам сообщений и ресурсов. Файлы сообщений и ресурсов позволяют ETW узнать, где найти ресурсы, необходимые для декодирования и отчета о событиях. Эти пути указывают на расположение файла драйвера (.sys). Драйвер должен быть установлен в указанном каталоге на целевом компьютере.

В этом примере используется именованная система каналов, канал для событий типа Admin. Этот канал определен в файле Winmeta.xml, который предоставляется комплектом драйверов Windows (WDK) в каталоге%WindowsSdkDir%\include\um. Системный канал защищен для приложений, работающих под учетными записями системной службы. Манифест содержит шаблоны событий, описывающие типы данных, предоставленные событиями при публикации, а также статическое и динамическое содержимое. В этом примере манифеста определяются три события: StartEvent, SampleEventAи UnloadEvent.

Помимо каналов, можно связать события с уровнями и ключевыми словами. Ключевые слова и уровни предоставляют способ включения событий и предоставления механизма фильтрации событий при публикации. Ключевые слова можно использовать для группировки логически связанных событий. Уровень можно использовать для указания серьезности или детализации события, например критических, ошибок, предупреждений или информационных сведений. Файл Winmeta.xml содержит предопределенные значения атрибутов событий.

При создании шаблона полезных данных события (сообщения о событии и данных) необходимо указать входные и выходные типы. Поддерживаемые типы описаны в разделе "Примечания" сложного типа 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. Компиляция манифеста инструментирования с помощью компилятора сообщений (Mc.exe)

Компилятор сообщений (Mc.exe) должен выполняться перед компиляцией исходного кода. Компилятор сообщений включен в комплект драйверов Windows (WDK). Компилятор сообщений создает файл заголовка, содержащий определения для поставщика событий, атрибутов событий, каналов и событий. Этот файл заголовка необходимо включить в исходный код. Компилятор сообщений также помещает созданный скрипт компилятора ресурсов (*.rc) и созданные .bin файлы (двоичные ресурсы), которые включает скрипт компилятора ресурсов.

Этот шаг можно включить в процесс сборки несколькими способами:

  • Добавление задачи компилятора сообщений в файл проекта драйвера (как показано в примере Eventdrv).

  • Добавление манифеста инструментирования и настройка свойств компилятора сообщений с помощью Visual Studio.

Добавление задачи компилятора сообщений в файл проекта. Пример включения компилятора сообщений в процесс сборки см. в файле проекта для примера Eventdrv. В файле Eventdrv.vcxproj есть <раздел MessageCompile> , который вызывает компилятор сообщения. Компилятор сообщений использует файл манифеста (evntdrv.xml) в качестве входных данных для создания файла заголовка evntdrvEvents.h. Этот раздел также указывает пути для созданных RC-файлов и включает макросы ведения журнала в режиме ядра. Вы можете скопировать этот раздел и добавить его в файл проекта драйвера (.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>

При создании примера Eventdrv.sys Visual Studio создает необходимые файлы для трассировки событий. Он также добавляет манифест evntdrv.xml в список файлов ресурсов для проекта драйвера. Вы можете выбрать и сохранить манифест (или щелкните правой кнопкой мыши), чтобы просмотреть страницы свойств компилятора сообщений.

Добавление манифеста инструментирования с помощью Visual Studio

Манифест инструментирования можно добавить в проект драйвера, а затем настроить свойства компилятора сообщений для создания необходимых файлов ресурсов и заголовков.

Добавление манифеста инструментирования в проект с помощью Visual Studio

  1. В Обозреватель решений добавьте файл манифеста в проект драйвера. Выберите и удерживайте (или щелкните правой кнопкой мыши) Файлы > ресурсов, добавьте > существующий элемент (например, evntdrv.xml или mydriver.man).

  2. Выберите и удерживайте (или щелкните правой кнопкой мыши) файл, который вы только что добавили, и используйте страницы свойств, чтобы изменить тип элемента на MessageCompile и нажмите кнопку "Применить".

  3. Отображаются свойства компилятора сообщений. В разделе "Общие" задайте следующие параметры и нажмите кнопку "Применить".

    Общие Параметр
    Создание макросов ведения журнала в режиме ядра Да (-km)
    Использование базового имени входных данных Да (-b)
  4. В разделе "Параметры файла" задайте следующие параметры и нажмите кнопку "Применить".

    Параметры файла Параметр
    Создание файла заголовка для хранения счетчика Да
    Путь к файлу заголовка $(IntDir)
    Путь к файлам созданных RC и двоичных сообщений Да
    Путь к файлу RC $(IntDir)
    Имя базы созданных файлов $(Filename)

По умолчанию компилятор сообщений использует базовое имя входного файла в качестве базового имени создаваемых файлов. Чтобы указать базовое имя, задайте поле "Имя базы созданных файлов" (-z). В примере Eventdr.sys базовое имя имеет значение evntdrvEvents , чтобы оно соответствовало имени файла заголовка evntdrvEvents.h, которое входит в evntdrv.c.

Примечание.

Если вы не включаете созданный RC-файл в проект Visual Studio, при установке файла манифеста могут появиться сообщения об ошибках о ресурсах.

4. Добавьте созданный код для создания (публикации) событий (регистрация, отмена регистрации и запись событий)

В манифесте инструментирования вы определили имена поставщика событий и дескрипторов событий. При компиляции манифеста инструментирования с помощью компилятора сообщений компилятор сообщения создает файл заголовка, описывающий ресурсы, а также определяет макросы для событий. Теперь необходимо добавить созданный код в драйвер, чтобы вызвать эти события.

  1. В исходном файле добавьте файл заголовка события, созданный компилятором сообщений (MC.exe). Например, в примере Eventdrv исходный файл Evntdrv.c включает файл заголовка (evntdrvEvents.h), созданный на предыдущем шаге:

    #include "evntdrvEvents.h"  
    
  2. Добавьте макросы, которые регистрируют и отменяют регистрацию драйвера в качестве поставщика событий. Например, в файле заголовка для примера Eventdrv (evntdrvEvents.h) компилятор сообщений создает макросы на основе имени поставщика. В манифесте пример Eventdrv использует имя "Пример драйвера" в качестве имени поставщика. Компилятор сообщений объединяет имя поставщика с макросом события для регистрации поставщика, в данном случае 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
    

    Добавьте макрос поставщика> EventRegisterфункцию DriverEntry. Добавьте эту функцию после кода, который создает и инициализирует объект устройства. Обратите внимание, что необходимо сопоставить вызов функции поставщика EventRegister с вызовом поставщика> EventUnregister.><< Вы можете отменить регистрацию драйвера в подпрограмме выгрузки* драйвера.

       // DriverEntry function
       // ...
    
    
        // Register with ETW
        //
        EventRegisterSample_Driver();
    
  3. Добавьте созданный код в исходные файлы драйвера для записи (поднятия) событий, указанных в манифесте. Файл заголовка, скомпилированный из манифеста, содержит созданный код драйвера. Используйте функции событий> EventWrite<, определенные в файле заголовка, для публикации сообщений трассировки в ETW. Например, в следующем коде показаны макросы для событий, определенных в evntdrvEvents.h для примера Eventdrv.

    Макросы для записи этих событий вызываются: EventWriteStartEvent, EventWriteSampleEventAи EventWriteUnloadEvent. Как видно в определении этих макросов, определение макроса автоматически включает макрос события> EventEnabled<, который проверяет, включена ли событие. Проверка устраняет необходимость сборки полезных данных, если событие не включено.

    
    ///
    // 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\
    
    

    Добавьте макросы событий> EventWrite<в исходный код для вызываемого события. Например, в следующем фрагменте кода показана подпрограмма DriverEntry из примера Eventdrv. DriverEntry включает макросы для регистрации драйвера в ETW (EventRegisterSample_Driver) и макроса для записи события драйвера в 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;
    }
    

Добавьте все макросы событий> EventWrite<в исходный код для вызываемых событий. Пример Eventdrv можно просмотреть, как вызываются другие два макроса для событий в исходном коде драйвера.

  1. Отмена регистрации драйвера в качестве поставщика событий с помощью макроса поставщика> EventUnregister<из созданного файла заголовка.

    Поместите этот вызов функции в подпрограмму выгрузки драйвера. После вызова макроса поставщика> EventUnregister<не следует вызывать вызовы трассировки. Сбой отмены регистрации поставщика событий может привести к ошибкам при выгрузке процесса, так как все функции обратного вызова, связанные с процессом, больше не являются допустимыми.

        // DriverUnload function
        // ...
        //
    
        //  Unregister the driver as an ETW provider
        //
        EventUnregisterSample_Driver();
    

5. Создание драйвера

Если вы добавили манифест инструментирования в проект и настроили свойства компилятора сообщений (MC.exe), можно создать проект драйвера или решение с помощью Visual Studio и MSBuild.

  1. Откройте решение драйвера в Visual Studio.

  2. Создайте пример из меню "Сборка", выбрав "Создать решение". Дополнительные сведения о создании решений см. в разделе "Создание драйвера".

6. Установка манифеста

Необходимо установить манифест в целевой системе, чтобы потребители событий (например, журнал событий) могли найти расположение двоичного файла, содержащего метаданные события. Если вы добавили задачу компилятора сообщений в проект драйвера на шаге 3, манифест инструментирования был скомпилирован и файлы ресурсов были созданы при создании драйвера. Используйте служебную программу командной строки событий Windows (Wevtutil.exe), чтобы установить манифест. Синтаксис для установки манифеста выглядит следующим образом:

wevtutil.exe im drivermanifest

Например, чтобы установить манифест для примера драйвера Evntdrv.sys, откройте окна командной строки с повышенными привилегиями (от имени администратора) перейдите в каталог, где находится файл evntdrv.xml, и введите следующую команду:

Wevtutil.exe im evntdrv.xml

После завершения сеанса трассировки удалите манифест с помощью следующего синтаксиса.

wevtutil.exe um drivermanifest

Например, чтобы удалить манифест для примера Eventdrv:

Wevtutil.exe um evntdrv.xml

7. Проверка драйвера для проверки поддержки ETW

Установите драйвер. Упражнение драйвера для создания действия трассировки. Просмотрите результаты в Просмотр событий. Вы также можете запустить tracelog, а затем запустить Tracerpt, средство обработки журналов трассировки событий, управление, сбор и просмотр журналов трассировки событий.