функция обратного вызова 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 или при вызове функции Register, созданной 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
должен быть значимым (например, набор счетчиков process может использовать 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
этом случае потребителю нужны только данные об экземплярах с значениемCollectData.InstanceId
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
).
Для справки в этом примере ниже приведена функция Register, созданная 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
примера KcsGeometricWaveCallback
KCS функция указывается как 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 (включая Wdm.h, Ntddk.h) |
IRQL | IRQL <=APC_LEVEL |
См. также раздел
Библиотека счетчиков производительности (PERFLIB версии 2.0)