Condividi tramite


Aggiunta di traccia eventi ai driver in modalità kernel

Questa sezione descrive come usare l'API in modalità kernel ETW (Event Tracing for Windows) per aggiungere la traccia degli eventi ai driver in modalità kernel. L'API in modalità kernel ETW è stata introdotta con Windows Vista e non è supportata nei sistemi operativi precedenti. Usa Traccia software WPP o Traccia eventi WMI se il driver deve supportare la funzionalità di traccia in Windows 2000 e versioni successive.

Suggerimento

Per visualizzare il codice di esempio che illustra come implementare ETW usando Windows Driver Kit (WDK) e Visual Studio, vedere l'esempio Eventdrv.

Contenuto della sezione:

Flusso di lavoro - Aggiunta di traccia eventi ai driver in modalità kernel

1. Decidere il tipo di eventi da generare e dove pubblicarli

2. Creare un manifesto di strumentazione che definisce il provider, gli eventi e i canali

3. Compilare il manifesto di strumentazione usando il compilatore di messaggi (Mc.exe)

4. Aggiungere il codice generato per generare (pubblicare) gli eventi (registrare, annullare la registrazione e scrivere eventi)

5. Compilare il driver

6. Installare il manifesto

7. Testare il driver per verificare il supporto ETW

Flusso di lavoro - Aggiunta di traccia eventi ai driver in modalità kernel

Diagramma di flusso che mostra il processo per aggiungere la traccia degli eventi ai driver in modalità kernel.

1. Decidere il tipo di eventi da generare e dove pubblicarli

Prima di iniziare a scrivere codice, è necessario decidere quale tipo di eventi si vuole che il driver registri tramite Event Tracing for Windows (ETW). Ad esempio, è possibile registrare eventi che consentono di diagnosticare i problemi dopo la distribuzione del driver o gli eventi che potrebbero essere utili durante lo sviluppo del driver. Per informazioni, vedere Informazioni di riferimento sul registro eventi di Windows.

I tipi di eventi vengono identificati con i canali. Un canale è un flusso denominato di eventi di tipo Admin, Operational, Analytical o Debug indirizzati a un pubblico specifico, simile a un canale televisivo. Un canale recapita gli eventi dal provider di eventi ai registri eventi e ai consumer di eventi. Per informazioni, vedere Definizione dei canali.

Durante lo sviluppo, è molto probabile che si sia interessati a tracciare gli eventi che consentono di eseguire il debug del codice. Questo stesso canale può essere usato nel codice di produzione per risolvere i problemi che potrebbero verificarsi dopo la distribuzione del driver. È anche possibile tracciare gli eventi che possono essere usati per misurare le prestazioni; questi eventi possono aiutare i professionisti IT a ottimizzare le prestazioni del server e a identificare i colli di bottiglia di rete.

2. Creare un manifesto di strumentazione che definisce il provider, gli eventi e i canali

Il manifesto di strumentazione è un file XML che fornisce una descrizione formale degli eventi generati da un provider. Il manifesto di strumentazione identifica il provider di eventi, specifica il canale o i canali (fino a otto) e descrive gli eventi e modelli usati dagli eventi. Inoltre, il manifesto di strumentazione consente la localizzazione delle stringhe, in modo da localizzare i messaggi di traccia. Il sistema di eventi e i consumer di eventi possono usare i dati XML strutturati forniti nel manifesto per eseguire query e analisi.

Per informazioni sul manifesto di strumentazione, vedere Scrittura di un manifesto di strumentazione (Windows), Schema EventManifest (Windows) e Uso del registro eventi di Windows (Windows).For information about the instrumentation manifest, see Writing an Instrumentation Manifest (Windows), EventManifest Schema (Windows) and Using Windows Event Log (Windows).

Il manifesto di strumentazione seguente mostra un provider di eventi che usa il nome "Driver di esempio". Si noti che questo nome non deve corrispondere al nome del file binario del driver. Il manifesto specifica anche un GUID per il provider e i percorsi dei file di messaggio e risorse. I file di messaggio e risorse comunicano a ETW dove individuare le risorse necessarie per decodificare e segnalare gli eventi. Questi percorsi puntano al percorso del file del driver (.sys). Il driver deve essere installato nella directory specificata nel computer di destinazione.

Nell'esempio viene usato il canale denominato System, un canale per gli eventi di tipo Admin. Questo canale viene definito nel file Winmeta.xml fornito con Windows Driver Kit (WDK) nella directory%WindowsSdkDir%\include\um. Il canale di sistema è protetto per le applicazioni in esecuzione con gli account del servizio di sistema. Il manifesto include i modelli di evento che descrivono i tipi di dati forniti con gli eventi quando vengono pubblicati, insieme al relativo contenuto statico e dinamico. Questo manifesto di esempio definisce tre eventi: StartEvent, SampleEventAe UnloadEvent.

Oltre ai canali, è possibile associare eventi a livelli e parole chiave. Le parole chiave e i livelli consentono di abilitare gli eventi e fornire un meccanismo per filtrare gli eventi quando vengono pubblicati. Le parole chiave possono essere usate per raggruppare gli eventi correlati logicamente. Un livello può essere usato per indicare la gravità o il livello di dettaglio di un evento, ad esempio critico, errore, avviso o informativo. Il file Winmeta.xml contiene valori predefiniti per gli attributi dell'evento.

Quando si crea un modello per il payload dell'evento (messaggio di evento e dati), è necessario specificare i tipi di input e output. I tipi supportati sono descritti nella sezione Osservazioni di Tipo complesso InputType (Windows).The supported types are described in the Remarks section of InputType Complex Type (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. Compilare il manifesto di strumentazione usando il compilatore di messaggi (Mc.exe)

Il compilatore di messaggi (Mc.exe) deve essere eseguito prima di compilare il codice sorgente. Il compilatore di messaggi è incluso in Windows Driver Kit (WDK). Il compilatore di messaggi crea un file di intestazione che contiene definizioni per il provider di eventi, gli attributi degli eventi, i canali e gli eventi. È necessario includere questo file di intestazione nel codice sorgente. Il compilatore di messaggi inserisce anche lo script del compilatore di risorse generato (*.rc) e i file .bin generati (risorse binarie) inclusi nello script del compilatore di risorse.

È possibile includere questo passaggio come parte del processo di compilazione in due modi:

  • Aggiunta dell'attività del compilatore di messaggi al file di progetto del driver (come illustrato nell'esempio Eventdrv).

  • Uso di Visual Studio per aggiungere il manifesto di strumentazione e configurare le proprietà del compilatore di messaggi.

Aggiunta di un'attività del compilatore di messaggi al file di progetto Per un esempio di come è possibile includere il compilatore di messaggi nel processo di compilazione, esaminare il file di progetto per l'esempio Eventdrv. Nel file Eventdrv.vcxproj è presente una <sezione MessageCompile> che chiama il compilatore di messaggi. Il compilatore di messaggi usa il file manifesto (evntdrv.xml) come input per generare il file di intestazione evntdrvEvents.h. Questa sezione specifica anche i percorsi per i file RC generati e abilita le macro di registrazione in modalità kernel. È possibile copiare questa sezione e aggiungerla al file di progetto driver (.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>

Quando si compila l'esempio di Eventdrv.sys, Visual Studio crea i file necessari per la traccia degli eventi. Aggiunge inoltre il manifesto evntdrv.xml all'elenco di File di risorse per il progetto driver. È possibile selezionare e tenere premuto (o fare clic con il pulsante destro del mouse) sul manifesto per visualizzare le pagine delle proprietà del compilatore di messaggi.

Uso di Visual Studio per aggiungere il manifesto di strumentazione

È possibile aggiungere il manifesto di strumentazione al progetto driver e quindi configurare le proprietà del compilatore di messaggi per compilare i file di risorsa e intestazione necessari.

Per aggiungere il manifesto di strumentazione al progetto usando Visual Studio

  1. Nella Esplora soluzioni aggiungere il file manifesto al progetto driver. Selezionare e tenere premuto (o fare clic con il pulsante destro del mouse) File > di risorse Aggiungi > elemento esistente (ad esempio, evntdrv.xml o mydriver.man).

  2. Selezionare e tenere premuto (o fare clic con il pulsante destro del mouse) sul file appena aggiunto e usare le pagine delle proprietà per modificare il tipo di elemento in MessageCompile e selezionare Applica.

  3. Vengono visualizzate le proprietà del compilatore di messaggi. In Impostazioni generali impostare le opzioni seguenti e quindi selezionare Applica.

    Generali Impostazione
    Generare macro di registrazione in modalità kernel Sì (km)
    Usare il nome di base dell'input Sì (-b)
  4. In Opzioni file impostare le opzioni seguenti e quindi selezionare Applica.

    Opzioni relative ai file Impostazione
    Generare un file di intestazione per contenere il contatore
    Percorso del file di intestazione $(IntDir)
    Percorso dei file di messaggi binari e RC generati
    Percorso file RC $(IntDir)
    Nome di base dei file generati $(Filename)

Per impostazione predefinita, il compilatore di messaggi usa il nome di base del file di input come nome di base dei file generati. Per specificare un nome di base, impostare il campo Nome base file generati (-z). Nell'esempio di Eventdr.sys il nome di base viene impostato su evntdrvEvents in modo che corrisponda al nome del file di intestazione evntdrvEvents.h, incluso in evntdrv.c.

Nota

Se non si include il file RC generato nel progetto di Visual Studio, è possibile che vengano visualizzati messaggi di errore relativi alle risorse non trovate quando si installa il file manifesto.

4. Aggiungere il codice generato per generare (pubblicare) gli eventi (registrare, annullare la registrazione e scrivere eventi)

Nel manifesto di strumentazione sono stati definiti i nomi del provider di eventi e dei descrittori di eventi. Quando si compila il manifesto di strumentazione con il compilatore di messaggi, il compilatore di messaggi genera un file di intestazione che descrive le risorse e definisce anche le macro per gli eventi. È ora necessario aggiungere il codice generato al driver per generare questi eventi.

  1. Nel file di origine includere il file di intestazione dell'evento generato dal compilatore di messaggi (MC.exe). Nell'esempio Eventdrv, ad esempio, il file di origine Evntdrv.c include il file di intestazione (evntdrvEvents.h) generato nel passaggio precedente:

    #include "evntdrvEvents.h"  
    
  2. Aggiungere le macro che registrano e annullano la registrazione del driver come provider di eventi. Ad esempio, nel file di intestazione per l'esempio Eventdrv (evntdrvEvents.h), il compilatore di messaggi crea macro in base al nome del provider. Nel manifesto l'esempio Eventdrv usa il nome "Driver di esempio" come nome del provider. Il compilatore di messaggi combina il nome del provider con la macro evento per registrare il provider, in questo 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
    

    Aggiungere la macro del provider> EventRegister<alla funzione DriverEntry. Aggiungere questa funzione dopo il codice che crea e inizializza l'oggetto dispositivo. Si noti che è necessario associare la chiamata alla funzione del provider> EventRegister<con una chiamata al provider> EventUnregister.< È possibile annullare la registrazione del driver nella routine scaricamento* del driver.

       // DriverEntry function
       // ...
    
    
        // Register with ETW
        //
        EventRegisterSample_Driver();
    
  3. Aggiungere il codice generato ai file di origine del driver per scrivere (generare) gli eventi specificati nel manifesto. Il file di intestazione compilato dal manifesto contiene il codice generato per il driver. Usare le funzioni evento EventWrite<> definite nel file di intestazione per pubblicare messaggi di traccia in ETW. Ad esempio, il codice seguente mostra le macro per gli eventi definiti in evntdrvEvents.h per l'esempio Eventdrv.

    Le macro per scrivere questi eventi sono denominate : EventWriteStartEvent, EventWriteSampleEventAe EventWriteUnloadEvent. Come si può notare nella definizione di queste macro, la definizione di macro include automaticamente una macro evento EventEnabled>< che controlla se l'evento è abilitato. Il controllo elimina la necessità di compilare il payload se l'evento non è abilitato.

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

    Aggiungere le macro evento EventWrite<> nel codice sorgente per gli eventi generati. Ad esempio, il frammento di codice seguente mostra la routine DriverEntry dell'esempio Eventdrv. DriverEntry include le macro per registrare il driver con ETW (EventRegisterSample_Driver) e la macro per scrivere l'evento driver in 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;
    }
    

Aggiungere tutte le macro evento EventWrite>< nel codice sorgente per gli eventi generati. È possibile esaminare l'esempio Eventdrv per vedere come vengono chiamate le altre due macro per gli eventi nel codice sorgente del driver.

  1. Annullare la registrazione del driver come provider di eventi usando la macro del provider> EventUnregister<dal file di intestazione generato.

    Inserire questa chiamata di funzione nella routine di scaricamento del driver. Non è necessario eseguire chiamate di traccia dopo la chiamata della macro del provider> EventUnregister.< L'annullamento della registrazione del provider di eventi può causare errori quando il processo viene scaricato perché le funzioni di callback associate al processo non sono più valide.

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

5. Compilare il driver

Se il manifesto dello strumento è stato aggiunto al progetto e sono state configurate le proprietà del compilatore di messaggi (MC.exe), è possibile compilare il progetto o la soluzione driver usando Visual Studio e MSBuild.

  1. Aprire la soluzione driver in Visual Studio.

  2. Compilare l'esempio dal menu Compila selezionando Compila soluzione. Per altre informazioni sulla creazione di soluzioni, vedere Compilazione di un driver.

6. Installare il manifesto

È necessario installare il manifesto nel sistema di destinazione in modo che i consumer di eventi (ad esempio, il registro eventi) possano trovare il percorso del file binario che contiene i metadati dell'evento. Se è stata aggiunta l'attività del compilatore di messaggi al progetto driver nel passaggio 3, il manifesto di strumentazione è stato compilato e i file di risorse sono stati generati durante la compilazione del driver. Usare l'utilità della riga di comando dell'evento di Windows (Wevtutil.exe) per installare il manifesto. La sintassi per installare il manifesto è la seguente:

wevtutil.exe drivermanifest

Ad esempio, per installare il manifesto per il driver di esempio Evntdrv.sys, aprire una finestra del prompt dei comandi con privilegi elevati (Esegui come amministratore) passare alla directory in cui si trova il file evntdrv.xml e immettere il comando seguente:

Wevtutil.exe im evntdrv.xml

Al termine della sessione di traccia, disinstallare il manifesto usando la sintassi seguente.

wevtutil.exe drivermanifest

Ad esempio, per disinstallare il manifesto per l'esempio Eventdrv:

Wevtutil.exe um evntdrv.xml

7. Testare il driver per verificare il supporto ETW

Installare il driver. Esercizio del driver per generare l'attività di traccia. Visualizzare i risultati nella Visualizzatore eventi. È anche possibile eseguire Tracelog, quindi eseguire Tracerpt, uno strumento per l'elaborazione dei log di traccia eventi, per controllare, raccogliere e visualizzare i log di traccia eventi.