Skip to content

Commit d5e33f7

Browse files
authored
Merge pull request #1839 from OrbintSoft/remove-wmi
Remove wmi for Get physical CPU core count
2 parents 3ba33b4 + f2b0aa5 commit d5e33f7

File tree

2 files changed

+74
-21
lines changed

2 files changed

+74
-21
lines changed

cpu/cpu_test.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,17 @@ func TestTimes(t *testing.T) {
5959
}
6060

6161
func TestCounts(t *testing.T) {
62-
v, err := Counts(true)
62+
logicalCount, err := Counts(true)
6363
common.SkipIfNotImplementedErr(t, err)
6464
require.NoError(t, err)
65-
assert.NotZerof(t, v, "could not get logical CPU counts: %v", v)
66-
t.Logf("logical cores: %d", v)
67-
v, err = Counts(false)
65+
assert.NotZerof(t, logicalCount, "could not get logical CPU counts: %v", logicalCount)
66+
t.Logf("logical cores: %d", logicalCount)
67+
physicalCount, err := Counts(false)
6868
common.SkipIfNotImplementedErr(t, err)
6969
require.NoError(t, err)
70-
assert.NotZerof(t, v, "could not get physical CPU counts: %v", v)
71-
t.Logf("physical cores: %d", v)
70+
assert.NotZerof(t, physicalCount, "could not get physical CPU counts: %v", physicalCount)
71+
t.Logf("physical cores: %d", physicalCount)
72+
assert.GreaterOrEqualf(t, logicalCount, physicalCount, "logical CPU count should be greater than or equal to physical CPU count: %v >= %v", logicalCount, physicalCount)
7273
}
7374

7475
func TestTimeStat_String(t *testing.T) {

cpu/cpu_windows.go

+67-15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package cpu
55

66
import (
77
"context"
8+
"errors"
89
"fmt"
910
"strconv"
1011
"unsafe"
@@ -15,7 +16,10 @@ import (
1516
"github.com/shirou/gopsutil/v4/internal/common"
1617
)
1718

18-
var procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
19+
var (
20+
procGetNativeSystemInfo = common.Modkernel32.NewProc("GetNativeSystemInfo")
21+
procGetLogicalProcessorInformationEx = common.Modkernel32.NewProc("GetLogicalProcessorInformationEx")
22+
)
1923

2024
type win32_Processor struct { //nolint:revive //FIXME
2125
Family uint16
@@ -200,30 +204,78 @@ type systemInfo struct {
200204
wProcessorRevision uint16
201205
}
202206

203-
func CountsWithContext(ctx context.Context, logical bool) (int, error) {
207+
type groupAffinity struct {
208+
mask uintptr // https://learn.microsoft.com/it-it/windows-hardware/drivers/kernel/interrupt-affinity-and-priority#about-kaffinity
209+
group uint16
210+
reserved [3]uint16
211+
}
212+
213+
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship
214+
type processorRelationship struct {
215+
flags byte
216+
efficientClass byte
217+
reserved [20]byte
218+
groupCount uint16
219+
groupMask [1]groupAffinity
220+
}
221+
222+
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
223+
type systemLogicalProcessorInformationEx struct {
224+
Relationship uint32
225+
Size uint32
226+
Processor processorRelationship
227+
}
228+
229+
func getPhysicalCoreCount() (int, error) {
230+
var length uint32
231+
const relationAll = 0xffff
232+
const relationProcessorCore = 0x0
233+
234+
// First call to determine the required buffer size
235+
_, _, err := procGetLogicalProcessorInformationEx.Call(uintptr(relationAll), 0, uintptr(unsafe.Pointer(&length)))
236+
if err != nil && !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
237+
return 0, fmt.Errorf("failed to get buffer size: %w", err)
238+
}
239+
240+
// Allocate the buffer
241+
buffer := make([]byte, length)
242+
243+
// Second call to retrieve the processor information
244+
_, _, err = procGetLogicalProcessorInformationEx.Call(uintptr(relationAll), uintptr(unsafe.Pointer(&buffer[0])), uintptr(unsafe.Pointer(&length)))
245+
if err != nil && !errors.Is(err, windows.NTE_OP_OK) {
246+
return 0, fmt.Errorf("failed to get logical processor information: %w", err)
247+
}
248+
249+
// Iterate through the buffer to count physical cores
250+
offset := uintptr(0)
251+
ncpus := 0
252+
for offset < uintptr(length) {
253+
info := (*systemLogicalProcessorInformationEx)(unsafe.Pointer(uintptr(unsafe.Pointer(&buffer[0])) + offset))
254+
if info.Relationship == relationProcessorCore {
255+
ncpus++
256+
}
257+
offset += uintptr(info.Size)
258+
}
259+
260+
return ncpus, nil
261+
}
262+
263+
func CountsWithContext(_ context.Context, logical bool) (int, error) {
204264
if logical {
205-
// https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97
265+
// Get logical processor count https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L97
206266
ret := windows.GetActiveProcessorCount(windows.ALL_PROCESSOR_GROUPS)
207267
if ret != 0 {
208268
return int(ret), nil
209269
}
270+
210271
var systemInfo systemInfo
211272
_, _, err := procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
212273
if systemInfo.dwNumberOfProcessors == 0 {
213274
return 0, err
214275
}
215276
return int(systemInfo.dwNumberOfProcessors), nil
216277
}
217-
// physical cores https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L499
218-
// for the time being, try with unreliable and slow WMI call…
219-
var dst []win32_Processor
220-
q := wmi.CreateQuery(&dst, "")
221-
if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil {
222-
return 0, err
223-
}
224-
var count uint32
225-
for _, d := range dst {
226-
count += d.NumberOfCores
227-
}
228-
return int(count), nil
278+
279+
// Get physical core count https://github.com/giampaolo/psutil/blob/d01a9eaa35a8aadf6c519839e987a49d8be2d891/psutil/_psutil_windows.c#L499
280+
return getPhysicalCoreCount()
229281
}

0 commit comments

Comments
 (0)