Reputation: 31
I am trying to get some stats about the GPU. Mainly I would like to get the total VRAM and the amount in use, as well as the GPU utilization (would prefer the 3d core but will settle for total usage).
After messing around for a while I found that I could display all PDH counters using the following program.
#include <windows.h>
#include <iostream>
#include <pdh.h>
#include <pdhmsg.h>
#pragma comment(lib, "pdh.lib")
void browseCounters() {
PDH_BROWSE_DLG_CONFIG dlgConfig = {0};
dlgConfig.bIncludeInstanceIndex = FALSE;
dlgConfig.bSingleCounterPerAdd = TRUE;
dlgConfig.bSingleCounterPerDialog = TRUE;
dlgConfig.bLocalCountersOnly = FALSE;
dlgConfig.bWildCardInstances = TRUE;
dlgConfig.bHideDetailBox = TRUE;
dlgConfig.bInitializePath = FALSE;
dlgConfig.bDisableMachineSelection = FALSE;
dlgConfig.bIncludeCostlyObjects = FALSE;
dlgConfig.bShowObjectBrowser = FALSE;
dlgConfig.hWndOwner = NULL;
dlgConfig.szReturnPathBuffer = new char[PDH_MAX_COUNTER_PATH];
dlgConfig.cchReturnPathLength = PDH_MAX_COUNTER_PATH;
dlgConfig.pCallBack = NULL;
dlgConfig.dwCallBackArg = 0;
dlgConfig.CallBackStatus = ERROR_SUCCESS;
dlgConfig.dwDefaultDetailLevel = PERF_DETAIL_WIZARD;
// Create a non-const copy of the string literal
char *caption = new char[17];
strcpy(caption, "Select a counter");
dlgConfig.szDialogBoxCaption = caption;
PDH_STATUS status = PdhBrowseCounters(&dlgConfig);
if (status == ERROR_SUCCESS) {
std::wcout << L"Selected Counter: " << dlgConfig.szReturnPathBuffer << std::endl;
} else {
std::cout << "Error: " << status << std::endl;
}
delete[] dlgConfig.szReturnPathBuffer;
}
int main() {
browseCounters();
system("pause");
return 0;
}
After looking through the available counters I see a few that are exactly what I want. I see there is a GPU Engine tab. After selecting that and hitting ok I get the counter "\GPU Engine(*)\Utilization Percentage". Using this counter always returns 0 for utilization percentage. I have tried using different counters and putting my GPU name in the counter path but I can't get this to work right.
Here's the function I'm having trouble with. Main calls this function and the while loop prints the GPU utilization forever.
void printGPU() {
PDH_HQUERY gpuQuery;
PDH_HCOUNTER gpuCounter;
// Open a query
PdhOpenQueryW(NULL, 0, &gpuQuery);
// Replace "Your_GPU_Instance_Name" with the actual name of your GPU instance
wchar_t gpuCounterPath[MAX_PATH];
swprintf_s(gpuCounterPath, L"\\GPU Engine(*)\\Utilization Percentage");
PDH_STATUS pdhStatus = PdhAddCounterW(gpuQuery, gpuCounterPath, 0, &gpuCounter);
if (pdhStatus != ERROR_SUCCESS) {
std::cerr << "Error adding GPU counter: " << pdhStatus << std::endl;
return;
}
while (true) { // Adjust the loop condition based on your needs
// Collect data
PdhCollectQueryData(gpuQuery);
// Wait for a short period (e.g., 1 second)
Sleep(1000);
// Collect data again
PdhCollectQueryData(gpuQuery);
// Read and update GPU usage
PDH_FMT_COUNTERVALUE counterValue;
PdhGetFormattedCounterValue(gpuCounter, PDH_FMT_DOUBLE, NULL, &counterValue);
double gpuUsage = counterValue.doubleValue;
std::cout << "GPU Utilization: " << gpuUsage << "%" << std::endl;
}
// Close the query
PdhCloseQuery(gpuQuery);
}
I am doing the same thing with the CPU and for that it works correctly. I simply update the list of cores with their percent usage. This makes me suspect that the path is incorrect.
void GetCpuUsage() { // Function to get CPU usage percentage for each core
PDH_HQUERY coresQuery;
PDH_HCOUNTER counters[numCores]; // numcores is gotten earier in the code and is set to be the number of system cores
PDH_HCOUNTER totalCounter; // New counter for total CPU usage
// Open a query
PdhOpenQuery(NULL, 0, &coresQuery);
// Add counters for each CPU core
for (int i = 0; i < numCores; ++i) {
wchar_t counterPath[MAX_PATH];
swprintf_s(counterPath, L"\\Processor(%d)\\%% Processor Time", i);
// Convert wide string to narrow string
char narrowCounterPath[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, counterPath, -1, narrowCounterPath, MAX_PATH, NULL, NULL);
PdhAddCounter(coresQuery, narrowCounterPath, 0, &counters[i]);
}
// Add counter for total CPU usage
wchar_t totalCounterPath[MAX_PATH];
swprintf_s(totalCounterPath, L"\\Processor(_Total)\\%% Processor Time");
// Convert wide string to narrow string
char narrowTotalCounterPath[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, totalCounterPath, -1, narrowTotalCounterPath, MAX_PATH, NULL, NULL);
PdhAddCounter(coresQuery, narrowTotalCounterPath, 0, &totalCounter);
while(pollCPUusage) { // loop forever while main program is open
// Collect data
PdhCollectQueryData(coresQuery);
// Wait for a short period (e.g., 1 second)
Sleep(500);
// Collect data again
PdhCollectQueryData(coresQuery);
// critical section
{
// lock mutex
std::lock_guard<std::mutex> lock(coresMutex);
// Read and update CPU usage for each core
for (int i = 0; i < numCores; ++i) {
PDH_FMT_COUNTERVALUE counterValue;
PdhGetFormattedCounterValue(counters[i], PDH_FMT_DOUBLE, NULL, &counterValue);
cores[i] = counterValue.doubleValue;
//std::cout << "CPU Core " << i << " Usage: " << counterValue.doubleValue << "%" << std::endl;
}
// Read and update total CPU usage
PDH_FMT_COUNTERVALUE totalCounterValue;
PdhGetFormattedCounterValue(totalCounter, PDH_FMT_DOUBLE, NULL, &totalCounterValue);
totalPctUsage = totalCounterValue.doubleValue;
} // end of critical section mutex auto unlocks at this point
}
// Close the query
PdhCloseQuery(coresQuery);
}
Any help is greatly appreciated.
Upvotes: 1
Views: 722
Reputation: 61
You GPU counter has a wildcard (*) in the instance name. So you need to use the PdhGetFormattedCounterArray function instead of PdhGetFormattedCounterValue (https://learn.microsoft.com/en-us/windows/win32/api/pdh/nf-pdh-pdhgetformattedcounterarrayw). To calculate total GPU utilization, loop through all the entries in the array returned and sum them up.
Upvotes: 1