CPerformHelper.h
#pragma once
#include <windows.h>
#include <tchar.h>
#include <string>
#include <pdh.h>
#include <string>
#include <mutex>
#include <thread>
#include <map>
#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif
// CPU 使用率
#define PERFM_PATH_CPU_UTILITY "\\Processor Information(_Total)\\% Processor Utility"
// CPU 性能
#define PERFM_PATH_CPU_PERFORMANCE "\\Processor Information(_Total)\\% Processor Performance"
// CPU 主频
#define PERFM_PATH_CPU_FREQUENCY "\\Processor Information(_Total)\\Processor Frequency"
// 磁盘读写
#define PERFM_PATH_DISK_READ_RATE "\\PhysicalDisk(_Total)\\Disk Read Bytes/sec"
#define PERFM_PATH_DISK_WRITE_RATE "\\PhysicalDisk(_Total)\\Disk Write Bytes/sec"
// 网络收发
#define PERFM_PATH_NETWORK_RECV_RATE "\\Network Interface(*)\\Bytes Received/sec"
#define PERFM_PATH_NETWORK_SENT_RATE "\\Network Interface(*)\\Bytes Sent/sec"
// GPU
#define PERFM_PATH_GPU_UTILITY "\\GPU Engine(*)\\Utilization Percentage"
#ifdef UNICODE
using PerfMonInfo = std::map<std::wstring, HCOUNTER> ;
#else
using PerfMonInfo = std::map<std::string, HCOUNTER>;
#endif
class CPerformHelper
{
public:
CPerformHelper();
virtual ~CPerformHelper();
// 初始化
bool Initialize();
// 反初始化
void Uninitialize();
// 添加性能计数器
bool AddCounter(const _tstring& strCounterPath);
// 移除性能计数器
bool RemoveCounter(const _tstring& strCounterPath);
// 设置性能采集时间间隔
void SetCollectInterval(DWORD millisecond = 1000);
// 获取性能计数器数值
bool GetFormattedCounterValue(const _tstring& strCounterPath, DWORD dwFormat, PPDH_FMT_COUNTERVALUE pValue);
// 获取性能计数器数值数组
bool GetFormattedCounterArray(const _tstring& strCounterPath, DWORD dwFormat, PPDH_FMT_COUNTERVALUE pValue);
// 开始采集数据
bool StartCollect();
private:
void _Sleep(int millisecond);
private:
HQUERY m_hQuery;
bool m_fQuit;
DWORD m_msCollectInterval;
std::mutex m_Mutex;
PerfMonInfo m_hPerfMonInfos;
std::thread m_task;
};
CPerformHelper.cpp
#include "CPerformHelper.h"
#include <pdh.h>
#include <pdhmsg.h>
#pragma comment(lib, "pdh.lib")
CPerformHelper::CPerformHelper()
:
m_fQuit(false),
m_hQuery(NULL),
m_msCollectInterval(1000)
{
}
CPerformHelper::~CPerformHelper()
{
Uninitialize();
}
void CPerformHelper::_Sleep(int millisecond)
{
do
{
const int span = 1;
if (millisecond < span)
{
std::this_thread::sleep_for(std::chrono::milliseconds(millisecond));
return;
}
clock_t tmBegin = clock();
while (true)
{
if (::clock() - tmBegin > millisecond)
{
break;
}
if (m_fQuit)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(span));
}
} while (false);
}
bool CPerformHelper::Initialize()
{
PDH_STATUS status = ERROR_SUCCESS;
if (NULL == m_hQuery)
{
status = PdhOpenQuery(NULL, 0, &m_hQuery);
}
if (ERROR_SUCCESS != status)
{
return false;
}
return true;
}
void CPerformHelper::Uninitialize()
{
m_fQuit = true;
if (m_task.joinable())
{
m_task.join();
}
m_fQuit = false;
if (m_hQuery)
{
::PdhCloseQuery(m_hQuery);
m_hQuery = NULL;
}
for (auto& item : m_hPerfMonInfos)
{
if (NULL != item.second)
{
::PdhRemoveCounter(item.second);
}
}
m_hPerfMonInfos.clear();
}
bool CPerformHelper::AddCounter(const _tstring& strCounterPath)
{
HCOUNTER hCounter = NULL;
PDH_STATUS status = ERROR_SUCCESS;
status = ::PdhAddCounter(m_hQuery, strCounterPath.c_str(), 0, &hCounter);
if (ERROR_SUCCESS != status)
{
return false;
}
m_Mutex.lock();
m_hPerfMonInfos.insert(std::make_pair(strCounterPath, hCounter));
m_Mutex.unlock();
return true;
}
bool CPerformHelper::RemoveCounter(const _tstring& szCounterPath)
{
bool fResult = false;
m_Mutex.lock();
auto itFind = m_hPerfMonInfos.find(szCounterPath);
if (itFind != m_hPerfMonInfos.end())
{
::PdhRemoveCounter(itFind->second);
m_hPerfMonInfos.erase(itFind);
fResult = true;
}
m_Mutex.unlock();
return fResult;
}
void CPerformHelper::SetCollectInterval(DWORD millisecond)
{
if (millisecond < 100)
{
m_msCollectInterval = 100;
}
m_msCollectInterval = millisecond;
}
bool CPerformHelper::StartCollect()
{
PDH_STATUS status = ERROR_SUCCESS;
status = ::PdhCollectQueryData(m_hQuery);
if (ERROR_SUCCESS != status)
{
return false;
}
m_task = std::move(std::thread([&]() {
PDH_STATUS status = ERROR_SUCCESS;
while ((ERROR_SUCCESS == status) && !m_fQuit)
{
status = ::PdhCollectQueryData(m_hQuery);
if (ERROR_SUCCESS != status)
{
break;
}
_Sleep(m_msCollectInterval);
}
}));
return ERROR_SUCCESS == status;
}
bool CPerformHelper::GetFormattedCounterValue(const _tstring& strCounterPath, DWORD dwFormat, PPDH_FMT_COUNTERVALUE pValue)
{
PDH_STATUS status = ERROR_SUCCESS;
auto itFind = m_hPerfMonInfos.find(strCounterPath);
if (m_hPerfMonInfos.end() == itFind)
{
return false;
}
status = ::PdhGetFormattedCounterValue(
itFind->second,
dwFormat,
(LPDWORD)NULL,
pValue);
return ERROR_SUCCESS == status;
}
bool CPerformHelper::GetFormattedCounterArray(const _tstring& strCounterPath, DWORD dwFormat, PPDH_FMT_COUNTERVALUE pValue)
{
PPDH_FMT_COUNTERVALUE_ITEM pAryValue = NULL;
PDH_STATUS status = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
DWORD dwItemCount = 0;
auto itFind = m_hPerfMonInfos.find(strCounterPath);
if (m_hPerfMonInfos.end() == itFind)
{
return false;
}
do
{
status = ::PdhGetFormattedCounterArray(
itFind->second,
dwFormat,
&dwBufferSize,
&dwItemCount,
NULL
);
if (PDH_MORE_DATA != status)
{
break;
}
pAryValue = (PPDH_FMT_COUNTERVALUE_ITEM)::HeapAlloc(::GetProcessHeap(), 0, dwBufferSize);
if (NULL == pAryValue)
{
break;
}
status = ::PdhGetFormattedCounterArray(
itFind->second,
dwFormat,
&dwBufferSize,
&dwItemCount,
pAryValue
);
PDH_FMT_COUNTERVALUE value = { 0 };
for (int i = 0; i < dwItemCount; i++)
{
if (PDH_FMT_DOUBLE == dwFormat)
{
value.doubleValue += pAryValue[i].FmtValue.doubleValue;
}
if (PDH_FMT_LARGE == dwFormat)
{
value.largeValue += pAryValue[i].FmtValue.largeValue;
}
if (PDH_FMT_LONG == dwFormat)
{
value.longValue += pAryValue[i].FmtValue.longValue;
}
}
if (pValue)
{
*pValue = value;
}
} while (false);
if (pAryValue)
{
::HeapFree(::GetProcessHeap(), 0, pAryValue);
}
return ERROR_SUCCESS == status;
}
main.cpp
#include <iostream>
#include "Win32Utils/CDeviceHelper.h"
#include "Win32Utils/CWmiQueryHelper.h"
#include "CPerformHelper.h"
#include "Win32Utils/CDeviceHelper.h"
// 数据单位
enum eUnitType
{
eUT_Auto, // 自动
eUT_b, // (2 ^ 000) Bit
eUT_Kb, // (2 ^ 010) Bit
eUT_Mb, // (2 ^ 020) Bit
eUT_Gb, // (2 ^ 030) Bit
eUT_Tb, // (2 ^ 040) Bit
eUT_Pb, // (2 ^ 050) Bit
eUT_Eb, // (2 ^ 060) Bit
eUT_Zb, // (2 ^ 070) Bit
eUT_Yb, // (2 ^ 080) Bit
eUT_Bb, // (2 ^ 090) Bit
eUT_Nb, // (2 ^ 100) Bit
eUT_Db, // (2 ^ 110) Bit
eUT_Cb, // (2 ^ 120) Bit
eUT_Xb, // (2 ^ 130) Bit
eUT_B, // Byte 2 ^ 000 Byte
eUT_KB, // Kilobyte 2 ^ 010 Byte
eUT_MB, // Megabyte 2 ^ 020 Byte
eUT_GB, // Gigabyte 2 ^ 030 Byte
eUT_TB, // Terabyte 2 ^ 040 Byte
eUT_PB, // Petabyte 2 ^ 050 Byte
eUT_EB, // Exabyte 2 ^ 060 Byte
eUT_ZB, // Zettabyte 2 ^ 070 Byte
eUT_YB, // Yottabyte 2 ^ 080 Byte
eUT_BB, // Brontobyte 2 ^ 090 Byte
eUT_NB, // NonaByte 2 ^ 100 Byte
eUT_DB, // DoggaByte 2 ^ 110 Byte
eUT_CB, // corydonbyte 2 ^ 120 Byte
eUT_XB, // Xerobyte 2 ^ 130 Byte
eUT_Max
};
typedef struct _DATA_UNIT_INFO
{
double value; // 数值
eUnitType eUnit; // 单位
_tstring strUnitStr; // 单位字符串
_tstring strOutput; // 内容(数值 + 单位字符串)
}DATA_UNIT_INFO;
static DATA_UNIT_INFO FormatByteSize(
double nBytesSize, // 输入值
eUnitType eSrcUnit = eUnitType::eUT_Auto, // 原始单位
eUnitType eDestUnit = eUnitType::eUT_Auto, // 目标单位
bool fHasUnits = true, // 结果字符串数值添加单位
bool fSpace = true, // 结果字符串数值与单位之间添加空格
int nInteger = 1, // 整数部分长度
int nPrecision = 1 // 小数部分长度
);
void ConsoleOutput(LPCTSTR pFormat, ...);
void ConsoleClear();
int main()
{
setlocale(LC_ALL, "");
std::vector<VIDEO_ADAPTER_DESC_INFO> vResult = CDeviceHelper::GetAllVideoAdapterDesc();
CDeviceHelper obj;
clock_t tmBegin = ::clock();
DEVICE_INFO info = obj.GetDeviceInfo();
clock_t tmEnd = ::clock();
ConsoleOutput(_T("cost time: %d\r\n"), tmEnd - tmBegin);
CPerformHelper perfmon;
perfmon.Initialize();
perfmon.SetCollectInterval(1000);
perfmon.AddCounter(_T(PERFM_PATH_CPU_UTILITY));
perfmon.AddCounter(_T(PERFM_PATH_DISK_READ_RATE));
perfmon.AddCounter(_T(PERFM_PATH_DISK_WRITE_RATE));
perfmon.AddCounter(_T(PERFM_PATH_CPU_PERFORMANCE));
perfmon.AddCounter(_T(PERFM_PATH_CPU_FREQUENCY));
perfmon.AddCounter(_T(PERFM_PATH_NETWORK_RECV_RATE));
perfmon.AddCounter(_T(PERFM_PATH_NETWORK_SENT_RATE));
perfmon.AddCounter(_T(PERFM_PATH_GPU_UTILITY));
perfmon.AddCounter(_T(PERFM_PATH_GPU_MEMORY_DEDICATED_USAGE_UTILITY));
perfmon.AddCounter(_T(PERFM_PATH_GPU_MEMORY_SHARE_USAGE_UTILITY));
perfmon.AddCounter(_T(PERFM_PATH_GPU_MEMORY_TOTAL_COMMITTED_USAGE_UTILITY));
perfmon.StartCollect();
PDH_FMT_COUNTERVALUE value = { 0 };
SIZE_T DedicatedVideoMemory = vResult[0].AdapterDesc.DedicatedVideoMemory;
SIZE_T DedicatedSystemMemory = vResult[0].AdapterDesc.DedicatedSystemMemory;
SIZE_T SharedSystemMemory = vResult[0].AdapterDesc.SharedSystemMemory;
MEMORYSTATUSEX memStatus = { 0 };
memStatus.dwLength = sizeof(MEMORYSTATUSEX);
double lfFrequency = 0.0f;
while (true)
{
::GlobalMemoryStatusEx(&memStatus);
ConsoleClear();
::SetConsoleCursorPosition(::GetStdHandle(STD_OUTPUT_HANDLE), { 0, 0 });
//获取CPU占用率
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_CPU_UTILITY), PDH_FMT_DOUBLE, &value))
{
ConsoleOutput(_T("CPU 利用率: %.2lf%% "), value.doubleValue);
//ConsoleOutput(_T("\r\n"));
}
//获取CPU性能
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_CPU_FREQUENCY), PDH_FMT_DOUBLE, &value))
{
lfFrequency = value.doubleValue;
}
//获取CPU性能
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_CPU_PERFORMANCE), PDH_FMT_DOUBLE, &value))
{
ConsoleOutput(_T("当前主频: %.2lf Ghz "), value.doubleValue / 100.0f * lfFrequency / 1000.0f);
ConsoleOutput(_T("基准频率: %.2lf Ghz"), lfFrequency / 1000.0f);
ConsoleOutput(_T("\r\n"));
}
//物理内存
ConsoleOutput(_T("内存: %.1lf"), FormatByteSize(memStatus.ullTotalPhys - memStatus.ullAvailPhys, eUnitType::eUT_B, eUnitType::eUT_GB).value);
ConsoleOutput(_T("/"));
ConsoleOutput(_T("%s "), FormatByteSize(memStatus.ullTotalPhys, eUnitType::eUT_B, eUnitType::eUT_GB).strOutput.c_str());
ConsoleOutput(_T("%.2lf%%"), (double)(memStatus.ullTotalPhys - memStatus.ullAvailPhys) * 100 /(double)memStatus.ullTotalPhys);
ConsoleOutput(_T("\r\n"));
//获取GPU占用率
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_GPU_UTILITY), PDH_FMT_DOUBLE, &value))
{
ConsoleOutput(_T("GPU 利用率: %.1lf%%"), value.doubleValue);
ConsoleOutput(_T("\r\n"));
}
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_GPU_MEMORY_DEDICATED_USAGE_UTILITY), PDH_FMT_DOUBLE, &value))
{
ConsoleOutput(_T("专用 GPU 内存: %0.1lf"), FormatByteSize(value.doubleValue, eUnitType::eUT_B, eUnitType::eUT_GB).value);
ConsoleOutput(_T("/%s"), FormatByteSize(DedicatedVideoMemory).strOutput.c_str());
ConsoleOutput(_T("\r\n"));
}
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_GPU_MEMORY_SHARE_USAGE_UTILITY), PDH_FMT_DOUBLE, &value))
{
ConsoleOutput(_T("共享 GPU 内存: %0.1lf"), FormatByteSize(value.doubleValue, eUnitType::eUT_B, eUnitType::eUT_GB).value);
ConsoleOutput(_T("/%s"), FormatByteSize(SharedSystemMemory).strOutput.c_str());
ConsoleOutput(_T("\r\n"));
}
//获取硬盘读速度
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_DISK_READ_RATE), PDH_FMT_DOUBLE, &value))
{
ConsoleOutput(_T("磁盘 读取: %s/s "), FormatByteSize(value.doubleValue).strOutput.c_str());
}
//获取硬盘写速度
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_DISK_WRITE_RATE), PDH_FMT_DOUBLE, &value))
{
ConsoleOutput(_T("磁盘 写入: %s/s"), FormatByteSize(value.doubleValue).strOutput.c_str());
ConsoleOutput(_T("\r\n"));
}
//获取网络读速度
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_NETWORK_RECV_RATE), PDH_FMT_DOUBLE, &value))
{
ConsoleOutput(_T("网络 接收: %sps "), FormatByteSize(value.doubleValue).strOutput.c_str());
}
//获取网络写速度
if (perfmon.GetFormattedCounterArray(_T(PERFM_PATH_NETWORK_SENT_RATE), PDH_FMT_DOUBLE, &value))
{
ConsoleOutput(_T("网络 发送: %sps"), FormatByteSize(value.doubleValue).strOutput.c_str());
ConsoleOutput(_T("\r\n"));
}
ConsoleOutput(_T("\r\n"));
::Sleep(1000);
}
return 0;
}
void ConsoleOutput(LPCTSTR pFormat, ...)
{
size_t nCchCount = MAX_PATH;
_tstring strResult(nCchCount, 0);
va_list args;
va_start(args, pFormat);
do
{
//格式化输出字符串
int nSize = _vsntprintf_s(&strResult[0], nCchCount, _TRUNCATE, pFormat, args);
if (-1 != nSize)
{
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFOEX ScreenBuffer = { 0 };
ScreenBuffer.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
::GetConsoleScreenBufferInfoEx(console, &ScreenBuffer);
// 如果在行开头写入文本, 则先清空当前行内容
if (0 == ScreenBuffer.dwCursorPosition.X)
{
DWORD dwNumberOfCharsWritten = 0;
::FillConsoleOutputCharacter(
console,
_T(' '),
ScreenBuffer.dwMaximumWindowSize.X,
{ 0, ScreenBuffer.dwCursorPosition.Y },
&dwNumberOfCharsWritten
);
}
::WriteConsole(console, strResult.c_str(), nSize, NULL, NULL);
break;
}
//缓冲大小超限终止
if (nCchCount >= INT32_MAX)
{
break;
}
//重新分配缓冲
nCchCount *= 2;
strResult.resize(nCchCount);
} while (true);
va_end(args);
}
void ConsoleClear()
{
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFOEX ScreenBuffer = { 0 };
ScreenBuffer.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
::GetConsoleScreenBufferInfoEx(console, &ScreenBuffer);
for (SHORT i = ScreenBuffer.dwCursorPosition.Y; i < ScreenBuffer.dwMaximumWindowSize.Y; i++)
{
DWORD dwNumberOfCharsWritten = 0;
::FillConsoleOutputCharacter(
console,
_T(' '),
ScreenBuffer.dwMaximumWindowSize.X,
{ 0, ScreenBuffer.dwCursorPosition.Y },
&dwNumberOfCharsWritten
);
}
}
DATA_UNIT_INFO FormatByteSize(
double nBytesSize,
eUnitType eSrcUnit/* = eUnitType::eUT_Auto*/,
eUnitType eDestUnit/* = eUnitType::eUT_Auto*/,
bool fHasUnits/* = true*/,
bool fSpace/* = true*/,
int nInteger/* = 1*/,
int nPrecision/* = 3*/
)
{
TCHAR szFormatBuf[MAX_PATH] = { 0 };
TCHAR szResultBuf[MAX_PATH] = { 0 };
DATA_UNIT_INFO dataUnitInfo;
bool fSrcBit = false;
bool fDestBit = false;
LPCTSTR strUnitByteName[] = {
_T("B"),
_T("KB"),
_T("MB"),
_T("GB"),
_T("TB"),
_T("PB"),
_T("EB"),
_T("ZB"),
_T("YB"),
_T("BB"),
_T("NB"),
_T("DB"),
_T("CB"),
_T("XB"),
};
LPCTSTR strUnitBitName[] = {
_T("b"),
_T("Kb"),
_T("Mb"),
_T("Gb"),
_T("Tb"),
_T("Pb"),
_T("Eb"),
_T("Zb"),
_T("Yb"),
_T("Bb"),
_T("Nb"),
_T("Db"),
_T("Cb"),
_T("Xb"),
};
// 原始单位 比特 -> 字节
if (eSrcUnit >= eUnitType::eUT_b && eSrcUnit < eUnitType::eUT_B)
{
fSrcBit = true;
eSrcUnit = (eUnitType)(eSrcUnit + (eUnitType::eUT_B - eUnitType::eUT_b));
}
// 目标单位 比特 -> 字节
if (eDestUnit >= eUnitType::eUT_b && eDestUnit < eUnitType::eUT_B)
{
fDestBit = true;
eDestUnit = (eUnitType)(eDestUnit + (eUnitType::eUT_B - eUnitType::eUT_b));
}
// 原始单位转换
for (int i = eUnitType::eUT_B; i < eSrcUnit; i++)
{
nBytesSize *= 1024.0f;
}
// 自动
int nUnitTypeIndex = eUnitType::eUT_B;
if (eUnitType::eUT_Auto == eDestUnit)
{
double nCurUnitSize = 1.0f;
double nNextUnitSize = 1024.0f;
int nUnitTypeMaxIndex = eUnitType::eUT_Max - 1;
for (int i = 0; i < _countof(strUnitByteName) && nUnitTypeIndex < nUnitTypeMaxIndex; i++)
{
if ((nBytesSize >= nCurUnitSize && nBytesSize < nNextUnitSize) || 0 == nNextUnitSize || 0 == nBytesSize)
{
break;
}
nCurUnitSize *= 1024.0f;
nNextUnitSize *= 1024.0f;
nUnitTypeIndex++;
}
eDestUnit = (eUnitType)nUnitTypeIndex;
}
{
::_stprintf_s(szFormatBuf, _countof(szFormatBuf), _T("%%%d.%dlf"), nInteger + nPrecision + 1, nPrecision);
double fUnitSize = 1.0f;
for (int i = eUnitType::eUT_B; i < eDestUnit; i++)
{
fUnitSize *= 1024.0f;
}
if (fSrcBit)
{
fUnitSize *= 8.0f;
}
if (fDestBit)
{
nBytesSize *= 8.0f;
}
double lfResult = nBytesSize / fUnitSize;
::_stprintf_s(szResultBuf, _countof(szResultBuf), szFormatBuf, lfResult);
dataUnitInfo.strOutput = szResultBuf;
dataUnitInfo.value = lfResult;
if (fHasUnits)
{
if (fSpace)
{
dataUnitInfo.strOutput += _T(" ");
}
if (fDestBit)
{
dataUnitInfo.strOutput += strUnitBitName[eDestUnit - eUnitType::eUT_B];
dataUnitInfo.strUnitStr = strUnitBitName[eDestUnit - eUnitType::eUT_B];
dataUnitInfo.eUnit = (eUnitType)(eDestUnit + (eUnitType::eUT_B - eUnitType::eUT_b));
}
else
{
dataUnitInfo.strOutput += strUnitByteName[eDestUnit - eUnitType::eUT_B];
dataUnitInfo.strUnitStr = strUnitByteName[eDestUnit - eUnitType::eUT_B];
dataUnitInfo.eUnit = eDestUnit;
}
}
}
return dataUnitInfo;
}
仓库
https://gitee.com/flame_cyclone/c_device_utils