функция обратного вызова 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 (которая вызывает 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 (или функцию AddXxx, созданную CTRPP), один раз для каждого экземпляра набора счетчиков. Дополнительные сведения см. в функции AddXxx, созданной CTRPP,.
При необходимости можно использовать следующие оптимизации в более сложных реализациях:
Если
Info->CollectData.CounterMask != (UINT64)-1
, потребитель не нуждается во всех счетчиках в наборе счетчиков. В этом случае обратный вызов может оптимизировать сбор данных, оставив соответствующие значения как 0 в блоке данных счетчика.Если
Info->CollectData.InstanceId != PCW_ANY_INSTANCE_ID
то потребитель хочет только данные о экземплярах сInstanceId
равнымCollectData.InstanceId
. Обратный вызов может оптимизировать сбор данных, пропуская вызовPcwAddInstance
для экземпляров с несоединяющимисяInstanceId
.Если
Info->CollectData.InstanceMask != "*"
потребитель хочет только данные о экземплярах сInstanceName
, соответствующий шаблону подстановочных знаковCollectData.InstanceMask
. Обратный вызов может оптимизировать сбор данных, пропуская вызовPcwAddInstance
для экземпляров с несоединяющимисяInstanceName
. Сопоставление подстановочных знаков трудно реализовать правильно, поэтому эта оптимизация рекомендуется только в том случае, если сбор данных экземпляров очень дорогой.
В большинстве случаев реализация обратного вызова для запроса PcwCallbackEnumerateInstances
будет идентична реализации PcwCallbackCollectData
. Обратный вызов может при необходимости оптимизировать сбор данных путем пропуска фактических данных счетчика в вызове PcwAddInstance
(т. е. путем передачи 0 и NULL для Count
и Data
параметров).
Реализация обратного вызова может быть структурирована следующим образом:
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
).
Для справки в этом примере ниже приведена функция регистра, созданная CTRPP, для манифеста KCS.man
из примера KCS.
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
указывается как Callback
при регистрации счетчика KcsRegisterGeometricWave
.
//
// Register Countersets during DriverEntry. (TODO: Unregister at driver unload.)
//
Status = KcsRegisterGeometricWave(KcsGeometricWaveCallback, NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
Требования
Требование | Ценность |
---|---|
минимальные поддерживаемые клиентские | Доступно в Windows 7 и более поздних версиях Windows. |
целевая платформа | Настольный |
заголовка | wdm.h (include Wdm.h, Ntddk.h) |
IRQL | IRQL <=APC_LEVEL |
См. также
библиотека счетчиков производительности (PERFLIB версии 2.0)