2023-03-17 17:56:31 +08:00
|
|
|
#include "detection/cpuusage/cpuusage.h"
|
2026-01-06 09:48:38 +08:00
|
|
|
#include "common/mallocHelper.h"
|
2026-03-07 09:24:38 +08:00
|
|
|
#include "common/debug.h"
|
2022-12-16 00:14:21 +08:00
|
|
|
|
|
|
|
|
#include <ntstatus.h>
|
2025-07-02 19:32:18 +08:00
|
|
|
#include <windows.h>
|
|
|
|
|
#include <wchar.h>
|
2026-01-06 09:48:38 +08:00
|
|
|
#include "common/windows/perflib_.h"
|
2026-03-07 09:24:38 +08:00
|
|
|
#include "common/windows/nt.h"
|
2022-12-16 00:14:21 +08:00
|
|
|
|
2025-07-02 19:32:18 +08:00
|
|
|
static const char* getInfoByNqsi(FFlist* cpuTimes)
|
2022-12-16 00:14:21 +08:00
|
|
|
{
|
|
|
|
|
ULONG size = 0;
|
|
|
|
|
if(NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL, 0, &size) != STATUS_INFO_LENGTH_MISMATCH)
|
|
|
|
|
return "NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL) failed";
|
|
|
|
|
|
|
|
|
|
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* FF_AUTO_FREE pinfo = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*)malloc(size);
|
|
|
|
|
if(!NT_SUCCESS(NtQuerySystemInformation(SystemProcessorPerformanceInformation, pinfo, size, &size)))
|
|
|
|
|
return "NtQuerySystemInformation(SystemProcessorPerformanceInformation, size) failed";
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < size / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); ++i)
|
|
|
|
|
{
|
|
|
|
|
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* coreInfo = pinfo + i;
|
|
|
|
|
|
2025-06-30 19:37:04 +08:00
|
|
|
// KernelTime includes IdleTime and DpcTime.
|
2022-12-16 00:14:21 +08:00
|
|
|
coreInfo->KernelTime.QuadPart -= coreInfo->IdleTime.QuadPart;
|
|
|
|
|
|
2023-10-10 16:00:23 +08:00
|
|
|
uint64_t inUse = (uint64_t) (coreInfo->UserTime.QuadPart + coreInfo->KernelTime.QuadPart);
|
|
|
|
|
uint64_t total = inUse + (uint64_t)coreInfo->IdleTime.QuadPart;
|
|
|
|
|
|
|
|
|
|
FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes);
|
|
|
|
|
*info = (FFCpuUsageInfo) {
|
2024-06-17 22:51:02 +08:00
|
|
|
.inUseAll = inUse,
|
|
|
|
|
.totalAll = total,
|
2023-10-10 16:00:23 +08:00
|
|
|
};
|
2022-12-16 00:14:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2025-07-01 20:53:46 +08:00
|
|
|
|
2025-07-02 19:32:18 +08:00
|
|
|
static const char* getInfoByPerflib(FFlist* cpuTimes)
|
2025-07-01 20:53:46 +08:00
|
|
|
{
|
2025-07-02 19:32:18 +08:00
|
|
|
static HANDLE hQuery = NULL;
|
|
|
|
|
|
|
|
|
|
if (hQuery == NULL)
|
2025-07-01 20:53:46 +08:00
|
|
|
{
|
2025-07-02 19:32:18 +08:00
|
|
|
struct FFPerfQuerySpec
|
|
|
|
|
{
|
|
|
|
|
PERF_COUNTER_IDENTIFIER Identifier;
|
|
|
|
|
WCHAR Name[16];
|
|
|
|
|
} querySpec = {
|
|
|
|
|
.Identifier = {
|
|
|
|
|
// Processor Information GUID
|
|
|
|
|
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers\{383487a6-3676-4870-a4e7-d45b30c35629}\{b4fc721a-0378-476f-89ba-a5a79f810b36}
|
|
|
|
|
.CounterSetGuid = { 0xb4fc721a, 0x0378, 0x476f, {0x89, 0xba, 0xa5, 0xa7, 0x9f, 0x81, 0x0b, 0x36} },
|
|
|
|
|
.Size = sizeof(querySpec),
|
|
|
|
|
.CounterId = PERF_WILDCARD_COUNTER, // https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data
|
|
|
|
|
.InstanceId = PERF_WILDCARD_COUNTER,
|
|
|
|
|
},
|
|
|
|
|
.Name = PERF_WILDCARD_INSTANCE,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (PerfOpenQueryHandle(NULL, &hQuery) != ERROR_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
PerfCloseQueryHandle(hQuery);
|
|
|
|
|
hQuery = INVALID_HANDLE_VALUE;
|
|
|
|
|
return "PerfOpenQueryHandle() failed";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PerfAddCounters(hQuery, &querySpec.Identifier, sizeof(querySpec)) != ERROR_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
PerfCloseQueryHandle(hQuery);
|
|
|
|
|
hQuery = INVALID_HANDLE_VALUE;
|
|
|
|
|
return "PerfAddCounters() failed";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (querySpec.Identifier.Status != ERROR_SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
PerfCloseQueryHandle(hQuery);
|
|
|
|
|
hQuery = INVALID_HANDLE_VALUE;
|
|
|
|
|
return "PerfAddCounters() reports invalid identifier";
|
|
|
|
|
}
|
2025-07-01 20:53:46 +08:00
|
|
|
}
|
|
|
|
|
|
2025-07-02 19:32:18 +08:00
|
|
|
if (hQuery == INVALID_HANDLE_VALUE)
|
|
|
|
|
return "Init hQuery failed";
|
2025-07-01 20:53:46 +08:00
|
|
|
|
|
|
|
|
DWORD dataSize = 0;
|
|
|
|
|
if (PerfQueryCounterData(hQuery, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY)
|
|
|
|
|
return "PerfQueryCounterData(NULL) failed";
|
|
|
|
|
|
|
|
|
|
if (dataSize <= sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER))
|
|
|
|
|
return "instance doesn't exist";
|
|
|
|
|
|
|
|
|
|
FF_AUTO_FREE PERF_DATA_HEADER* const pDataHeader = (PERF_DATA_HEADER*)malloc(dataSize);
|
|
|
|
|
if (PerfQueryCounterData(hQuery, pDataHeader, dataSize, &dataSize) != ERROR_SUCCESS)
|
|
|
|
|
return "PerfQueryCounterData(pDataHeader) failed";
|
|
|
|
|
|
|
|
|
|
PERF_COUNTER_HEADER* pCounterHeader = (PERF_COUNTER_HEADER*)(pDataHeader + 1);
|
|
|
|
|
if (pCounterHeader->dwType != PERF_COUNTERSET)
|
|
|
|
|
return "Invalid counter type";
|
|
|
|
|
|
|
|
|
|
PERF_MULTI_COUNTERS* pMultiCounters = (PERF_MULTI_COUNTERS*)(pCounterHeader + 1);
|
|
|
|
|
if (pMultiCounters->dwCounters == 0)
|
|
|
|
|
return "No CPU counters found";
|
|
|
|
|
|
|
|
|
|
PERF_MULTI_INSTANCES* pMultiInstances = (PERF_MULTI_INSTANCES*)((BYTE*)pMultiCounters + pMultiCounters->dwSize);
|
|
|
|
|
if (pMultiInstances->dwInstances == 0)
|
|
|
|
|
return "No CPU instances found";
|
|
|
|
|
|
|
|
|
|
PERF_INSTANCE_HEADER* pInstanceHeader = (PERF_INSTANCE_HEADER*)(pMultiInstances + 1);
|
|
|
|
|
for (DWORD iInstance = 0; iInstance < pMultiInstances->dwInstances; ++iInstance)
|
|
|
|
|
{
|
|
|
|
|
const wchar_t* instanceName = (const wchar_t*)((BYTE*)pInstanceHeader + sizeof(*pInstanceHeader));
|
|
|
|
|
|
|
|
|
|
PERF_COUNTER_DATA* pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pInstanceHeader + pInstanceHeader->Size);
|
|
|
|
|
|
|
|
|
|
uint64_t processorUtility = UINT64_MAX, utilityBase = UINT64_MAX;
|
|
|
|
|
for (ULONG iCounter = 0; iCounter != pMultiCounters->dwCounters; iCounter++)
|
|
|
|
|
{
|
|
|
|
|
DWORD* pCounterIds = (DWORD*)(pMultiCounters + 1);
|
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data
|
|
|
|
|
switch (pCounterIds[iCounter]) {
|
|
|
|
|
case 26: // % Processor Utility (#26, Type=PERF_AVERAGE_BULK)
|
|
|
|
|
assert(pCounterData->dwDataSize == sizeof(uint64_t));
|
|
|
|
|
processorUtility = *(uint64_t*)(pCounterData + 1);
|
|
|
|
|
break;
|
|
|
|
|
case 27: // % Utility Base (#27, Type=PERF_AVERAGE_BASE)
|
|
|
|
|
assert(pCounterData->dwDataSize == sizeof(uint32_t));
|
|
|
|
|
utilityBase = *(uint32_t*)(pCounterData + 1) * 100LLU;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pCounterData + pCounterData->dwSize);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-01 22:08:08 +08:00
|
|
|
if (wcschr(instanceName, L'_') == NULL /* ignore `_Total` */)
|
2025-07-01 20:53:46 +08:00
|
|
|
{
|
2025-07-01 22:08:08 +08:00
|
|
|
if (processorUtility == UINT64_MAX)
|
|
|
|
|
return "Counter \"% Processor Utility\" are not supported";
|
|
|
|
|
|
2025-07-01 20:53:46 +08:00
|
|
|
FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes);
|
|
|
|
|
*info = (FFCpuUsageInfo) {
|
|
|
|
|
.inUseAll = processorUtility,
|
|
|
|
|
.totalAll = utilityBase,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pInstanceHeader = (PERF_INSTANCE_HEADER*)pCounterData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2025-07-02 19:32:18 +08:00
|
|
|
|
|
|
|
|
const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
|
|
|
|
|
{
|
2026-03-07 09:24:38 +08:00
|
|
|
const char* error = NULL;
|
|
|
|
|
|
|
|
|
|
if (ffIsWindows10OrGreater())
|
2025-07-02 19:32:18 +08:00
|
|
|
{
|
2026-03-07 09:24:38 +08:00
|
|
|
error = getInfoByPerflib(cpuTimes);
|
|
|
|
|
FF_DEBUG("Get CPU usage info by Perflib: %s", error ?: "success");
|
|
|
|
|
if (!error) return NULL;
|
2025-07-02 19:32:18 +08:00
|
|
|
ffListClear(cpuTimes);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 09:24:38 +08:00
|
|
|
error = getInfoByNqsi(cpuTimes);
|
|
|
|
|
FF_DEBUG("Get CPU usage info by NtQuerySystemInformation: %s", error ?: "success");
|
|
|
|
|
return error;
|
2025-07-02 19:32:18 +08:00
|
|
|
}
|