/ Performance,Windows / 38浏览

如何通过 PDH(Performance Data Helper) 获取性能计数器的值

0. 引入

Performance Monitor 中主要通过不同的计数器来监听不同模块的值。基于计数器来进行分析,可以开箱即用,使用非常低的开销获取到系统和应用的信息。

如果我们需要在自己的应用中,直接使用这些信息,Windows 也提供了一套 API,PDH(Performance Data Helper),详细信息可以看:https://learn.microsoft.com/en-us/windows/win32/perfctrs/consuming-counter-data

接下来我们就来看看,如何通过 C++ 来获取性能计数器的值。

  1. 创建 Query
HQUERY hQuery; 

PDH_STATUS status = PdhOpenQuery(NULL, 0, &hQuery);
  1. 指定计数器
HCOUNTER hCounter;

// \Processor(_Total)\Interrupts/sec
PDH_STATUS status = PdhAddCounter(hQuery, counterPath.c_str(), 0, &hCounter);
  1. 收集数据
PDH_STATUS status = PdhCollectQueryData(hQuery);

Sleep(1000); // 等待 1 秒

PDH_STATUS status = PdhCollectQueryData(hQuery);

这里需要先获取一次,等待 1s 后再获取一次,如果只获取一次可能会存在某些数据不存在的问题。

  1. 格式化数据
PDH_FMT_COUNTERVALUE counterValue;
PDH_STATUS status = PdhGetFormattedCounterValue(hCounter, PDH_FMT_DOUBLE, NULL, &counterValue);

double result = counterValue.doubleValue;

汇总 Demo 如下

将性能计数器封装成类:

PerformanceCounter.h 如下

#pragma once

#include <windows.h>
#include <pdh.h>
#include <pdhmsg.h>
#include <string>
#include <map>
#include <mutex>
#include <stdexcept>

#pragma comment(lib, "pdh.lib")

class PerformanceCounter {
public:
    // 构造函数
    PerformanceCounter();

    // 禁止复制构造和赋值
    PerformanceCounter(const PerformanceCounter&) = delete;
    PerformanceCounter& operator=(const PerformanceCounter&) = delete;

    // 移动构造和赋值
    PerformanceCounter(PerformanceCounter&& other) noexcept;
    PerformanceCounter& operator=(PerformanceCounter&& other) noexcept;

    // 添加计数器
    void AddCounter(const std::wstring& counterPath);

    // 移除计数器
    void RemoveCounter(const std::wstring& counterPath);

    // 收集数据
    void CollectData();

    // 获取所有计数器的值
    std::map<std::wstring, double> GetValues();

    // 析构函数
    ~PerformanceCounter();

private:
    HQUERY hQuery;                              // 查询句柄
    std::map<std::wstring, HCOUNTER> counterHandles; // 计数器路径与句柄的映射
    std::mutex mtx;                             // 用于线程安全

    void Close();                               // 关闭查询并释放资源
};

PerformanceCounter.cpp 如下

#include "perf_counter.h"
#include <stdexcept>
#include <iostream>

// 构造函数
PerformanceCounter::PerformanceCounter() : hQuery(nullptr) {
    PDH_STATUS status = PdhOpenQuery(NULL, 0, &hQuery);
    if (status != ERROR_SUCCESS) {
        throw std::runtime_error("Failed to open PDH query.");
    }
}

// 移动构造函数
PerformanceCounter::PerformanceCounter(PerformanceCounter&& other) noexcept
    : hQuery(other.hQuery), counterHandles(std::move(other.counterHandles)) {
    other.hQuery = nullptr;
}

// 移动赋值运算符
PerformanceCounter& PerformanceCounter::operator=(PerformanceCounter&& other) noexcept {
    if (this != &other) {
        Close();
        hQuery = other.hQuery;
        counterHandles = std::move(other.counterHandles);
        other.hQuery = nullptr;
    }
    return *this;
}

// 添加计数器
void PerformanceCounter::AddCounter(const std::wstring& counterPath) {
    std::lock_guard<std::mutex> lock(mtx);
    if (counterHandles.find(counterPath) != counterHandles.end()) {
        throw std::runtime_error("Counter already exists.");
    }

    HCOUNTER hCounter;
    PDH_STATUS status = PdhAddCounter(hQuery, counterPath.c_str(), 0, &hCounter);
    if (status != ERROR_SUCCESS) {
        throw std::runtime_error("Failed to add counter.");
    }

    counterHandles[counterPath] = hCounter;
}

// 移除计数器
void PerformanceCounter::RemoveCounter(const std::wstring& counterPath) {
    std::lock_guard<std::mutex> lock(mtx);
    auto it = counterHandles.find(counterPath);
    if (it == counterHandles.end()) {
        throw std::runtime_error("Counter not found.");
    }

    PdhRemoveCounter(it->second);
    counterHandles.erase(it);
}

// 收集数据
void PerformanceCounter::CollectData() {
    std::lock_guard<std::mutex> lock(mtx);
    PDH_STATUS status = PdhCollectQueryData(hQuery);
    if (status != ERROR_SUCCESS) {
        throw std::runtime_error("Failed to collect PDH data.");
    }
}

// 获取所有计数器的值
std::map<std::wstring, double> PerformanceCounter::GetValues() {
    std::lock_guard<std::mutex> lock(mtx);
    std::map<std::wstring, double> results;

    for (const auto& [path, hCounter] : counterHandles) {
        PDH_FMT_COUNTERVALUE counterValue;
        PDH_STATUS status = PdhGetFormattedCounterValue(hCounter, PDH_FMT_DOUBLE, NULL, &counterValue);
        if (status != ERROR_SUCCESS) {
            throw std::runtime_error("Failed to get value.");
        }
        results[path] = counterValue.doubleValue;
    }

    return results;
}

// 关闭查询并释放资源
void PerformanceCounter::Close() {
    if (hQuery) {
        PdhCloseQuery(hQuery);
        hQuery = nullptr;
    }
}

// 析构函数
PerformanceCounter::~PerformanceCounter() {
    Close();
}
【译】ETW 堆跟踪 – 每个分配都被记录
【译】ETW 堆跟踪 – 每个分配都被记录
【译】Wait Analysis – 寻找空闲时间
【译】Wait Analysis – 寻找空闲时间
如何通过 ETW Provider 来记录应用日志
如何通过 ETW Provider 来记录应用日志
如何通过 C++ 实时监听 ETW 事件
如何通过 C++ 实时监听 ETW 事件
【译】调查并确定 Windows 运行速度变慢问题
【译】调查并确定 Windows 运行速度变慢问题
【译】丢失的 WPA 文档 —— 磁盘使用
【译】丢失的 WPA 文档 —— 磁盘使用

0

  1. This post has no comment yet

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注