Поделиться через


функция обратного вызова PCW_CALLBACK (wdm.h)

При необходимости поставщики могут реализовать PCW_CALLBACK функцию для получения уведомлений, когда потребители выполняют запросы, такие как перечисление экземпляров или сбор данных счетчиков. Библиотека счетчиков производительности (PERFLIB версии 2.0) вызывает функцию PCW_CALLBACK перед выполнением запроса потребителя.

Синтаксис

PCW_CALLBACK PcwCallback;

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

Параметры

[in] Type

Значение перечисления PCW_CALLBACK_TYPE , указывающее, почему был вызван обратный вызов. Допустимые значения: PcwCallbackAddCounter, PcwCallbackRemoveCounter, PcwCallbackEnumerateInstances и PcwCallbackCollectData.

[in] Info

Указатель на PCW_CALLBACK_INFORMATION объединение, предоставляющее сведения о том, почему был вызван обратный вызов поставщика. Сведения будут указаны в поле, соответствующем параметру Type . Например, если Type == PcwCallbackEnumerateInstances тогда сведения будут находиться в Info->EnumerateInstances.

[in, optional] Context

Контекст обратного вызова, предоставленный поставщиком при вызове PcwRegister или при вызове функции Register, созданной CTRPP (которая вызывает PcwRegister).

Возвращаемое значение

Функция PCW_CALLBACK обратного вызова должна возвращать, STATUS_SUCCESS если обратный вызов выполнен без ошибок, или код ошибки в NTSTATUS противном случае. Обратите внимание, что этот код возврата предназначен только для информационных целей, и обработка запроса потребителя будет продолжена, даже если обратный вызов возвращает ошибку.

Комментарии

Поставщики наборов счетчиков могут предоставлять сведения потребителю через две разные системы:

  • Поставщик может использовать PcwCreateInstance и PcwCloseInstance для ведения списка доступных экземпляров и соответствующих данных счетчика. Эта система проста в реализации, но ограничена в гибкости. При использовании этой системы поставщику не нужно предоставлять функцию обратного вызова. Дополнительные сведения об этой системе см. в документации по PcwCreateInstance.

  • Поставщик может предоставить функцию PCW_CALLBACK , которая будет вызываться библиотекой счетчиков производительности при необходимости для сбора данных.

Реализация обратного вызова должна быть потокобезопасной. Несколько разных потребителей могут одновременно запрашивать данные у поставщика в разных потоках.

Обратный вызов должен обрабатывать PcwCallbackEnumerateInstances типы запросов и PcwCallbackCollectData . Обратный вызов обычно не требует обработки запросов других типов, но в сложных сценариях обратный вызов также может обрабатывать PcwCallbackAddCounter и PcwCallbackRemoveCounter для оптимизации сбора данных (т. е. для отключения отслеживания статистики, если запросы не активны).

Обратный вызов отвечает за создание значений Name и Id для экземпляров набора счетчиков.

  • Значения экземпляра Id должны быть стабильными с течением времени (один и тот же логический экземпляр должен использовать одно и то же Id значение для всех вызовов обратного вызова), быть уникальными (например, не использовать 0 для всех экземпляров) и быть меньше 0xFFFFFFFE (не использовать PCW_ANY_INSTANCE_ID). По возможности экземпляр Id должен быть значимым (например, набор счетчиков process может использовать PID в качестве Id) вместо произвольного (например, порядковый номер).

  • Значения экземпляра Name должны быть стабильными с течением времени (один и тот же логический экземпляр должен использовать одно и то же Name значение для всех вызовов обратного вызова) и должны быть уникальными. Если набор счетчиков поддерживает несколько экземпляров, экземпляр Name не должен быть пустым. Сопоставление строк выполняется с помощью сравнения без учета регистра, поэтому Name значения не должны отличаться только по регистру.

При обработке PcwCallbackCollectData запросов базовая реализация обратного вызова будет просто вызывать PcwAddInstance (или функцию AddXxx, созданную CTRPP) один раз для каждого экземпляра набора счетчиков. Дополнительные сведения см. в разделе Функция AddXxx, созданная CTRPP.

При необходимости в более сложных реализациях можно использовать следующие оптимизации:

  • Если Info->CollectData.CounterMask != (UINT64)-1 тогда потребителю не нужны все счетчики в наборе счетчиков. В этом случае обратный вызов может оптимизировать сбор данных, оставив соответствующие значения как 0 в блоке данных счетчика.

  • В Info->CollectData.InstanceId != PCW_ANY_INSTANCE_ID этом случае потребителю нужны только данные об экземплярах с значением CollectData.InstanceIdInstanceId . Обратный вызов может оптимизировать сбор данных, пропуская вызов PcwAddInstance для экземпляров с несовпадениями InstanceId.

  • Если Info->CollectData.InstanceMask != "*" тогда потребителю нужны только данные об экземплярах с атрибутом InstanceName , который соответствует шаблону подстановочных CollectData.InstanceMaskзнаков . Обратный вызов может оптимизировать сбор данных, пропуская вызов PcwAddInstance для экземпляров с несовпадениями InstanceName. Сопоставление с подстановочными знаками трудно реализовать правильно, поэтому эту оптимизацию рекомендуется использовать только в том случае, если сбор данных экземпляра является очень дорогостоящим.

В большинстве случаев реализация обратного вызова для PcwCallbackEnumerateInstances запроса будет идентична реализации для PcwCallbackCollectData. Обратный вызов может при необходимости оптимизировать сбор данных, пропуская фактические данные счетчиков в вызове PcwAddInstance (т. е. передавая значения 0 и NULL для Count параметров и Data ).

Реализация обратного вызова может быть структурирована следующим образом:

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

Большинство поставщиков наборов счетчиков используют средство CTRPP для обработки манифеста набора счетчиков и создания вспомогательных функций, включая оболочку PcwRegister функций (CTRPP создает дескрипторы счетчиков) и PcwAddInstance (CTRPP создает код для упаковки структур данных поставщика в формат, необходимый для PcwAddInstance).

Для справки в этом примере ниже приведена функция Register, созданная CTRPP для манифеста KCS.man из примера 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);
}

Поставщик набора счетчиков реализует функцию PCW_CALLBACK для обработки запросов потребителей. В следующем примере кода показана PCW_CALLBACK функция с именем KcsGeometricWaveCallback , которая перечисляет и собирает имитированные данные. (Обратите внимание, что KcsAddGeometricWave это вспомогающая функция, созданная CTRPP, которая вызывает 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 примера KcsGeometricWaveCallback KCS функция указывается как Callback при KcsRegisterGeometricWave регистрации набора счетчиков.

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

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

Требования

Требование Значение
Минимальная версия клиента Доступно в Windows 7 и более поздних версиях Windows.
Целевая платформа Персональный компьютер
Верхняя часть wdm.h (включая Wdm.h, Ntddk.h)
IRQL IRQL <=APC_LEVEL

См. также раздел

PcwRegister

PcwAddInstance

PcwCreateInstance

PCW_CALLBACK_TYPE

PCW_CALLBACK_INFORMATION

CTRPP

Библиотека счетчиков производительности (PERFLIB версии 2.0)