PCW_CALLBACK função de retorno de chamada (wdm.h)

Opcionalmente, os provedores podem implementar uma PCW_CALLBACK função para receber notificações quando os consumidores fazem solicitações, como enumerar instâncias ou coletar dados de contraconjunto. A Biblioteca de Contadores de Desempenho (PERFLIB versão 2.0) chama a PCW_CALLBACK função antes de concluir a solicitação do consumidor.

Sintaxe

PCW_CALLBACK PcwCallback;

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

Parâmetros

[in] Type

Um PCW_CALLBACK_TYPE valor de enumeração que indica por que o retorno de chamada foi invocado. Os valores possíveis são PcwCallbackAddCounter, PcwCallbackRemoveCounter, PcwCallbackEnumerateInstances e PcwCallbackCollectData.

[in] Info

Um ponteiro para uma união PCW_CALLBACK_INFORMATION que fornece detalhes sobre por que o retorno de chamada do provedor foi invocado. Os detalhes estarão no campo correspondente ao Type parâmetro . Por exemplo, se Type == PcwCallbackEnumerateInstances os detalhes estiverem em Info->EnumerateInstances.

[in, optional] Context

O contexto de retorno de chamada fornecido pelo provedor ao chamar PcwRegister ou ao chamar a função Register gerada por CTRPP (que invoca PcwRegister).

Retornar valor

A PCW_CALLBACK função de retorno de chamada deverá retornar STATUS_SUCCESS se o retorno de chamada for concluído sem erros ou se um NTSTATUS código de erro for o contrário. Observe que esse código de retorno é apenas para fins informativos e que o processamento da solicitação do consumidor continuará mesmo que o retorno de chamada retorne um erro.

Comentários

Os provedores de contraconjunto podem fornecer informações ao consumidor por meio de dois sistemas diferentes:

  • O provedor pode usar PcwCreateInstance e PcwCloseInstance para manter uma lista de instâncias disponíveis e os dados de contador correspondentes. Esse sistema é simples de implementar, mas limitado em flexibilidade. Ao usar esse sistema, o provedor não precisa fornecer uma função de retorno de chamada. Para obter mais informações sobre esse sistema, consulte a documentação de PcwCreateInstance.

  • O provedor pode fornecer uma PCW_CALLBACK função que será invocada pela Biblioteca de Contadores de Desempenho conforme necessário para coletar dados.

A implementação do retorno de chamada deve ser thread-safe. Vários consumidores diferentes podem solicitar dados simultaneamente do provedor em threads diferentes.

O retorno de chamada deve lidar com os PcwCallbackEnumerateInstances tipos de solicitação e PcwCallbackCollectData . O retorno de chamada geralmente não precisa lidar com outros tipos de solicitação, mas em cenários complexos o retorno de chamada também pode manipular PcwCallbackAddCounter e PcwCallbackRemoveCounter otimizar a coleta de dados (ou seja, para desabilitar o acompanhamento de estatísticas quando nenhuma consulta estiver ativa).

O retorno de chamada é responsável por gerar Name valores e Id para as instâncias de contador.

  • Os valores de instância Id DEVEM ser estáveis ao longo do tempo (a mesma instância lógica deve usar o mesmo Id valor para todas as invocações do retorno de chamada), deve ser exclusivo (por exemplo, não use apenas 0 para todas as instâncias) e deve ser menor que 0xFFFFFFFE (não use PCW_ANY_INSTANCE_ID). Quando possível, a instância Id deve ser significativa (por exemplo, um contador de processo pode usar um PID como o Id) em vez de arbitrário (por exemplo, um número de sequência).

  • Os valores de instância Name DEVEM ser estáveis ao longo do tempo (a mesma instância lógica deve usar o mesmo Name valor para todas as invocações do retorno de chamada) e DEVE ser exclusiva. Se o contador der suporte a várias instâncias, a instância Name não deverá estar em branco. A correspondência de cadeia de caracteres é feita usando uma comparação que não diferencia maiúsculas de minúsculas, portanto Name , os valores não devem ser diferentes apenas por maiúsculas e minúsculas.

Ao lidar com PcwCallbackCollectData solicitações, uma implementação de retorno de chamada básica simplesmente invocará PcwAddInstance (ou a função AddXxx gerada por CTRPP) uma vez para cada instância de contador. Para obter mais informações, consulte Função AddXxx gerada por CTRPP.

As seguintes otimizações podem ser usadas em implementações mais avançadas quando necessário:

  • Se Info->CollectData.CounterMask != (UINT64)-1 o consumidor não precisar de todos os contadores no contador. Nesse caso, o retorno de chamada pode otimizar a coleta de dados deixando os valores correspondentes como 0 no bloco de dados do contador.

  • Se Info->CollectData.InstanceId != PCW_ANY_INSTANCE_ID o consumidor quiser apenas dados sobre instâncias com um InstanceId igual a CollectData.InstanceId. O retorno de chamada pode otimizar a coleta de dados ignorando a chamada para PcwAddInstance para instâncias com não correspondentes InstanceId.

  • Se Info->CollectData.InstanceMask != "*" o consumidor quiser apenas dados sobre instâncias com um InstanceName que corresponda ao padrão curinga de CollectData.InstanceMask. O retorno de chamada pode otimizar a coleta de dados ignorando a chamada para PcwAddInstance para instâncias com não correspondentes InstanceName. A correspondência de caracteres curinga é difícil de implementar corretamente, portanto, essa otimização é recomendada somente quando a coleta de dados de instância é muito cara.

Na maioria dos casos, a implementação de retorno de chamada para uma PcwCallbackEnumerateInstances solicitação será idêntica à implementação de um PcwCallbackCollectData. Opcionalmente, o retorno de chamada pode otimizar a coleta de dados omitindo os dados reais do contador na chamada para PcwAddInstance (ou seja, passando 0 e NULL para os Count parâmetros e Data ).

Uma implementação de retorno de chamada pode ser estruturada da seguinte maneira:

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;
}

A maioria dos provedores de contraconjunto usa a ferramenta CTRPP para processar seu manifesto de contraconjunto e gerar funções auxiliares, incluindo o encapsulamento PcwRegister de funções (CTRPP gera os descritores de contador) e PcwAddInstance (CTRPP gera código para encapsular as estruturas de dados do provedor no formato exigido por PcwAddInstance).

Para referência neste exemplo, veja a seguir a função Register gerada por CTRPP para o KCS.man manifesto do exemplo 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);
}

O provedor de contadores implementa a PCW_CALLBACK função para lidar com solicitações do consumidor. O exemplo de código a seguir mostra uma PCW_CALLBACK função chamada KcsGeometricWaveCallback que enumera e coleta dados simulados. (Observe que KcsAddGeometricWave é uma função auxiliar gerada por CTRPP que chama 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 Na rotina do exemplo de KCS, a KcsGeometricWaveCallback função é especificada como o Callback quando KcsRegisterGeometricWave registra o contador.

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

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

Requisitos

Requisito Valor
Cliente mínimo com suporte Disponível no Windows 7 e versões posteriores do Windows.
Plataforma de Destino Área de Trabalho
Cabeçalho wdm.h (include Wdm.h, Ntddk.h)
IRQL IRQL <=APC_LEVEL

Confira também

PcwRegister

PcwAddInstance

PcwCreateInstance

PCW_CALLBACK_TYPE

PCW_CALLBACK_INFORMATION

CTRPP

Biblioteca de Contadores de Desempenho (PERFLIB versão 2.0)