Uso delle funzioni PerfLib per utilizzare i dati dei contatore
Usare le funzioni Consumer PerfLib per utilizzare i dati sulle prestazioni dei provider di dati delle prestazioni V2 quando non è possibile usare le funzioni di Supporto dati prestazioni (PDH). Queste funzioni possono essere usate durante la scrittura di applicazioni OneCore per raccogliere contatori V2 o quando è necessario raccogliere contatori V2 con dipendenze e overhead minimi.
Suggerimento
Le funzioni Consumer PerfLib V2 sono più difficili da usare rispetto alle funzioni PDH (Performance Data Helper) e supportano solo la raccolta di dati dai provider V2. Le funzioni PDH devono essere preferite per la maggior parte delle applicazioni.
Le funzioni Consumer PerfLib V2 sono l'API di basso livello per la raccolta di dati dai provider V2. Le funzioni Consumer PerfLib V2 non supportano la raccolta di dati dai provider V1.
Avviso
Le funzioni consumer PerfLib V2 possono potenzialmente raccogliere dati da origini non attendibili, ad esempio da servizi locali con privilegi limitati o da computer remoti. Le funzioni consumer PerfLib V2 non convalidano i dati per l'integrità o la coerenza. Spetta all'applicazione consumer verificare che i dati restituiti siano coerenti, ad esempio che i valori Size nel blocco di dati restituiti non superino le dimensioni effettive del blocco di dati restituito. Ciò è particolarmente importante quando l'applicazione consumer viene eseguita con privilegi elevati.
Utilizzo di PerfLib
L'intestazione perflib.h
include le dichiarazioni usate dai provider in modalità utente V2 (ad esempio l'API del provider PerfLib) e i consumer V2 (ad esempio l'API Consumer PerfLib). Dichiara le funzioni seguenti per l'utilizzo dei dati sulle prestazioni V2:
- Usare PerfEnumerateCounterSet per ottenere i GUID dei contatori registrati dai provider V2 nel sistema.
- Usare PerfQueryCounterSetRegistrationInfo per ottenere informazioni su un determinato counterset, ad esempio il nome, la descrizione, il tipo (a istanza singola o a istanza multipla), i tipi di contatore, i nomi dei contatori e le descrizioni dei contatori.
- Usare PerfEnumerateCounterSetInstances per ottenere i nomi delle istanze attualmente attive di un counterset a istanze multipli.
- Usare PerfOpenQueryHandle per creare un nuovo handle di query da usare per raccogliere dati da uno o più contatori.
- Usare PerfCloseQueryHandle per chiudere un handle di query.
- Usare PerfAddCounters per aggiungere una query a un handle di query.
- Usare PerfDeleteCounters per rimuovere una query da un handle di query.
- Usare PerfQueryCounterInfo per ottenere informazioni sulle query correnti in un handle di query.
- Usare PerfQueryCounterData per raccogliere i dati sulle prestazioni da un handle di query.
Se il consumer utilizza i dati solo da un provider specifico in cui gli indici GUID e contatori sono stabili e si ha accesso al file di simboli generato da CTRPP (dal parametro CTRPP -ch
), è possibile impostare come hardcoded i valori guiD del contatore e dell'indice dei contatori nel consumer.
In caso contrario, sarà necessario caricare i metadati del counterset per determinare i GUID del contatore e gli indici dei contatori da usare nella query come indicato di seguito:
- Usare PerfEnumerateCounterSet per ottenere un elenco di GUID di contatori supportati.
- Per ogni GUID, usare PerfQueryCounterSetRegistrationInfo per caricare il nome del contatore. Arrestare quando si trova il nome che si sta cercando.
- Usare PerfQueryCounterSetRegistrationInfo per caricare i metadati rimanenti (configurazione del counterset, nomi dei contatori, indici dei contatori, tipi di contatore) per tale contatore.
Se è sufficiente conoscere i nomi delle istanze attualmente attive di un counterset (ad esempio, se non sono necessari i valori effettivi dei dati sulle prestazioni), è possibile usare PerfEnumerateCounterSetInstances. Accetta un GUID del counterset come input e restituisce un PERF_INSTANCE_HEADER
blocco con i nomi e gli ID delle istanze attualmente attive del contatore specificato.
Query
Per raccogliere i dati sulle prestazioni, è necessario creare un handle di query, aggiungervi query e raccogliere i dati dalle query.
A un handle di query possono essere associate molte query. Quando si chiama PerfQueryCounterData per un handle di query, PerfLib eseguirà tutte le query dell'handle e raccoglierà tutti i risultati.
Ogni query specifica un GUID del contatore, un filtro del nome dell'istanza, un filtro ID istanza facoltativo e un filtro ID contatore facoltativo.
- Il GUID del counterset è obbligatorio. Indica il GUID del contatore da cui la query raccoglierà i dati. È simile alla
FROM
clausola di una query SQL. - Il filtro del nome dell'istanza è obbligatorio. Indica un criterio con caratteri jolly che il nome dell'istanza deve corrispondere per l'istanza da includere nella query, con
*
l'indicazione di "qualsiasi carattere" e?
che indica "un carattere". Per i contatori a istanza singola, questo deve essere impostato su una stringa""
di lunghezza zero. Per i contatori a più istanze , questo deve essere impostato su una stringa non vuota (usare"*"
per accettare tutti i nomi di istanza). È simile a unaWHERE InstanceName LIKE NameFilter
clausola di una query SQL. - Il filtro ID istanza è facoltativo. Se presente (ad esempio, se impostato su un valore diverso
0xFFFFFFFF
da ), indica che la query deve raccogliere solo le istanze in cui l'ID istanza corrisponde all'ID specificato. Se assente (ad esempio, se impostato su0xFFFFFFFF
), indica che la query deve accettare tutti gli ID istanza. È simile a unaWHERE InstanceId == IdFilter
clausola di una query SQL. - Il filtro ID contatore è facoltativo. Se presente (ad esempio, se impostato su un valore diverso
PERF_WILDCARD_COUNTER
da ), indica che la query deve raccogliere un singolo contatore, simile a unaSELECT CounterName
clausola di una query SQL. Se assente (ad esempio, se impostato suPERF_WILDCARD_COUNTER
), indica che la query deve raccogliere tutti i contatori disponibili, analogamente a unaSELECT *
clausola di una query SQL.
Usare PerfAddCounters per aggiungere query a un handle di query. Usare PerfDeleteCounters per rimuovere query da un handle di query.
Dopo aver modificato le query in un handle di query, usare PerfQueryCounterInfo per ottenere gli indici di query. Gli indici indicano l'ordine in cui i risultati della query verranno restituiti da PerfQueryCounterData (i risultati non corrispondono sempre all'ordine in cui sono state aggiunte le query).
Quando l'handle di query è pronto, usare PerfQueryCounterData per raccogliere i dati. In genere si raccolgono periodicamente i dati (una volta al secondo o una volta al minuto) e quindi si elaborano i dati in base alle esigenze.
Nota
I contatori delle prestazioni non sono progettati per essere raccolti più volte al secondo.
PerfQueryCounterData restituirà un PERF_DATA_HEADER
blocco costituito da un'intestazione di dati con timestamp seguiti da una sequenza di PERF_COUNTER_HEADER
blocchi, ognuno contenente i risultati di una query.
PERF_COUNTER_HEADER
Può contenere diversi tipi di dati, come indicato dal valore del dwType
campo:
-
PERF_ERROR_RETURN
- PerfLib non può recuperare i dati dei contatori validi dal provider. -
PERF_SINGLE_COUNTER
- La query era relativa a un singolo contatore da un contatore a istanza singola. I risultati contengono solo il valore del contatore richiesto. -
PERF_MULTIPLE_COUNTERS
- La query è stata eseguita per più contatori da un contatore a istanza singola. Il risultato contiene i valori del contatore insieme alle informazioni per la corrispondenza di ogni valore con il contatore corrispondente ,ad esempio le intestazioni di colonna. -
PERF_MULTIPLE_INSTANCES
- La query era relativa a un singolo contatore da un contatore a istanze multipla. I risultati contengono le informazioni sull'istanza (ad esempio le intestazioni di riga) e un valore del contatore per ogni istanza. -
PERF_COUNTERSET
- La query è stata eseguita per più contatori da un contatore a più istanze. I risultati contengono le informazioni sull'istanza (ad esempio le intestazioni di riga), i valori dei contatori per ogni istanza e le informazioni per la corrispondenza di ogni valore con il contatore corrispondente ,ad esempio le intestazioni di colonna.
I valori restituiti da PerfQueryCounterData sono UINT32
o UINT64
valori non elaborati. Queste operazioni richiedono in genere un'elaborazione per produrre i valori formattati previsti. L'elaborazione richiesta dipende dal tipo del contatore. Molti tipi di contatori richiedono informazioni aggiuntive per l'elaborazione completa, ad esempio un timestamp o un valore di un contatore "base" nello stesso esempio.
Alcuni tipi di contatori sono contatori "delta" che sono significativi solo se confrontati con i dati di un campione precedente. Ad esempio, un contatore di tipo PERF_SAMPLE_COUNTER
ha un valore formattato che dovrebbe mostrare una frequenza (il numero di volte in cui una particolare cosa è avvenuta al secondo nell'intervallo di campionamento), ma il valore non elaborato effettivo è solo un conteggio (il numero di volte in cui una determinata cosa è avvenuta in totale). Per produrre il valore "rate" formattato, è necessario applicare la formula corrispondente al tipo di contatore. La formula per PERF_SAMPLE_COUNTER
è (N1 - N0) / ((T1 - T0) / F)
: sottrae il valore dell'esempio corrente dal valore del campione precedente (fornendo il numero di volte in cui si è verificato l'evento durante l'intervallo di campionamento) quindi dividere il risultato per il numero di secondi nell'intervallo di campionamento (ottenuto sottraendo il timestamp dell'esempio corrente dal timestamp del campione precedente e dividendo per frequenza per convertire l'intervallo di tempo in secondi).
Per altre informazioni sul calcolo dei valori formattati dai valori non elaborati, vedere Calcolo dei valori contatori .
Esempio
Il codice seguente implementa un consumer che usa le funzioni Consumer PerfLib V2 per leggere le informazioni sulle prestazioni della CPU dal contatore "Informazioni processore".
Il consumer è organizzato come segue:
- La
CpuPerfCounters
classe implementa la logica per l'utilizzo dei dati sulle prestazioni. Incapsula un handle di query e un buffer di dati in cui vengono registrati i risultati della query. - Lo
CpuPerfTimestamp
struct archivia le informazioni sul timestamp per un esempio. Ogni volta che i dati vengono raccolti, il chiamante riceve un singoloCpuPerfTimestamp
oggetto . - Lo
CpuPerfData
struct archivia le informazioni sulle prestazioni (nome dell'istanza e valori delle prestazioni non elaborate) per una CPU. Ogni volta che i dati vengono raccolti, il chiamante riceve una matrice diCpuPerfData
(una per CPU).
In questo esempio vengono usati i valori GUID e ID contatore hardcoded perché sono ottimizzati per un contatore specifico (Informazioni processore) che non modificherà i valori GUID o ID. Una classe più generica che legge i dati sulle prestazioni da contatori arbitrari deve usare PerfQueryCounterSetRegistrationInfo per cercare il mapping tra gli ID contatori e i valori dei contatori in fase di esecuzione.
Un semplice CpuPerfCountersConsumer.cpp
programma illustra come usare i valori della CpuPerfCounters
classe .
CpuPerfCounters.h
#pragma once
#include <sal.h>
// Contains timestamp data for a Processor Information data collection.
struct CpuPerfTimestamp
{
__int64 PerfTimeStamp; // Timestamp from the high-resolution clock.
__int64 PerfTime100NSec; // The number of 100 nanosecond intervals since January 1, 1601, in Coordinated Universal Time (UTC).
__int64 PerfFreq; // The frequency of the high-resolution clock.
};
// Contains the raw data from a Processor Information sample.
// Note that the values returned are raw data. Converting from raw data to a
// friendly value may require computation, and the computation may require
// two samples of data. The computation depends on the Type and the formula
// to use for each type.
// For example, ProcessorTime contains raw data of type PERF_100NSEC_TIMER_INV.
// Given two samples of data, s0 at time t0 and s1 at time t1, the friendly
// "% Processor Time" value is computed as:
// 100*(1-(s1.ProcessorTime-s0.ProcessorTime)/(t1.PerfTime100NSec-t0.PerfTime100NSec))
struct CpuPerfData
{
wchar_t Name[16]; // Format: "NumaNode,NumaIndex", "NumaNode,_Total", or "_Total".
__int64 unsigned ProcessorTime; // % Processor Time (#0, Type=PERF_100NSEC_TIMER_INV)
__int64 unsigned UserTime; // % User Time (#1, Type=PERF_100NSEC_TIMER)
__int64 unsigned PrivilegedTime; // % Privileged Time (#2, Type=PERF_100NSEC_TIMER)
__int32 unsigned Interrupts; // Interrupts / sec (#3, Type=PERF_COUNTER_COUNTER)
__int64 unsigned DpcTime; // % DPC Time (#4, Type=PERF_100NSEC_TIMER)
__int64 unsigned InterruptTime; // % Interrupt Time (#5, Type=PERF_100NSEC_TIMER)
__int32 unsigned DpcsQueued; // DPCs Queued / sec (#6, Type=PERF_COUNTER_COUNTER)
__int32 unsigned Dpcs; // DPC Rate (#7, Type=PERF_COUNTER_RAWCOUNT)
__int64 unsigned IdleTime; // % Idle Time (#8, Type=PERF_100NSEC_TIMER)
__int64 unsigned C1Time; // % C1 Time (#9, Type=PERF_100NSEC_TIMER)
__int64 unsigned C2Time; // % C2 Time (#10, Type=PERF_100NSEC_TIMER)
__int64 unsigned C3Time; // % C3 Time (#11, Type=PERF_100NSEC_TIMER)
__int64 unsigned C1Transitions; // C1 Transitions / sec (#12, Type=PERF_COUNTER_BULK_COUNT)
__int64 unsigned C2Transitions; // C2 Transitions / sec (#13, Type=PERF_COUNTER_BULK_COUNT)
__int64 unsigned C3Transitions; // C3 Transitions / sec (#14, Type=PERF_COUNTER_BULK_COUNT)
__int64 unsigned PriorityTime; // % Priority Time (#15, Type=PERF_100NSEC_TIMER_INV)
__int32 unsigned ParkingStatus; // Parking Status (#16, Type=PERF_COUNTER_RAWCOUNT)
__int32 unsigned ProcessorFrequency; // Processor Frequency (#17, Type=PERF_COUNTER_RAWCOUNT)
__int32 unsigned PercentMaximumFrequency; // % of Maximum Frequency (#18, Type=PERF_COUNTER_RAWCOUNT)
__int32 unsigned ProcessorStateFlags; // Processor State Flags (#19, Type=PERF_COUNTER_RAWCOUNT)
__int32 unsigned ClockInterrupts; // Clock Interrupts / sec (#20, Type=PERF_COUNTER_COUNTER)
__int64 unsigned AverageIdleTime; // Average Idle Time (#21, Type=PERF_PRECISION_100NS_TIMER)
__int64 unsigned AverageIdleTimeBase; // Average Idle Time Base (#22, Type=PERF_PRECISION_TIMESTAMP)
__int64 unsigned IdleBreakEvents; // Idle Break Events / sec (#23, Type=PERF_COUNTER_BULK_COUNT)
__int64 unsigned ProcessorPerformance; // % Processor Performance (#24, Type=PERF_AVERAGE_BULK)
__int32 unsigned ProcessorPerformanceBase; // % Processor Performance Base (#25, Type=PERF_AVERAGE_BASE)
__int64 unsigned ProcessorUtility; // % Processor Utility (#26, Type=PERF_AVERAGE_BULK)
__int64 unsigned PrivilegedUtility; // % Privileged Utility (#28, Type=PERF_AVERAGE_BULK)
__int32 unsigned UtilityBase; // % Utility Base (#27, Type=PERF_AVERAGE_BASE)
__int32 unsigned PercentPerformanceLimit; // % Performance Limit (#30, Type=PERF_COUNTER_RAWCOUNT)
__int32 unsigned PerformanceLimitFlags; // Performance Limit Flags (#31, Type=PERF_COUNTER_RAWCOUNT)
};
// Performs data collection from the Processor Information performance counter.
class CpuPerfCounters
{
public:
CpuPerfCounters(CpuPerfCounters const&) = delete;
void operator=(CpuPerfCounters const&) = delete;
~CpuPerfCounters();
CpuPerfCounters() noexcept;
// Reads CPU performance counter data.
// Returns ERROR_SUCCESS (0) on success, ERROR_MORE_DATA if bufferCount is
// too small, or another Win32 error code on failure.
_Success_(return == 0)
unsigned
ReadData(
_Out_opt_ CpuPerfTimestamp* timestamp,
_Out_cap_post_count_(bufferCount, *bufferUsed) CpuPerfData* buffer,
unsigned bufferCount,
_Out_ unsigned* bufferUsed) noexcept;
private:
void* m_hQuery;
void* m_pData;
unsigned m_cbData;
};
CpuPerfCounters.cpp
#include "CpuPerfCounters.h"
#include <windows.h>
#include <perflib.h>
#include <winperf.h>
#include <stdlib.h>
_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(cb)
static void*
AllocateBytes(unsigned cb) noexcept
{
return malloc(cb);
}
static void
FreeBytes(_Pre_maybenull_ _Post_invalid_ void* p) noexcept
{
if (p)
{
free(p);
}
return;
}
static void
AssignCounterValue(
_Inout_ __int32 unsigned* pVar,
_In_ PERF_COUNTER_DATA const* pData) noexcept
{
if (pData->dwDataSize == 4 ||
pData->dwDataSize == 8)
{
*pVar = *reinterpret_cast<__int32 unsigned const*>(pData + 1);
}
}
static void
AssignCounterValue(
_Inout_ __int64 unsigned* pVar,
_In_ PERF_COUNTER_DATA const* pData) noexcept
{
if (pData->dwDataSize == 8)
{
*pVar = *reinterpret_cast<__int64 unsigned const*>(pData + 1);
}
else if (pData->dwDataSize == 4)
{
*pVar = *reinterpret_cast<__int32 unsigned const*>(pData + 1);
}
}
CpuPerfCounters::~CpuPerfCounters()
{
if (m_hQuery)
{
PerfCloseQueryHandle(m_hQuery);
}
FreeBytes(m_pData);
}
CpuPerfCounters::CpuPerfCounters() noexcept
: m_hQuery()
, m_pData()
, m_cbData()
{
return;
}
_Success_(return == 0)
unsigned
CpuPerfCounters::ReadData(
_Out_opt_ CpuPerfTimestamp* timestamp,
_Out_cap_post_count_(bufferCount, *bufferUsed) CpuPerfData* buffer,
unsigned bufferCount,
_Out_ unsigned* bufferUsed) noexcept
{
unsigned status;
DWORD cbData;
PERF_DATA_HEADER* pDataHeader;
PERF_COUNTER_HEADER const* pCounterHeader;
PERF_MULTI_COUNTERS const* pMultiCounters;
PERF_MULTI_INSTANCES const* pMultiInstances;
PERF_INSTANCE_HEADER const* pInstanceHeader;
unsigned cInstances = 0;
if (m_hQuery == nullptr)
{
HANDLE hQuery = 0;
status = PerfOpenQueryHandle(nullptr, &hQuery);
if (ERROR_SUCCESS != status)
{
goto Done;
}
struct
{
PERF_COUNTER_IDENTIFIER Identifier;
WCHAR Name[4];
} querySpec = {
{
// ProcessorCounterSetGuid
{ 0xb4fc721a, 0x378, 0x476f, 0x89, 0xba, 0xa5, 0xa7, 0x9f, 0x81, 0xb, 0x36 },
0,
sizeof(querySpec),
PERF_WILDCARD_COUNTER, // Get all data for each CPU.
0xFFFFFFFF // Get data for all instance IDs.
},
L"*" // Get data for all instance names.
};
status = PerfAddCounters(hQuery, &querySpec.Identifier, sizeof(querySpec));
if (ERROR_SUCCESS == status)
{
status = querySpec.Identifier.Status;
}
if (ERROR_SUCCESS != status)
{
PerfCloseQueryHandle(hQuery);
goto Done;
}
// NOTE: A program that adds more than one query to the handle would need to call
// PerfQueryCounterInfo to determine the order of the query results.
m_hQuery = hQuery;
}
for (;;)
{
cbData = 0;
pDataHeader = static_cast<PERF_DATA_HEADER*>(m_pData);
status = PerfQueryCounterData(m_hQuery, pDataHeader, m_cbData, &cbData);
if (ERROR_SUCCESS == status)
{
break;
}
else if (ERROR_NOT_ENOUGH_MEMORY != status)
{
goto Done;
}
FreeBytes(m_pData);
m_cbData = 0;
m_pData = AllocateBytes(cbData);
if (nullptr == m_pData)
{
status = ERROR_OUTOFMEMORY;
goto Done;
}
m_cbData = cbData;
}
// PERF_DATA_HEADER block = PERF_DATA_HEADER + dwNumCounters PERF_COUNTER_HEADER blocks
if (cbData < sizeof(PERF_DATA_HEADER) ||
cbData < pDataHeader->dwTotalSize ||
pDataHeader->dwTotalSize < sizeof(PERF_DATA_HEADER) ||
pDataHeader->dwNumCounters != 1)
{
status = ERROR_INVALID_DATA;
goto Done;
}
// PERF_COUNTERSET block = PERF_COUNTER_HEADER + PERF_MULTI_COUNTERS block + PERF_MULTI_INSTANCES block
cbData = pDataHeader->dwTotalSize - sizeof(PERF_DATA_HEADER);
pCounterHeader = reinterpret_cast<PERF_COUNTER_HEADER*>(pDataHeader + 1);
if (cbData < sizeof(PERF_COUNTER_HEADER) ||
cbData < pCounterHeader->dwSize ||
pCounterHeader->dwSize < sizeof(PERF_COUNTER_HEADER) ||
PERF_COUNTERSET != pCounterHeader->dwType)
{
status = ERROR_INVALID_DATA;
goto Done;
}
// PERF_MULTI_COUNTERS block = PERF_MULTI_COUNTERS + dwCounters DWORDs
cbData = pCounterHeader->dwSize - sizeof(PERF_COUNTER_HEADER);
pMultiCounters = reinterpret_cast<PERF_MULTI_COUNTERS const*>(pCounterHeader + 1);
if (cbData < sizeof(PERF_MULTI_COUNTERS) ||
cbData < pMultiCounters->dwSize ||
pMultiCounters->dwSize < sizeof(PERF_MULTI_COUNTERS) ||
(pMultiCounters->dwSize - sizeof(PERF_MULTI_COUNTERS)) / sizeof(DWORD) < pMultiCounters->dwCounters)
{
status = ERROR_INVALID_DATA;
goto Done;
}
// PERF_MULTI_INSTANCES block = PERF_MULTI_INSTANCES + dwInstances instance data blocks
cbData -= pMultiCounters->dwSize;
pMultiInstances = reinterpret_cast<PERF_MULTI_INSTANCES const*>((LPCBYTE)pMultiCounters + pMultiCounters->dwSize);
if (cbData < sizeof(PERF_MULTI_INSTANCES) ||
cbData < pMultiInstances->dwTotalSize ||
pMultiInstances->dwTotalSize < sizeof(PERF_MULTI_INSTANCES))
{
status = ERROR_INVALID_DATA;
goto Done;
}
cInstances = pMultiInstances->dwInstances;
if (bufferCount < cInstances)
{
status = ERROR_MORE_DATA;
goto Done;
}
memset(buffer, 0, sizeof(buffer[0]) * cInstances);
cbData = pMultiInstances->dwTotalSize - sizeof(PERF_MULTI_INSTANCES);
pInstanceHeader = reinterpret_cast<PERF_INSTANCE_HEADER const*>(pMultiInstances + 1);
for (unsigned iInstance = 0; iInstance != pMultiInstances->dwInstances; iInstance += 1)
{
CpuPerfData& d = buffer[iInstance];
// instance data block = PERF_INSTANCE_HEADER block + dwCounters PERF_COUNTER_DATA blocks
if (cbData < sizeof(PERF_INSTANCE_HEADER) ||
cbData < pInstanceHeader->Size ||
pInstanceHeader->Size < sizeof(PERF_INSTANCE_HEADER))
{
status = ERROR_INVALID_DATA;
goto Done;
}
unsigned const instanceNameMax = (pInstanceHeader->Size - sizeof(PERF_INSTANCE_HEADER)) / sizeof(WCHAR);
WCHAR const* const instanceName = reinterpret_cast<WCHAR const*>(pInstanceHeader + 1);
if (instanceNameMax == wcsnlen(instanceName, instanceNameMax))
{
status = ERROR_INVALID_DATA;
goto Done;
}
wcsncpy_s(d.Name, instanceName, _TRUNCATE);
cbData -= pInstanceHeader->Size;
PERF_COUNTER_DATA const* pCounterData = reinterpret_cast<PERF_COUNTER_DATA const*>((LPCBYTE)pInstanceHeader + pInstanceHeader->Size);
for (unsigned iCounter = 0; iCounter != pMultiCounters->dwCounters; iCounter += 1)
{
if (cbData < sizeof(PERF_COUNTER_DATA) ||
cbData < pCounterData->dwSize ||
pCounterData->dwSize < sizeof(PERF_COUNTER_DATA) + 8 ||
pCounterData->dwSize - sizeof(PERF_COUNTER_DATA) < pCounterData->dwDataSize)
{
status = ERROR_INVALID_DATA;
goto Done;
}
DWORD const* pCounterIds = reinterpret_cast<DWORD const*>(pMultiCounters + 1);
switch (pCounterIds[iCounter])
{
case 0: // PERF_100NSEC_TIMER_INV
AssignCounterValue(&d.ProcessorTime, pCounterData);
break;
case 1: // PERF_100NSEC_TIMER
AssignCounterValue(&d.UserTime, pCounterData);
break;
case 2: // PERF_100NSEC_TIMER
AssignCounterValue(&d.PrivilegedTime, pCounterData);
break;
case 3: // PERF_COUNTER_COUNTER
AssignCounterValue(&d.Interrupts, pCounterData);
break;
case 4: // PERF_100NSEC_TIMER
AssignCounterValue(&d.DpcTime, pCounterData);
break;
case 5: // PERF_100NSEC_TIMER
AssignCounterValue(&d.InterruptTime, pCounterData);
break;
case 6: // PERF_COUNTER_COUNTER
AssignCounterValue(&d.DpcsQueued, pCounterData);
break;
case 7: // PERF_COUNTER_RAWCOUNT
AssignCounterValue(&d.Dpcs, pCounterData);
break;
case 8: // PERF_100NSEC_TIMER
AssignCounterValue(&d.IdleTime, pCounterData);
break;
case 9: // PERF_100NSEC_TIMER
AssignCounterValue(&d.C1Time, pCounterData);
break;
case 10: // PERF_100NSEC_TIMER
AssignCounterValue(&d.C2Time, pCounterData);
break;
case 11: // PERF_100NSEC_TIMER
AssignCounterValue(&d.C3Time, pCounterData);
break;
case 12: // PERF_COUNTER_BULK_COUNT
AssignCounterValue(&d.C1Transitions, pCounterData);
break;
case 13: // PERF_COUNTER_BULK_COUNT
AssignCounterValue(&d.C2Transitions, pCounterData);
break;
case 14: // PERF_COUNTER_BULK_COUNT
AssignCounterValue(&d.C3Transitions, pCounterData);
break;
case 15: // PERF_100NSEC_TIMER_INV
AssignCounterValue(&d.PriorityTime, pCounterData);
break;
case 16: // PERF_COUNTER_RAWCOUNT
AssignCounterValue(&d.ParkingStatus, pCounterData);
break;
case 17: // PERF_COUNTER_RAWCOUNT
AssignCounterValue(&d.ProcessorFrequency, pCounterData);
break;
case 18: // PERF_COUNTER_RAWCOUNT
AssignCounterValue(&d.PercentMaximumFrequency, pCounterData);
break;
case 19: // PERF_COUNTER_RAWCOUNT
AssignCounterValue(&d.ProcessorStateFlags, pCounterData);
break;
case 20: // PERF_COUNTER_COUNTER
AssignCounterValue(&d.ClockInterrupts, pCounterData);
break;
case 21: // PERF_PRECISION_100NS_TIMER
AssignCounterValue(&d.AverageIdleTime, pCounterData);
break;
case 22: // PERF_PRECISION_TIMESTAMP
AssignCounterValue(&d.AverageIdleTimeBase, pCounterData);
break;
case 23: // PERF_COUNTER_BULK_COUNT
AssignCounterValue(&d.IdleBreakEvents, pCounterData);
break;
case 24: // PERF_AVERAGE_BULK
AssignCounterValue(&d.ProcessorPerformance, pCounterData);
break;
case 25: // PERF_AVERAGE_BASE
AssignCounterValue(&d.ProcessorPerformanceBase, pCounterData);
break;
case 26: // PERF_AVERAGE_BULK
AssignCounterValue(&d.ProcessorUtility, pCounterData);
break;
case 28: // PERF_AVERAGE_BULK
AssignCounterValue(&d.PrivilegedUtility, pCounterData);
break;
case 27: // PERF_AVERAGE_BASE
AssignCounterValue(&d.UtilityBase, pCounterData);
break;
case 30: // PERF_COUNTER_RAWCOUNT
AssignCounterValue(&d.PercentPerformanceLimit, pCounterData);
break;
case 31: // PERF_COUNTER_RAWCOUNT
AssignCounterValue(&d.PerformanceLimitFlags, pCounterData);
break;
}
cbData -= pCounterData->dwSize;
pCounterData = reinterpret_cast<PERF_COUNTER_DATA const*>((LPCBYTE)pCounterData + pCounterData->dwSize);
}
pInstanceHeader = reinterpret_cast<PERF_INSTANCE_HEADER const*>(pCounterData);
}
if (nullptr != timestamp)
{
timestamp->PerfTimeStamp = pDataHeader->PerfTimeStamp;
timestamp->PerfTime100NSec = pDataHeader->PerfTime100NSec;
timestamp->PerfFreq = pDataHeader->PerfFreq;
}
status = ERROR_SUCCESS;
Done:
*bufferUsed = cInstances;
return status;
}
CpuPerfCountersConsumer.cpp
#include <windows.h>
#include <stdio.h>
#include "CpuPerfCounters.h"
#include <utility>
int wmain()
{
unsigned status;
unsigned const dataMax = 30; // Support up to 30 instances
CpuPerfCounters cpc;
CpuPerfTimestamp timestamp[2];
CpuPerfTimestamp* t0 = timestamp + 0;
CpuPerfTimestamp* t1 = timestamp + 1;
CpuPerfData data[dataMax * 2];
CpuPerfData* d0 = data + 0;
CpuPerfData* d1 = data + dataMax;
unsigned used;
status = cpc.ReadData(t0, d0, dataMax, &used);
printf("ReadData0 used=%u, status=%u\n", used, status);
for (unsigned iSample = 1; iSample != 10; iSample += 1)
{
Sleep(1000);
status = cpc.ReadData(t1, d1, dataMax, &used);
printf("ReadData%u used=%u, status=%u\n", iSample, used, status);
if (status == ERROR_SUCCESS && used != 0)
{
// Show the ProcessorTime value from instance #0 (usually the "_Total" instance):
auto& s0 = d0[0];
auto& s1 = d1[0];
printf(" %ls/%ls = %f\n", s0.Name, s1.Name,
100.0 * (1.0 - static_cast<double>(s1.ProcessorTime - s0.ProcessorTime) / static_cast<double>(t1->PerfTime100NSec - t0->PerfTime100NSec)));
std::swap(t0, t1); // Swap "current" and "previous" timestamp pointers.
std::swap(d0, d1); // Swap "current" and "previous" sample pointers.
}
}
return status;
}