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
エラー コードを返す必要があります。 このリターン コードは情報提供のみを目的としており、コールバックからエラーが返された場合でも、コンシューマーの要求の処理は続行されることに注意してください。
備考
カウンターセット プロバイダーは、次の 2 つの異なるシステムを介してコンシューマーに情報を提供できます。
プロバイダーは、
PcwCreateInstance
とPcwCloseInstance
を使用して、使用可能なインスタンスと対応するカウンター データの一覧を維持できます。 このシステムは簡単に実装できますが、柔軟性は限られています。 このシステムを使用する場合、プロバイダーはコールバック関数を指定する必要はありません。 このシステムの詳細については、PcwCreateInstanceのドキュメントを参照してください。プロバイダーは、データを収集するために必要に応じてパフォーマンス カウンター ライブラリによって呼び出される
PCW_CALLBACK
関数を提供できます。
コールバック実装はスレッド セーフである必要があります。 複数の異なるコンシューマーが、異なるスレッドでプロバイダーにデータを同時に要求する場合があります。
コールバックは、PcwCallbackEnumerateInstances
と PcwCallbackCollectData
要求の種類を処理する必要があります。 通常、コールバックは他の要求の種類を処理する必要はありませんが、複雑なシナリオでは、コールバックはデータ収集を最適化するために 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
の呼び出しで実際のカウンター データを省略することによってデータ収集を最適化できます (つまり、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 |