diff --git a/pkg/gohai/cpu/cpu_test.go b/pkg/gohai/cpu/cpu_test.go index c7f13b7b32a4..38e5c08f703c 100644 --- a/pkg/gohai/cpu/cpu_test.go +++ b/pkg/gohai/cpu/cpu_test.go @@ -2,12 +2,15 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2014-present Datadog, Inc. +//go:build test + package cpu import ( "testing" "github.com/DataDog/datadog-agent/pkg/gohai/utils" + "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/stretchr/testify/assert" ) @@ -63,6 +66,22 @@ func TestCPUAsJSON(t *testing.T) { var decodedCPU CPU utils.RequireMarshallJSON(t, cpuInfo, &decodedCPU) + // output CPUCores, CPULogicalProcessors, Family, Mhz, Model, ModelName, Stepping, VendorID, CacheSizeKB, CacheSizeL1Bytes, CacheSizeL2Bytes, CacheSizeL3Bytes, CPUNumaNodes, CPUPkgs + log.Infof("CPUCores: %s", decodedCPU.CPUCores) + log.Infof("CPULogicalProcessors: %s", decodedCPU.CPULogicalProcessors) + log.Infof("Family: %s", decodedCPU.Family) + log.Infof("Mhz: %s", decodedCPU.Mhz) + log.Infof("Model: %s", decodedCPU.Model) + log.Infof("ModelName: %s", decodedCPU.ModelName) + log.Infof("Stepping: %s", decodedCPU.Stepping) + log.Infof("VendorID: %s", decodedCPU.VendorID) + log.Infof("CacheSizeKB: %s", decodedCPU.CacheSizeKB) + log.Infof("CacheSizeL1Bytes: %s", decodedCPU.CacheSizeL1Bytes) + log.Infof("CacheSizeL2Bytes: %s", decodedCPU.CacheSizeL2Bytes) + log.Infof("CacheSizeL3Bytes: %s", decodedCPU.CacheSizeL3Bytes) + log.Infof("CPUNumaNodes: %s", decodedCPU.CPUNumaNodes) + log.Infof("CPUPkgs: %s", decodedCPU.CPUPkgs) + utils.AssertDecodedValue(t, decodedCPU.CPUCores, &cpuInfo.CPUCores, "") utils.AssertDecodedValue(t, decodedCPU.CPULogicalProcessors, &cpuInfo.CPULogicalProcessors, "") utils.AssertDecodedValue(t, decodedCPU.Family, &cpuInfo.Family, "") diff --git a/pkg/gohai/cpu/cpu_windows.c b/pkg/gohai/cpu/cpu_windows.c new file mode 100644 index 000000000000..23e04fe1538a --- /dev/null +++ b/pkg/gohai/cpu/cpu_windows.c @@ -0,0 +1,93 @@ +// This file is licensed under the MIT License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025-present Datadog, Inc. + +#include "cpu_windows.h" +#include +#include + +// computeCoresAndProcessors gets CPU information using Windows API +int computeCoresAndProcessors(CPU_INFO* cpuInfo) { + DWORD buflen = 0; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* buffer = NULL; + BOOL done = FALSE; + DWORD returnLength = 0; + DWORD byteOffset = 0; + + // Initialize the struct + memset(cpuInfo, 0, sizeof(CPU_INFO)); + + // First call to get required buffer size + GetLogicalProcessorInformationEx(RelationAll, NULL, &buflen); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + return GetLastError(); + } + + buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(buflen); + if (buffer == NULL) { + return ERROR_OUTOFMEMORY; + } + + if (!GetLogicalProcessorInformationEx(RelationAll, buffer, &buflen)) { + free(buffer); + return GetLastError(); + } + + // Walk through the buffer + byteOffset = 0; + while (byteOffset < buflen) { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)((char*)buffer + byteOffset); + + switch (ptr->Relationship) { + case RelationProcessorCore: + cpuInfo->corecount++; + // Count logical processors in this core + for (WORD i = 0; i < ptr->Processor.GroupCount; i++) { + GROUP_AFFINITY* groupAffinity = &ptr->Processor.GroupMask[i]; + ULONG64 mask = groupAffinity->Mask; + while (mask) { + if (mask & 1) { + cpuInfo->logicalcount++; + } + mask >>= 1; + } + } + break; + + case RelationNumaNode: + cpuInfo->numaNodeCount++; + break; + + case RelationCache: + switch (ptr->Cache.Level) { + case 1: + cpuInfo->l1CacheSize += ptr->Cache.CacheSize; + break; + case 2: + cpuInfo->l2CacheSize += ptr->Cache.CacheSize; + break; + case 3: + cpuInfo->l3CacheSize += ptr->Cache.CacheSize; + break; + } + break; + + case RelationProcessorPackage: + cpuInfo->pkgcount++; + break; + + case RelationGroup: + cpuInfo->relationGroups = ptr->Group.MaximumGroupCount; + for (WORD i = 0; i < ptr->Group.ActiveGroupCount; i++) { + cpuInfo->maxProcsInGroups += ptr->Group.GroupInfo[i].MaximumProcessorCount; + cpuInfo->activeProcsInGroups += ptr->Group.GroupInfo[i].ActiveProcessorCount; + } + break; + } + + byteOffset += ptr->Size; + } + + free(buffer); + return 0; +} \ No newline at end of file diff --git a/pkg/gohai/cpu/cpu_windows.go b/pkg/gohai/cpu/cpu_windows.go index e3bf16e17376..851c3725c1e4 100644 --- a/pkg/gohai/cpu/cpu_windows.go +++ b/pkg/gohai/cpu/cpu_windows.go @@ -1,10 +1,16 @@ // This file is licensed under the MIT License. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright © 2015 Kentaro Kuribayashi // Copyright 2014-present Datadog, Inc. +//go:build windows + package cpu +/* +#cgo LDFLAGS: -lkernel32 +#include "cpu_windows.h" +*/ +import "C" import ( "fmt" "regexp" @@ -12,40 +18,21 @@ import ( "strings" "unsafe" - "golang.org/x/sys/windows" - "github.com/DataDog/datadog-agent/pkg/gohai/utils" + "github.com/DataDog/datadog-agent/pkg/util/log" + "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" ) const registryHive = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0" -// see https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex -const ( - // RelationProcessorCore retrieves information about logical processors - // that share a single processor core. - RelationProcessorCore = 0 - // RelationNumaNode retrieves information about logical processors - // that are part of the same NUMA node. - RelationNumaNode = 1 - // RelationCache retrieves information about logical processors - // that share a cache. - RelationCache = 2 - // RelationProcessorPackage retrieves information about logical processors - // that share a physical package. - RelationProcessorPackage = 3 - // RelationGroup retrieves information about logical processors - // that share a processor group. - RelationGroup = 4 -) - -// SYSTEM_INFO contains information about the current computer system. +// systemInfo contains information about the current computer system. // This includes the architecture and type of the processor, the number // of processors in the system, the page size, and other such information. // see https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info // //nolint:revive -type SYSTEM_INFO struct { +type systemInfo struct { wProcessorArchitecture uint16 wReserved uint16 dwPageSize uint32 @@ -59,96 +46,110 @@ type SYSTEM_INFO struct { wProcessorRevision uint16 } -// CPU_INFO contains information about cpu, eg. number of cores, cache size -// -//nolint:revive -type CPU_INFO struct { - numaNodeCount int // number of NUMA nodes - pkgcount int // number of packages (physical CPUS) - corecount int // total number of cores - logicalcount int // number of logical CPUS - l1CacheSize uint32 // layer 1 cache size - l2CacheSize uint32 // layer 2 cache size - l3CacheSize uint32 // layer 3 cache size - //nolint:unused - relationGroups int // number of cpu relation groups - //nolint:unused - maxProcsInGroups int // max number of processors - //nolint:unused - activeProcsInGroups int // active processors +// cpuInfo holds CPU information +type cpuInfo struct { + corecount int + logicalcount int + pkgcount int + numaNodeCount int + relationGroups int + maxProcsInGroups int + activeProcsInGroups int + l1CacheSize uint64 + l2CacheSize uint64 + l3CacheSize uint64 + VendorID string + ModelName string + Mhz float64 + Family string + Model string + Stepping string } -func countBits(num uint64) (count int) { - count = 0 - for num > 0 { - if (num & 0x1) == 1 { - count++ - } - num >>= 1 +func extract(caption, field string) string { + re := regexp.MustCompile(fmt.Sprintf("%s [0-9]* ", field)) + matches := re.FindStringSubmatch(caption) + if len(matches) > 0 { + return strings.Split(matches[0], " ")[1] } - return -} - -func getSystemInfo() (si SYSTEM_INFO) { - var mod = windows.NewLazyDLL("kernel32.dll") - var gsi = mod.NewProc("GetSystemInfo") - - // syscall does not fail - //nolint:errcheck - gsi.Call(uintptr(unsafe.Pointer(&si))) - return + return "" } func getCPUInfo() *Info { - cpuInfo := &Info{ - CacheSizeKB: utils.NewErrorValue[uint64](utils.ErrNotCollectable), + var cInfo cpuInfo + var cCpuInfo C.CPU_INFO + + ret := C.computeCoresAndProcessors(&cCpuInfo) + if ret != 0 { + log.Errorf("failed to get CPU information, error code: %d", ret) + return &Info{} } + // Copy C struct values to Go struct + cInfo.corecount = int(cCpuInfo.corecount) + cInfo.logicalcount = int(cCpuInfo.logicalcount) + cInfo.pkgcount = int(cCpuInfo.pkgcount) + cInfo.numaNodeCount = int(cCpuInfo.numaNodeCount) + cInfo.relationGroups = int(cCpuInfo.relationGroups) + cInfo.maxProcsInGroups = int(cCpuInfo.maxProcsInGroups) + cInfo.activeProcsInGroups = int(cCpuInfo.activeProcsInGroups) + cInfo.l1CacheSize = uint64(cCpuInfo.l1CacheSize) + cInfo.l2CacheSize = uint64(cCpuInfo.l2CacheSize) + cInfo.l3CacheSize = uint64(cCpuInfo.l3CacheSize) + + // Get additional CPU information from Windows registry k, err := registry.OpenKey(registry.LOCAL_MACHINE, registryHive, registry.QUERY_VALUE) - if err == nil { - defer k.Close() - - dw, _, err := k.GetIntegerValue("~MHz") - cpuInfo.Mhz = utils.NewValueFrom(float64(dw), err) - - s, _, err := k.GetStringValue("ProcessorNameString") - cpuInfo.ModelName = utils.NewValueFrom(s, err) - - s, _, err = k.GetStringValue("VendorIdentifier") - cpuInfo.VendorID = utils.NewValueFrom(s, err) - - s, _, err = k.GetStringValue("Identifier") - if err == nil { - cpuInfo.Family = utils.NewValue(extract(s, "Family")) - } else { - cpuInfo.Family = utils.NewErrorValue[string](err) - } - } else { - cpuInfo.Mhz = utils.NewErrorValue[float64](err) - cpuInfo.ModelName = utils.NewErrorValue[string](err) - cpuInfo.VendorID = utils.NewErrorValue[string](err) - cpuInfo.Family = utils.NewErrorValue[string](err) + if err != nil { + log.Errorf("failed to open registry key: %v", err) + return &Info{} } + defer k.Close() - cpus, cpuerr := computeCoresAndProcessors() - cpuInfo.CPUPkgs = utils.NewValueFrom(uint64(cpus.pkgcount), cpuerr) - cpuInfo.CPUNumaNodes = utils.NewValueFrom(uint64(cpus.numaNodeCount), cpuerr) - cpuInfo.CPUCores = utils.NewValueFrom(uint64(cpus.corecount), cpuerr) - cpuInfo.CPULogicalProcessors = utils.NewValueFrom(uint64(cpus.logicalcount), cpuerr) - cpuInfo.CacheSizeL1Bytes = utils.NewValueFrom(uint64(cpus.l1CacheSize), cpuerr) - cpuInfo.CacheSizeL2Bytes = utils.NewValueFrom(uint64(cpus.l2CacheSize), cpuerr) - cpuInfo.CacheSizeL3Bytes = utils.NewValueFrom(uint64(cpus.l3CacheSize), cpuerr) + if mhz, _, err := k.GetIntegerValue("~MHz"); err == nil { + cInfo.Mhz = float64(mhz) + } - si := getSystemInfo() - cpuInfo.Model = utils.NewValue(strconv.Itoa(int((si.wProcessorRevision >> 8) & 0xFF))) - cpuInfo.Stepping = utils.NewValue(strconv.Itoa(int(si.wProcessorRevision & 0xFF))) + if name, _, err := k.GetStringValue("ProcessorNameString"); err == nil { + cInfo.ModelName = name + } - return cpuInfo -} + if vendor, _, err := k.GetStringValue("VendorIdentifier"); err == nil { + cInfo.VendorID = vendor + } -func extract(caption, field string) string { - re := regexp.MustCompile(fmt.Sprintf("%s [0-9]* ", field)) - return strings.Split(re.FindStringSubmatch(caption)[0], " ")[1] + if identifier, _, err := k.GetStringValue("Identifier"); err == nil { + cInfo.Family = extract(identifier, "Family") + } + + // Get system info for model and stepping + var si systemInfo + var mod = windows.NewLazyDLL("kernel32.dll") + var gsi = mod.NewProc("GetSystemInfo") + //nolint:errcheck + gsi.Call(uintptr(unsafe.Pointer(&si))) + + cInfo.Model = strconv.Itoa(int((si.wProcessorRevision >> 8) & 0xFF)) + cInfo.Stepping = strconv.Itoa(int(si.wProcessorRevision & 0xFF)) + + // Convert to Info struct + info := &Info{ + VendorID: utils.NewValue(cInfo.VendorID), + ModelName: utils.NewValue(cInfo.ModelName), + CPUCores: utils.NewValue(uint64(cInfo.corecount)), + CPULogicalProcessors: utils.NewValue(uint64(cInfo.logicalcount)), + Mhz: utils.NewValue(cInfo.Mhz), + Family: utils.NewValue(cInfo.Family), + Model: utils.NewValue(cInfo.Model), + Stepping: utils.NewValue(cInfo.Stepping), + CPUPkgs: utils.NewValue(uint64(cInfo.pkgcount)), + CPUNumaNodes: utils.NewValue(uint64(cInfo.numaNodeCount)), + CacheSizeL1Bytes: utils.NewValue(cInfo.l1CacheSize), + CacheSizeL2Bytes: utils.NewValue(cInfo.l2CacheSize), + CacheSizeL3Bytes: utils.NewValue(cInfo.l3CacheSize), + CacheSizeKB: utils.NewErrorValue[uint64](utils.ErrNotCollectable), + } + + return info } diff --git a/pkg/gohai/cpu/cpu_windows.h b/pkg/gohai/cpu/cpu_windows.h new file mode 100644 index 000000000000..4e424714f9ee --- /dev/null +++ b/pkg/gohai/cpu/cpu_windows.h @@ -0,0 +1,25 @@ +// This file is licensed under the MIT License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025-present Datadog, Inc. + +#ifndef CPU_WINDOWS_H +#define CPU_WINDOWS_H + +// CPU_INFO mirrors the Go struct +typedef struct { + int corecount; + int logicalcount; + int pkgcount; + int numaNodeCount; + int relationGroups; + int maxProcsInGroups; + int activeProcsInGroups; + unsigned long long l1CacheSize; + unsigned long long l2CacheSize; + unsigned long long l3CacheSize; +} CPU_INFO; + +// computeCoresAndProcessors gets CPU information using Windows API +int computeCoresAndProcessors(CPU_INFO* cpuInfo); + +#endif // CPU_WINDOWS_H \ No newline at end of file diff --git a/pkg/gohai/cpu/cpu_windows_386.go b/pkg/gohai/cpu/cpu_windows_386.go deleted file mode 100644 index e31c91c9b003..000000000000 --- a/pkg/gohai/cpu/cpu_windows_386.go +++ /dev/null @@ -1,87 +0,0 @@ -// This file is licensed under the MIT License. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright © 2015 Kentaro Kuribayashi -// Copyright 2014-present Datadog, Inc. - -package cpu - -import ( - "encoding/binary" - "unsafe" - - "golang.org/x/sys/windows" -) - -// SYSTEM_LOGICAL_PROCESSOR_INFORMATION describes the relationship -// between the specified processor set. -// see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information -// -//nolint:revive -type SYSTEM_LOGICAL_PROCESSOR_INFORMATION struct { - ProcessorMask uintptr - Relationship int // enum (int) - // in the Windows header, this is a union of a byte, a DWORD, - // and a cacheDescriptor structure - dataunion [16]byte -} - -// systemLogicalProcessorInformationSize is the size of -// SYSTEM_LOGICAL_PROCESSOR_INFORMATION struct -const systemLogicalProcessorInformationSize = 24 - -func getSystemLogicalProcessorInformationSize() int { - return systemLogicalProcessorInformationSize -} - -func byteArrayToProcessorStruct(data []byte) (info SYSTEM_LOGICAL_PROCESSOR_INFORMATION) { - info.ProcessorMask = uintptr(binary.LittleEndian.Uint32(data)) - info.Relationship = int(binary.LittleEndian.Uint32(data[4:])) - copy(info.dataunion[0:16], data[8:24]) - return -} - -func computeCoresAndProcessors() (cpuInfo CPU_INFO, err error) { - var mod = windows.NewLazyDLL("kernel32.dll") - var getProcInfo = mod.NewProc("GetLogicalProcessorInformation") - var buflen uint32 - - // first, figure out how much we need - status, _, err := getProcInfo.Call(uintptr(0), - uintptr(unsafe.Pointer(&buflen))) - if status == 0 { - if err != windows.ERROR_INSUFFICIENT_BUFFER { - // only error we're expecing here is insufficient buffer - // anything else is a failure - return - } - } else { - // this shouldn't happen. Errno won't be set (because the function) - // succeeded. So just return something to indicate we've failed - err = windows.Errno(2) - return - } - buf := make([]byte, buflen) - status, _, err = getProcInfo.Call(uintptr(unsafe.Pointer(&buf[0])), - uintptr(unsafe.Pointer(&buflen))) - if status == 0 { - return - } - // walk through each of the buffers - - for i := 0; uint32(i) < buflen; i += getSystemLogicalProcessorInformationSize() { - info := byteArrayToProcessorStruct(buf[i : i+getSystemLogicalProcessorInformationSize()]) - - switch info.Relationship { - case RelationNumaNode: - cpuInfo.numaNodeCount++ - - case RelationProcessorCore: - cpuInfo.corecount++ - cpuInfo.logicalcount += countBits(uint64(info.ProcessorMask)) - - case RelationProcessorPackage: - cpuInfo.pkgcount++ - } - } - return -} diff --git a/pkg/gohai/cpu/cpu_windows_amd64.go b/pkg/gohai/cpu/cpu_windows_amd64.go deleted file mode 100644 index 61b401c2181f..000000000000 --- a/pkg/gohai/cpu/cpu_windows_amd64.go +++ /dev/null @@ -1,286 +0,0 @@ -// This file is licensed under the MIT License. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright © 2015 Kentaro Kuribayashi -// Copyright 2014-present Datadog, Inc. - -package cpu - -import ( - "encoding/binary" - "unsafe" - - "golang.org/x/sys/windows" -) - -// GROUP_AFFINITY represents a processor group-specific affinity, -// such as the affinity of a thread. -// see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_affinity -// -//nolint:revive -type GROUP_AFFINITY struct { - Mask uintptr - Group uint16 - Reserved [3]uint16 -} - -// NUMA_NODE_RELATIONSHIP represents information about a NUMA node -// in a processor group. -// see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-numa_node_relationship -// -//nolint:revive -type NUMA_NODE_RELATIONSHIP struct { - NodeNumber uint32 - Reserved [20]uint8 - GroupMask GROUP_AFFINITY -} - -// CACHE_RELATIONSHIP describes cache attributes. -// see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_relationship -// -//nolint:revive -type CACHE_RELATIONSHIP struct { - Level uint8 - Associativity uint8 - LineSize uint16 - CacheSize uint32 - CacheType int // enum in C - Reserved [20]uint8 - GroupMask GROUP_AFFINITY -} - -// PROCESSOR_GROUP_INFO represents the number and affinity of processors -// in a processor group. -// see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_group_info -// -//nolint:revive -type PROCESSOR_GROUP_INFO struct { - MaximumProcessorCount uint8 - ActiveProcessorCount uint8 - Reserved [38]uint8 - ActiveProcessorMask uintptr -} - -// GROUP_RELATIONSHIP represents information about processor groups. -// see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_relationship -// -//nolint:revive -type GROUP_RELATIONSHIP struct { - MaximumGroupCount uint16 - ActiveGroupCount uint16 - Reserved [20]uint8 - // variable size array of PROCESSOR_GROUP_INFO -} - -// PROCESSOR_RELATIONSHIP represents information about affinity -// within a processor group. -// see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship -// -//nolint:revive -type PROCESSOR_RELATIONSHIP struct { - Flags uint8 - EfficiencyClass uint8 - WReserved [20]uint8 - GroupCount uint16 - // what follows is an array of zero or more GROUP_AFFINITY structures -} - -// SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX contains information about -// the relationships of logical processors and related hardware. -// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex -// -//nolint:revive -type SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct { - Relationship int - Size uint32 - // what follows is a C union of - // PROCESSOR_RELATIONSHIP, - // NUMA_NODE_RELATIONSHIP, - // CACHE_RELATIONSHIP, - // GROUP_RELATIONSHIP -} - -func byteArrayToGroupAffinity(data []byte) (affinity GROUP_AFFINITY, consumed uint32, err error) { - err = nil - affinity.Mask = uintptr(binary.LittleEndian.Uint64(data)) - affinity.Group = binary.LittleEndian.Uint16(data[8:]) - // can skip the reserved, but count it - consumed = 16 - return - -} -func byteArrayToProcessorInformationExStruct(data []byte) (info SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, consumed uint32, err error) { - err = nil - info.Relationship = int(binary.LittleEndian.Uint32(data)) - info.Size = binary.LittleEndian.Uint32(data[4:]) - - consumed = 8 - return -} - -func byteArrayToProcessorRelationshipStruct(data []byte) (proc PROCESSOR_RELATIONSHIP, groupMask []GROUP_AFFINITY, consumed uint32, err error) { - err = nil - proc.Flags = data[0] - proc.EfficiencyClass = data[1] - proc.GroupCount = uint16(binary.LittleEndian.Uint32(data[22:])) - consumed = 24 - if proc.GroupCount != 0 { - gm := make([]GROUP_AFFINITY, proc.GroupCount) - - for i := uint16(0); i < proc.GroupCount; i++ { - var used uint32 - var ga GROUP_AFFINITY - ga, used, err = byteArrayToGroupAffinity(data[consumed:]) - if err != nil { - return - } - gm[i] = ga - consumed += used - } - groupMask = gm - } - return -} - -func byteArrayToNumaNode(data []byte) (numa NUMA_NODE_RELATIONSHIP, consumed uint32, err error) { - numa.NodeNumber = binary.LittleEndian.Uint32(data) - // skip 20 bytes of reserved - consumed = 24 - aff, used, err := byteArrayToGroupAffinity(data[consumed:]) - numa.GroupMask = aff - consumed += used - return -} - -func byteArrayToRelationCache(data []byte) (cache CACHE_RELATIONSHIP, consumed uint32, err error) { - cache.Level = data[0] - cache.Associativity = data[1] - cache.LineSize = binary.LittleEndian.Uint16(data[2:]) - cache.CacheSize = binary.LittleEndian.Uint32(data[4:]) - cache.CacheType = int(binary.LittleEndian.Uint32(data[8:])) - // skip 20 bytes - consumed = 32 - ga, used, err := byteArrayToGroupAffinity(data[consumed:]) - cache.GroupMask = ga - consumed += used - return - -} - -func byteArrayToRelationGroup(data []byte) (group GROUP_RELATIONSHIP, gi []PROCESSOR_GROUP_INFO, consumed uint32, err error) { - group.MaximumGroupCount = binary.LittleEndian.Uint16(data) - group.ActiveGroupCount = binary.LittleEndian.Uint16(data[4:]) - consumed = 24 - if group.ActiveGroupCount > 0 { - groups := make([]PROCESSOR_GROUP_INFO, group.ActiveGroupCount) - for i := uint16(0); i < group.ActiveGroupCount; i++ { - groups[i].MaximumProcessorCount = data[consumed] - consumed++ - groups[i].ActiveProcessorCount = data[consumed] - consumed++ - consumed += 38 // reserved - groups[i].ActiveProcessorMask = uintptr(binary.LittleEndian.Uint64(data[consumed:])) - consumed += 8 - } - } - return -} - -func computeCoresAndProcessors() (CPU_INFO, error) { - var cpuInfo CPU_INFO - var mod = windows.NewLazyDLL("kernel32.dll") - var getProcInfo = mod.NewProc("GetLogicalProcessorInformationEx") - var buflen uint32 - - // first, figure out how much we need - status, _, callErr := getProcInfo.Call(uintptr(0xFFFF), // all relationships. - uintptr(0), - uintptr(unsafe.Pointer(&buflen))) - if status == 0 { - if callErr != windows.ERROR_INSUFFICIENT_BUFFER { - // only error we're expecing here is insufficient buffer - // anything else is a failure - return cpuInfo, callErr - } - } else { - // this shouldn't happen. Errno won't be set (because the function) - // succeeded. So just return something to indicate we've failed - return cpuInfo, windows.Errno(1) - } - buf := make([]byte, buflen) - status, _, callErr = getProcInfo.Call( - uintptr(0xFFFF), // still want all relationships - uintptr(unsafe.Pointer(&buf[0])), - uintptr(unsafe.Pointer(&buflen))) - if status == 0 { - return cpuInfo, callErr - } - // walk through each of the buffers - - bufused := uint32(0) - for bufused < buflen { - info, used, decodeerr := byteArrayToProcessorInformationExStruct(buf[bufused:]) - if decodeerr != nil { - return cpuInfo, decodeerr - } - bufused += used - if info.Size == 0 { - break - } - switch info.Relationship { - case RelationProcessorCore: - core, groupMask, used, decodeerr := byteArrayToProcessorRelationshipStruct(buf[bufused:]) - if decodeerr != nil { - return cpuInfo, decodeerr - } - bufused += used - cpuInfo.corecount++ - for j := uint16(0); j < core.GroupCount; j++ { - cpuInfo.logicalcount += countBits(uint64(groupMask[j].Mask)) - } - case RelationNumaNode: - _, used, decodeerr := byteArrayToNumaNode(buf[bufused:]) - if decodeerr != nil { - return cpuInfo, decodeerr - } - cpuInfo.numaNodeCount++ - bufused += used - - case RelationCache: - cache, used, decodeerr := byteArrayToRelationCache(buf[bufused:]) - if decodeerr != nil { - return cpuInfo, decodeerr - } - bufused += used - switch cache.Level { - case 1: - cpuInfo.l1CacheSize = cache.CacheSize - case 2: - cpuInfo.l2CacheSize = cache.CacheSize - case 3: - cpuInfo.l3CacheSize = cache.CacheSize - } - case RelationProcessorPackage: - _, _, used, decodeerr := byteArrayToProcessorRelationshipStruct(buf[bufused:]) - if decodeerr != nil { - return cpuInfo, decodeerr - } - bufused += used - cpuInfo.pkgcount++ - - case RelationGroup: - group, groupInfo, used, decodeerr := byteArrayToRelationGroup(buf[bufused:]) - if decodeerr != nil { - return cpuInfo, decodeerr - } - bufused += used - cpuInfo.relationGroups += int(group.MaximumGroupCount) - for _, info := range groupInfo { - cpuInfo.maxProcsInGroups += int(info.MaximumProcessorCount) - cpuInfo.activeProcsInGroups += int(info.ActiveProcessorCount) - } - - } - } - - return cpuInfo, nil -} diff --git a/releasenotes/notes/windows-agent-cpu-info-18e54faf1ffbd15a.yaml b/releasenotes/notes/windows-agent-cpu-info-18e54faf1ffbd15a.yaml new file mode 100644 index 000000000000..1ded02f6985a --- /dev/null +++ b/releasenotes/notes/windows-agent-cpu-info-18e54faf1ffbd15a.yaml @@ -0,0 +1,12 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +fixes: + - | + * Fixed inaccurate cpu_cores reported by gohai + * JIRA: https://datadoghq.atlassian.net/browse/WINA-1171