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
,使用者仅希望有关实例的数据,其InstanceId
等于CollectData.InstanceId
。 回调可以通过跳过对具有不匹配InstanceId
实例的PcwAddInstance
调用来优化数据收集。如果
Info->CollectData.InstanceMask != "*"
,使用者仅希望包含与CollectData.InstanceMask
通配符模式匹配的InstanceName
实例的数据。 回调可以通过跳过对具有不匹配InstanceName
实例的PcwAddInstance
调用来优化数据收集。 通配符匹配很难正确实现,因此,仅当实例数据收集非常昂贵时,才建议使用此优化。
在大多数情况下,PcwCallbackEnumerateInstances
请求的回调实现与 PcwCallbackCollectData
的实现相同。 回调可以通过省略调用 PcwAddInstance
中的实际计数器数据(即传递 Count
和 Data
参数的 0 和 NULL)来优化数据收集。
回调实现的结构如下:
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 示例中 KCS.man
清单的 CTRPP 生成的 Register 函数。
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
函数来处理使用者请求。 下面的代码示例演示一个名为 KcsGeometricWaveCallback
的 PCW_CALLBACK
函数,用于枚举和收集模拟数据。 (请注意,KcsAddGeometricWave
是调用 PcwAddInstance
的 CTRPP 生成的帮助程序函数。
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;
}
在 KCS 示例的 DriverEntry
例程中,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 |