7
7
"context"
8
8
"errors"
9
9
"fmt"
10
+ "math/bits"
10
11
"strconv"
11
12
"unsafe"
12
13
@@ -57,6 +58,20 @@ const (
57
58
win32_SystemProcessorPerformanceInfoSize = uint32 (unsafe .Sizeof (win32_SystemProcessorPerformanceInformation {})) //nolint:revive //FIXME
58
59
)
59
60
61
+ type relationship uint32
62
+
63
+ // https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
64
+ const (
65
+ relationProcessorCore = relationship (0 )
66
+ relationProcessorPackage = relationship (3 )
67
+ )
68
+
69
+ const (
70
+ kAffinitySize = unsafe .Sizeof (int (0 ))
71
+ // https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/interrupt-affinity-and-priority
72
+ maxLogicalProcessorsPerGroup = uint32 (unsafe .Sizeof (kAffinitySize * 8 ))
73
+ )
74
+
60
75
// Times returns times stat per cpu and combined for all CPUs
61
76
func Times (percpu bool ) ([]TimesStat , error ) {
62
77
return TimesWithContext (context .Background (), percpu )
@@ -101,6 +116,21 @@ func Info() ([]InfoStat, error) {
101
116
return InfoWithContext (context .Background ())
102
117
}
103
118
119
+ // this function iterates over each set bit in the package affinity mask, each bit represent a logical processor in a group (assuming you are iteriang over a package mask)
120
+ // the function is used also to compute the global logical processor number
121
+ // https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups
122
+ // see https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-group_affinity
123
+ // and https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-processor_relationship
124
+ // and https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
125
+ func forEachSetBit64 (mask uint64 , fn func (bit int )) {
126
+ m := mask
127
+ for m != 0 {
128
+ b := bits .TrailingZeros64 (m )
129
+ fn (b )
130
+ m &= m - 1
131
+ }
132
+ }
133
+
104
134
func InfoWithContext (ctx context.Context ) ([]InfoStat , error ) {
105
135
var ret []InfoStat
106
136
var dst []win32_Processor
@@ -121,14 +151,42 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
121
151
Family : strconv .FormatUint (uint64 (l .Family ), 10 ),
122
152
VendorID : l .Manufacturer ,
123
153
ModelName : l .Name ,
124
- Cores : int32 (l .NumberOfLogicalProcessors ),
154
+ Cores : int32 (l .NumberOfLogicalProcessors ), // TO BE REMOVED, set by getSystemLogicalProcessorInformationEx
125
155
PhysicalID : procID ,
126
156
Mhz : float64 (l .MaxClockSpeed ),
127
157
Flags : []string {},
128
158
}
129
159
ret = append (ret , cpu )
130
160
}
131
161
162
+ processorPackages , err := getSystemLogicalProcessorInformationEx (relationProcessorPackage )
163
+ if err != nil {
164
+ // return an error whem wmi will be removed
165
+ // return ret, fmt.Errorf("failed to get processor package information: %w", err)
166
+ return ret , nil
167
+ }
168
+
169
+ if len (processorPackages ) != len (ret ) {
170
+ // this should never happen, but it's kept for safety until wmi is removed
171
+ return ret , nil
172
+ }
173
+
174
+ for _ , pkg := range processorPackages {
175
+ logicalCount := 0
176
+ for i , ga := range pkg .processor .groupMask {
177
+ g := int (ga .group )
178
+ forEachSetBit64 (uint64 (ga .mask ), func (bit int ) {
179
+ // the global logical processor label
180
+ globalLpl := g * int (maxLogicalProcessorsPerGroup ) + bit
181
+ if globalLpl >= 0 {
182
+ logicalCount ++
183
+ }
184
+ })
185
+
186
+ ret [i ].Cores = int32 (logicalCount )
187
+ }
188
+ }
189
+
132
190
return ret , nil
133
191
}
134
192
@@ -207,7 +265,7 @@ type systemInfo struct {
207
265
}
208
266
209
267
type groupAffinity struct {
210
- mask uintptr // https://learn.microsoft.com/it-it /windows-hardware/drivers/kernel/interrupt-affinity-and-priority#about-kaffinity
268
+ mask uintptr // https://learn.microsoft.com/en-us /windows-hardware/drivers/kernel/interrupt-affinity-and-priority#about-kaffinity
211
269
group uint16
212
270
reserved [3 ]uint16
213
271
}
@@ -223,43 +281,43 @@ type processorRelationship struct {
223
281
224
282
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_logical_processor_information_ex
225
283
type systemLogicalProcessorInformationEx struct {
226
- Relationship uint32
227
- Size uint32
228
- Processor processorRelationship
284
+ relationship uint32
285
+ size uint32
286
+ processor processorRelationship
229
287
}
230
288
231
- func getPhysicalCoreCount ( ) (int , error ) {
289
+ func getSystemLogicalProcessorInformationEx ( relationship relationship ) ([] systemLogicalProcessorInformationEx , error ) {
232
290
var length uint32
233
- const relationAll = 0xffff
234
- const relationProcessorCore = 0x0
235
-
236
291
// First call to determine the required buffer size
237
- _ , _ , err := procGetLogicalProcessorInformationEx .Call (uintptr (relationAll ), 0 , uintptr (unsafe .Pointer (& length )))
292
+ _ , _ , err := procGetLogicalProcessorInformationEx .Call (uintptr (relationship ), 0 , uintptr (unsafe .Pointer (& length )))
238
293
if err != nil && ! errors .Is (err , windows .ERROR_INSUFFICIENT_BUFFER ) {
239
- return 0 , fmt .Errorf ("failed to get buffer size: %w" , err )
294
+ return nil , fmt .Errorf ("failed to get buffer size: %w" , err )
240
295
}
241
296
242
297
// Allocate the buffer
243
298
buffer := make ([]byte , length )
244
299
245
300
// Second call to retrieve the processor information
246
- _ , _ , err = procGetLogicalProcessorInformationEx .Call (uintptr (relationAll ), uintptr (unsafe .Pointer (& buffer [0 ])), uintptr (unsafe .Pointer (& length )))
301
+ _ , _ , err = procGetLogicalProcessorInformationEx .Call (uintptr (relationship ), uintptr (unsafe .Pointer (& buffer [0 ])), uintptr (unsafe .Pointer (& length )))
247
302
if err != nil && ! errors .Is (err , windows .NTE_OP_OK ) {
248
- return 0 , fmt .Errorf ("failed to get logical processor information: %w" , err )
303
+ return nil , fmt .Errorf ("failed to get logical processor information: %w" , err )
249
304
}
250
305
251
- // Iterate through the buffer to count physical cores
306
+ // Convert the byte slice into a slice of systemLogicalProcessorInformationEx structs
252
307
offset := uintptr (0 )
253
- ncpus := 0
308
+ var infos [] systemLogicalProcessorInformationEx
254
309
for offset < uintptr (length ) {
255
310
info := (* systemLogicalProcessorInformationEx )(unsafe .Pointer (uintptr (unsafe .Pointer (& buffer [0 ])) + offset ))
256
- if info .Relationship == relationProcessorCore {
257
- ncpus ++
258
- }
259
- offset += uintptr (info .Size )
311
+ infos = append (infos , * info )
312
+ offset += uintptr (info .size )
260
313
}
261
314
262
- return ncpus , nil
315
+ return infos , nil
316
+ }
317
+
318
+ func getPhysicalCoreCount () (int , error ) {
319
+ infos , err := getSystemLogicalProcessorInformationEx (relationProcessorCore )
320
+ return len (infos ), err
263
321
}
264
322
265
323
func CountsWithContext (_ context.Context , logical bool ) (int , error ) {
0 commit comments