1 package cpuid
2
3 import (
4 "archive/zip"
5 "fmt"
6 "io/ioutil"
7 "math"
8 "path/filepath"
9 "sort"
10 "strings"
11 "testing"
12 )
13
14 type fakecpuid map[uint32][][]uint32
15
16 type idfuncs struct {
17 cpuid func(op uint32) (eax, ebx, ecx, edx uint32)
18 cpuidex func(op, op2 uint32) (eax, ebx, ecx, edx uint32)
19 xgetbv func(index uint32) (eax, edx uint32)
20 }
21
22 func (f fakecpuid) String() string {
23 var out = make([]string, 0, len(f))
24 for key, val := range f {
25 for _, v := range val {
26 out = append(out, fmt.Sprintf("CPUID %08x: [%08x, %08x, %08x, %08x]", key, v[0], v[1], v[2], v[3]))
27 }
28 }
29 sorter := sort.StringSlice(out)
30 sort.Sort(&sorter)
31 return strings.Join(sorter, "\n")
32 }
33
34 func mockCPU(def []byte) func() {
35 lines := strings.Split(string(def), "\n")
36 anyfound := false
37 fakeID := make(fakecpuid)
38 for _, line := range lines {
39 line = strings.Trim(line, "\r\t ")
40 if !strings.HasPrefix(line, "CPUID") {
41 continue
42 }
43
44 if strings.HasPrefix(line, "CPUID 00000000") {
45 if anyfound {
46 break
47 }
48 }
49
50
51
52 items := strings.Split(line, ":")
53 if len(items) < 2 {
54 if len(line) == 51 || len(line) == 50 {
55 items = []string{line[0:14], line[15:]}
56 } else {
57 items = strings.Split(line, "\t")
58 if len(items) != 2 {
59
60 continue
61 }
62 }
63 }
64 items = items[0:2]
65 vals := strings.Trim(items[1], "\r\n ")
66
67 var idV uint32
68 n, err := fmt.Sscanf(items[0], "CPUID %x", &idV)
69 if err != nil || n != 1 {
70 continue
71 }
72 existing, ok := fakeID[idV]
73 if !ok {
74 existing = make([][]uint32, 0)
75 }
76
77 values := make([]uint32, 4)
78 n, err = fmt.Sscanf(vals, "%x-%x-%x-%x", &values[0], &values[1], &values[2], &values[3])
79 if n != 4 || err != nil {
80 n, err = fmt.Sscanf(vals, "%x %x %x %x", &values[0], &values[1], &values[2], &values[3])
81 if n != 4 || err != nil {
82
83 continue
84 }
85 }
86
87 existing = append(existing, values)
88 fakeID[idV] = existing
89 anyfound = true
90 }
91
92 restorer := func(f idfuncs) func() {
93 return func() {
94 cpuid = f.cpuid
95 cpuidex = f.cpuidex
96 xgetbv = f.xgetbv
97 }
98 }(idfuncs{cpuid: cpuid, cpuidex: cpuidex, xgetbv: xgetbv})
99
100 cpuid = func(op uint32) (eax, ebx, ecx, edx uint32) {
101 if op == 0x80000000 || op == 0 || op == 0x4000000c {
102 var ok bool
103 _, ok = fakeID[op]
104 if !ok {
105 return 0, 0, 0, 0
106 }
107 }
108 first, ok := fakeID[op]
109 if !ok {
110 if op > maxFunctionID() {
111 panic(fmt.Sprintf("Base not found: %v, request:%#v\n", fakeID, op))
112 } else {
113
114 return 0, 0, 0, 0
115 }
116 }
117 theid := first[0]
118 return theid[0], theid[1], theid[2], theid[3]
119 }
120 cpuidex = func(op, op2 uint32) (eax, ebx, ecx, edx uint32) {
121 if op == 0x80000000 {
122 var ok bool
123 _, ok = fakeID[op]
124 if !ok {
125 return 0, 0, 0, 0
126 }
127 }
128 first, ok := fakeID[op]
129 if !ok {
130 if op > maxExtendedFunction() {
131 panic(fmt.Sprintf("Extended not found Info: %v, request:%#v, %#v\n", fakeID, op, op2))
132 } else {
133
134 return 0, 0, 0, 0
135 }
136 }
137 if int(op2) >= len(first) {
138
139 return 0, 0, 0, 0
140 }
141 theid := first[op2]
142 return theid[0], theid[1], theid[2], theid[3]
143 }
144 xgetbv = func(index uint32) (eax, edx uint32) {
145 first, ok := fakeID[1]
146 if !ok {
147 panic(fmt.Sprintf("XGETBV not supported %v", fakeID))
148 }
149 second := first[0]
150
151 if (second[2] & 1 << 26) == 0 {
152 panic(fmt.Sprintf("XGETBV not supported %v", fakeID))
153 }
154
155 return math.MaxUint32, math.MaxUint32
156 }
157 return restorer
158 }
159
160 func TestMocks(t *testing.T) {
161 zr, err := zip.OpenReader("testdata/cpuid_data.zip")
162 if err != nil {
163 t.Skip("No testdata:", err)
164 }
165 defer zr.Close()
166 for _, f := range zr.File {
167 t.Run(filepath.Base(f.Name), func(t *testing.T) {
168 rc, err := f.Open()
169 if err != nil {
170 t.Fatal(err)
171 }
172 content, err := ioutil.ReadAll(rc)
173 if err != nil {
174 t.Fatal(err)
175 }
176 rc.Close()
177 t.Log("Opening", f.FileInfo().Name())
178 restore := mockCPU(content)
179 Detect()
180 t.Log("Name:", CPU.BrandName)
181 n := maxFunctionID()
182 t.Logf("Max Function:0x%x", n)
183 n = maxExtendedFunction()
184 t.Logf("Max Extended Function:0x%x", n)
185 t.Log("VendorString:", CPU.VendorString)
186 t.Log("VendorID:", CPU.VendorID)
187 t.Log("PhysicalCores:", CPU.PhysicalCores)
188 t.Log("ThreadsPerCore:", CPU.ThreadsPerCore)
189 t.Log("LogicalCores:", CPU.LogicalCores)
190 t.Log("Family", CPU.Family, "Model:", CPU.Model, "Stepping:", CPU.Stepping)
191 t.Log("Features:", strings.Join(CPU.FeatureSet(), ","))
192 t.Log("Microarchitecture level:", CPU.X64Level())
193 t.Log("Cacheline bytes:", CPU.CacheLine)
194 t.Log("L1 Instruction Cache:", CPU.Cache.L1I, "bytes")
195 t.Log("L1 Data Cache:", CPU.Cache.L1D, "bytes")
196 t.Log("L2 Cache:", CPU.Cache.L2, "bytes")
197 t.Log("L3 Cache:", CPU.Cache.L3, "bytes")
198 t.Log("Hz:", CPU.Hz, "Hz")
199 t.Log("Boost:", CPU.BoostFreq, "Hz")
200 if CPU.AVX10Level > 0 {
201 t.Log("AVX10 level:", CPU.AVX10Level)
202 }
203 if CPU.LogicalCores > 0 && CPU.PhysicalCores > 0 {
204 if CPU.LogicalCores != CPU.PhysicalCores*CPU.ThreadsPerCore {
205 t.Fatalf("Core count mismatch, LogicalCores (%d) != PhysicalCores (%d) * CPU.ThreadsPerCore (%d)",
206 CPU.LogicalCores, CPU.PhysicalCores, CPU.ThreadsPerCore)
207 }
208 }
209
210 if CPU.ThreadsPerCore > 1 && !CPU.Supports(HTT) {
211 t.Fatalf("Hyperthreading not detected")
212 }
213 if CPU.ThreadsPerCore == 1 && CPU.Supports(HTT) {
214 t.Fatalf("Hyperthreading detected, but only 1 Thread per core")
215 }
216 restore()
217 })
218 }
219
220 Detect()
221
222 }
223
View as plain text