Condividi tramite


PCW_CALLBACK funzione di callback (wdm.h)

I provider possono facoltativamente implementare una funzione di PCW_CALLBACK per ricevere notifiche quando i consumer effettuano richieste, ad esempio l'enumerazione delle istanze o la raccolta di dati del contatore. La Performance Counter Library (PERFLIB versione 2.0) chiama la funzione PCW_CALLBACK 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 possibili sono PcwCallbackAddCounter, PcwCallbackRemoveCounter, PcwCallbackEnumerateInstancese 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 presenti nel campo corrispondente al parametro Type. Ad esempio, se Type == PcwCallbackEnumerateInstances i dettagli saranno inclusi 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 funzione di callback PCW_CALLBACK deve restituire STATUS_SUCCESS se il callback è stato completato senza errori o un codice di errore NTSTATUS 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.

Osservazioni

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 funzione PCW_CALLBACK 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 contemporaneamente dati dal provider in thread diversi.

Il callback deve gestire i tipi di richiesta PcwCallbackEnumerateInstances 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 per ottimizzare la raccolta dati ( ad esempio per disabilitare il rilevamento delle statistiche quando non sono attive query).

Il callback è responsabile della generazione di valori Name e Id per le istanze del contatore.

  • I valori Id dell'istanza DEVONO essere stabili nel tempo (la stessa istanza logica deve usare lo stesso valore Id per tutte le chiamate del callback), deve essere univoco (ad esempio, non usare semplicemente 0 per tutte le istanze) e deve essere minore 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 Name dell'istanza DEVONO essere stabili nel tempo (la stessa istanza logica deve usare lo stesso valore Name 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 valori non devono essere diversi solo per maiuscole e minuscole.

Quando si gestiscono le richieste di PcwCallbackCollectData, 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 informazioni, vedere 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 dei dati lasciando i valori corrispondenti come 0 nel blocco dati del contatore.

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

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

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

Un'implementazione di callback può essere strutturata nel modo seguente:

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 di CTRPP per elaborare il manifesto del contatore e generare funzioni helper, incluse le funzioni di wrapping PcwRegister (CTRPP genera i descrittori dei contatori) 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 manifesto KCS.man dell'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 funzione PCW_CALLBACK per gestire le richieste consumer. Nell'esempio di codice seguente viene illustrata una funzione PCW_CALLBACK denominata KcsGeometricWaveCallback che enumera e raccoglie 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;
}

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

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

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

Fabbisogno

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

Vedere anche

PcwRegister

PcwAddInstance

PcwCreateInstance

PCW_CALLBACK_TYPE

PCW_CALLBACK_INFORMATION

CTRPP

libreria dei contatori delle prestazioni (PERFLIB versione 2.0)