使用 PerfLib 函數取用計數器資料
當您無法使用 效能資料協助程式 (PDH) 函式時,請使用 PerfLib 消費者函式,從 V2 效能數據提供者取用效能數據。 撰寫 OneCore 應用程式以收集 V2 計數器集,或當您需要收集具有最少相依性和額外負荷的 V2 計數器集時,可能會使用這些函式。
提示
PerfLib V2 取用者函式比效能資料協助程式 (PDH) 函式更難使用,且僅支援從 V2 提供者收集數據。 大部分的應用程式都應該優先使用 PDH 函式。
PerfLib V2 使用者函式是低層次 API,可從 V2 提供者收集資料。 PerfLib V2 取用者函式不支援從 V1 提供者收集數據。
警告
PerfLib V2 取用者函式可能會從不受信任的來源收集數據,例如從有限許可權的本機服務或遠端計算機收集數據。 PerfLib V2 取用者函式不會驗證數據的完整性或一致性。 取用者應用程式必須確認傳回的數據是否一致,例如傳回的數據區塊中的 Size 值不會超過傳回數據區塊的實際大小。 當取用者應用程式以較高的許可權執行時,這特別重要。
PerfLib 使用量
perflib.h
標頭文件包含 V2 使用者模式提供者與使用者所用的宣告(即 PerfLib 提供者 API 和 PerfLib 使用者 API)。 它會宣告下列函式來取用 V2 效能資料:
- 使用 PerfEnumerateCounterSet 取得系統上 V2 提供者所註冊之計數器集的 GUID。
- 使用 PerfQueryCounterSetRegistrationInfo 取得特定計數器集的相關信息,例如計數器集的名稱、描述、類型(單一實例或多重實例)、計數器類型、計數器名稱和計數器描述。
- 使用 PerfEnumerateCounterSetInstances 來取得多實例計數器集目前作用中實例的名稱。
- 使用 PerfOpenQueryHandle 建立新的查詢句柄,以用於從一或多個計數器集收集數據。
- 使用 PerfCloseQueryHandle 來關閉查詢句柄。
- 使用 PerfAddCounters,將查詢新增至查詢處理程序。
- 使用 PerfDeleteCounters 從查詢句柄中移除查詢。
- 使用 PerfQueryCounterInfo 取得查詢句柄上目前查詢的相關信息。
- 使用 PerfQueryCounterData 從查詢句柄收集效能數據。
如果您的使用者僅從 GUID 和計數器索引穩定的特定提供者取用數據,而您也能存取由 CTRPP產生的符號檔(來自 CTRPP -ch
參數),那麼您可以將計數器集 GUID 和計數器索引值直接硬式編碼到使用者中。
否則,您必須載入計數器集元數據,以判斷要用於查詢中的計數器集 GUID(s) 和計數器索引,如下所示:
- 使用 PerfEnumerateCounterSet 來取得支援的計數器集 GUID 清單。
- 針對每個 GUID,請使用 PerfQueryCounterSetRegistrationInfo 載入計數器集名稱。 當您找到您要尋找的名稱時,請停止。
- 使用 PerfQueryCounterSetRegistrationInfo 載入該計數器集的其餘元數據(計數器集組態、計數器名稱、計數器索引、計數器類型)。
如果您只需要知道計數器集目前使用中實例的名稱(例如,如果您不需要實際的效能數據值),您可以使用 PerfEnumerateCounterSetInstances。 這會接受計數器集 GUID 作為輸入,並傳回具有指定計數器集目前使用中實例名稱和標識碼的 PERF_INSTANCE_HEADER
區塊。
查詢
若要收集效能數據,您必須建立查詢句柄、新增查詢,以及從查詢收集數據。
查詢句柄可以有許多與其相關聯的查詢。 當您針對查詢句柄呼叫 PerfQueryCounterData 時,PerfLib 會執行所有句柄的查詢並收集所有結果。
每個查詢都會指定計數器集 GUID、實例名稱篩選、選擇性實例識別元篩選,以及選擇性的計數器標識元篩選。
- 需要計數器集 GUID。 指出查詢將從中收集數據之計數器集的 GUID。 這類似於 SQL 查詢的
FROM
子句。 - 需要實例名稱篩選。 它表示實例名稱必須符合實例名稱才能包含在查詢中的通配符模式,
*
表示「任何字元」,?
表示「一個字元」。 針對單一實例計數器集,這個 MUST 設定為長度為零的字串""
。 針對多重實例計數器集,此 MUST 設為非空白字串(使用"*"
接受所有實例名稱)。 這類似於 SQL 查詢的WHERE InstanceName LIKE NameFilter
子句。 - 實例標識碼篩選是選擇性的。 如果存在 ,即如果設定為
0xFFFFFFFF
以外的值,則表示查詢應該只收集實例標識元符合指定標識符的實例。 如果不存在 (亦即如果設定為0xFFFFFFFF
),則表示查詢應該接受所有實例標識碼。 這類似於 SQL 查詢的WHERE InstanceId == IdFilter
子句。 - 計數器標識碼篩選是選擇性的。 如果該值存在,也就是說,如果該值被設為除了
PERF_WILDCARD_COUNTER
之外的其他值,則表示查詢應該收集單一計數器,類似於 SQL 查詢的SELECT CounterName
子句。 如果不存在 (亦即如果設定為PERF_WILDCARD_COUNTER
),則表示查詢應該收集所有可用的計數器,類似於 SQL 查詢的SELECT *
子句。
使用 PerfAddCounters 將查詢新增至查詢句柄。 使用 PerfDeleteCounters,從查詢句柄中移除查詢。
變更查詢句柄中的查詢之後,請使用 PerfQueryCounterInfo 來取得查詢索引。 索引表示查詢結果的傳回順序 PerfQueryCounterData(結果不一定符合新增查詢的順序)。
查詢句柄準備就緒后,請使用 PerfQueryCounterData 收集數據。 您通常會定期收集數據(一秒或一分鐘一次),然後視需要處理數據。
注意
性能計數器並非設計為每秒收集超過一次。
PerfQueryCounterData 會傳回 PERF_DATA_HEADER
區塊,其中包含具有時間戳的數據標頭,後面接著一連串 PERF_COUNTER_HEADER
區塊,每個區塊都包含一個查詢的結果。
PERF_COUNTER_HEADER
可能包含各種不同類型的數據,如 [dwType
] 字段的值所指示:
-
PERF_ERROR_RETURN
- PerfLib 無法從提供者取得有效的計數器數據。 -
PERF_SINGLE_COUNTER
- 查詢是來自單一實例計數器集的單一計數器。 結果只包含要求的計數器值。 -
PERF_MULTIPLE_COUNTERS
- 查詢來自單一實例計數器集的多個計數器。 結果包含計數器值,以及對應每個值與相應計數器的資訊(亦即欄位標題)。 -
PERF_MULTIPLE_INSTANCES
- 查詢是來自多重實例計數器集的單一計數器。 結果包含實例資訊(也就是數據列標題)和每個實例的一個計數器值。 -
PERF_COUNTERSET
- 查詢來自多重實例計數器集的多個計數器。 結果包含實例資訊(即數據列標題)、每個實例的計數器值,以及比對每個值與對應計數器的資訊(亦即數據行標題)。
PerfQueryCounterData 所傳回的值是 UINT32
或 UINT64
的原始值。 這些通常需要一些處理才能產生預期的格式化值。 必要的處理取決於計數器的類型。 許多計數器類型需要其他資訊才能完成處理,例如來自相同範例中「基底」計數器的時間戳或值。
某些計數器類型是「增量」計數器,只有與先前樣本的數據相較時才有意義。 例如,類型為 PERF_SAMPLE_COUNTER
的計數器具有一個格式值,預期會顯示速率(在取樣間隔中每秒發生特定事物的次數),但實際的原始值只是一個計數(共發生特定項目的次數)。 若要產生格式化的「速率」值,您必須套用對應至計數器類型的公式。
PERF_SAMPLE_COUNTER
的公式是 (N1 - N0) / ((T1 - T0) / F)
:從先前樣本的值減去目前的樣本值(提供樣本間隔期間發生的事件次數),然後將結果除以樣本間隔中的秒數(藉由從先前樣本的時間戳減去目前的樣本時間戳,並除以頻率將時間範圍轉換成秒)。
如需從原始值計算格式化值的詳細資訊,請參閱 計算計數器值。
樣本
下列程式代碼會實作取用者,該取用者會使用 PerfLib V2 取用者函式從「處理器資訊」計數器集讀取 CPU 效能資訊。
消費者的結構如下:
-
CpuPerfCounters
類別會實作取用效能數據的邏輯。 它會封裝查詢句柄和數據緩衝區,以記錄查詢結果。 -
CpuPerfTimestamp
結構會儲存範例的時間戳資訊。 每次收集資料時,呼叫端都會收到單一CpuPerfTimestamp
。 -
CpuPerfData
結構會儲存一個CPU的效能資訊(實例名稱和原始效能值)。 每次收集數據時,呼叫端都會收到CpuPerfData
陣列(每個 CPU 一個)。
此範例會使用硬式編碼計數器集 GUID 和計數器標識碼值,因為它已針對不會變更 GUID 或識別符值的特定計數器集 (Processor Information) 進行優化。 從任意計數器集讀取效能數據的更泛型類別,需要使用 PerfQueryCounterSetRegistrationInfo 來查閱計數器標識符與運行時間計數器值之間的對應。
簡單的 CpuPerfCountersConsumer.cpp
程式示範如何使用來自 CpuPerfCounters
類別的值。
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;
}