blob: 3131e8c161e8b385cfb5ddcaf6d37cd46d57f499 [file] [log] [blame]
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
package cpuid
import (
"fmt"
"strings"
"testing"
)
func TestLastID(t *testing.T) {
if lastID.String() != "lastID" {
t.Fatal("stringer not updated, run go generate")
}
}
func TestLastVendorID(t *testing.T) {
if lastVendor.String() != "lastVendor" {
t.Fatal("stringer not updated, run go generate")
}
}
// There is no real way to test a CPU identifier, since results will
// obviously differ on each machine.
func TestCPUID(t *testing.T) {
Detect()
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("Name:", CPU.BrandName)
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("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("VM:", CPU.VM())
t.Log("BoostFreq:", CPU.BoostFreq, "Hz")
}
func TestExample(t *testing.T) {
Detect()
// Print basic CPU information:
fmt.Println("Name:", CPU.BrandName)
fmt.Println("PhysicalCores:", CPU.PhysicalCores)
fmt.Println("ThreadsPerCore:", CPU.ThreadsPerCore)
fmt.Println("LogicalCores:", CPU.LogicalCores)
fmt.Println("Family", CPU.Family, "Model:", CPU.Model, "Vendor ID:", CPU.VendorID)
fmt.Println("Features:", strings.Join(CPU.FeatureSet(), ","))
fmt.Println("Cacheline bytes:", CPU.CacheLine)
fmt.Println("L1 Data Cache:", CPU.Cache.L1D, "bytes")
fmt.Println("L1 Instruction Cache:", CPU.Cache.L1D, "bytes")
fmt.Println("L2 Cache:", CPU.Cache.L2, "bytes")
fmt.Println("L3 Cache:", CPU.Cache.L3, "bytes")
fmt.Println("Frequency", CPU.Hz, "hz")
// Test if we have these specific features:
if CPU.Supports(SSE, SSE2) {
fmt.Println("We have Streaming SIMD 2 Extensions")
}
}
func TestDumpCPUID(t *testing.T) {
n := int(maxFunctionID())
for i := 0; i <= n; i++ {
a, b, c, d := cpuidex(uint32(i), 0)
t.Logf("CPUID %08x: %08x-%08x-%08x-%08x", i, a, b, c, d)
ex := uint32(1)
for {
a2, b2, c2, d2 := cpuidex(uint32(i), ex)
if a2 == a && b2 == b && d2 == d || ex > 50 || a2 == 0 {
break
}
t.Logf("CPUID %08x: %08x-%08x-%08x-%08x", i, a2, b2, c2, d2)
a, b, c, d = a2, b2, c2, d2
ex++
}
}
n2 := maxExtendedFunction()
for i := uint32(0x80000000); i <= n2; i++ {
a, b, c, d := cpuid(i)
t.Logf("CPUID %08x: %08x-%08x-%08x-%08x", i, a, b, c, d)
}
}
func Example() {
// Print basic CPU information:
fmt.Println("Name:", CPU.BrandName)
fmt.Println("PhysicalCores:", CPU.PhysicalCores)
fmt.Println("ThreadsPerCore:", CPU.ThreadsPerCore)
fmt.Println("LogicalCores:", CPU.LogicalCores)
fmt.Println("Family", CPU.Family, "Model:", CPU.Model)
fmt.Println("Features:", CPU.FeatureSet())
fmt.Println("Cacheline bytes:", CPU.CacheLine)
}
func TestBrandNameZero(t *testing.T) {
if len(CPU.BrandName) > 0 {
// Cut out last byte
last := []byte(CPU.BrandName[len(CPU.BrandName)-1:])
if last[0] == 0 {
t.Fatal("last byte was zero")
} else if last[0] == 32 {
t.Fatal("whitespace wasn't trimmed")
}
}
}
// TestSGX tests SGX detection
func TestSGX(t *testing.T) {
got := CPU.SGX.Available
expected := CPU.featureSet.inSet(SGX)
if got != expected {
t.Fatalf("SGX: expected %v, got %v", expected, got)
}
t.Log("SGX Support:", got)
if CPU.SGX.Available {
var total uint64 = 0
leaves := false
for _, s := range CPU.SGX.EPCSections {
t.Logf("SGX EPC section: base address 0x%x, size %v", s.BaseAddress, s.EPCSize)
total += s.EPCSize
leaves = true
}
if leaves && total == 0 {
t.Fatal("SGX enabled without any available EPC memory")
}
}
}
// TestAMDMemEncryption tests AMDMemEncryption detection
func TestAMDMemEncryption(t *testing.T) {
got := CPU.AMDMemEncryption.Available
expected := CPU.featureSet.inSet(SME) || CPU.featureSet.inSet(SEV)
if got != expected {
t.Fatalf("AMDMemEncryption: expected %v, got %v", expected, got)
}
t.Log("AMDMemEncryption Support:", got)
}
func TestHas(t *testing.T) {
Detect()
defer Detect()
feats := CPU.FeatureSet()
for _, feat := range feats {
f := ParseFeature(feat)
if f == UNKNOWN {
t.Error("Got unknown feature:", feat)
continue
}
if !CPU.Has(f) {
t.Error("CPU.Has returned false, want true")
}
if !CPU.Supports(f) {
t.Error("CPU.Supports returned false, want true")
}
// Disable it.
CPU.Disable(f)
if CPU.Has(f) {
t.Error("CPU.Has returned true, want false")
}
if CPU.Supports(f) {
t.Error("CPU.Supports returned true, want false")
}
// Reenable
CPU.Enable(f)
if !CPU.Has(f) {
t.Error("CPU.Has returned false, want true")
}
if !CPU.Supports(f) {
t.Error("CPU.Supports returned false, want true")
}
}
}
// TestSGXLC tests SGX Launch Control detection
func TestSGXLC(t *testing.T) {
got := CPU.SGX.LaunchControl
expected := CPU.featureSet.inSet(SGXLC)
if got != expected {
t.Fatalf("SGX: expected %v, got %v", expected, got)
}
t.Log("SGX Launch Control Support:", got)
}
// Test VM function
func TestVM(t *testing.T) {
got := CPU.VM()
expected := CPU.featureSet.inSet(HYPERVISOR)
if got != expected {
t.Fatalf("TestVM: expected %v, got %v", expected, got)
}
t.Log("TestVM:", got)
}
// Test RTCounter function
func TestRtCounter(t *testing.T) {
a := CPU.RTCounter()
b := CPU.RTCounter()
t.Log("CPU Counter:", a, b, b-a)
}
// Prints the value of Ia32TscAux()
func TestIa32TscAux(t *testing.T) {
ecx := CPU.Ia32TscAux()
t.Logf("Ia32TscAux:0x%x\n", ecx)
if ecx != 0 {
chip := (ecx & 0xFFF000) >> 12
core := ecx & 0xFFF
t.Log("Likely chip, core:", chip, core)
}
}
func TestThreadsPerCoreNZ(t *testing.T) {
if CPU.ThreadsPerCore == 0 {
t.Fatal("threads per core is zero")
}
}
// Prints the value of LogicalCPU()
func TestLogicalCPU(t *testing.T) {
t.Log("Currently executing on cpu:", CPU.LogicalCPU())
}
func TestMaxFunction(t *testing.T) {
expect := maxFunctionID()
if CPU.maxFunc != expect {
t.Fatal("Max function does not match, expected", expect, "but got", CPU.maxFunc)
}
expect = maxExtendedFunction()
if CPU.maxExFunc != expect {
t.Fatal("Max Extended function does not match, expected", expect, "but got", CPU.maxFunc)
}
}
// This example will calculate the chip/core number on Linux
// Linux encodes numa id (<<12) and core id (8bit) into TSC_AUX.
func ExampleCPUInfo_Ia32TscAux() {
ecx := CPU.Ia32TscAux()
if ecx == 0 {
fmt.Println("Unknown CPU ID")
return
}
chip := (ecx & 0xFFF000) >> 12
core := ecx & 0xFFF
fmt.Println("Chip, Core:", chip, core)
}
func TestCombineFeatures(t *testing.T) {
cpu := CPU
for i := FeatureID(0); i < lastID; i++ {
if cpu.Has(i) != cpu.HasAll(CombineFeatures(i)) {
t.Errorf("id %d:%s mismatch", i, i.String())
}
}
}
func BenchmarkFlags(b *testing.B) {
var a bool
var cpu = CPU
b.Run("ids", func(b *testing.B) {
for i := 0; i < b.N; i++ {
a = cpu.Supports(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE) || a
}
_ = a
})
b.Run("features", func(b *testing.B) {
f := CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE)
for i := 0; i < b.N; i++ {
a = cpu.HasAll(f) || a
}
_ = a
})
b.Run("id", func(b *testing.B) {
for i := 0; i < b.N; i++ {
a = cpu.Has(CMOV) || a
}
_ = a
})
b.Run("feature", func(b *testing.B) {
f := CombineFeatures(CMOV)
for i := 0; i < b.N; i++ {
a = cpu.HasAll(f) || a
}
_ = a
})
}