次の方法で共有


PerfLib 関数を使用したカウンター データの使用

パフォーマンス データ ヘルパー (PDH) 関数を使用できない場合は、PerfLib コンシューマー関数を使用して、V2 パフォーマンス データ プロバイダーのパフォーマンス データを使用します。 これらの関数は、V2 カウンターセットを収集するために OneCore アプリケーションを作成するとき、または依存関係とオーバーヘッドを最小限に抑えて V2 カウンターセットを収集する必要がある場合に使用できます。

ヒント

PerfLib V2 コンシューマー関数は、パフォーマンス データ ヘルパー (PDH) 関数よりも使いにくく、V2 プロバイダーからのデータ収集のみをサポートします。 ほとんどのアプリケーションでは、PDH 関数を使用することをお勧めします。

PerfLib V2 コンシューマー関数は、 V2 プロバイダーからデータを収集するための低レベル API です。 PerfLib V2 コンシューマー関数では、 V1 プロバイダーからのデータ収集はサポートされていません。

警告

PerfLib V2 コンシューマー関数は、制限付き特権のローカル サービスやリモート マシンからなど、信頼されていないソースからデータを収集する可能性があります。 PerfLib V2 コンシューマー関数は、整合性または整合性のデータを検証しません。 返されるデータが一貫していることを確認するのはコンシューマー アプリケーションのユーザーの操作です。たとえば、返されるデータ ブロックの Size 値が、返されるデータ ブロックの実際のサイズを超えないことを確認します。 これは、コンシューマー アプリケーションが昇格された特権で実行される場合に特に重要です。

PerfLib の使用状況

perflib.hヘッダーには、V2 ユーザー モード プロバイダー (PerfLib プロバイダー API) と V2 コンシューマー (PerfLib コンシューマー API) で使用される宣言が含まれます。 V2 パフォーマンス データを使用するための次の関数が宣言されています。

  • PerfEnumerateCounterSet を使用して、システム上の V2 プロバイダーによって登録されているカウンターセットの GUID を取得します。
  • PerfQueryCounterSetRegistrationInfo を使用して、カウンターセットの名前、説明、型 (単一インスタンスまたはマルチインスタンス)、カウンターの種類、カウンター名、カウンターの説明など、特定のカウンターセットに関する情報を取得します。
  • PerfEnumerateCounterSetInstances を使用して、マルチインスタンス カウンターセットの現在アクティブなインスタンスの名前を取得します。
  • PerfOpenQueryHandle を使用して、1 つ以上のカウンターセットからデータを収集するために使用する新しいクエリ ハンドルを作成します。
  • クエリ ハンドルを閉じるには、 PerfCloseQueryHandle を使用します。
  • PerfAddCounters を使用して、クエリ ハンドルにクエリを追加します。
  • PerfDeleteCounters を使用して、クエリ ハンドルからクエリを削除します。
  • PerfQueryCounterInfo を使用して、クエリ ハンドルの現在のクエリに関する情報を取得します。
  • PerfQueryCounterData を使用して、クエリ ハンドルからパフォーマンス データを収集します。

コンシューマーが、GUID とカウンター インデックスが安定している特定のプロバイダーからのみデータを使用し、 (CTRPP パラメーターから -ch ) CTRPP 生成シンボル ファイルにアクセスできる場合は、カウンターセット GUID とカウンター インデックス値をコンシューマーにハードコーディングできます。

それ以外の場合は、カウンターセットメタデータを読み込んで、クエリで使用するカウンターセット GUID とカウンターインデックスを次のように決定する必要があります。

  • PerfEnumerateCounterSet を使用して、サポートされているカウンターセット GUID の一覧を取得します。
  • GUID ごとに 、PerfQueryCounterSetRegistrationInfo を使用してカウンターセット名を読み込みます。 探している名前が見つかると停止します。
  • PerfQueryCounterSetRegistrationInfo を使用して、そのカウンターセットの残りのメタデータ (カウンターセット構成、カウンター名、カウンター インデックス、カウンター型) を読み込みます。

カウンターセットの現在アクティブなインスタンスの名前を知る必要がある場合 (つまり、実際のパフォーマンス データ値が不要な場合)、 PerfEnumerateCounterSetInstances を使用できます。 これにより、カウンターセット GUID が入力として受け取られ、指定されたカウンターセットの現在アクティブなインスタンスの名前と ID を持つブロックが返 PERF_INSTANCE_HEADER されます。

クエリ

パフォーマンス データを収集するには、クエリ ハンドルを作成し、それにクエリを追加し、クエリからデータを収集する必要があります。

クエリ ハンドルには、多数のクエリを関連付けることができます。 クエリ ハンドル に対して PerfQueryCounterData を呼び出すと、PerfLib はハンドルのすべてのクエリを実行し、すべての結果を収集します。

各クエリでは、カウンターセット GUID、インスタンス名フィルター、オプションのインスタンス ID フィルター、およびオプションのカウンター ID フィルターを指定します。

  • カウンターセット GUID が必要です。 これは、クエリがデータを収集するカウンターセットの GUID を示します。 これは、SQL クエリの FROM 句に似ています。
  • インスタンス名フィルターが必要です。 これは、インスタンス名がクエリに含まれるインスタンスと一致する必要があるワイルドカード パターンを示し、 * "any characters" を示し ? 、"1 文字" を示します。 単一インスタンス カウンターセットの場合、長さ 0 の文字列 ""に設定する必要があります。 マルチインスタンス カウンターセットの場合、これは空でない文字列に設定する 必要があります (すべてのインスタンス名を受け入れるには を使用 "*" します)。 これは、SQL クエリの 句に似ています WHERE InstanceName LIKE NameFilter
  • インスタンス ID フィルターは省略可能です。 存在する場合 (つまり、 以外 0xFFFFFFFFの値に設定されている場合)、クエリは、インスタンス ID が指定された ID と一致するインスタンスのみを収集する必要があることを示します。 存在しない場合 (つまり、 に設定されている 0xFFFFFFFF場合)、クエリはすべてのインスタンス ID を受け入れる必要があることを示します。 これは、SQL クエリの 句に似ています WHERE InstanceId == IdFilter
  • カウンター ID フィルターは省略可能です。 存在する場合 (つまり、 以外 PERF_WILDCARD_COUNTERの値に設定されている場合)、SQL クエリの 句と同様に、クエリで 1 つのカウンターを SELECT CounterName 収集する必要があることを示します。 存在しない場合 (つまり、 に PERF_WILDCARD_COUNTER設定されている場合)、SQL クエリの 句と同様に、クエリで使用可能なすべてのカウンターを SELECT * 収集する必要があることを示します。

PerfAddCounters を使用してクエリ ハンドルにクエリを追加します。 クエリ ハンドルからクエリを削除するには、 PerfDeleteCounters を使用します。

クエリ ハンドル内のクエリを変更した後、 PerfQueryCounterInfo を使用してクエリ インデックスを取得します。 インデックスは、 PerfQueryCounterData によってクエリ結果が返される順序を示します (結果は、クエリが追加された順序と常に一致するとは限りません)。

クエリ ハンドルの準備ができたら、 PerfQueryCounterData を使用してデータを収集します。 通常は、定期的に (1 秒に 1 回または 1 分に 1 回) データを収集し、必要に応じてデータを処理します。

注意

パフォーマンス カウンターは、1 秒に 1 回以上収集されるようには設計されていません。

PerfQueryCounterData はブロックを PERF_DATA_HEADER 返します。このブロックは、タイムスタンプを持つデータ ヘッダーの後にブロックの PERF_COUNTER_HEADER シーケンスが続き、それぞれに 1 つのクエリの結果が含まれます。

には PERF_COUNTER_HEADER 、フィールドの値で示されているように、さまざまな種類のデータが dwType 含まれる場合があります。

  • PERF_ERROR_RETURN - PerfLib は、プロバイダーから有効なカウンター データを取得できません。
  • PERF_SINGLE_COUNTER - クエリは、単一インスタンス のカウンターセットからの 1 つのカウンターに対して行われました。 結果には、要求されたカウンター値のみが含まれます。
  • PERF_MULTIPLE_COUNTERS - クエリは、1 つのインスタンス カウンターセットからの複数のカウンターに対して行われました。 結果には、カウンター値と、各値を対応するカウンター (列見出しなど) と照合するための情報が含まれます。
  • PERF_MULTIPLE_INSTANCES - クエリは、マルチインスタンス カウンターセットからの 1 つのカウンターに対して行われました。 結果には、インスタンス情報 (行見出しなど) とインスタンスごとに 1 つのカウンター値が含まれます。
  • PERF_COUNTERSET - クエリは、マルチインスタンス カウンターセットからの複数のカウンターに対して行われました。 結果には、インスタンス情報 (行見出し)、各インスタンスのカウンター値、および各値を対応するカウンター (列見出し) と照合するための情報が含まれます。

PerfQueryCounterData によって返される値は、またはUINT64生の値ですUINT32。 これらは通常、予期される書式設定された値を生成するために何らかの処理が必要です。 必要な処理は、カウンターの種類によって異なります。 多くのカウンターの種類では、タイムスタンプや同じサンプルの "基本" カウンターからの値など、完全な処理のために追加情報が必要です。

一部のカウンターの種類は、前のサンプルのデータと比較した場合にのみ意味のある "デルタ" カウンターです。 たとえば、型 PERF_SAMPLE_COUNTER のカウンターには、レート (サンプル間隔で 1 秒あたりに発生した特定のことが発生した回数) を示す書式設定された値がありますが、実際の生の値は単なるカウント (特定のものが合計で発生した回数) です。 書式設定された "rate" 値を生成するには、カウンターの種類に対応する数式を適用する必要があります。 の数式 PERF_SAMPLE_COUNTER は です (N1 - N0) / ((T1 - T0) / F)。前のサンプルの値から現在のサンプルの値を減算し (サンプル間隔中にイベントが発生した回数を指定します)、結果をサンプル間隔の秒数で除算します (前のサンプルのタイムスタンプから現在のサンプルのタイムスタンプを減算し、頻度で除算してタイムスパンを秒に変換します)。

生の値から書式設定された 値を計算する 方法の詳細については、「カウンター値の計算」を参照してください。

サンプル

次のコードでは、PerfLib V2 Consumer 関数を使用して "プロセッサ情報" カウンターセットから CPU パフォーマンス情報を読み取るコンシューマーを実装します。

コンシューマーは次のように編成されています。

  • クラスは CpuPerfCounters 、パフォーマンス データを使用するためのロジックを実装します。 クエリ ハンドルと、クエリ結果が記録されるデータ バッファーをカプセル化します。
  • 構造体には CpuPerfTimestamp 、サンプルのタイムスタンプ情報が格納されます。 データが収集されるたびに、呼び出し元は 1 つの CpuPerfTimestampを受け取ります。
  • 構造体には CpuPerfData 、1 つの CPU のパフォーマンス情報 (インスタンス名と生のパフォーマンス値) が格納されます。 データが収集されるたびに、呼び出し元は の配列 CpuPerfData (CPU ごとに 1 つ) を受け取ります。

このサンプルでは、GUID または ID 値を変更しない特定のカウンターセット (プロセッサ情報) 用に最適化されているため、ハードコーディングされたカウンターセット GUID とカウンター ID 値を使用します。 任意のカウンターセットからパフォーマンス データを読み取るより汎用的なクラスでは、 PerfQueryCounterSetRegistrationInfo を使用して、実行時にカウンター ID とカウンター値間のマッピングを検索する必要があります。

単純な 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 can be found on MSDN.
// 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;
}