123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- package cpuid
- import (
- "archive/zip"
- "fmt"
- "io/ioutil"
- "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 {
- 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 0, 0
- }
- 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 {
- 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)
- n = maxExtendedFunction()
- t.Logf("Max Extended Function:0x%x\n", n)
- t.Log("PhysicalCores:", CPU.PhysicalCores)
- t.Log("ThreadsPerCore:", CPU.ThreadsPerCore)
- t.Log("LogicalCores:", CPU.LogicalCores)
- t.Log("Family", CPU.Family, "Model:", CPU.Model)
- t.Log("Features:", CPU.Features)
- 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")
- 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.HTT() {
- t.Fatalf("Hyperthreading not detected")
- }
- if CPU.ThreadsPerCore == 1 && CPU.HTT() {
- t.Fatalf("Hyperthreading detected, but only 1 Thread per core")
- }
- restore()
- }
- Detect()
- }
|