PCW_CALLBACK función de devolución de llamada (wdm.h)
Opcionalmente, los proveedores pueden implementar una función de PCW_CALLBACK
para recibir notificaciones cuando los consumidores realizan solicitudes como enumerar instancias o recopilar datos de conjunto de contadores. La biblioteca de contadores de rendimiento de (PERFLIB versión 2.0) llama a la función PCW_CALLBACK
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 de 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 parámetro Type
. 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 función de devolución de llamada PCW_CALLBACK
debe devolver STATUS_SUCCESS
si la devolución de llamada se completó sin errores o un código de error NTSTATUS
de lo contrario. Tenga en cuenta que este código de retorno solo tiene fines informativos y que el procesamiento de la solicitud del consumidor continuará incluso si la devolución de llamada devuelve un error.
Observaciones
Los proveedores 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 función
PCW_CALLBACK
que invocará la biblioteca de contadores de rendimiento 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 tipos de solicitud PcwCallbackEnumerateInstances
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 puede controlar PcwCallbackAddCounter
y PcwCallbackRemoveCounter
para optimizar la recopilación de datos (es decir, para deshabilitar el seguimiento de estadísticas cuando no hay ninguna consulta activa).
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 mismo valor deId
para todas las invocaciones de la devolución de llamada), debe ser único (por ejemplo, no use simplemente 0 para todas las instancias) y debe ser menor 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 a lo largo del tiempo (la misma instancia lógica debe usar el mismo valorName
para todas las invocaciones de la devolución de llamada) y DEBE ser único. 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 que no distingue mayúsculas de minúsculas, por lo queName
valores no deben diferir solo por mayúsculas y minúsculas.
Al controlar solicitudes PcwCallbackCollectData
, una implementación básica de devolución de llamada 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 instancias con unInstanceId
igual aCollectData.InstanceId
. La devolución de llamada puede optimizar la recopilación de datos omitiendo la llamada aPcwAddInstance
para instancias conInstanceId
no coincidentes.Si
Info->CollectData.InstanceMask != "*"
, el consumidor solo quiere datos sobre instancias con unInstanceName
que coincida 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 conInstanceName
no coincidentes. 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 solicitud de PcwCallbackEnumerateInstances
será idéntica a la implementación de un 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 parámetros de Count
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 contadores usan la herramienta CTRPP para procesar su manifiesto de conjunto de contadores y generar funciones auxiliares, incluidas las funciones de ajuste PcwRegister
(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 manifiesto de KCS.man
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 función PCW_CALLBACK
para controlar las solicitudes de consumidor. En el ejemplo de código siguiente se muestra una función PCW_CALLBACK
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 rutina DriverEntry
del ejemplo de KCS, la función KcsGeometricWaveCallback
se especifica como la 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 | Valor |
---|---|
cliente mínimo admitido | Disponible en Windows 7 y versiones posteriores de Windows. |
de la plataforma de destino de | Escritorio |
encabezado de | wdm.h (include Wdm.h, Ntddk.h) |
irQL | IRQL <=APC_LEVEL |
Consulte también
biblioteca de contadores de rendimiento de (PERFLIB versión 2.0)