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
必要があります。 このリターン コードは情報提供のみを目的としており、コールバックからエラーが返された場合でも、コンシューマーの要求の処理が続行されることに注意してください。
注釈
カウンターセット プロバイダーは、次の 2 つの異なるシステムを介してコンシューマーに情報を提供できます。
プロバイダーは、 と
PcwCloseInstance
を使用PcwCreateInstance
して、使用可能なインスタンスと対応するカウンター データの一覧を維持できます。 このシステムは簡単に実装できますが、柔軟性は限られています。 このシステムを使用する場合、プロバイダーはコールバック関数を指定する必要はありません。 このシステムの詳細については、 PcwCreateInstance のドキュメントを参照してください。プロバイダーは、データを
PCW_CALLBACK
収集するために必要に応じてパフォーマンス カウンター ライブラリによって呼び出される関数を提供できます。
コールバック実装はスレッド セーフである必要があります。 複数の異なるコンシューマーが、異なるスレッドでプロバイダーから同時にデータを要求する場合があります。
コールバックは、 と PcwCallbackCollectData
要求の型をPcwCallbackEnumerateInstances
処理する必要があります。 通常、コールバックは他の要求の種類を処理する必要はありませんが、複雑なシナリオでは、コールバックがデータ収集を処理 PcwCallbackAddCounter
して PcwCallbackRemoveCounter
最適化する場合もあります (つまり、アクティブなクエリがない場合に統計の追跡を無効にする場合)。
コールバックは、カウンターセット インスタンスの Name
と Id
の値を生成する役割を担います。
インスタンス
Id
の値は、時間の経過と同時に安定している必要があります (同じ論理インスタンスはコールバックのすべての呼び出しに同じId
値を使用する必要があります)。一意である必要があります (たとえば、すべてのインスタンスに 0 を使用するだけではいけません)。また、0xFFFFFFFE未満にする必要があります (使用PCW_ANY_INSTANCE_ID
しないでください)。 可能であれば、インスタンスId
は意味のあるものにする必要があります (たとえば、プロセス カウンターセットでは、任意の (シーケンス番号など) ではなく、 としてId
PID を使用する場合があります)。インスタンス
Name
の値は、時間の経過と同時に安定している必要があります (同じ論理インスタンスがコールバックのすべての呼び出しに同じName
値を使用する必要があります)。また、一意である必要があります。 カウンターセットが複数のインスタンスをサポートしている場合は、インスタンスName
を空白にしないでください。 文字列の一致は大文字と小文字を区別しない比較を使用して行われるのでName
、値は大文字と小文字のみで異なるべきではありません。
要求を処理 PcwCallbackCollectData
する場合、基本的なコールバック実装では、カウンターセット インスタンスごとに PcwAddInstance (または CTRPP によって生成された AddXxx 関数) を 1 回呼び出すだけです。 詳細については、「 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 と 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 サンプルのマニフェストに対 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
処理する 関数を実装します。 次のコード例は、シミュレートされたデータを 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;
}
KCS サンプルの DriverEntry
ルーチンでは、 KcsGeometricWaveCallback
関数はカウンターセットを Callback
登録するときに KcsRegisterGeometricWave
として指定されます。
//
// Register Countersets during DriverEntry. (TODO: Unregister at driver unload.)
//
Status = KcsRegisterGeometricWave(KcsGeometricWaveCallback, NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
要件
要件 | 値 |
---|---|
サポートされている最小のクライアント | Windows 7 以降のバージョンの Windows で使用できます。 |
対象プラットフォーム | デスクトップ |
Header | wdm.h (Wdm.h、Ntddk.h を含む) |
IRQL | IRQL <=APC_LEVEL |