[machine] Add Windows CPU interrogation to TMM.
It turns out we can use KernelArch() for all 3 platforms, which lets us
factor that up to crossplatform.CPUs().
Factor cpuModel() out of crossplatform.CPUs() so we can unit-test it.
isaAndBitness() can be private now; we didn't end up calling it from
Windows-specific code.
Change-Id: I79dc5ffa869aba99f8fa9c9eddaa670fb5d21333
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/576156
Commit-Queue: Erik Rose <erikrose@google.com>
Reviewed-by: Eric Boren <borenet@google.com>
diff --git a/machine/go/test_machine_monitor/standalone/BUILD.bazel b/machine/go/test_machine_monitor/standalone/BUILD.bazel
index 8d8095a..d899ef5 100644
--- a/machine/go/test_machine_monitor/standalone/BUILD.bazel
+++ b/machine/go/test_machine_monitor/standalone/BUILD.bazel
@@ -43,6 +43,7 @@
],
"@io_bazel_rules_go//go/platform:windows": [
"//go/skerr",
+ "//machine/go/test_machine_monitor/standalone/crossplatform",
"//machine/go/test_machine_monitor/standalone/windows",
"@com_github_shirou_gopsutil//host",
],
diff --git a/machine/go/test_machine_monitor/standalone/crossplatform/BUILD.bazel b/machine/go/test_machine_monitor/standalone/crossplatform/BUILD.bazel
index d3d768e..587a423 100644
--- a/machine/go/test_machine_monitor/standalone/crossplatform/BUILD.bazel
+++ b/machine/go/test_machine_monitor/standalone/crossplatform/BUILD.bazel
@@ -6,7 +6,10 @@
srcs = ["crossplatform.go"],
importpath = "go.skia.org/infra/machine/go/test_machine_monitor/standalone/crossplatform",
visibility = ["//visibility:public"],
- deps = ["//go/skerr"],
+ deps = [
+ "//go/skerr",
+ "@com_github_shirou_gopsutil//host",
+ ],
)
go_test(
@@ -15,6 +18,8 @@
embed = [":crossplatform"],
deps = [
"//go/testutils/unittest",
+ "@com_github_shirou_gopsutil//host",
"@com_github_stretchr_testify//assert",
+ "@com_github_stretchr_testify//require",
],
)
diff --git a/machine/go/test_machine_monitor/standalone/crossplatform/crossplatform.go b/machine/go/test_machine_monitor/standalone/crossplatform/crossplatform.go
index 5da0ca0..19befce 100644
--- a/machine/go/test_machine_monitor/standalone/crossplatform/crossplatform.go
+++ b/machine/go/test_machine_monitor/standalone/crossplatform/crossplatform.go
@@ -7,12 +7,13 @@
"strconv"
"strings"
+ "github.com/shirou/gopsutil/host"
"go.skia.org/infra/go/skerr"
)
-// ISAAndBitness, given an architecture like "x86_64", extracts both an instruction set architecture
+// isaAndBitness, given an architecture like "x86_64", extracts both an instruction set architecture
// and bit width, e.g. "x86" and "64".
-func ISAAndBitness(arch string) (isa, bitness string, err error) {
+func isaAndBitness(arch string) (isa, bitness string, err error) {
// gopsutil can spit out i386, i686, arm, aarch64, ia64, or x86_64 under Windows; "" as a
// fallback; whatever uname's utsname.machine struct member (a string) has on POSIX. On our lab
// Macs, that's arm64 or x64_86 (exposed by uname -m). Our lab Linuxes: x86_64.
@@ -57,26 +58,36 @@
return ""
}
+// cpuModel takes a vendor name and a brand string (see CPUs()) and synthesizes a descriptive model
+// string from the two. If it fails to do so, it returns "".
+func cpuModel(vendor, brandString string) string {
+ if vendor == "GenuineIntel" {
+ return intelModel(brandString)
+ } else {
+ return strings.ReplaceAll(brandString, " ", "_")
+ }
+}
+
// CPUs is the brains behind various platform-specific CPUs() functions, broken off for testing. It
// takes the architecture, vendor name and a "brand string", which is a model signifier whose format
// is vendor-specific, and returns a Swarming-style description of the host's CPU, in various
// precisions, e.g. ["x86", "x86-64", "x86-64-i5-5350U"]. The first (ISA) and second (bit width)
// will always be returned (if returned error is nil). The third (model number) will be added if we
// succeed in extracting it.
-func CPUs(arch string, vendor string, brandString string) ([]string, error) {
- isa, bitness, err := ISAAndBitness(arch)
+func CPUs(vendor, brandString string) ([]string, error) {
+ arch, err := host.KernelArch()
+ if err != nil {
+ return nil, skerr.Wrapf(err, "failed to get CPU architecture")
+ }
+
+ isa, bitness, err := isaAndBitness(arch)
if err != nil {
return nil, skerr.Wrap(err)
}
ret := []string{isa, fmt.Sprintf("%s-%s", isa, bitness)}
- var model string
- if vendor == "GenuineIntel" {
- model = intelModel(brandString)
- } else {
- model = strings.ReplaceAll(brandString, " ", "_")
- }
+ model := cpuModel(vendor, brandString)
if model != "" {
ret = append(ret, fmt.Sprintf("%s-%s-%s", isa, bitness, model))
}
diff --git a/machine/go/test_machine_monitor/standalone/crossplatform/crossplatform_test.go b/machine/go/test_machine_monitor/standalone/crossplatform/crossplatform_test.go
index a869d8e..163b630 100644
--- a/machine/go/test_machine_monitor/standalone/crossplatform/crossplatform_test.go
+++ b/machine/go/test_machine_monitor/standalone/crossplatform/crossplatform_test.go
@@ -3,68 +3,92 @@
import (
"testing"
+ "github.com/shirou/gopsutil/host"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"go.skia.org/infra/go/testutils/unittest"
)
-func assertCPUDimensions(t *testing.T, arch string, vendor string, brandString string, expected []string, failureMessage string) {
- dimensions, err := CPUs(arch, vendor, brandString)
- assert.NoError(t, err)
- assert.Equal(t, expected, dimensions, failureMessage)
+func assertIsaAndBitnessEqual(t *testing.T, arch, expectedIsa, expectedBitness string) {
+ isa, bitness, err := isaAndBitness(arch)
+ require.NoError(t, err)
+ assert.Equal(t, expectedIsa, isa)
+ assert.Equal(t, expectedBitness, bitness)
}
-func TestCPUs_ParsingAndBitWidthAndArchMapping(t *testing.T) {
+func TestIsaAndBitness(t *testing.T) {
unittest.SmallTest(t)
- assertCPUDimensions(
+ assertIsaAndBitnessEqual(t, "x86_64", "x86", "64")
+ assertIsaAndBitnessEqual(t, "amd64", "x86", "64")
+ assertIsaAndBitnessEqual(t, "aarch64", "arm64", "64")
+
+ _, _, err := isaAndBitness("kersmoo")
+ assert.Error(t, err, "An unknown CPU architecture should result in an error (and we should add it to the mapping).")
+}
+
+func assertCPUModelEqual(t *testing.T, vendor, brandString, expected, failureMessage string) {
+ assert.Equal(t, expected, cpuModel(vendor, brandString), failureMessage)
+}
+
+func TestCPUModel(t *testing.T) {
+ unittest.SmallTest(t)
+ assertCPUModelEqual(
t,
- "x86_64",
"GenuineIntel",
"Intel(R) Core(TM) i7-9750H v2 CPU @ 2.60GHz",
- []string{"x86", "x86-64", "x86-64-i7-9750H v2"},
- "x86_64 should be recognized as x86 ISA, and Intel model numbers should be extracted.",
+ "i7-9750H v2",
+ "Intel model numbers should be extracted.",
)
- assertCPUDimensions(
+ assertCPUModelEqual(
t,
- "amd64",
"Wackadoo Inc.",
"Wackadoo ALU i5-9600",
- []string{"x86", "x86-64", "x86-64-Wackadoo_ALU_i5-9600"},
- "amd64 should be recognized as x86 ISA, and non-Intel model numbers should be smooshed into snake_case.",
+ "Wackadoo_ALU_i5-9600",
+ "Non-Intel model numbers should be smooshed into snake_case.",
)
- assertCPUDimensions(
+ assertCPUModelEqual(
t,
- "aarch64",
"GenuineIntel",
"something it fails to extract anything from",
- []string{"arm64", "arm64-64"},
- "aarch64 should be recognized as arm64 ISA, and an unrecognizable Intel brand string should result in no third element.",
- )
- assertCPUDimensions(
- t,
- "arm64",
"",
- "",
- []string{"arm64", "arm64-64"},
- "Empty vendor and brand string should result in no third element.",
+ "An unrecognizable Intel brand string should result in no extracted model.",
)
- assertCPUDimensions(
+ assertCPUModelEqual(
t,
- "arm64",
"",
"Wackadoo ALU",
- []string{"arm64", "arm64-64", "arm64-64-Wackadoo_ALU"},
- "Empty vendor and full brand string should result in smooshed brand string for third element.",
+ "Wackadoo_ALU",
+ "An unrecognizable Intel brand string should result in no extracted model.",
)
}
-func TestCPUs_UnrecognizedArch_ReturnsError(t *testing.T) {
+func TestCPUs(t *testing.T) {
unittest.SmallTest(t)
- _, err := CPUs(
- "kersmoo",
- "GenuineIntel",
- "Intel(R) Core(TM) i7-9750H v2 CPU @ 2.60GHz",
+
+ arch, err := host.KernelArch()
+ require.NoError(t, err)
+ isa, bitness, err := isaAndBitness(arch)
+ require.NoError(t, err)
+ vendor := "GenuineIntel"
+ brandString := "Intel(R) Core(TM) i7-9750H v2 CPU @ 2.60GHz"
+ model := cpuModel(vendor, brandString)
+
+ dimensions, err := CPUs(vendor, brandString)
+ require.NoError(t, err)
+ assert.Equal(
+ t,
+ []string{isa, isa + "-" + bitness, isa + "-" + bitness + "-" + model},
+ dimensions,
+ "If a model can be extracted, there should be a third slice element containing it.",
)
- assert.Error(t, err, "An unknown CPU architecture should result in an error (and we should add it to the mapping).")
+ dimensions, err = CPUs("", "")
+ require.NoError(t, err)
+ assert.Equal(
+ t,
+ []string{isa, isa + "-" + bitness},
+ dimensions,
+ "Empty vendor and brand string should result in no third element.",
+ )
}
func TestVersionsOfAllPrecisions(t *testing.T) {
diff --git a/machine/go/test_machine_monitor/standalone/standalone_darwin.go b/machine/go/test_machine_monitor/standalone/standalone_darwin.go
index 06ff1b1..9bf9c04 100644
--- a/machine/go/test_machine_monitor/standalone/standalone_darwin.go
+++ b/machine/go/test_machine_monitor/standalone/standalone_darwin.go
@@ -23,14 +23,10 @@
// CPUs returns a Swarming-style description of the host's CPU, in various precisions.
func CPUs(ctx context.Context) ([]string, error) {
- arch, err := host.KernelArch()
- if err != nil {
- return nil, skerr.Wrapf(err, "failed to get Mac CPU architecture")
- }
// It is perfectly normal for these sysctl keys to be missing sometimes:
vendor, _ := unix.Sysctl("machdep.cpu.vendor") // Sysctl returns "" on failure.
brandString, _ := unix.Sysctl("machdep.cpu.brand_string")
- return crossplatform.CPUs(arch, vendor, brandString)
+ return crossplatform.CPUs(vendor, brandString)
}
// GPUs returns Swarming-style descriptions of all the host's GPUs, in various precisions, all
diff --git a/machine/go/test_machine_monitor/standalone/standalone_linux.go b/machine/go/test_machine_monitor/standalone/standalone_linux.go
index 1d362cb..2d22cef 100644
--- a/machine/go/test_machine_monitor/standalone/standalone_linux.go
+++ b/machine/go/test_machine_monitor/standalone/standalone_linux.go
@@ -27,11 +27,6 @@
// kernel, we do not. None of our jobs care about that distinction, nor, I think, do any of our
// boxes run like that.
func CPUs(ctx context.Context) ([]string, error) {
- arch, err := host.KernelArch()
- if err != nil {
- return nil, skerr.Wrapf(err, "failed to get Linux CPU architecture")
- }
-
procFile, err := os.Open("/proc/cpuinfo")
if err != nil {
return nil, skerr.Wrap(err)
@@ -43,7 +38,7 @@
return nil, skerr.Wrapf(err, "failed to get vendor and brand string")
}
- return crossplatform.CPUs(arch, vendor, brandString)
+ return crossplatform.CPUs(vendor, brandString)
}
// nvidiaVersion returns the version of the installed Nvidia GPU driver, "" if not available.
diff --git a/machine/go/test_machine_monitor/standalone/standalone_windows.go b/machine/go/test_machine_monitor/standalone/standalone_windows.go
index b2fe6e5..c324040 100644
--- a/machine/go/test_machine_monitor/standalone/standalone_windows.go
+++ b/machine/go/test_machine_monitor/standalone/standalone_windows.go
@@ -5,6 +5,7 @@
"github.com/shirou/gopsutil/host"
"go.skia.org/infra/go/skerr"
+ "go.skia.org/infra/machine/go/test_machine_monitor/standalone/crossplatform"
"go.skia.org/infra/machine/go/test_machine_monitor/standalone/windows"
)
@@ -21,8 +22,7 @@
}
func CPUs(ctx context.Context) ([]string, error) {
- var ret []string
- return ret, nil
+ return crossplatform.CPUs("", "")
}
func GPUs(ctx context.Context) ([]string, error) {