PCW_CALLBACK función de devolución de llamada (wdm.h)
Opcionalmente, los proveedores pueden implementar una PCW_CALLBACK
función para recibir notificaciones cuando los consumidores realizan solicitudes como enumerar instancias o recopilar datos de conjunto de contadores. La biblioteca de contadores de rendimiento (PERFLIB versión 2.0) llama a la PCW_CALLBACK
función antes de completar la solicitud del consumidor.
Sintaxis
PCW_CALLBACK PcwCallback;
NTSTATUS PcwCallback(
[in] PCW_CALLBACK_TYPE Type,
[in] PPCW_CALLBACK_INFORMATION Info,
[in, optional] PVOID Context
)
{...}
Parámetros
[in] Type
Valor de enumeración PCW_CALLBACK_TYPE que indica por qué se invocó la devolución de llamada. Los valores posibles son PcwCallbackAddCounter
, PcwCallbackRemoveCounter
, PcwCallbackEnumerateInstances
y PcwCallbackCollectData
.
[in] Info
Puntero a una unión PCW_CALLBACK_INFORMATION que proporciona detalles sobre por qué se invocó la devolución de llamada del proveedor. Los detalles estarán en el campo correspondiente al Type
parámetro . Por ejemplo, si Type == PcwCallbackEnumerateInstances
los detalles estarán en Info->EnumerateInstances
.
[in, optional] Context
Contexto de devolución de llamada proporcionado por el proveedor al llamar a PcwRegister o al llamar a la función Register generada por CTRPP (que invoca PcwRegister
).
Valor devuelto
La PCW_CALLBACK
función de devolución de llamada debe devolverse STATUS_SUCCESS
si la devolución de llamada se completó sin errores o un NTSTATUS
código de error en caso contrario. Tenga en cuenta que este código de retorno es solo para fines informativos y que el procesamiento de la solicitud del consumidor continuará incluso si la devolución de llamada devuelve un error.
Comentarios
Los proveedores de conjuntos de contadores pueden proporcionar información al consumidor a través de dos sistemas diferentes:
El proveedor puede usar
PcwCreateInstance
yPcwCloseInstance
para mantener una lista de instancias disponibles y los datos de contador correspondientes. Este sistema es sencillo de implementar pero limitado en flexibilidad. Al usar este sistema, el proveedor no necesita proporcionar una función de devolución de llamada. Para obtener más información sobre este sistema, consulte la documentación de PcwCreateInstance.El proveedor puede proporcionar una
PCW_CALLBACK
función que la biblioteca de contadores de rendimiento invocará según sea necesario para recopilar datos.
La implementación de devolución de llamada debe ser segura para subprocesos. Varios consumidores diferentes pueden solicitar simultáneamente datos del proveedor en subprocesos diferentes.
La devolución de llamada debe controlar los PcwCallbackEnumerateInstances
tipos de solicitud y PcwCallbackCollectData
. Normalmente, la devolución de llamada no necesita controlar otros tipos de solicitud, pero en escenarios complejos, la devolución de llamada también podría controlar PcwCallbackAddCounter
y PcwCallbackRemoveCounter
optimizar la recopilación de datos (es decir, para deshabilitar el seguimiento de estadísticas cuando no hay consultas activas).
La devolución de llamada es responsable de generar Name
y Id
valores para las instancias del conjunto de contadores.
Los valores de instancia
Id
DEBEN ser estables con el tiempo (la misma instancia lógica debe usar el mismoId
valor para todas las invocaciones de la devolución de llamada), deben ser únicos (por ejemplo, no simplemente use 0 para todas las instancias) y deben ser menores que 0xFFFFFFFE (no usePCW_ANY_INSTANCE_ID
). Cuando sea posible, la instanciaId
debe ser significativa (por ejemplo, un conjunto de contadores de procesos podría usar un PID comoId
) en lugar de arbitrario (por ejemplo, un número de secuencia).Los valores de instancia
Name
DEBEN ser estables con el tiempo (la misma instancia lógica debe usar el mismoName
valor para todas las invocaciones de la devolución de llamada) y DEBEN ser únicos. Si el conjunto de contadores admite varias instancias, la instanciaName
no debe estar en blanco. La coincidencia de cadenas se realiza mediante una comparación sin distinción entre mayúsculas y minúsculas, por lo queName
los valores no deben diferir solo por mayúsculas y minúsculas.
Al controlar PcwCallbackCollectData
las solicitudes, una implementación de devolución de llamada básica simplemente invocará PcwAddInstance (o la función AddXxx generada por CTRPP) una vez para cada instancia del conjunto de contadores. Para obtener más información, consulta función AddXxx generada por CTRPP.
Las siguientes optimizaciones se pueden usar en implementaciones más avanzadas cuando sea necesario:
Si
Info->CollectData.CounterMask != (UINT64)-1
el consumidor no necesita todos los contadores del conjunto de contadores. En este caso, la devolución de llamada puede optimizar la recopilación de datos dejando los valores correspondientes como 0 en el bloque de datos del contador.Si
Info->CollectData.InstanceId != PCW_ANY_INSTANCE_ID
el consumidor solo quiere datos sobre las instancias con unInstanceId
valor igual aCollectData.InstanceId
. La devolución de llamada puede optimizar la recopilación de datos omitiendo la llamada aPcwAddInstance
para instancias que no coincidenInstanceId
con .Si
Info->CollectData.InstanceMask != "*"
el consumidor solo quiere datos sobre las instancias con unInstanceName
que coincide con el patrón de caracteres comodín deCollectData.InstanceMask
. La devolución de llamada puede optimizar la recopilación de datos omitiendo la llamada aPcwAddInstance
para instancias que no coincidenInstanceName
con . La coincidencia de caracteres comodín es difícil de implementar correctamente, por lo que esta optimización solo se recomienda cuando la recopilación de datos de instancia es muy costosa.
En la mayoría de los casos, la implementación de devolución de llamada de una PcwCallbackEnumerateInstances
solicitud será idéntica a la implementación de .PcwCallbackCollectData
La devolución de llamada puede optimizar opcionalmente la recopilación de datos omitiendo los datos de contador reales de la llamada a PcwAddInstance
(es decir, pasando 0 y NULL para los Count
parámetros y Data
).
Una implementación de devolución de llamada podría estructurarse de la siguiente manera:
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;
}
La mayoría de los proveedores de conjuntos de contadores usan la herramienta CTRPP para procesar su manifiesto de conjunto de contadores y generar funciones auxiliares, incluido el ajuste PcwRegister
de funciones (CTRPP genera los descriptores de contador) y PcwAddInstance
(CTRPP genera código para encapsular las estructuras de datos del proveedor en el formato requerido por PcwAddInstance
).
Como referencia en este ejemplo, lo siguiente es la función Register generada por CTRPP para el KCS.man
manifiesto del ejemplo de 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);
}
El proveedor de conjuntos de contadores implementa la PCW_CALLBACK
función para controlar las solicitudes de consumidor. En el ejemplo de código siguiente se muestra una PCW_CALLBACK
función denominada KcsGeometricWaveCallback
que enumera y recopila datos simulados. (Tenga en cuenta que KcsAddGeometricWave
es una función auxiliar generada por CTRPP que llama a 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;
}
En la DriverEntry
rutina del ejemplo de KCS, la KcsGeometricWaveCallback
función se especifica como Callback
cuando KcsRegisterGeometricWave
registra el conjunto de contadores.
//
// Register Countersets during DriverEntry. (TODO: Unregister at driver unload.)
//
Status = KcsRegisterGeometricWave(KcsGeometricWaveCallback, NULL);
if (!NT_SUCCESS(Status)) {
return Status;
}
Requisitos
Requisito | Value |
---|---|
Cliente mínimo compatible | Disponible en Windows 7 y versiones posteriores de Windows. |
Plataforma de destino | Escritorio |
Encabezado | wdm.h (incluya Wdm.h, Ntddk.h) |
IRQL | IRQL <=APC_LEVEL |
Consulte también
Biblioteca de contadores de rendimiento (PERFLIB versión 2.0)