Source
/* Check if GetActiveProcessorGroupCount() is available. See comments in get_cpu_num_win32() for details. */
/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/
/* shortcut to avoid extra verbosity */
typedef PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX PSYS_LPI_EX;
/* pointer to GetLogicalProcessorInformationEx(), it's not guaranteed to be available */
typedef BOOL (WINAPI *GETLPIEX)(LOGICAL_PROCESSOR_RELATIONSHIP, PSYS_LPI_EX, PDWORD);
static GETLPIEX get_lpiex;
/******************************************************************************
* *
* Purpose: finds number of active logical CPUs *
* *
* Return value: number of CPUs or 0 on failure *
* *
******************************************************************************/
int get_cpu_num_win32(void)
{
/* pointer to GetActiveProcessorCount() */
typedef DWORD (WINAPI *GETACTIVEPC)(WORD);
ZBX_THREAD_LOCAL static GETACTIVEPC get_act;
SYSTEM_INFO sysInfo;
PSYS_LPI_EX buffer = NULL;
int cpu_count = 0;
/* The rationale for checking dynamically if specific functions are implemented */
/* in kernel32.lib is because these may not be available in certain Windows versions. */
/* E.g. GetActiveProcessorCount() is available from Windows 7 onward (and not in Windows Vista or XP) */
/* We can't resolve this using conditional compilation unless we release multiple agents */
/* targeting different sets of Windows APIs. */
/* First, let's try GetLogicalProcessorInformationEx() method. It's the most reliable way */
/* because it counts logical CPUs (aka threads) regardless of whether the application is */
/* 32 or 64-bit. GetActiveProcessorCount() may return incorrect value (e.g. 64 CPUs for systems */
/* with 128 CPUs) if executed under WoW64. */
if (NULL == get_lpiex)
{
get_lpiex = (GETLPIEX)GetProcAddress(GetModuleHandle(L"kernel32.dll"),
"GetLogicalProcessorInformationEx");
}
if (NULL != get_lpiex)
{
DWORD buffer_length = 0;
/* first run with empty arguments to figure the buffer length */
if (get_lpiex(RelationProcessorCore, NULL, &buffer_length) ||
ERROR_INSUFFICIENT_BUFFER != GetLastError())
{
goto fallback;
}
buffer = (PSYS_LPI_EX)zbx_malloc(buffer, (size_t)buffer_length);
if (get_lpiex(RelationProcessorCore, buffer, &buffer_length))
{
PSYS_LPI_EX ptr;
for (unsigned int i = 0; i < buffer_length; i += (unsigned int)ptr->Size)
{
ptr = (PSYS_LPI_EX)((PBYTE)buffer + i);
for (WORD group = 0; group < ptr->Processor.GroupCount; group++)
{
for (KAFFINITY mask = ptr->Processor.GroupMask[group].Mask; mask != 0;
mask >>= 1)
{
cpu_count += mask & 1;
}
}