mirror of
https://github.com/optim-enterprises-bv/kubernetes.git
synced 2025-11-02 11:18:16 +00:00
426 lines
9.9 KiB
Go
426 lines
9.9 KiB
Go
//go:build windows
|
|
// +build windows
|
|
|
|
/*
|
|
Copyright 2024 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package winstats
|
|
|
|
import (
|
|
cadvisorapi "github.com/google/cadvisor/info/v1"
|
|
"github.com/stretchr/testify/assert"
|
|
"testing"
|
|
"unsafe"
|
|
)
|
|
|
|
func TestGROUP_AFFINITY_Processors(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
Mask uint64
|
|
Group uint16
|
|
want []int
|
|
}{
|
|
{
|
|
name: "empty",
|
|
Mask: 0,
|
|
Group: 0,
|
|
want: []int{},
|
|
},
|
|
{
|
|
name: "empty group 2",
|
|
Mask: 0,
|
|
Group: 1,
|
|
want: []int{},
|
|
},
|
|
{
|
|
name: "cpu 1 Group 0",
|
|
Mask: 1,
|
|
Group: 0,
|
|
want: []int{0},
|
|
},
|
|
{
|
|
name: "cpu 64 Group 0",
|
|
Mask: 1 << 63,
|
|
Group: 0,
|
|
want: []int{63},
|
|
},
|
|
{
|
|
name: "cpu 128 Group 1",
|
|
Mask: 1 << 63,
|
|
Group: 1,
|
|
want: []int{127},
|
|
},
|
|
{
|
|
name: "cpu 128 (Group 1)",
|
|
Mask: 1 << 63,
|
|
Group: 1,
|
|
want: []int{127},
|
|
},
|
|
{
|
|
name: "Mask 1 Group 2",
|
|
Mask: 1,
|
|
Group: 2,
|
|
want: []int{128},
|
|
},
|
|
{
|
|
name: "64 cpus group 0",
|
|
Mask: 0xffffffffffffffff,
|
|
Group: 0,
|
|
want: makeRange(0, 63),
|
|
},
|
|
{
|
|
name: "64 cpus group 1",
|
|
Mask: 0xffffffffffffffff,
|
|
Group: 1,
|
|
want: makeRange(64, 127),
|
|
},
|
|
{
|
|
name: "64 cpus group 1",
|
|
Mask: 0xffffffffffffffff,
|
|
Group: 1,
|
|
want: makeRange(64, 127),
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
a := GroupAffinity{
|
|
Mask: tt.Mask,
|
|
Group: tt.Group,
|
|
}
|
|
assert.Equalf(t, tt.want, a.Processors(), "Processors()")
|
|
})
|
|
}
|
|
}
|
|
|
|
// https://stackoverflow.com/a/39868255/697126
|
|
func makeRange(min, max int) []int {
|
|
a := make([]int, max-min+1)
|
|
for i := range a {
|
|
a[i] = min + i
|
|
}
|
|
return a
|
|
}
|
|
|
|
func TestCpusToGroupAffinity(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
cpus []int
|
|
want map[int]*GroupAffinity
|
|
}{
|
|
{
|
|
name: "empty",
|
|
want: map[int]*GroupAffinity{},
|
|
},
|
|
{
|
|
name: "single cpu group 0",
|
|
cpus: []int{0},
|
|
want: map[int]*GroupAffinity{
|
|
0: {
|
|
Mask: 1,
|
|
Group: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "single cpu group 0",
|
|
cpus: []int{63},
|
|
want: map[int]*GroupAffinity{
|
|
0: {
|
|
Mask: 1 << 63,
|
|
Group: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "single cpu group 1",
|
|
cpus: []int{64},
|
|
want: map[int]*GroupAffinity{
|
|
1: {
|
|
Mask: 1,
|
|
Group: 1,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple cpus same group",
|
|
cpus: []int{0, 1, 2},
|
|
want: map[int]*GroupAffinity{
|
|
0: {
|
|
Mask: 1 | 2 | 4, // Binary OR to combine the masks
|
|
Group: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple cpus different groups",
|
|
cpus: []int{0, 64},
|
|
want: map[int]*GroupAffinity{
|
|
0: {
|
|
Mask: 1,
|
|
Group: 0,
|
|
},
|
|
1: {
|
|
Mask: 1,
|
|
Group: 1,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "multiple cpus different groups",
|
|
cpus: []int{0, 1, 2, 64, 65, 66},
|
|
want: map[int]*GroupAffinity{
|
|
0: {
|
|
Mask: 1 | 2 | 4,
|
|
Group: 0,
|
|
},
|
|
1: {
|
|
Mask: 1 | 2 | 4,
|
|
Group: 1,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "64 cpus group 0",
|
|
cpus: makeRange(0, 63),
|
|
want: map[int]*GroupAffinity{
|
|
0: {
|
|
Mask: 0xffffffffffffffff, // All 64 bits set
|
|
Group: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "64 cpus group 1",
|
|
cpus: makeRange(64, 127),
|
|
want: map[int]*GroupAffinity{
|
|
1: {
|
|
Mask: 0xffffffffffffffff, // All 64 bits set
|
|
Group: 1,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equalf(t, tt.want, CpusToGroupAffinity(tt.cpus), "CpusToGroupAffinity(%v)", tt.cpus)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_convertWinApiToCadvisorApi(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
buffer []byte
|
|
expectedNumOfCores int
|
|
expectedNumOfSockets int
|
|
expectedNodes []cadvisorapi.Node
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "empty",
|
|
buffer: []byte{},
|
|
expectedNumOfCores: 0,
|
|
expectedNumOfSockets: 0,
|
|
expectedNodes: []cadvisorapi.Node{},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "single core",
|
|
buffer: createProcessorRelationships([]int{0}),
|
|
expectedNumOfCores: 1,
|
|
expectedNumOfSockets: 1,
|
|
expectedNodes: []cadvisorapi.Node{
|
|
{
|
|
Id: 0,
|
|
Cores: []cadvisorapi.Core{
|
|
{
|
|
Id: 1,
|
|
Threads: []int{0},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "single core, multiple cpus",
|
|
buffer: createProcessorRelationships([]int{0, 1, 2}),
|
|
expectedNumOfCores: 1,
|
|
expectedNumOfSockets: 1,
|
|
expectedNodes: []cadvisorapi.Node{
|
|
{
|
|
Id: 0,
|
|
Cores: []cadvisorapi.Core{
|
|
{
|
|
Id: 1,
|
|
Threads: []int{0, 1, 2},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "single core, multiple groups",
|
|
buffer: createProcessorRelationships([]int{0, 64}),
|
|
expectedNumOfCores: 1,
|
|
expectedNumOfSockets: 1,
|
|
expectedNodes: []cadvisorapi.Node{
|
|
{
|
|
Id: 0,
|
|
Cores: []cadvisorapi.Core{
|
|
{
|
|
Id: 1,
|
|
Threads: []int{0, 64},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "buffer to small",
|
|
buffer: createProcessorRelationships([]int{0, 64})[:48],
|
|
expectedNumOfCores: 1,
|
|
expectedNumOfSockets: 1,
|
|
expectedNodes: []cadvisorapi.Node{
|
|
{
|
|
Id: 0,
|
|
Cores: []cadvisorapi.Core{
|
|
{
|
|
Id: 1,
|
|
Threads: []int{0, 64},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
numOfCores, numOfSockets, nodes, err := convertWinApiToCadvisorApi(tt.buffer)
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
assert.Equalf(t, tt.expectedNumOfCores, numOfCores, "num of cores")
|
|
assert.Equalf(t, tt.expectedNumOfSockets, numOfSockets, "num of sockets")
|
|
for node := range nodes {
|
|
assert.Equalf(t, tt.expectedNodes[node].Id, nodes[node].Id, "node id")
|
|
for core := range nodes[node].Cores {
|
|
assert.Equalf(t, tt.expectedNodes[node].Cores[core].Id, nodes[node].Cores[core].Id, "core id")
|
|
assert.Equalf(t, len(tt.expectedNodes[node].Cores[core].Threads), len(nodes[node].Cores[core].Threads), "num of threads")
|
|
for _, thread := range nodes[node].Cores[core].Threads {
|
|
assert.Truef(t, containsThread(tt.expectedNodes[node].Cores[core].Threads, thread), "thread %d", thread)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func containsThread(threads []int, thread int) bool {
|
|
for _, t := range threads {
|
|
if t == thread {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func genBuffer(infos ...systemLogicalProcessorInformationEx) []byte {
|
|
var buffer []byte
|
|
for _, info := range infos {
|
|
buffer = append(buffer, structToBytes(info)...)
|
|
}
|
|
return buffer
|
|
}
|
|
|
|
func createProcessorRelationships(cpus []int) []byte {
|
|
groups := CpusToGroupAffinity(cpus)
|
|
grouplen := len(groups)
|
|
groupAffinities := make([]GroupAffinity, 0, grouplen)
|
|
for _, group := range groups {
|
|
groupAffinities = append(groupAffinities, *group)
|
|
}
|
|
return genBuffer(systemLogicalProcessorInformationEx{
|
|
Relationship: uint32(relationProcessorCore),
|
|
Size: uint32(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_SIZE + PROCESSOR_RELATIONSHIP_SIZE + (GROUP_AFFINITY_SIZE * grouplen)),
|
|
data: processorRelationship{
|
|
Flags: 0,
|
|
EfficiencyClass: 0,
|
|
Reserved: [20]byte{},
|
|
GroupCount: uint16(grouplen),
|
|
GroupMasks: groupAffinities,
|
|
},
|
|
}, systemLogicalProcessorInformationEx{
|
|
Relationship: uint32(relationNumaNode),
|
|
Size: uint32(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_SIZE + NUMA_NODE_RELATIONSHIP_SIZE + (GROUP_AFFINITY_SIZE * grouplen)),
|
|
data: numaNodeRelationship{
|
|
NodeNumber: 0,
|
|
Reserved: [18]byte{},
|
|
GroupCount: uint16(grouplen),
|
|
GroupMasks: groupAffinities,
|
|
}}, systemLogicalProcessorInformationEx{
|
|
Relationship: uint32(relationProcessorPackage),
|
|
Size: uint32(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_SIZE + PROCESSOR_RELATIONSHIP_SIZE + (GROUP_AFFINITY_SIZE * grouplen)),
|
|
data: processorRelationship{
|
|
Flags: 0,
|
|
EfficiencyClass: 0,
|
|
Reserved: [20]byte{},
|
|
GroupCount: uint16(grouplen),
|
|
GroupMasks: groupAffinities,
|
|
},
|
|
})
|
|
}
|
|
|
|
const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_SIZE = 8
|
|
const PROCESSOR_RELATIONSHIP_SIZE = 24
|
|
const NUMA_NODE_RELATIONSHIP_SIZE = 24
|
|
const GROUP_AFFINITY_SIZE = int(unsafe.Sizeof(GroupAffinity{})) // this one is known at compile time
|
|
|
|
func structToBytes(info systemLogicalProcessorInformationEx) []byte {
|
|
var pri []byte = (*(*[SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_SIZE]byte)(unsafe.Pointer(&info)))[:SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX_SIZE]
|
|
|
|
switch info.data.(type) {
|
|
case processorRelationship:
|
|
rel := info.data.(processorRelationship)
|
|
var prBytes []byte = (*(*[PROCESSOR_RELATIONSHIP_SIZE]byte)(unsafe.Pointer(&rel)))[:PROCESSOR_RELATIONSHIP_SIZE]
|
|
pri = append(pri, prBytes...)
|
|
|
|
groupAffinities := rel.GroupMasks.([]GroupAffinity)
|
|
|
|
for _, groupAffinity := range groupAffinities {
|
|
var groupByte []byte = (*(*[GROUP_AFFINITY_SIZE]byte)(unsafe.Pointer(&groupAffinity)))[:]
|
|
pri = append(pri, groupByte...)
|
|
}
|
|
case numaNodeRelationship:
|
|
numa := info.data.(numaNodeRelationship)
|
|
var nameBytes []byte = (*(*[NUMA_NODE_RELATIONSHIP_SIZE]byte)(unsafe.Pointer(&numa)))[:NUMA_NODE_RELATIONSHIP_SIZE]
|
|
pri = append(pri, nameBytes...)
|
|
|
|
groupAffinities := numa.GroupMasks.([]GroupAffinity)
|
|
|
|
for _, groupAffinity := range groupAffinities {
|
|
var groupByte []byte = (*(*[GROUP_AFFINITY_SIZE]byte)(unsafe.Pointer(&groupAffinity)))[:]
|
|
pri = append(pri, groupByte...)
|
|
}
|
|
}
|
|
|
|
return pri
|
|
}
|