Compartir a través de


PCW_CALLBACK función de devolución de llamada (wdm.h)

Opcionalmente, los proveedores pueden implementar una PCW_CALLBACK función para recibir notificaciones cuando los consumidores realizan solicitudes como enumerar instancias o recopilar datos de conjunto de contadores. La biblioteca de contadores de rendimiento (PERFLIB versión 2.0) llama a la PCW_CALLBACK función antes de completar la solicitud del consumidor.

Sintaxis

PCW_CALLBACK PcwCallback;

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

Parámetros

[in] Type

Valor de enumeración PCW_CALLBACK_TYPE que indica por qué se invocó la devolución de llamada. Los valores posibles son PcwCallbackAddCounter, PcwCallbackRemoveCounter, PcwCallbackEnumerateInstances y PcwCallbackCollectData.

[in] Info

Puntero a una unión PCW_CALLBACK_INFORMATION que proporciona detalles sobre por qué se invocó la devolución de llamada del proveedor. Los detalles estarán en el campo correspondiente al Type parámetro . Por ejemplo, si Type == PcwCallbackEnumerateInstances los detalles estarán en Info->EnumerateInstances.

[in, optional] Context

Contexto de devolución de llamada proporcionado por el proveedor al llamar a PcwRegister o al llamar a la función Register generada por CTRPP (que invoca PcwRegister).

Valor devuelto

La PCW_CALLBACK función de devolución de llamada debe devolverse STATUS_SUCCESS si la devolución de llamada se completó sin errores o un NTSTATUS código de error en caso contrario. Tenga en cuenta que este código de retorno es solo para fines informativos y que el procesamiento de la solicitud del consumidor continuará incluso si la devolución de llamada devuelve un error.

Comentarios

Los proveedores de conjuntos de contadores pueden proporcionar información al consumidor a través de dos sistemas diferentes:

  • El proveedor puede usar PcwCreateInstance y PcwCloseInstance para mantener una lista de instancias disponibles y los datos de contador correspondientes. Este sistema es sencillo de implementar pero limitado en flexibilidad. Al usar este sistema, el proveedor no necesita proporcionar una función de devolución de llamada. Para obtener más información sobre este sistema, consulte la documentación de PcwCreateInstance.

  • El proveedor puede proporcionar una PCW_CALLBACK función que la biblioteca de contadores de rendimiento invocará según sea necesario para recopilar datos.

La implementación de devolución de llamada debe ser segura para subprocesos. Varios consumidores diferentes pueden solicitar simultáneamente datos del proveedor en subprocesos diferentes.

La devolución de llamada debe controlar los PcwCallbackEnumerateInstances tipos de solicitud y PcwCallbackCollectData . Normalmente, la devolución de llamada no necesita controlar otros tipos de solicitud, pero en escenarios complejos, la devolución de llamada también podría controlar PcwCallbackAddCounter y PcwCallbackRemoveCounter optimizar la recopilación de datos (es decir, para deshabilitar el seguimiento de estadísticas cuando no hay consultas activas).

La devolución de llamada es responsable de generar Name y Id valores para las instancias del conjunto de contadores.

  • Los valores de instancia Id DEBEN ser estables con el tiempo (la misma instancia lógica debe usar el mismo Id valor para todas las invocaciones de la devolución de llamada), deben ser únicos (por ejemplo, no simplemente use 0 para todas las instancias) y deben ser menores que 0xFFFFFFFE (no use PCW_ANY_INSTANCE_ID). Cuando sea posible, la instancia Id debe ser significativa (por ejemplo, un conjunto de contadores de procesos podría usar un PID como Id) en lugar de arbitrario (por ejemplo, un número de secuencia).

  • Los valores de instancia Name DEBEN ser estables con el tiempo (la misma instancia lógica debe usar el mismo Name valor para todas las invocaciones de la devolución de llamada) y DEBEN ser únicos. Si el conjunto de contadores admite varias instancias, la instancia Name no debe estar en blanco. La coincidencia de cadenas se realiza mediante una comparación sin distinción entre mayúsculas y minúsculas, por lo que Name los valores no deben diferir solo por mayúsculas y minúsculas.

Al controlar PcwCallbackCollectData las solicitudes, una implementación de devolución de llamada básica simplemente invocará PcwAddInstance (o la función AddXxx generada por CTRPP) una vez para cada instancia del conjunto de contadores. Para obtener más información, consulta función AddXxx generada por CTRPP.

Las siguientes optimizaciones se pueden usar en implementaciones más avanzadas cuando sea necesario:

  • Si Info->CollectData.CounterMask != (UINT64)-1 el consumidor no necesita todos los contadores del conjunto de contadores. En este caso, la devolución de llamada puede optimizar la recopilación de datos dejando los valores correspondientes como 0 en el bloque de datos del contador.

  • Si Info->CollectData.InstanceId != PCW_ANY_INSTANCE_ID el consumidor solo quiere datos sobre las instancias con un InstanceId valor igual a CollectData.InstanceId. La devolución de llamada puede optimizar la recopilación de datos omitiendo la llamada a PcwAddInstance para instancias que no coinciden InstanceIdcon .

  • Si Info->CollectData.InstanceMask != "*" el consumidor solo quiere datos sobre las instancias con un InstanceName que coincide con el patrón de caracteres comodín de CollectData.InstanceMask. La devolución de llamada puede optimizar la recopilación de datos omitiendo la llamada a PcwAddInstance para instancias que no coinciden InstanceNamecon . La coincidencia de caracteres comodín es difícil de implementar correctamente, por lo que esta optimización solo se recomienda cuando la recopilación de datos de instancia es muy costosa.

En la mayoría de los casos, la implementación de devolución de llamada de una PcwCallbackEnumerateInstances solicitud será idéntica a la implementación de .PcwCallbackCollectData La devolución de llamada puede optimizar opcionalmente la recopilación de datos omitiendo los datos de contador reales de la llamada a PcwAddInstance (es decir, pasando 0 y NULL para los Count parámetros y Data ).

Una implementación de devolución de llamada podría estructurarse de la siguiente manera:

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 mayoría de los proveedores de conjuntos de contadores usan la herramienta CTRPP para procesar su manifiesto de conjunto de contadores y generar funciones auxiliares, incluido el ajuste PcwRegister de funciones (CTRPP genera los descriptores de contador) y PcwAddInstance (CTRPP genera código para encapsular las estructuras de datos del proveedor en el formato requerido por PcwAddInstance).

Como referencia en este ejemplo, lo siguiente es la función Register generada por CTRPP para el KCS.man manifiesto del ejemplo de 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);
}

El proveedor de conjuntos de contadores implementa la PCW_CALLBACK función para controlar las solicitudes de consumidor. En el ejemplo de código siguiente se muestra una PCW_CALLBACK función denominada KcsGeometricWaveCallback que enumera y recopila datos simulados. (Tenga en cuenta que KcsAddGeometricWave es una función auxiliar generada por CTRPP que llama a 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;
}

En la DriverEntry rutina del ejemplo de KCS, la KcsGeometricWaveCallback función se especifica como Callback cuando KcsRegisterGeometricWave registra el conjunto de contadores.

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

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

Requisitos

Requisito Value
Cliente mínimo compatible Disponible en Windows 7 y versiones posteriores de Windows.
Plataforma de destino Escritorio
Encabezado wdm.h (incluya Wdm.h, Ntddk.h)
IRQL IRQL <=APC_LEVEL

Consulte también

PcwRegister

PcwAddInstance

PcwCreateInstance

PCW_CALLBACK_TYPE

PCW_CALLBACK_INFORMATION

CTRPP

Biblioteca de contadores de rendimiento (PERFLIB versión 2.0)