PCW_CALLBACK funzione di callback (wdm.h)

I provider possono facoltativamente implementare una PCW_CALLBACK funzione per ricevere notifiche quando i consumer effettuano richieste come l'enumerazione di istanze o la raccolta di dati del counterset. La libreria dei contatori delle prestazioni (PERFLIB versione 2.0) chiama la PCW_CALLBACK funzione prima di completare la richiesta del consumer.

Sintassi

PCW_CALLBACK PcwCallback;

NTSTATUS PcwCallback(
  [in]           PCW_CALLBACK_TYPE Type,
  [in]           PPCW_CALLBACK_INFORMATION Info,
  [in, optional] PVOID Context
)
{...}

Parametri

[in] Type

Valore di enumerazione PCW_CALLBACK_TYPE che indica il motivo per cui è stato richiamato il callback. I valori consentiti sono PcwCallbackAddCounter, PcwCallbackRemoveCounter, PcwCallbackEnumerateInstances e PcwCallbackCollectData.

[in] Info

Puntatore a un'unione PCW_CALLBACK_INFORMATION che fornisce informazioni dettagliate sul motivo per cui è stato richiamato il callback del provider. I dettagli saranno nel campo corrispondente al Type parametro . Ad esempio, se Type == PcwCallbackEnumerateInstances i dettagli saranno in Info->EnumerateInstances.

[in, optional] Context

Contesto di callback fornito dal provider quando si chiama PcwRegister o quando si chiama la funzione Register generata da CTRPP (che richiama PcwRegister).

Valore restituito

La PCW_CALLBACK funzione di callback deve restituire STATUS_SUCCESS se il callback è stato completato senza errori o un NTSTATUS codice di errore in caso contrario. Si noti che questo codice restituito è solo a scopo informativo e che l'elaborazione della richiesta del consumer continuerà anche se il callback restituisce un errore.

Commenti

I provider di contatori possono fornire informazioni al consumer tramite due sistemi diversi:

  • Il provider può usare PcwCreateInstance e PcwCloseInstance per mantenere un elenco di istanze disponibili e i dati dei contatori corrispondenti. Questo sistema è semplice da implementare ma limitato in flessibilità. Quando si usa questo sistema, il provider non deve fornire una funzione di callback. Per altre informazioni su questo sistema, vedere la documentazione per PcwCreateInstance.

  • Il provider può fornire una PCW_CALLBACK funzione che verrà richiamata dalla libreria dei contatori delle prestazioni in base alle esigenze per raccogliere i dati.

L'implementazione del callback deve essere thread-safe. Più consumer diversi potrebbero richiedere simultaneamente dati dal provider in thread diversi.

Il callback deve gestire i PcwCallbackEnumerateInstances tipi di richiesta e PcwCallbackCollectData . Il callback in genere non deve gestire altri tipi di richiesta, ma in scenari complessi il callback potrebbe anche gestire PcwCallbackAddCounter e PcwCallbackRemoveCounter ottimizzare la raccolta dei dati( ad esempio per disabilitare il rilevamento delle statistiche quando non sono attive query).

Il callback è responsabile della generazione Name e Id dei valori per le istanze del counterset.

  • I valori di istanza Id DEVONO essere stabili nel tempo (la stessa istanza logica deve usare lo stesso Id valore per tutte le chiamate del callback), devono essere univoci (ad esempio, non usare semplicemente 0 per tutte le istanze) e devono essere minori di 0xFFFFFFFE (non usare PCW_ANY_INSTANCE_ID). Quando possibile, l'istanza Id deve essere significativa( ad esempio, un contatore process potrebbe usare un PID come Id) anziché arbitrario (ad esempio, un numero di sequenza).

  • I valori dell'istanza Name DEVONO essere stabili nel tempo (la stessa istanza logica deve usare lo stesso Name valore per tutte le chiamate del callback) e DEVE essere univoco. Se il contatore supporta più istanze, l'istanza Name non deve essere vuota. La corrispondenza di stringhe viene eseguita usando un confronto senza distinzione tra maiuscole e minuscole, pertanto Name i valori non devono essere diversi solo per maiuscole e minuscole.

Quando si gestiscono PcwCallbackCollectData le richieste, un'implementazione di callback di base richiama semplicemente PcwAddInstance (o la funzione AddXxx generata da CTRPP) una volta per ogni istanza del counterset. Per altre info, vedi Funzione AddXxx generata da CTRPP.

Quando necessario, è possibile usare le ottimizzazioni seguenti nelle implementazioni più avanzate:

  • Se Info->CollectData.CounterMask != (UINT64)-1 il consumer non necessita di tutti i contatori nel contatore. In questo caso, il callback può ottimizzare la raccolta dati lasciando i valori corrispondenti come 0 nel blocco di dati del contatore.

  • Se Info->CollectData.InstanceId != PCW_ANY_INSTANCE_ID il consumer vuole solo dati sulle istanze con un InstanceId valore uguale a CollectData.InstanceId. Il callback può ottimizzare la raccolta dei dati ignorando la chiamata a PcwAddInstance per le istanze con .InstanceId

  • Se Info->CollectData.InstanceMask != "*" il consumer vuole solo dati sulle istanze con un InstanceName oggetto che corrisponde al modello con caratteri jolly di CollectData.InstanceMask. Il callback può ottimizzare la raccolta dei dati ignorando la chiamata a PcwAddInstance per le istanze con .InstanceName La corrispondenza con caratteri jolly è difficile da implementare correttamente, pertanto questa ottimizzazione è consigliata solo quando la raccolta dei dati dell'istanza è molto costosa.

Nella maggior parte dei casi, l'implementazione di callback per una PcwCallbackEnumerateInstances richiesta sarà identica all'implementazione di per un oggetto PcwCallbackCollectData. Il callback può facoltativamente ottimizzare la raccolta dei dati omettendo i dati effettivi del contatore nella chiamata a PcwAddInstance ,ovvero passando 0 e NULL per i Count parametri e Data .

Un'implementazione di callback può essere strutturata come segue:

NTSTATUS NTAPI
MyProviderCallback(
    _In_ PCW_CALLBACK_TYPE Type,
    _In_ PPCW_CALLBACK_INFORMATION Info,
    _In_opt_ PVOID Context)
{
    PCW_MASK_INFORMATION* MaskInfo;
    PAGED_CODE();

    switch (Type)
    {
    case PcwCallbackCollectData:
        MaskInfo = &Info->CollectData;
        break;

    case PcwCallbackEnumerateInstances:
        MaskInfo = &Info->EnumerateInstances;
        break;

    case PcwCallbackAddCounter:
        // Optional (for optimizing data collection):
        // InterlockedIncrement(&CollectionEnableCount);
        return STATUS_SUCCESS; // Normally no action needed.

    case PcwCallbackRemoveCounter:
        // Optional (for optimizing data collection):
        // InterlockedDecrement(&CollectionEnableCount);
        return STATUS_SUCCESS; // Normally no action needed.
    }

    // Common code for CollectData and EnumerateInstances.
    // Note that this code needs to be thread-safe, as multiple
    // threads might invoke this callback at the same time.

    for (Instance : InstanceList) { // Pseudocode, need thread-safe enumeration
        NTSTATUS Status;

        // Optional optimization:
        // if (MaskInfo->InstanceId != PCW_ANY_INSTANCE_ID && Instance->Id != MaskInfo->InstanceId) {
        //     continue;
        // }

        // Note that in most cases, you'll use a CTRPP-generated Add wrapper instead of directly
        // calling PcwAddInstance.
        Status = PcwAddInstance(MaskInfo->Buffer,
                                &Instance->Name,
                                Instance->Id,
                                1, // Number of items in PcwData array
                                &Instance->PcwData);
        if (!NT_SUCCESS(Status)) {
            return Status;
        }
    }

    return STATUS_SUCCESS;
}

La maggior parte dei provider di contatori usa lo strumento CTRPP per elaborare il manifesto del contatore e generare funzioni helper, incluso il wrapping PcwRegister delle funzioni (CTRPP genera i descrittori del contatore) e PcwAddInstance (CTRPP genera il codice per il wrapping delle strutture di dati del provider nel formato richiesto da PcwAddInstance).

Per riferimento in questo esempio, di seguito è riportata la funzione Register generata da CTRPP per il KCS.man manifesto dall'esempio KCS.

EXTERN_C FORCEINLINE NTSTATUS
KcsRegisterGeometricWave(
    __in_opt PPCW_CALLBACK Callback,
    __in_opt PVOID CallbackContext
    )
{
    PCW_REGISTRATION_INFORMATION RegInfo;
    UNICODE_STRING Name = RTL_CONSTANT_STRING(L"Geometric Waves");
    PCW_COUNTER_DESCRIPTOR Descriptors[] = {
        { 1, 0, FIELD_OFFSET(GEOMETRIC_WAVE_VALUES, Triangle), RTL_FIELD_SIZE(GEOMETRIC_WAVE_VALUES, Triangle)},
        { 2, 0, FIELD_OFFSET(GEOMETRIC_WAVE_VALUES, Square), RTL_FIELD_SIZE(GEOMETRIC_WAVE_VALUES, Square)},
    };

    PAGED_CODE();

    RtlZeroMemory(&RegInfo, sizeof RegInfo);

    RegInfo.Version = PCW_CURRENT_VERSION;
    RegInfo.Counters = Descriptors;
    RegInfo.CounterCount = RTL_NUMBER_OF(Descriptors);
    RegInfo.Callback = Callback;
    RegInfo.CallbackContext = CallbackContext;
    RegInfo.Name = &Name;

    return PcwRegister(&KcsGeometricWave, &RegInfo);
}

Il provider di contatori implementa la PCW_CALLBACK funzione per gestire le richieste consumer. Nell'esempio di codice seguente viene illustrata una PCW_CALLBACK funzione denominata KcsGeometricWaveCallback che enumera e raccoglie i dati simulati. Si noti che KcsAddGeometricWave è una funzione helper generata da CTRPP che chiama PcwAddInstance.)

NTSTATUS
KcsAddGeometricInstance (
    _In_ PPCW_BUFFER Buffer,
    _In_ PCWSTR Name,
    _In_ ULONG Id,
    _In_ ULONG MinimalValue,
    _In_ ULONG Amplitude
    )
{
    ULONG Index;
    LARGE_INTEGER Timestamp;
    UNICODE_STRING UnicodeName;
    GEOMETRIC_WAVE_VALUES Values;

    PAGED_CODE();

    KeQuerySystemTime(&Timestamp);

    Index = (Timestamp.QuadPart / 10000000) % 10;

    Values.Triangle = MinimalValue + Amplitude * abs(5 - Index) / 5;
    Values.Square = MinimalValue + Amplitude * (Index < 5);

    RtlInitUnicodeString(&UnicodeName, Name);

    return KcsAddGeometricWave(Buffer, &UnicodeName, Id, &Values);
}

NTSTATUS NTAPI
KcsGeometricWaveCallback (
    __in PCW_CALLBACK_TYPE Type,
    __in PPCW_CALLBACK_INFORMATION Info,
    __in_opt PVOID Context
    )
{
    NTSTATUS Status;
    UNICODE_STRING UnicodeName;

    UNREFERENCED_PARAMETER(Context);

    PAGED_CODE();

    switch (Type) {
    case PcwCallbackEnumerateInstances:

        //
        // Instances are being enumerated, so we add them without values.
        //

        RtlInitUnicodeString(&UnicodeName, L"Small Wave");
        Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer,
                                     &UnicodeName,
                                     0,
                                     NULL);
        if (!NT_SUCCESS(Status)) {
            return Status;
        }

        RtlInitUnicodeString(&UnicodeName, L"Medium Wave");
        Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer,
                                     &UnicodeName,
                                     1,
                                     NULL);
        if (!NT_SUCCESS(Status)) {
            return Status;
        }

        RtlInitUnicodeString(&UnicodeName, L"Large Wave");
        Status = KcsAddGeometricWave(Info->EnumerateInstances.Buffer,
                                     &UnicodeName,
                                     2,
                                     NULL);
        if (!NT_SUCCESS(Status)) {
            return Status;
        }

        break;

 case PcwCallbackCollectData:

        //
        // Add values for 3 instances of Geometric Wave Counterset.
        //

        Status = KcsAddGeometricInstance(Info->CollectData.Buffer,
                                         L"Small Wave",
                                         0,
                                         40,
                                         20);
        if (!NT_SUCCESS(Status)) {
            return Status;
        }

        Status = KcsAddGeometricInstance(Info->CollectData.Buffer,
                                         L"Medium Wave",
                                         1,
                                         30,
                                         40);
        if (!NT_SUCCESS(Status)) {
            return Status;
        }

        Status = KcsAddGeometricInstance(Info->CollectData.Buffer,
                                         L"Large Wave",
                                         2,
                                         20,
                                         60);
        if (!NT_SUCCESS(Status)) {
            return Status;
        }

        break;
    }

    return STATUS_SUCCESS;
}

DriverEntry Nella routine dell'esempio KCS la KcsGeometricWaveCallback funzione viene specificata come quando CallbackKcsRegisterGeometricWave registra il contatore.

    //
    // Register Countersets during DriverEntry. (TODO: Unregister at driver unload.)
    //

    Status = KcsRegisterGeometricWave(KcsGeometricWaveCallback, NULL);
    if (!NT_SUCCESS(Status)) {
        return Status;
    }

Requisiti

Requisito Valore
Client minimo supportato Disponibile in Windows 7 e versioni successive di Windows.
Piattaforma di destinazione Desktop
Intestazione wdm.h (include Wdm.h, Ntddk.h)
IRQL IRQL <=APC_LEVEL

Vedi anche

PcwRegister

PcwAddInstance

PcwCreateInstance

PCW_CALLBACK_TYPE

PCW_CALLBACK_INFORMATION

CTRPP

Libreria dei contatori delle prestazioni (PERFLIB versione 2.0)