Sdílet prostřednictvím


Přidání trasování událostí do ovladačů Kernel-Mode

Tato část popisuje, jak pomocí rozhraní API pro trasování událostí pro Windows (ETW) přidat trasování událostí do ovladačů režimu jádra. Rozhraní API režimu jádra ETW bylo zavedeno ve Windows Vista a není podporováno na starších operačních systémech. Pokud váš ovladač potřebuje podporovat funkce trasování v systémech Windows 2000 a novějších, použijte WPP Software Tracing nebo trasování událostí rozhraní WMI.

Návod

Chcete-li zobrazit Ukázkový kód, který ukazuje, jak implementovat ETW (Trasování událostí) pomocí sady Windows Driver Kit (WDK) a Visual Studio, podívejte se na ukázku Eventdrv.

V této části:

Pracovní postup – přidání trasování událostí do ovladačů Kernel-Mode

1. Rozhodněte typ událostí, které mají být vyvolání a kde je publikovat.

2. Vytvoření manifestu instrumentace, který definuje poskytovatele, události a kanály

3. Zkompilujte manifest instrumentace pomocí kompilátoru zpráv (Mc.exe)

4. Přidejte vygenerovaný kód pro vyvolání (publikování) událostí (registrace, zrušení registrace a zápis událostí)

5. Sestavení ovladače

6. Instalace manifestu

7. Otestujte ovladač a ověřte podporu pro ETW.

Pracovní postup – přidání trasování událostí do ovladačů Kernel-Mode

Vývojový diagram znázorňující proces přidání trasování událostí do ovladačů v režimu jádra

1. Rozhodněte typ událostí, které mají být vyvolání a kde je publikovat.

Než začnete psát kód, musíte se rozhodnout, jaký typ událostí má ovladač protokolovat prostřednictvím trasování událostí pro Windows (ETW). Můžete například chtít protokolovat události, které vám pomůžou diagnostikovat problémy po distribuci ovladače nebo události, které vám můžou pomoct při vývoji ovladače. Informace najdete v tématu Referenční informace k protokolu událostí systému Windows.

Typy událostí jsou identifikovány pomocí kanálů. Kanál je pojmenovaný stream událostí typu Admin, Operational, Analytical nebo Debug směrovaný na konkrétní cílovou skupinu, podobně jako televizní kanál. Kanál doručuje události od poskytovatele událostí do protokolů událostí a příjemců událostí. Informace naleznete v tématu Definování kanálů.

Během vývoje vás s největší pravděpodobností zajímá trasování událostí, které vám pomůžou ladit kód. Stejný kanál je možné použít v produkčním kódu, který vám pomůže vyřešit problémy, které se můžou objevit po nasazení ovladače. Můžete také chtít sledovat události, které lze použít k měření výkonu; tyto události můžou it specialistům pomoct vyladit výkon serveru a můžou pomoct identifikovat kritické body sítě.

2. Vytvoření manifestu instrumentace, který definuje poskytovatele, události a kanály

Manifest instrumentace je soubor XML, který poskytuje formální popis událostí, které poskytovatel vyvolá. Manifest instrumentace identifikuje zprostředkovatele událostí, určuje kanál nebo kanály (až osm) a popisuje události a šablony, které události používají. Manifest instrumentace navíc umožňuje lokalizaci řetězců, abyste mohli lokalizovat zprávy trasování. Systém událostí a příjemci událostí můžou k provádění dotazů a analýzy využít strukturovaná data XML poskytnutá v manifestu.

Informace o manifestu instrumentace naleznete v tématu Zápis manifestu instrumentace (Windows), EventManifest Schema (Windows) a Použití protokolu událostí systému Windows (Windows).

Následující manifest instrumentace ukazuje zprostředkovatele událostí, který používá název "Ukázkový ovladač". Všimněte si, že tento název nemusí být stejný jako název binárního souboru ovladače. Manifest také určuje identifikátor GUID pro zprostředkovatele a cesty k zprávám a souborům prostředků. Soubory se zprávami a prostředky umožňují ETW zjistit, kde jsou umístěny prostředky potřebné k dekódování a hlášení událostí. Tyto cesty ukazují na umístění souboru ovladače (.sys). Ovladač musí být nainstalován v zadaném adresáři v cílovém počítači.

Příklad používá pojmenovaný kanál System, kanál pro události typu Admin. Tento kanál je definován v souboru Winmeta.xml, který je součástí sady Windows Driver Kit (WDK) v adresáři%WindowsSdkDir%\include\um. Kanál System je zabezpečený pro aplikace spuštěné v rámci účtů systémových služeb. Manifest obsahuje šablony událostí popisující typy dat zadaných s událostmi při jejich publikování spolu s jejich statickým a dynamickým obsahem. Tento ukázkový manifest definuje tři události: StartEvent, SampleEventAa UnloadEvent.

Kromě kanálů můžete události přidružit k úrovním a klíčovým slovům. Klíčová slova a úrovně poskytují způsob, jak povolit události a poskytnout mechanismus filtrování událostí při jejich publikování. Klíčová slova se dají použít k seskupení logicky souvisejících událostí. Úroveň lze použít k označení závažnosti nebo podrobností události, například kritické, chybové, upozornění nebo informační. Soubor Winmeta.xml obsahuje předdefinované hodnoty atributů událostí.

Při vytváření šablony pro datovou část události (zpráva události a data) musíte zadat vstupní a výstupní typy. Podporované typy jsou popsány v části Poznámky komplexního typu 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. Zkompilujte manifest instrumentace pomocí kompilátoru zpráv (Mc.exe)

Před kompilací zdrojového kódu musí být spuštěn kompilátor zpráv (Mc.exe). Kompilátor zpráv je součástí sady Windows Driver Kit (WDK). Kompilátor zpráv vytvoří soubor hlaviček, který obsahuje definice pro zprostředkovatele událostí, atributy událostí, kanály a události. Tento soubor hlaviček musíte zahrnout do zdrojového kódu. Kompilátor zpráv také umístí vygenerovaný skript kompilátoru prostředků (*.rc) a vygenerované soubory .bin (binární prostředky), které obsahuje skript kompilátoru prostředků.

Tento krok můžete zahrnout jako součást procesu sestavení několika způsoby:

  • Přidání úlohy kompilátoru zpráv do souboru projektu ovladače (jak je znázorněno v ukázce Eventdrv).

  • Pomocí sady Visual Studio přidáte manifest instrumentace a nakonfigurujete vlastnosti kompilátoru zpráv.

Přidání úlohy kompilátoru zpráv do souboru projektu Příklad zahrnutí kompilátoru zpráv do procesu sestavení najdete v souboru projektu ukázky Eventdrv. V souboru Eventdrv.vcxproj existuje <část MessageCompile> , která volá kompilátor zpráv. Kompilátor zpráv používá jako vstup soubor manifestu (evntdrv.xml) k vygenerování souboru hlavičky evntdrvEvents.h. Tato část také určuje cesty pro vygenerované soubory RC a povolí makra protokolování režimu jádra. Tento oddíl můžete zkopírovat a přidat ho do souboru projektu ovladače (.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>

Když sestavíte ukázku Eventdrv.sys, Visual Studio vytvoří potřebné soubory pro trasování událostí. Také přidá manifest evntdrv.xml do seznamu souborů prostředků pro projekt ovladače. Manifest můžete vybrat a podržet (nebo na něj kliknout pravým tlačítkem myši) a zobrazit stránky vlastností kompilátoru zpráv.

Přidání manifestu instrumentace pomocí sady Visual Studio

Do projektu ovladače můžete přidat manifest instrumentace a potom nakonfigurovat vlastnosti kompilátoru zpráv tak, aby se sestavily potřebné soubory prostředků a hlaviček.

Přidání manifestu instrumentace do projektu pomocí sady Visual Studio

  1. V Průzkumníku řešení přidejte soubor manifestu do projektu ovladače. Vyberte a podržte (nebo klikněte pravým tlačítkem) Soubory prostředků > Přidat > existující položku (například evntdrv.xml nebo mydriver.man).

  2. Vyberte a podržte (nebo klikněte pravým tlačítkem myši) soubor, který jste právě přidali, a pomocí stránek vlastností změňte typ položky na MessageCompile a vyberte Použít.

  3. Zobrazí se vlastnosti kompilátoru zpráv. V části Obecné nastavte následující možnosti a pak vyberte Použít.

    Obecné Nastavení
    Vytváření maker pro protokolování v režimu jádra Ano (-km)
    Použít základní název vstupu Ano (-b)
  4. V části Možnosti souboru nastavte následující možnosti a pak vyberte Použít.

    Možnosti souboru Nastavení
    Vygenerujte soubor hlaviček, který obsahuje čítač Ano
    Cesta k souboru záhlaví $(IntDir)
    Vygenerovaná rc a cesta k binárním souborům zpráv Ano
    Cesta k souboru RC $(IntDir)
    Vygenerovaný základní název souborů $(Název souboru)

Ve výchozím nastavení používá kompilátor zpráv základní název vstupního souboru jako základní název souborů, které generuje. Chcete-li zadat základní název, nastavte pole Generated Files Base Name (-z). V ukázce Eventdr.sys je základní název nastaven na evntdrvEvents tak, aby odpovídal názvu souboru hlavičky evntdrvEvents.h, který je součástí evntdrv.c.

Poznámka:

Pokud do projektu sady Visual Studio nezahrnete vygenerovaný soubor .rc, mohou se při instalaci souboru manifestu zobrazit chybové zprávy o prostředcích, které nebyly nalezeny.

4. Přidejte vygenerovaný kód pro vyvolání (publikování) událostí (registrace, zrušení registrace a zápis událostí)

V manifestu instrumentace jste definovali názvy zprostředkovatele události a popisovače událostí. Když zkompilujete manifest instrumentace pomocí kompilátoru zpráv, kompilátor zpráv vygeneruje hlavičkový soubor popisující prostředky a také definuje makra pro události. Teď musíte do ovladače přidat vygenerovaný kód, aby se tyto události vyvolaly.

  1. Do zdrojového souboru zahrňte soubor hlavičky události, který je vytvořen kompilátorem zpráv (MC.exe). Například v ukázce Eventdrv zdrojový soubor Evntdrv.c obsahuje hlavičkový soubor (evntdrvEvents.h), který byl vygenerován v předchozím kroku:

    #include "evntdrvEvents.h"  
    
  2. Přidejte makra, která registrují a zruší registraci ovladače jako zprostředkovatele událostí. Například v souboru hlaviček pro ukázku Eventdrv (evntdrvEvents.h) kompilátor zpráv vytvoří makra na základě názvu zprostředkovatele. V manifestu používá ukázka Eventdrv název "Sample Driver" jako název zprostředkovatele. Kompilátor zpráv zkombinuje název zprostředkovatele s makrem události a zaregistruje zprostředkovatele v tomto případě 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
    

    Přidejte makro EventRegister<zprostředkovatele> do funkce DriverEntry. Přidejte tuto funkci za kód, který vytvoří a inicializuje objekt zařízení. Všimněte si, že volání funkce EventRegister<zprostředkovatele> musí být spárováno s voláním funkce EventUnregister<zprostředkovatele>. Můžete odregistrovat ovladač ve své rutině Uvolnění*.

       // DriverEntry function
       // ...
    
    
        // Register with ETW
        //
        EventRegisterSample_Driver();
    
  3. Přidejte vygenerovaný kód do zdrojových souborů ovladače pro zápis (vyvolání) událostí zadaných v manifestu. Soubor hlaviček, který jste zkompilovali z manifestu, obsahuje vygenerovaný kód pro ovladač. Pomocí funkcí EventWrite<událostí> definovaných v souboru hlaviček publikujte trasovací zprávy do ETW. Například následující kód ukazuje makra pro události definované v evntdrvEvents.h pro ukázku Eventdrv.

    Makra pro zápis těchto událostí se nazývají: EventWriteStartEvent, EventWriteSampleEventAa EventWriteUnloadEvent. Jak vidíte v definici těchto maker, definice makra automaticky zahrnuje makro EventEnabled<události>, které kontroluje, zda je událost povolena. Ověření eliminuje potřebu vytvoření datové části, pokud není událost aktivována.

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

    Přidejte makra EventWrite<události> do zdrojového kódu pro události, které vyvoláváte. Například následující fragment kódu ukazuje rutinu DriverEntry z ukázky Eventdrv. DriverEntry obsahuje makra pro registraci ovladače ve službě ETW (EventRegisterSample_Driver) a makro pro zápis události ovladače do 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;
    }
    

Přidejte všechny makra EventWrite<událostí> do zdrojového kódu pro události, které vyvoláváte. Ukázku Eventdrv si můžete prohlédnout a podívat se, jak se ostatní dvě makra volají pro události ve zdrojovém kódu ovladače.

  1. Zrušte registraci ovladače jako zprostředkovatele událostí pomocí makra EventUnregister<provider> z vygenerovaného hlavičkového souboru.

    Toto volání funkce umístěte do rutiny uvolnění ovladače. Po zavolání makra EventUnregister<zprostředkovatele> by se neměla provádět žádná volání trasování. Neschopnost zrušit registraci zprostředkovatele událostí může způsobit chyby při uvolnění procesu, protože jakékoli callbackové funkce přidružené k procesu již nejsou platné.

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

5. Sestavení ovladače

Pokud jste do projektu přidali manifest nástroje a nakonfigurovali vlastnosti kompilátoru zpráv (MC.exe), můžete vytvořit projekt nebo řešení ovladače pomocí Visual Studio a MSBuild.

  1. Otevřete řešení ovladače v sadě Visual Studio.

  2. Sestavte projekt z nabídky Sestavení výběrem možnosti Sestavit řešení. Další informace o vytváření řešení naleznete v tématu Sestavení ovladače.

6. Instalace manifestu

Manifest je nutné nainstalovat do cílového systému, aby příjemci událostí (například protokol událostí) mohli najít umístění binárního souboru, který obsahuje metadata události. Pokud jste do projektu ovladače v kroku 3 přidali úlohu kompilátoru zpráv, zkompiloval se manifest instrumentace a soubory prostředků se vygenerovaly při sestavení ovladače. K instalaci manifestu použijte nástroj příkazového řádku události systému Windows (Wevtutil.exe). Syntaxe instalace manifestu je následující:

wevtutil.exe imdrivermanifest

Pokud chcete například nainstalovat manifest pro ukázkový ovladač Evntdrv.sys, otevřete okno příkazového řádku se zvýšenými oprávněními (Spustit jako správce) přejděte do adresáře, kde se nachází soubor evntdrv.xml, a zadejte následující příkaz:

Wevtutil.exe im evntdrv.xml

Po dokončení relace trasování odinstalujte manifest pomocí následující syntaxe.

wevtutil.exe umdrivermanifest

Například odinstalace manifestu pro ukázku Eventdrv:

Wevtutil.exe um evntdrv.xml

7. Otestujte ovladač a ověřte podporu ETW.

Nainstalujte ovladač. Cvičením ovladače vygenerujte aktivitu trasování. Prohlédněte si výsledky v Prohlížeči událostí. Můžete také spustit Tracelog a pak spustit Tracerpt, nástroj pro zpracování protokolů trasování událostí, řídit, shromažďovat a zobrazovat protokoly trasování událostí.