| package cpuid |
| |
| import ( |
| "archive/zip" |
| "fmt" |
| "io/ioutil" |
| "math" |
| "path/filepath" |
| "sort" |
| "strings" |
| "testing" |
| ) |
| |
| type fakecpuid map[uint32][][]uint32 |
| |
| type idfuncs struct { |
| cpuid func(op uint32) (eax, ebx, ecx, edx uint32) |
| cpuidex func(op, op2 uint32) (eax, ebx, ecx, edx uint32) |
| xgetbv func(index uint32) (eax, edx uint32) |
| } |
| |
| func (f fakecpuid) String() string { |
| var out = make([]string, 0, len(f)) |
| for key, val := range f { |
| for _, v := range val { |
| out = append(out, fmt.Sprintf("CPUID %08x: [%08x, %08x, %08x, %08x]", key, v[0], v[1], v[2], v[3])) |
| } |
| } |
| sorter := sort.StringSlice(out) |
| sort.Sort(&sorter) |
| return strings.Join(sorter, "\n") |
| } |
| |
| func mockCPU(def []byte) func() { |
| lines := strings.Split(string(def), "\n") |
| anyfound := false |
| fakeID := make(fakecpuid) |
| for _, line := range lines { |
| line = strings.Trim(line, "\r\t ") |
| if !strings.HasPrefix(line, "CPUID") { |
| continue |
| } |
| // Only collect for first cpu |
| if strings.HasPrefix(line, "CPUID 00000000") { |
| if anyfound { |
| break |
| } |
| } |
| //if !strings.Contains(line, "-") { |
| // continue |
| //} |
| items := strings.Split(line, ":") |
| if len(items) < 2 { |
| if len(line) == 51 || len(line) == 50 { |
| items = []string{line[0:14], line[15:]} |
| } else { |
| items = strings.Split(line, "\t") |
| if len(items) != 2 { |
| //fmt.Println("not found:", line, "len:", len(line)) |
| continue |
| } |
| } |
| } |
| items = items[0:2] |
| vals := strings.Trim(items[1], "\r\n ") |
| |
| var idV uint32 |
| n, err := fmt.Sscanf(items[0], "CPUID %x", &idV) |
| if err != nil || n != 1 { |
| continue |
| } |
| existing, ok := fakeID[idV] |
| if !ok { |
| existing = make([][]uint32, 0) |
| } |
| |
| values := make([]uint32, 4) |
| n, err = fmt.Sscanf(vals, "%x-%x-%x-%x", &values[0], &values[1], &values[2], &values[3]) |
| if n != 4 || err != nil { |
| n, err = fmt.Sscanf(vals, "%x %x %x %x", &values[0], &values[1], &values[2], &values[3]) |
| if n != 4 || err != nil { |
| //fmt.Println("scanned", vals, "got", n, "Err:", err) |
| continue |
| } |
| } |
| |
| existing = append(existing, values) |
| fakeID[idV] = existing |
| anyfound = true |
| } |
| |
| restorer := func(f idfuncs) func() { |
| return func() { |
| cpuid = f.cpuid |
| cpuidex = f.cpuidex |
| xgetbv = f.xgetbv |
| } |
| }(idfuncs{cpuid: cpuid, cpuidex: cpuidex, xgetbv: xgetbv}) |
| |
| cpuid = func(op uint32) (eax, ebx, ecx, edx uint32) { |
| if op == 0x80000000 || op == 0 || op == 0x4000000c { |
| var ok bool |
| _, ok = fakeID[op] |
| if !ok { |
| return 0, 0, 0, 0 |
| } |
| } |
| first, ok := fakeID[op] |
| if !ok { |
| if op > maxFunctionID() { |
| panic(fmt.Sprintf("Base not found: %v, request:%#v\n", fakeID, op)) |
| } else { |
| // we have some entries missing |
| return 0, 0, 0, 0 |
| } |
| } |
| theid := first[0] |
| return theid[0], theid[1], theid[2], theid[3] |
| } |
| cpuidex = func(op, op2 uint32) (eax, ebx, ecx, edx uint32) { |
| if op == 0x80000000 { |
| var ok bool |
| _, ok = fakeID[op] |
| if !ok { |
| return 0, 0, 0, 0 |
| } |
| } |
| first, ok := fakeID[op] |
| if !ok { |
| if op > maxExtendedFunction() { |
| panic(fmt.Sprintf("Extended not found Info: %v, request:%#v, %#v\n", fakeID, op, op2)) |
| } else { |
| // we have some entries missing |
| return 0, 0, 0, 0 |
| } |
| } |
| if int(op2) >= len(first) { |
| //fmt.Printf("Extended not found Info: %v, request:%#v, %#v\n", fakeID, op, op2) |
| return 0, 0, 0, 0 |
| } |
| theid := first[op2] |
| return theid[0], theid[1], theid[2], theid[3] |
| } |
| xgetbv = func(index uint32) (eax, edx uint32) { |
| first, ok := fakeID[1] |
| if !ok { |
| panic(fmt.Sprintf("XGETBV not supported %v", fakeID)) |
| } |
| second := first[0] |
| // ECX bit 26 must be set |
| if (second[2] & 1 << 26) == 0 { |
| panic(fmt.Sprintf("XGETBV not supported %v", fakeID)) |
| } |
| // We don't have any data to return, unfortunately |
| return math.MaxUint32, math.MaxUint32 |
| } |
| return restorer |
| } |
| |
| func TestMocks(t *testing.T) { |
| zr, err := zip.OpenReader("testdata/cpuid_data.zip") |
| if err != nil { |
| t.Skip("No testdata:", err) |
| } |
| defer zr.Close() |
| for _, f := range zr.File { |
| t.Run(filepath.Base(f.Name), func(t *testing.T) { |
| rc, err := f.Open() |
| if err != nil { |
| t.Fatal(err) |
| } |
| content, err := ioutil.ReadAll(rc) |
| if err != nil { |
| t.Fatal(err) |
| } |
| rc.Close() |
| t.Log("Opening", f.FileInfo().Name()) |
| restore := mockCPU(content) |
| Detect() |
| t.Log("Name:", CPU.BrandName) |
| n := maxFunctionID() |
| t.Logf("Max Function:0x%x", n) |
| n = maxExtendedFunction() |
| t.Logf("Max Extended Function:0x%x", n) |
| t.Log("VendorString:", CPU.VendorString) |
| t.Log("VendorID:", CPU.VendorID) |
| t.Log("PhysicalCores:", CPU.PhysicalCores) |
| t.Log("ThreadsPerCore:", CPU.ThreadsPerCore) |
| t.Log("LogicalCores:", CPU.LogicalCores) |
| t.Log("Family", CPU.Family, "Model:", CPU.Model, "Stepping:", CPU.Stepping) |
| t.Log("Features:", strings.Join(CPU.FeatureSet(), ",")) |
| t.Log("Microarchitecture level:", CPU.X64Level()) |
| t.Log("Cacheline bytes:", CPU.CacheLine) |
| t.Log("L1 Instruction Cache:", CPU.Cache.L1I, "bytes") |
| t.Log("L1 Data Cache:", CPU.Cache.L1D, "bytes") |
| t.Log("L2 Cache:", CPU.Cache.L2, "bytes") |
| t.Log("L3 Cache:", CPU.Cache.L3, "bytes") |
| t.Log("Hz:", CPU.Hz, "Hz") |
| t.Log("Boost:", CPU.BoostFreq, "Hz") |
| if CPU.AVX10Level > 0 { |
| t.Log("AVX10 level:", CPU.AVX10Level) |
| } |
| if CPU.LogicalCores > 0 && CPU.PhysicalCores > 0 { |
| if CPU.LogicalCores != CPU.PhysicalCores*CPU.ThreadsPerCore { |
| t.Fatalf("Core count mismatch, LogicalCores (%d) != PhysicalCores (%d) * CPU.ThreadsPerCore (%d)", |
| CPU.LogicalCores, CPU.PhysicalCores, CPU.ThreadsPerCore) |
| } |
| } |
| |
| if CPU.ThreadsPerCore > 1 && !CPU.Supports(HTT) { |
| t.Fatalf("Hyperthreading not detected") |
| } |
| if CPU.ThreadsPerCore == 1 && CPU.Supports(HTT) { |
| t.Fatalf("Hyperthreading detected, but only 1 Thread per core") |
| } |
| restore() |
| }) |
| } |
| |
| Detect() |
| |
| } |