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
如果回呼完成且沒有錯誤,或NTSTATUS
錯誤碼,則回呼函式應該會傳回 STATUS_SUCCESS
。 請注意,此傳回碼僅供參考之用,而且即使回呼傳回錯誤,取用者的要求處理仍會繼續。
備註
計數器集提供者可以透過兩個不同的系統,將資訊提供給取用者:
提供者可以使用
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 != "*"
取用者只想要與InstanceName
符合 通配符模式CollectData.InstanceMask
的 實例相關數據。 回呼可以略過 對 具有不相符InstanceName
之實例的呼叫PcwAddInstance
,以優化數據收集。 通配符比對很難正確實作,因此只有在實例數據收集非常昂貴時,才建議使用這項優化。
在大部分情況下,要求的回呼實 PcwCallbackEnumerateInstances
作會與的實作 PcwCallbackCollectData
相同。 回呼可以選擇性地藉由省略對 (呼叫PcwAddInstance
中的實際計數器數據來優化數據收集,也就是將 和 Data
參數的0和NULL Count
傳遞給) 。
回呼實作的結構如下:
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
這是呼叫 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;
}
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 |