1
0

mockcpu_test.go 5.1 KB

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