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