Home > OS >  Using GetLogicalProcessorInformationEx() in Python via ctypes on a two socket system with more than
Using GetLogicalProcessorInformationEx() in Python via ctypes on a two socket system with more than

Time:10-25

So I'm trying to get processor groupings by socket. I've got a POC in C that does it, output is as follows.

[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,],[0,1,2,3,4,5,6,7,8,9,10,11,],]
[[12,13,14,15,16,17,18,19,20,21,22,23,],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,],]

And while I could just do something like the following, I'd much rather have code continuity and keep the project pure python. Plus this seemed like a good puzzle to solve for experiences sake.

socket_groups = eval(run_cmd_popen("get_logical_processor_information.exe", return_output=True).strip("\n"))

What I have thus far works, but only on one socket systems, for two socket systems, the second socket doesn't seem to have any data, and prints the following.

[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,][0,1,2,3,4,5,6,7,8,9,10,11,]]
[]

Here is what I have so far:

import os
import math
import platform
from ctypes.wintypes import BYTE, INT, WORD, DWORD, BOOL, PDWORD, UINT
from ctypes import Structure, Union, WinDLL, c_uint64, POINTER, byref, get_last_error, WinError

from wmi import WMI


if platform.machine().endswith('64'):
    KAFFINITY = c_uint64
else:
    KAFFINITY = UINT

wmi = WMI()
NUM_SOCKETS = 0
for wmi_obj in wmi.query(f'SELECT * FROM Win32_processor '):
    if wmi_obj is not None:
        NUM_SOCKETS  = 1

NUM_CPUS = os.cpu_count()
NUM_CPU_GROUPS = math.ceil(NUM_CPUS / 64)
ANYSIZE_ARRAY = NUM_CPU_GROUPS

ERROR_INSUFFICIENT_BUFFER = 122

RELATION_CACHE = 2
RELATION_NUMA_NODE = 1
RELATION_PROCESSOR_CORE = 0
RELATION_PROCESSOR_PACKAGE = 3
RELATION_GROUP = 4
RELATION_ALL = 0xffff


class _GROUP_AFFINITY(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_affinity
        typedef struct _GROUP_AFFINITY {
            KAFFINITY Mask;
            WORD      Group;
            WORD      Reserved[3];
        } GROUP_AFFINITY, *PGROUP_AFFINITY;
    '''
    _fields_ = (
        ('Mask', KAFFINITY),
        ('Group', WORD),
        ('Reserved', WORD * 3)
    )


class _PROCESSOR_GROUP_INFO(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_group_info
        typedef struct _PROCESSOR_GROUP_INFO {
            BYTE      MaximumProcessorCount;
            BYTE      ActiveProcessorCount;
            BYTE      Reserved[38];
            KAFFINITY ActiveProcessorMask;
        } PROCESSOR_GROUP_INFO, *PPROCESSOR_GROUP_INFO;
    '''
    _fields_ = (
        ('MaximumProcessorCount', BYTE),
        ('ActiveProcessorCount', BYTE),
        ('Reserved', BYTE * 38),
        ('ActiveProcessorMask', KAFFINITY),
    )


class _GROUP_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_relationship
        typedef struct _GROUP_RELATIONSHIP {
            WORD                 MaximumGroupCount;
            WORD                 ActiveGroupCount;
            BYTE                 Reserved[20];
            PROCESSOR_GROUP_INFO GroupInfo[ANYSIZE_ARRAY];
        } GROUP_RELATIONSHIP, *PGROUP_RELATIONSHIP;
    '''
    _fields_ = (
        ('MaximumGroupCount', WORD),
        ('ActiveGroupCount', WORD),
        ('Reserved', BYTE * 20),
        ('GroupInfo', _PROCESSOR_GROUP_INFO * ANYSIZE_ARRAY)
    )


class _DUMMYUNIONNAME_CACHE_RELATIONSHIP(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
        union {
            GROUP_AFFINITY GroupMask;
            GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
        } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('GroupMask', _GROUP_AFFINITY),
        ('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY)  # * number of groups
    )


class _CACHE_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
        typedef struct _CACHE_RELATIONSHIP {
            BYTE                 Level;
            BYTE                 Associativity;
            WORD                 LineSize;
            DWORD                CacheSize;
            PROCESSOR_CACHE_TYPE Type;
            BYTE                 Reserved[18];
            WORD                 GroupCount;
            union {
                GROUP_AFFINITY GroupMask;
                GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
            } DUMMYUNIONNAME;
        } CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP;
    '''
    _fields_ = (
        ('Level', BYTE),
        ('Associativity', BYTE),
        ('LineSize', WORD),
        ('CacheSize', DWORD),
        ('Type', INT),  # PROCESSOR_CACHE_TYPE is an Enum type, which, At least for GCC, is just a simple numeric type. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
        ('Reserved', BYTE * 18),
        ('GroupCount', WORD),
        ('DUMMYUNIONNAME ', _DUMMYUNIONNAME_CACHE_RELATIONSHIP)
    )


class _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
        union {
            GROUP_AFFINITY GroupMask;
            GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
        } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('GroupMask', _GROUP_AFFINITY),
        ('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY)  # * number of groups
    )


class _NUMA_NODE_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
        typedef struct _NUMA_NODE_RELATIONSHIP {
            DWORD NodeNumber;
            BYTE  Reserved[18];
            WORD  GroupCount;
            union {
                GROUP_AFFINITY GroupMask;
                GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
            } DUMMYUNIONNAME;
        } NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP;
    '''
    _fields_ = (
        ('NodeNumber', DWORD),
        ('Reserved', BYTE * 18),
        ('GroupCount', WORD),
        ('DUMMYUNIONNAME', _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP)
    )


class _PROCESSOR_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship
        typedef struct _PROCESSOR_RELATIONSHIP {
            BYTE           Flags;
            BYTE           EfficiencyClass;
            BYTE           Reserved[20];
            WORD           GroupCount;
            GROUP_AFFINITY GroupMask[ANYSIZE_ARRAY];
        } PROCESSOR_RELATIONSHIP, *PPROCESSOR_RELATIONSHIP;
    '''
    _fields_ = (
        ('Flags', BYTE),
        ('EfficiencyClass', BYTE),
        ('Reserved', BYTE * 20),
        ('GroupCount', WORD),
        ('GroupMask', _GROUP_AFFINITY * ANYSIZE_ARRAY)  # * number of groups
    )


class _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
            union {
                PROCESSOR_RELATIONSHIP Processor;
                NUMA_NODE_RELATIONSHIP NumaNode;
                CACHE_RELATIONSHIP     Cache;
                GROUP_RELATIONSHIP     Group;
            } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('Processor', _PROCESSOR_RELATIONSHIP),
        ('NumaNode', _NUMA_NODE_RELATIONSHIP),
        ('Cache', _CACHE_RELATIONSHIP),
        ('Group', _GROUP_RELATIONSHIP)
    )


class _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
        typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
            LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
            DWORD                          Size;
            union {
                PROCESSOR_RELATIONSHIP Processor;
                NUMA_NODE_RELATIONSHIP NumaNode;
                CACHE_RELATIONSHIP     Cache;
                GROUP_RELATIONSHIP     Group;
            } DUMMYUNIONNAME;
        } SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
    '''
    _anonymous_ = 'DUMMYUNIONNAME',
    _fields_ = (
        ('Relationship', INT),  # _LOGICAL_PROCESSOR_RELATIONSHIP is an Enum type, which, At least for GCC, is just a simple numeric type. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
        ('Size', DWORD),
        ('DUMMYUNIONNAME', _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
    )


def GetLogicalProcessorInformationEx(relation_type):
    '''https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
        BOOL GetLogicalProcessorInformationEx(
            [in]            LOGICAL_PROCESSOR_RELATIONSHIP           RelationshipType,
            [out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
            [in, out]       PDWORD                                   ReturnedLength
        );
    '''
    dll = WinDLL('kernel32', use_last_error=True)
    dll.GetLogicalProcessorInformationEx.argtypes = INT, POINTER(_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX), PDWORD
    dll.GetLogicalProcessorInformationEx.restype = BOOL

    byte_len = DWORD()

    # Call with null buffer to get required buffer size
    result = dll.GetLogicalProcessorInformationEx(relation_type, None, byref(byte_len))
    if (err := get_last_error()) != ERROR_INSUFFICIENT_BUFFER:
        raise WinError(err)

    # Allocate buffer
    allocated_structure = (_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * NUM_SOCKETS)()

    global byte_len_for_advance
    byte_len_for_advance = byte_len

    # Now do the call with the allocated buffer to fill it up
    result = dll.GetLogicalProcessorInformationEx(relation_type, allocated_structure, byref(byte_len))
    if not result:
        raise WinError(get_last_error())

    return allocated_structure


def _print_mask(mask):
    # print(f"{mask=}")
    digits = [int(x) for x in mask]

    group_core_list = []
    print("[", end='')
    core = 0
    for i in digits:
        if i == 1:
            print(f"{core},", end='')
            group_core_list.append(core)
        core  = 1
    print("]", end='')

    # print(group_core_list)


if __name__ == "__main__":
    enum_info = GetLogicalProcessorInformationEx(RELATION_PROCESSOR_PACKAGE)
    for p_info in enum_info:
        print("[", end='')
        for group_index in range(0, p_info.Processor.GroupCount):
            _print_mask(f"{p_info.Processor.GroupMask[group_index].Mask:8b}")
        print("]\n", end='')
    breakpoint

I think I need to do something with shifting a pointer to the next structure maybe? But I don't know how to go about that. Also I'm not solid on if _print_mask() will do exactly what I want when I get the second sockets info, but that is a later problem.

Any thought internet?


Edit

Thanks to Mark Tolonen for the answer/help!

Here is my working solution:

from typing import List, Dict
from ctypes.wintypes import BYTE, INT, WORD, DWORD, BOOL, PDWORD, WPARAM
from ctypes import Structure, Union, WinDLL, POINTER, byref, cast, get_last_error, WinError, c_char_p, create_string_buffer

KAFFINITY = WPARAM   # WPARAM is 32-bit or 64-bit unsigned depending on platform

ANYSIZE_ARRAY = 1

ERROR_INSUFFICIENT_BUFFER = 122

RELATION_CACHE = 2
RELATION_NUMA_NODE = 1
RELATION_PROCESSOR_CORE = 0
RELATION_PROCESSOR_PACKAGE = 3
RELATION_GROUP = 4
RELATION_ALL = 0xffff


class _GROUP_AFFINITY(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_affinity
        typedef struct _GROUP_AFFINITY {
            KAFFINITY Mask;
            WORD      Group;
            WORD      Reserved[3];
        } GROUP_AFFINITY, *PGROUP_AFFINITY;
    '''
    _fields_ = (
        ('Mask', KAFFINITY),
        ('Group', WORD),
        ('Reserved', WORD * 3)
    )


class _PROCESSOR_GROUP_INFO(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_group_info
        typedef struct _PROCESSOR_GROUP_INFO {
            BYTE      MaximumProcessorCount;
            BYTE      ActiveProcessorCount;
            BYTE      Reserved[38];
            KAFFINITY ActiveProcessorMask;
        } PROCESSOR_GROUP_INFO, *PPROCESSOR_GROUP_INFO;
    '''
    _fields_ = (
        ('MaximumProcessorCount', BYTE),
        ('ActiveProcessorCount', BYTE),
        ('Reserved', BYTE * 38),
        ('ActiveProcessorMask', KAFFINITY),
    )


class _GROUP_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_relationship
        typedef struct _GROUP_RELATIONSHIP {
            WORD                 MaximumGroupCount;
            WORD                 ActiveGroupCount;
            BYTE                 Reserved[20];
            PROCESSOR_GROUP_INFO GroupInfo[ANYSIZE_ARRAY];
        } GROUP_RELATIONSHIP, *PGROUP_RELATIONSHIP;
    '''
    _fields_ = (
        ('MaximumGroupCount', WORD),
        ('ActiveGroupCount', WORD),
        ('Reserved', BYTE * 20),
        ('GroupInfo', _PROCESSOR_GROUP_INFO * ANYSIZE_ARRAY)
    )


class _DUMMYUNIONNAME_CACHE_RELATIONSHIP(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
        union {
            GROUP_AFFINITY GroupMask;
            GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
        } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('GroupMask', _GROUP_AFFINITY),
        ('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY)  # * number of groups
    )


class _CACHE_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship
        typedef struct _CACHE_RELATIONSHIP {
            BYTE                 Level;
            BYTE                 Associativity;
            WORD                 LineSize;
            DWORD                CacheSize;
            PROCESSOR_CACHE_TYPE Type;
            BYTE                 Reserved[18];
            WORD                 GroupCount;
            union {
                GROUP_AFFINITY GroupMask;
                GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
            } DUMMYUNIONNAME;
        } CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP;
    '''
    _fields_ = (
        ('Level', BYTE),
        ('Associativity', BYTE),
        ('LineSize', WORD),
        ('CacheSize', DWORD),
        ('Type', INT),  # PROCESSOR_CACHE_TYPE is an Enum type, which, At least for GCC, is just a simple numeric type. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
        ('Reserved', BYTE * 18),
        ('GroupCount', WORD),
        ('DUMMYUNIONNAME ', _DUMMYUNIONNAME_CACHE_RELATIONSHIP)
    )


class _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
        union {
            GROUP_AFFINITY GroupMask;
            GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
        } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('GroupMask', _GROUP_AFFINITY),
        ('GroupMasks', _GROUP_AFFINITY * ANYSIZE_ARRAY)  # * number of groups
    )


class _NUMA_NODE_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship
        typedef struct _NUMA_NODE_RELATIONSHIP {
            DWORD NodeNumber;
            BYTE  Reserved[18];
            WORD  GroupCount;
            union {
                GROUP_AFFINITY GroupMask;
                GROUP_AFFINITY GroupMasks[ANYSIZE_ARRAY];
            } DUMMYUNIONNAME;
        } NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP;
    '''
    _fields_ = (
        ('NodeNumber', DWORD),
        ('Reserved', BYTE * 18),
        ('GroupCount', WORD),
        ('DUMMYUNIONNAME', _DUMMYUNIONNAME_NUMA_NODE_RELATIONSHIP)
    )


class _PROCESSOR_RELATIONSHIP(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship
        typedef struct _PROCESSOR_RELATIONSHIP {
            BYTE           Flags;
            BYTE           EfficiencyClass;
            BYTE           Reserved[20];
            WORD           GroupCount;
            GROUP_AFFINITY GroupMask[ANYSIZE_ARRAY];
        } PROCESSOR_RELATIONSHIP, *PPROCESSOR_RELATIONSHIP;
    '''
    _fields_ = (
        ('Flags', BYTE),
        ('EfficiencyClass', BYTE),
        ('Reserved', BYTE * 20),
        ('GroupCount', WORD),
        ('GroupMask', _GROUP_AFFINITY * ANYSIZE_ARRAY)  # * number of groups
    )


class _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Union):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
            union {
                PROCESSOR_RELATIONSHIP Processor;
                NUMA_NODE_RELATIONSHIP NumaNode;
                CACHE_RELATIONSHIP     Cache;
                GROUP_RELATIONSHIP     Group;
            } DUMMYUNIONNAME;
    '''
    _fields_ = (
        ('Processor', _PROCESSOR_RELATIONSHIP),
        ('NumaNode', _NUMA_NODE_RELATIONSHIP),
        ('Cache', _CACHE_RELATIONSHIP),
        ('Group', _GROUP_RELATIONSHIP)
    )


class _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX(Structure):
    '''https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
        typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
            LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
            DWORD                          Size;
            union {
                PROCESSOR_RELATIONSHIP Processor;
                NUMA_NODE_RELATIONSHIP NumaNode;
                CACHE_RELATIONSHIP     Cache;
                GROUP_RELATIONSHIP     Group;
            } DUMMYUNIONNAME;
        } SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
    '''
    _anonymous_ = 'DUMMYUNIONNAME',
    _fields_ = (
        ('Relationship', INT),  # _LOGICAL_PROCESSOR_RELATIONSHIP is an Enum type, which, At least for GCC, is just a simple numeric type. (https://stackoverflow.com/questions/1546355/using-enums-in-ctypes-structure/1546467#1546467)
        ('Size', DWORD),
        ('DUMMYUNIONNAME', _DUMMYUNIONNAME_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
    )


def _GetLogicalProcessorInformationEx(relation_type):
    '''https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
        BOOL GetLogicalProcessorInformationEx(
            [in]            LOGICAL_PROCESSOR_RELATIONSHIP           RelationshipType,
            [out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
            [in, out]       PDWORD                                   ReturnedLength
        );
    '''
    dll = WinDLL('kernel32', use_last_error=True)
    # manage the allocated buffer as a c_char_p for easier pointer manipulation
    dll.GetLogicalProcessorInformationEx.argtypes = INT, c_char_p, PDWORD
    dll.GetLogicalProcessorInformationEx.restype = BOOL

    byte_len = DWORD()

    # Call with null buffer to get required buffer size
    result = dll.GetLogicalProcessorInformationEx(relation_type, None, byref(byte_len))
    if (err := get_last_error()) != ERROR_INSUFFICIENT_BUFFER:
        raise WinError(err)

    # Allocate byte buffer
    buffer = create_string_buffer(byte_len.value)

    # Now do the call with the buffer to fill it up
    result = dll.GetLogicalProcessorInformationEx(relation_type, buffer, byref(byte_len))
    if not result:
        raise WinError(get_last_error())

    return buffer, byte_len.value   # return buffer and length


def get_group_affinty_mask_info() -> List[Dict[int, list]]:
    '''
    '''
    # The returned structure array can vary in element size, so walk the buffer by the size of its element.
    buffer, len = _GetLogicalProcessorInformationEx(RELATION_PROCESSOR_PACKAGE)
    offset = 0
    socket_group_infos = []
    while offset < len:
        # cast the current offset to the structure and extract contents
        processor_info = cast(byref(buffer, offset), POINTER(_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)).contents

        # print(f"{processor_info.Size=} {processor_info.Processor.GroupCount=}")

        # cast the group mask array to the correct size
        group_affinitys = cast(byref(processor_info.Processor.GroupMask), POINTER(_GROUP_AFFINITY * processor_info.Processor.GroupCount)).contents

        groups = {}
        for group_affinity in group_affinitys:
            # print(f"{group_affinity.Mask=:016X} {group_affinity.Group=}")

            group_cores = []
            core_index = 0
            # Convert the mask to the binary representation of an 8-digit, zero-padded on the left string.
            # Then turn that string of 1's and 0's into a list, and reverse it.
            for i in [int(x) for x in f"{group_affinity.Mask:8b}"][::-1]:
                if i == 1:
                    group_cores.append(core_index)
                core_index  = 1
            groups[group_affinity.Group] = group_cores

        socket_group_infos.append(groups)

        # advance by the size consumed
        offset  = processor_info.Size

    return socket_group_infos

CodePudding user response:

I only have a single core with 8 logical processors, so I can't verify I got everything right for your situation, but try this:

from ctypes.wintypes import BYTE, INT, WORD, DWORD, BOOL, PDWORD, UINT, WPARAM
from ctypes import (Structure, Union, WinDLL, c_uint64, POINTER, byref, cast,
                    get_last_error, WinError, c_char_p, create_string_buffer)

KAFFINITY = WPARAM   # WPARAM is 32-bit or 64-bit unsigned depending on platform
ANYSIZE_ARRAY = 1    # actual definition

#
# unchanged portion elided
#

def GetLogicalProcessorInformationEx(relation_type):
    '''https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
        BOOL GetLogicalProcessorInformationEx(
            [in]            LOGICAL_PROCESSOR_RELATIONSHIP           RelationshipType,
            [out, optional] PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
            [in, out]       PDWORD                                   ReturnedLength
        );
    '''
    dll = WinDLL('kernel32', use_last_error=True)
    # manage the allocated buffer as a c_char_p for easier pointer manipulation
    dll.GetLogicalProcessorInformationEx.argtypes = INT, c_char_p, PDWORD
    dll.GetLogicalProcessorInformationEx.restype = BOOL

    byte_len = DWORD()

    # Call with null buffer to get required buffer size
    result = dll.GetLogicalProcessorInformationEx(relation_type, None, byref(byte_len))
    if (err := get_last_error()) != ERROR_INSUFFICIENT_BUFFER:
        raise WinError(err)

    # Allocate byte buffer
    buffer = create_string_buffer(byte_len.value)

    # Now do the call with the buffer to fill it up
    result = dll.GetLogicalProcessorInformationEx(relation_type, buffer, byref(byte_len))
    if not result:
        raise WinError(get_last_error())

    return buffer,byte_len.value   # return buffer and length


if __name__ == "__main__":
    # The returned structure array can vary in element size,
    # so walk the buffer by the size of its element.
    buffer,len = GetLogicalProcessorInformationEx(RELATION_PROCESSOR_PACKAGE)
    offset = 0
    while offset < len:
        # cast the current offset to the structure and extract contents
        info = cast(byref(buffer,offset), POINTER(_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)).contents
        print(f'{info.Size=} {info.Processor.GroupCount=}')

        # cast the group mask array to the correct size
        gaffs = cast(byref(info.Processor.GroupMask),POINTER(_GROUP_AFFINITY * info.Processor.GroupCount)).contents
        for gaff in gaffs:
            print(f'{gaff.Mask=:016X} {gaff.Group=}')

        # advance by the size consumed
        offset  = info.Size

Output on my one core, 8 logical proc system:

info.Size=48 info.Processor.GroupCount=1
gaff.Mask=00000000000000FF gaff.Group=0

If this doesn't work for your complex system, post the returned buffer,len from my solution returned by your system and I'll fix it.

  • Related