Skip to content

Commit

Permalink
Add CPU Stepping
Browse files Browse the repository at this point in the history
And fix family/model for very old CPUs.

Fixes #111
  • Loading branch information
klauspost committed Jul 14, 2022
1 parent 4645384 commit 828051c
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 54 deletions.
2 changes: 1 addition & 1 deletion cmd/cpuid/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func main() {
fmt.Println("PhysicalCores:", cpuid.CPU.PhysicalCores)
fmt.Println("Threads Per Core:", cpuid.CPU.ThreadsPerCore)
fmt.Println("Logical Cores:", cpuid.CPU.LogicalCores)
fmt.Println("CPU Family", cpuid.CPU.Family, "Model:", cpuid.CPU.Model)
fmt.Println("CPU Family", cpuid.CPU.Family, "Model:", cpuid.CPU.Model, "Stepping:", cpuid.CPU.Stepping)
fmt.Println("Features:", strings.Join(cpuid.CPU.FeatureSet(), ","))
fmt.Println("Microarchitecture level:", cpuid.CPU.X64Level())
fmt.Println("Cacheline bytes:", cpuid.CPU.CacheLine)
Expand Down
28 changes: 21 additions & 7 deletions cpuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ type CPUInfo struct {
LogicalCores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable.
Family int // CPU family number
Model int // CPU model number
Stepping int // CPU stepping info
CacheLine int // Cache line size in bytes. Will be 0 if undetectable.
Hz int64 // Clock speed, if known, 0 otherwise. Will attempt to contain base clock speed.
BoostFreq int64 // Max clock speed, if known, 0 otherwise
Expand Down Expand Up @@ -677,7 +678,7 @@ func threadsPerCore() int {
if vend == AMD {
// Workaround for AMD returning 0, assume 2 if >= Zen 2
// It will be more correct than not.
fam, _ := familyModel()
fam, _, _ := familyModel()
_, _, _, d := cpuid(1)
if (d&(1<<28)) != 0 && fam >= 23 {
return 2
Expand Down Expand Up @@ -715,14 +716,27 @@ func logicalCores() int {
}
}

func familyModel() (int, int) {
func familyModel() (family, model, stepping int) {
if maxFunctionID() < 0x1 {
return 0, 0
return 0, 0, 0
}
eax, _, _, _ := cpuid(1)
family := ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff)
model := ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0)
return int(family), int(model)
// If BaseFamily[3:0] is less than Fh then ExtendedFamily[7:0] is reserved and Family is equal to BaseFamily[3:0].
family = int((eax >> 8) & 0xf)
extFam := family == 0x6 // Intel is 0x6, needs extended model.
if family == 0xf {
// Add ExtFamily
family += int((eax >> 20) & 0xff)
extFam = true
}
// If BaseFamily[3:0] is less than 0Fh then ExtendedModel[3:0] is reserved and Model is equal to BaseModel[3:0].
model = int((eax >> 4) & 0xf)
if extFam {
// Add ExtModel
model += int((eax >> 12) & 0xf0)
}
stepping = int(eax & 0xf)
return family, model, stepping
}

func physicalCores() int {
Expand Down Expand Up @@ -974,7 +988,7 @@ func support() flagSet {
if mfi < 0x1 {
return fs
}
family, model := familyModel()
family, model, _ := familyModel()

_, _, c, d := cpuid(1)
fs.setIf((d&(1<<0)) != 0, X87)
Expand Down
2 changes: 1 addition & 1 deletion cpuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestCPUID(t *testing.T) {
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("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")
Expand Down
2 changes: 1 addition & 1 deletion detect_x86.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func addInfo(c *CPUInfo, safe bool) {
c.maxExFunc = maxExtendedFunction()
c.BrandName = brandName()
c.CacheLine = cacheLine()
c.Family, c.Model = familyModel()
c.Family, c.Model, c.Stepping = familyModel()
c.featureSet = support()
c.SGX = hasSGX(c.featureSet.inSet(SGX), c.featureSet.inSet(SGXLC))
c.ThreadsPerCore = threadsPerCore()
Expand Down
92 changes: 48 additions & 44 deletions mockcpu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"math"
"path/filepath"
"sort"
"strings"
"testing"
Expand Down Expand Up @@ -163,53 +164,56 @@ func TestMocks(t *testing.T) {
}
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 = 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)
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.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)
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.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()
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()

}

0 comments on commit 828051c

Please sign in to comment.