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枚举值,该值指示调用回调的原因。 可能值为 PcwCallbackAddCounterPcwCallbackRemoveCounterPcwCallbackEnumerateInstancesPcwCallbackCollectData

[in] Info

指向 PCW_CALLBACK_INFORMATION 联合的指针,该联合提供有关调用提供程序回调的原因的详细信息。 详细信息将位于与 Type 参数对应的字段中。 例如,如果 Type == PcwCallbackEnumerateInstances 则详细信息将位于 中 Info->EnumerateInstances

[in, optional] Context

调用 PcwRegister 或调用 CTRPP 生成的 Register 函数时提供程序提供的回调上下文 (调用 PcwRegister) 。

返回值

PCW_CALLBACK如果回调完成且未出错,则回调函数应返回STATUS_SUCCESS,否则NTSTATUS返回错误代码。 请注意,此返回代码仅供参考,即使回调返回错误,使用者请求的处理也会继续。

注解

计数器集提供程序可以通过两个不同的系统向使用者提供信息:

  • 提供程序可以使用 PcwCreateInstancePcwCloseInstance 来维护可用实例和相应计数器数据的列表。 此系统易于实现,但灵活性有限。 使用此系统时,提供程序不需要提供回调函数。 有关此系统的详细信息,请参阅 PcwCreateInstance 的文档。

  • 提供程序可以提供一个 PCW_CALLBACK 函数,该函数将根据需要由性能计数器库调用以收集数据。

回调实现必须是线程安全的。 多个不同的使用者可能会同时从不同线程上的提供程序请求数据。

回调必须处理 PcwCallbackEnumerateInstancesPcwCallbackCollectData 请求类型。 回调通常不需要处理其他请求类型,但在复杂情况下,回调也可能处理 PcwCallbackAddCounterPcwCallbackRemoveCounter 优化数据收集 (,即在没有活动查询) 时禁用统计信息跟踪。

回调负责生成 Name 计数器集实例的 和 Id 值。

  • 实例 Id 值必须随时间推移保持稳定, (同一逻辑实例应对回调) 的所有调用使用相同的 Id 值,应是唯一的 (例如,不要仅对) 的所有实例使用 0,并且应小于0xFFFFFFFE (不使用 PCW_ANY_INSTANCE_ID) 。 如果可能,实例 Id 应该有意义 (例如,进程计数器集可能使用 PID 作为 Id) 而不是任意 (例如序列号) 。

  • 实例 Name 值必须随时间推移保持稳定, (同一逻辑实例应对回调) 的所有调用使用相同的 Name 值,并且必须是唯一的。 如果计数器集支持多个实例,则实例 Name 不应为空。 字符串匹配是使用不区分大小写的比较完成的,因此 Name 值不应仅因大小写而异。

处理 PcwCallbackCollectData 请求时,基本回调实现只需为每个计数器集实例调用 pcwAddInstance (或 CTRPP 生成的 AddXxx 函数) 一次。 有关详细信息,请参阅 CTRPP 生成的 AddXxx 函数

必要时,可在更高级的实现中使用以下优化:

  • 如果 Info->CollectData.CounterMask != (UINT64)-1 这样,使用者不需要计数器集中的所有计数器。 在这种情况下,回调可以通过在计数器数据块中将相应的值保留为 0 来优化数据收集。

  • 如果Info->CollectData.InstanceId != PCW_ANY_INSTANCE_ID这样,使用者只需要与 等于 CollectData.InstanceId的实例InstanceId有关的数据。 回调可以通过跳过对 具有不匹配 InstanceId的实例的调用PcwAddInstance来优化数据收集。

  • 如果Info->CollectData.InstanceMask != "*"这样,使用者仅需要与 的通配符模式匹配的 CollectData.InstanceMask实例InstanceName的相关数据。 回调可以通过跳过对 具有不匹配 InstanceName的实例的调用PcwAddInstance来优化数据收集。 通配符匹配很难正确实现,因此仅当实例数据收集非常昂贵时才建议进行这种优化。

在大多数情况下,请求的回调实现 PcwCallbackEnumerateInstances 与 的 PcwCallbackCollectData实现相同。 回调可以选择性地优化数据收集,方法是省略对 (调用中PcwAddInstance的实际计数器数据,即为) 和 Data 参数传递 0 和 NULLCount

回调实现的结构可如下所示:

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 格式。

对于此示例中的参考,下面是 KCS 示例中清单的 CTRPP 生成的 Register 函数 KCS.man

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 KCS 示例的例程中,当KcsGeometricWaveCallback注册计数器集时KcsRegisterGeometricWave, 函数被指定为 Callback

    //
    // 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)