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 或调用 CTRPP 生成的 Register 函数时提供程序提供的回调上下文 (调用 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
应该有意义 (例如,进程计数器集可能使用 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 |