Repository: prometheus/procfs
Pull request #296 (2020-05-18 22:16:33)

Added support for CPUInfo for ARM on Linux kernels 3.8+
Fixes issue #294

Hi @bluecmd, please have a look and let me know if you think I should do any changes on the PR. Thanks!

kadern0: @bluecmd I think it is ready for a second round :)

Patch:
diff --git a/Makefile.common b/Makefile.common
index b978dfc..9320176 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -150,6 +150,17 @@ else
 	$(GO) get $(GOOPTS) -t ./...
 endif
 
+.PHONY: update-go-deps
+update-go-deps:
+	@echo ">> updating Go dependencies"
+	@for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \
+		$(GO) get $$m; \
+	done
+	GO111MODULE=$(GO111MODULE) $(GO) mod tidy
+ifneq (,$(wildcard vendor))
+	GO111MODULE=$(GO111MODULE) $(GO) mod vendor
+endif
+
 .PHONY: common-test-short
 common-test-short: $(GOTEST_DIR)
 	@echo ">> running short tests"
diff --git a/cpuinfo.go b/cpuinfo.go
index bb3be99..d19ce85 100644
--- a/cpuinfo.go
+++ b/cpuinfo.go
@@ -190,15 +190,26 @@ func parseCPUInfoARM(info []byte) ([]CPUInfo, error) {
 	scanner := bufio.NewScanner(bytes.NewReader(info))
 
 	firstLine := firstNonEmptyLine(scanner)
-	if !strings.HasPrefix(firstLine, "Processor") || !strings.Contains(firstLine, ":") {
+	match, _ := regexp.MatchString("^[Pp]rocessor", firstLine)
+	if !match || !strings.Contains(firstLine, ":") {
 		return nil, errors.New("invalid cpuinfo file: " + firstLine)
 	}
 	field := strings.SplitN(firstLine, ": ", 2)
-	commonCPUInfo := CPUInfo{VendorID: field[1]}
-
 	cpuinfo := []CPUInfo{}
-	i := -1
 	featuresLine := ""
+	commonCPUInfo := CPUInfo{}
+	i := 0
+	if strings.TrimSpace(field[0]) == "Processor" {
+		commonCPUInfo = CPUInfo{ModelName: field[1]}
+		i = -1
+	} else {
+		v, err := strconv.ParseUint(field[1], 0, 32)
+		if err != nil {
+			return nil, err
+		}
+		firstcpu := CPUInfo{Processor: uint(v)}
+		cpuinfo = []CPUInfo{firstcpu}
+	}
 
 	for scanner.Scan() {
 		line := scanner.Text()
@@ -216,6 +227,11 @@ func parseCPUInfoARM(info []byte) ([]CPUInfo, error) {
 			}
 			cpuinfo[i].Processor = uint(v)
 		case "BogoMIPS":
+			if i == -1 {
+				cpuinfo = append(cpuinfo, commonCPUInfo) // There is only one processor
+				i++
+				cpuinfo[i].Processor = 0
+			}
 			v, err := strconv.ParseFloat(field[1], 64)
 			if err != nil {
 				return nil, err
@@ -223,6 +239,8 @@ func parseCPUInfoARM(info []byte) ([]CPUInfo, error) {
 			cpuinfo[i].BogoMips = v
 		case "Features":
 			featuresLine = line
+		case "model name":
+			cpuinfo[i].ModelName = field[1]
 		}
 	}
 	fields := strings.SplitN(featuresLine, ": ", 2)
@@ -230,6 +248,7 @@ func parseCPUInfoARM(info []byte) ([]CPUInfo, error) {
 		cpuinfo[i].Flags = strings.Fields(fields[1])
 	}
 	return cpuinfo, nil
+
 }
 
 func parseCPUInfoS390X(info []byte) ([]CPUInfo, error) {
diff --git a/internal/util/parse.go b/internal/util/parse.go
index 3f1b174..22cb07a 100644
--- a/internal/util/parse.go
+++ b/internal/util/parse.go
@@ -74,7 +74,7 @@ func ReadUintFromFile(path string) (uint64, error) {
 }
 
 // ReadIntFromFile reads a file and attempts to parse a int64 from it.
-func ReadIntFromFromFile(path string) (int64, error) {
+func ReadIntFromFile(path string) (int64, error) {
 	data, err := ioutil.ReadFile(path)
 	if err != nil {
 		return 0, err
diff --git a/sysfs/class_thermal.go b/sysfs/class_thermal.go
index c20639f..457c6bd 100644
--- a/sysfs/class_thermal.go
+++ b/sysfs/class_thermal.go
@@ -67,7 +67,7 @@ func parseClassThermalZone(zone string) (ClassThermalZoneStats, error) {
 	if err != nil {
 		return ClassThermalZoneStats{}, err
 	}
-	zoneTemp, err := util.ReadIntFromFromFile(filepath.Join(zone, "temp"))
+	zoneTemp, err := util.ReadIntFromFile(filepath.Join(zone, "temp"))
 	if err != nil {
 		return ClassThermalZoneStats{}, err
 	}


Test patch:
diff --git a/cpuinfo_test.go b/cpuinfo_test.go
index b6869ac..17eefbe 100644
--- a/cpuinfo_test.go
+++ b/cpuinfo_test.go
@@ -18,7 +18,7 @@ package procfs
 import "testing"
 
 const (
-	cpuinfoArm7 = `
+	cpuinfoArm7Legacy = `
 Processor : ARMv7 Processor rev 5 (v7l)
 processor : 0
 BogoMIPS : 2400.00
@@ -37,6 +37,65 @@ Hardware : sun8i
 Revision : 0000
 Serial : 5400503583203c3c040e`
 
+	cpuinfoArm7LegacyV1 = `
+Processor       : ARMv6-compatible processor rev 5 (v6l)
+BogoMIPS        : 791.34
+Features        : swp half thumb fastmult vfp edsp java 
+CPU implementer : 0x41
+CPU architecture: 6TEJ
+CPU variant     : 0x1
+CPU part        : 0xb36
+CPU revision    : 5
+
+Hardware        : IMAPX200
+Revision        : 0000
+Serial          : 0000000000000000`
+
+	cpuinfoArm7 = `
+processor : 0
+model name : ARMv7 Processor rev 3 (v7l)
+BogoMIPS : 108.00
+Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0xd08
+CPU revision : 3
+
+processor : 1
+model name : ARMv7 Processor rev 3 (v7l)
+BogoMIPS : 108.00
+Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0xd08
+CPU revision : 3
+
+processor : 2
+model name : ARMv7 Processor rev 3 (v7l)
+BogoMIPS : 108.00
+Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0xd08
+CPU revision : 3
+
+processor : 3
+model name : ARMv7 Processor rev 3 (v7l)
+BogoMIPS : 108.00
+Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0xd08
+CPU revision : 3
+
+Hardware : BCM2835
+Revision : c03111
+`
+
 	cpuinfoS390x = `
 vendor_id       : IBM/S390
 # processors    : 4
@@ -153,15 +212,15 @@ func TestCPUInfoX86(t *testing.T) {
 	}
 }
 
-func TestCPUInfoParseARM(t *testing.T) {
-	cpuinfo, err := parseCPUInfoARM([]byte(cpuinfoArm7))
+func TestCPUInfoParseARMLegacy(t *testing.T) {
+	cpuinfo, err := parseCPUInfoARM([]byte(cpuinfoArm7Legacy))
 	if err != nil || cpuinfo == nil {
 		t.Fatalf("unable to parse arm cpu info: %v", err)
 	}
 	if want, have := 2, len(cpuinfo); want != have {
 		t.Errorf("want number of processors %v, have %v", want, have)
 	}
-	if want, have := "ARMv7 Processor rev 5 (v7l)", cpuinfo[0].VendorID; want != have {
+	if want, have := "ARMv7 Processor rev 5 (v7l)", cpuinfo[0].ModelName; want != have {
 		t.Errorf("want vendor %v, have %v", want, have)
 	}
 	if want, have := "thumb", cpuinfo[1].Flags[2]; want != have {
@@ -169,6 +228,38 @@ func TestCPUInfoParseARM(t *testing.T) {
 	}
 }
 
+func TestCPUInfoParseARMLegacyV1(t *testing.T) {
+	cpuinfo, err := parseCPUInfoARM([]byte(cpuinfoArm7LegacyV1))
+	if err != nil || cpuinfo == nil {
+		t.Fatalf("unable to parse arm cpu info: %v", err)
+	}
+	if want, have := 1, len(cpuinfo); want != have {
+		t.Errorf("want number of processors %v, have %v", want, have)
+	}
+	if want, have := "ARMv6-compatible processor rev 5 (v6l)", cpuinfo[0].ModelName; want != have {
+		t.Errorf("want vendor %v, have %v", want, have)
+	}
+	if want, have := "thumb", cpuinfo[0].Flags[2]; want != have {
+		t.Errorf("want flag %v, have %v", want, have)
+	}
+}
+
+func TestCPUInfoParseARM(t *testing.T) {
+	cpuinfo, err := parseCPUInfoARM([]byte(cpuinfoArm7))
+	if err != nil || cpuinfo == nil {
+		t.Fatalf("unable to parse arm cpu info: %v", err)
+	}
+	if want, have := 4, len(cpuinfo); want != have {
+		t.Errorf("want number of processors %v, have %v", want, have)
+	}
+	if want, have := "ARMv7 Processor rev 3 (v7l)", cpuinfo[0].ModelName; want != have {
+		t.Errorf("want vendor %v, have %v", want, have)
+	}
+	if want, have := "thumb", cpuinfo[1].Flags[1]; want != have {
+		t.Errorf("want flag %v, have %v", want, have)
+	}
+}
+
 func TestCPUInfoParseS390X(t *testing.T) {
 	cpuinfo, err := parseCPUInfoS390X([]byte(cpuinfoS390x))
 	if err != nil || cpuinfo == nil {