diff --git a/pkg/cpuid/cpuid.go b/pkg/cpuid/cpuid.go index f4b1db896..c19606898 100644 --- a/pkg/cpuid/cpuid.go +++ b/pkg/cpuid/cpuid.go @@ -274,6 +274,7 @@ var x86FeatureStrings = map[Feature]string{ X86FeatureTM2: "tm2", X86FeatureSSSE3: "ssse3", X86FeatureCNXTID: "cid", + X86FeatureSDBG: "sdbg", X86FeatureFMA: "fma", X86FeatureCX16: "cx16", X86FeatureXTPR: "xtpr", @@ -352,10 +353,9 @@ var x86FeatureStrings = map[Feature]string{ X86FeatureAVX512VL: "avx512vl", // Block 3. - X86FeaturePREFETCHWT1: "prefetchwt1", - X86FeatureAVX512VBMI: "avx512vbmi", - X86FeatureUMIP: "umip", - X86FeaturePKU: "pku", + X86FeatureAVX512VBMI: "avx512vbmi", + X86FeatureUMIP: "umip", + X86FeaturePKU: "pku", // Block 4. X86FeatureXSAVEOPT: "xsaveopt", @@ -405,7 +405,6 @@ var x86FeatureStrings = map[Feature]string{ // flags, but will not get printed out in /proc/cpuinfo. var x86FeatureParseOnlyStrings = map[Feature]string{ // Block 0. - X86FeatureSDBG: "sdbg", X86FeatureOSXSAVE: "osxsave", // Block 2. @@ -414,6 +413,9 @@ var x86FeatureParseOnlyStrings = map[Feature]string{ X86FeatureIPT: "pt", X86FeatureCLFLUSHOPT: "clfushopt", + // Block 3. + X86FeaturePREFETCHWT1: "prefetchwt1", + // Block 4. X86FeatureXSAVES: "xsaves", } diff --git a/pkg/cpuid/cpuid_parse_test.go b/pkg/cpuid/cpuid_parse_test.go index 81b06f48c..954ed53b6 100644 --- a/pkg/cpuid/cpuid_parse_test.go +++ b/pkg/cpuid/cpuid_parse_test.go @@ -15,23 +15,112 @@ package cpuid import ( + "fmt" "io/ioutil" + "regexp" + "strconv" "strings" + "syscall" "testing" ) -// TestHostFeatureFlags ensures that package cpuid recognizes all features -// present on this host. +func kernelVersion() (int, int, error) { + var u syscall.Utsname + if err := syscall.Uname(&u); err != nil { + return 0, 0, err + } + + var r string + for _, b := range u.Release { + if b == 0 { + break + } + r += string(b) + } + + s := strings.Split(r, ".") + if len(s) < 2 { + return 0, 0, fmt.Errorf("kernel release missing major and minor component: %s", r) + } + + major, err := strconv.Atoi(s[0]) + if err != nil { + return 0, 0, fmt.Errorf("error parsing major version %q in %q: %v", s[0], r, err) + } + + minor, err := strconv.Atoi(s[1]) + if err != nil { + return 0, 0, fmt.Errorf("error parsing minor version %q in %q: %v", s[1], r, err) + } + + return major, minor, nil +} + +// TestHostFeatureFlags tests that all features detected by HostFeatureSet are +// on the host. +// +// It does *not* verify that all features reported by the host are detected by +// HostFeatureSet. +// +// i.e., test that HostFeatureSet is a subset of the host features. func TestHostFeatureFlags(t *testing.T) { cpuinfoBytes, _ := ioutil.ReadFile("/proc/cpuinfo") cpuinfo := string(cpuinfoBytes) t.Logf("Host cpu info:\n%s", cpuinfo) - for f := range HostFeatureSet().Set { - if f.flagString(false) == "" { + major, minor, err := kernelVersion() + if err != nil { + t.Fatalf("Unable to parse kernel version: %v", err) + } + + re := regexp.MustCompile(`(?m)^flags\s+: (.*)$`) + m := re.FindStringSubmatch(cpuinfo) + if len(m) != 2 { + t.Fatalf("Unable to extract flags from %q", cpuinfo) + } + + cpuinfoFlags := make(map[string]struct{}) + for _, f := range strings.Split(m[1], " ") { + cpuinfoFlags[f] = struct{}{} + } + + fs := HostFeatureSet() + + // All features have a string and appear in host cpuinfo. + for f := range fs.Set { + name := f.flagString(false) + if name == "" { t.Errorf("Non-parsable feature: %v", f) } - if s := f.flagString(true); !strings.Contains(cpuinfo, s) { + + // Special cases not consistently visible. We don't mind if + // they are exposed in earlier versions. + switch { + // Block 0. + case f == X86FeatureSDBG && (major < 4 || major == 4 && minor < 3): + // SDBG only exposed in + // b1c599b8ff80ea79b9f8277a3f9f36a7b0cfedce (4.3). + continue + // Block 3. + case f == X86FeatureAVX512VBMI && (major < 4 || major == 4 && minor < 10): + // AVX512VBMI only exposed in + // a8d9df5a509a232a959e4ef2e281f7ecd77810d6 (4.10). + continue + case f == X86FeatureUMIP && (major < 4 || major == 4 && minor < 15): + // UMIP only exposed in + // 3522c2a6a4f341058b8291326a945e2a2d2aaf55 (4.15). + continue + case f == X86FeaturePKU && (major < 4 || major == 4 && minor < 9): + // PKU only exposed in dfb4a70f20c5b3880da56ee4c9484bdb4e8f1e65 + // (4.9). + continue + } + + hidden := f.flagString(true) == "" + _, ok := cpuinfoFlags[name] + if hidden && ok { + t.Errorf("Unexpectedly hidden flag: %v", f) + } else if !hidden && !ok { t.Errorf("Non-native flag: %v", f) } }