|
3 | 3 |
|
4 | 4 | #include "util/mallocHelper.h"
|
5 | 5 |
|
| 6 | +#if !FF_ENABLE_CPUUSAGE_PERFLIB |
6 | 7 | #include <ntstatus.h>
|
7 | 8 | #include <winternl.h>
|
8 | 9 |
|
@@ -35,3 +36,111 @@ const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
|
35 | 36 |
|
36 | 37 | return NULL;
|
37 | 38 | }
|
| 39 | +#else |
| 40 | +#include <windows.h> |
| 41 | +#include "util/windows/perflib_.h" |
| 42 | +#include <wchar.h> |
| 43 | + |
| 44 | +static inline void ffPerfCloseQueryHandle(HANDLE* phQuery) |
| 45 | +{ |
| 46 | + if (*phQuery != NULL) |
| 47 | + { |
| 48 | + PerfCloseQueryHandle(*phQuery); |
| 49 | + *phQuery = NULL; |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +const char* ffGetCpuUsageInfo(FFlist* cpuTimes) |
| 54 | +{ |
| 55 | + struct FFPerfQuerySpec |
| 56 | + { |
| 57 | + PERF_COUNTER_IDENTIFIER Identifier; |
| 58 | + WCHAR Name[16]; |
| 59 | + } querySpec = { |
| 60 | + .Identifier = { |
| 61 | + // Processor Information GUID |
| 62 | + // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers\{383487a6-3676-4870-a4e7-d45b30c35629}\{b4fc721a-0378-476f-89ba-a5a79f810b36} |
| 63 | + .CounterSetGuid = { 0xb4fc721a, 0x0378, 0x476f, {0x89, 0xba, 0xa5, 0xa7, 0x9f, 0x81, 0x0b, 0x36} }, |
| 64 | + .Size = sizeof(querySpec), |
| 65 | + .CounterId = PERF_WILDCARD_COUNTER, // https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data |
| 66 | + .InstanceId = PERF_WILDCARD_COUNTER, |
| 67 | + }, |
| 68 | + .Name = PERF_WILDCARD_INSTANCE, |
| 69 | + }; |
| 70 | + |
| 71 | + __attribute__((__cleanup__(ffPerfCloseQueryHandle))) |
| 72 | + HANDLE hQuery = NULL; |
| 73 | + |
| 74 | + if (PerfOpenQueryHandle(NULL, &hQuery) != ERROR_SUCCESS) |
| 75 | + return "PerfOpenQueryHandle() failed"; |
| 76 | + |
| 77 | + if (PerfAddCounters(hQuery, &querySpec.Identifier, sizeof(querySpec)) != ERROR_SUCCESS) |
| 78 | + return "PerfAddCounters() failed"; |
| 79 | + |
| 80 | + if (querySpec.Identifier.Status != ERROR_SUCCESS) |
| 81 | + return "PerfAddCounters() reports invalid identifier"; |
| 82 | + |
| 83 | + DWORD dataSize = 0; |
| 84 | + if (PerfQueryCounterData(hQuery, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY) |
| 85 | + return "PerfQueryCounterData(NULL) failed"; |
| 86 | + |
| 87 | + if (dataSize <= sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER)) |
| 88 | + return "instance doesn't exist"; |
| 89 | + |
| 90 | + FF_AUTO_FREE PERF_DATA_HEADER* const pDataHeader = (PERF_DATA_HEADER*)malloc(dataSize); |
| 91 | + if (PerfQueryCounterData(hQuery, pDataHeader, dataSize, &dataSize) != ERROR_SUCCESS) |
| 92 | + return "PerfQueryCounterData(pDataHeader) failed"; |
| 93 | + |
| 94 | + PERF_COUNTER_HEADER* pCounterHeader = (PERF_COUNTER_HEADER*)(pDataHeader + 1); |
| 95 | + if (pCounterHeader->dwType != PERF_COUNTERSET) |
| 96 | + return "Invalid counter type"; |
| 97 | + |
| 98 | + PERF_MULTI_COUNTERS* pMultiCounters = (PERF_MULTI_COUNTERS*)(pCounterHeader + 1); |
| 99 | + if (pMultiCounters->dwCounters == 0) |
| 100 | + return "No CPU counters found"; |
| 101 | + |
| 102 | + PERF_MULTI_INSTANCES* pMultiInstances = (PERF_MULTI_INSTANCES*)((BYTE*)pMultiCounters + pMultiCounters->dwSize); |
| 103 | + if (pMultiInstances->dwInstances == 0) |
| 104 | + return "No CPU instances found"; |
| 105 | + |
| 106 | + PERF_INSTANCE_HEADER* pInstanceHeader = (PERF_INSTANCE_HEADER*)(pMultiInstances + 1); |
| 107 | + for (DWORD iInstance = 0; iInstance < pMultiInstances->dwInstances; ++iInstance) |
| 108 | + { |
| 109 | + const wchar_t* instanceName = (const wchar_t*)((BYTE*)pInstanceHeader + sizeof(*pInstanceHeader)); |
| 110 | + |
| 111 | + PERF_COUNTER_DATA* pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pInstanceHeader + pInstanceHeader->Size); |
| 112 | + |
| 113 | + uint64_t processorUtility = UINT64_MAX, utilityBase = UINT64_MAX; |
| 114 | + for (ULONG iCounter = 0; iCounter != pMultiCounters->dwCounters; iCounter++) |
| 115 | + { |
| 116 | + DWORD* pCounterIds = (DWORD*)(pMultiCounters + 1); |
| 117 | + // https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data |
| 118 | + switch (pCounterIds[iCounter]) { |
| 119 | + case 26: // % Processor Utility (#26, Type=PERF_AVERAGE_BULK) |
| 120 | + assert(pCounterData->dwDataSize == sizeof(uint64_t)); |
| 121 | + processorUtility = *(uint64_t*)(pCounterData + 1); |
| 122 | + break; |
| 123 | + case 27: // % Utility Base (#27, Type=PERF_AVERAGE_BASE) |
| 124 | + assert(pCounterData->dwDataSize == sizeof(uint32_t)); |
| 125 | + utilityBase = *(uint32_t*)(pCounterData + 1) * 100LLU; |
| 126 | + break; |
| 127 | + } |
| 128 | + |
| 129 | + pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pCounterData + pCounterData->dwSize); |
| 130 | + } |
| 131 | + |
| 132 | + if (wcschr(instanceName, L'_') == NULL /* ignore `_Total` */ && processorUtility != UINT64_MAX && utilityBase != UINT64_MAX) |
| 133 | + { |
| 134 | + FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes); |
| 135 | + *info = (FFCpuUsageInfo) { |
| 136 | + .inUseAll = processorUtility, |
| 137 | + .totalAll = utilityBase, |
| 138 | + }; |
| 139 | + } |
| 140 | + |
| 141 | + pInstanceHeader = (PERF_INSTANCE_HEADER*)pCounterData; |
| 142 | + } |
| 143 | + |
| 144 | + return NULL; |
| 145 | +} |
| 146 | +#endif |
0 commit comments