Repository: prometheus/procfs
Pull request #318 (2020-07-22 09:54:09)

Add CPUInfo parsing for RISCV
Currently only 64-bit RISCV is supported (GOARCH=riscv64) by the Go
compiler, but the cpuinfo format would be the same for 32-bit RISCV.

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 3ea6b88..4224ea8 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -34,7 +34,7 @@ jobs:
     steps:
     - checkout
     - run: sudo pip install codespell
-    - run: codespell --skip=".git,./vendor,ttar,fixtures.ttar,./fixtures,go.mod,go.sum" -L uint,packages\',ded,alo,als,te,sie
+    - run: codespell --skip=".git,./vendor,ttar,fixtures.ttar,./fixtures,go.mod,go.sum" -L uint,packages\',ded,alo,als,te,sie,hart
 
 workflows:
   version: 2
diff --git a/cpuinfo.go b/cpuinfo.go
index 935157e..b9fb589 100644
--- a/cpuinfo.go
+++ b/cpuinfo.go
@@ -407,6 +407,46 @@ func parseCPUInfoPPC(info []byte) ([]CPUInfo, error) {
 	return cpuinfo, nil
 }
 
+func parseCPUInfoRISCV(info []byte) ([]CPUInfo, error) {
+	scanner := bufio.NewScanner(bytes.NewReader(info))
+
+	firstLine := firstNonEmptyLine(scanner)
+	if !strings.HasPrefix(firstLine, "processor") || !strings.Contains(firstLine, ":") {
+		return nil, errors.New("invalid cpuinfo file: " + firstLine)
+	}
+	field := strings.SplitN(firstLine, ": ", 2)
+	v, err := strconv.ParseUint(field[1], 0, 32)
+	if err != nil {
+		return nil, err
+	}
+	firstcpu := CPUInfo{Processor: uint(v)}
+	cpuinfo := []CPUInfo{firstcpu}
+	i := 0
+
+	for scanner.Scan() {
+		line := scanner.Text()
+		if !strings.Contains(line, ":") {
+			continue
+		}
+		field := strings.SplitN(line, ": ", 2)
+		switch strings.TrimSpace(field[0]) {
+		case "processor":
+			v, err := strconv.ParseUint(field[1], 0, 32)
+			if err != nil {
+				return nil, err
+			}
+			i = int(v)
+			cpuinfo = append(cpuinfo, CPUInfo{}) // start of the next processor
+			cpuinfo[i].Processor = uint(v)
+		case "hart":
+			cpuinfo[i].CoreID = field[1]
+		case "isa":
+			cpuinfo[i].ModelName = field[1]
+		}
+	}
+	return cpuinfo, nil
+}
+
 func parseCPUInfoDummy(_ []byte) ([]CPUInfo, error) { // nolint:unused,deadcode
 	return nil, errors.New("not implemented")
 }
diff --git a/cpuinfo_others.go b/cpuinfo_others.go
index db3809d..95b5b4e 100644
--- a/cpuinfo_others.go
+++ b/cpuinfo_others.go
@@ -12,7 +12,7 @@
 // limitations under the License.
 
 // +build linux
-// +build !386,!amd64,!arm,!arm64,!mips,!mips64,!mips64le,!mipsle,!ppc64,!ppc64le,!s390x
+// +build !386,!amd64,!arm,!arm64,!mips,!mips64,!mips64le,!mipsle,!ppc64,!ppc64le,!riscv64,!s390x
 
 package procfs
 
diff --git a/fixtures.ttar b/fixtures.ttar
index 868c857..bcfadf4 100644
--- a/fixtures.ttar
+++ b/fixtures.ttar
@@ -1966,7 +1966,7 @@ Lines: 1
 Mode: 444
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Path: fixtures/proc/mdstat
-Lines: 56
+Lines: 60
 Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
 
 md3 : active raid6 sda1[8] sdh1[7] sdg1[6] sdf1[5] sde1[11] sdd1[3] sdc1[10] sdb1[9] sdd1[10](S) sdd2[11](S)
@@ -1989,6 +1989,10 @@ md8 : active raid1 sdb1[1] sda1[0] sdc[2](S) sde[3](S)
       195310144 blocks [2/2] [UU]
       [=>...................]  resync =  8.5% (16775552/195310144) finish=17.0min speed=259783K/sec
 
+md201 : active raid1 sda3[0] sdb3[1]
+      1993728 blocks super 1.2 [2/2] [UU]
+      [=>...................]  check =  5.7% (114176/1993728) finish=0.2min speed=114176K/sec
+
 md7 : active raid6 sdb1[0] sde1[3] sdd1[2] sdc1[1](F)
       7813735424 blocks super 1.2 level 6, 512k chunk, algorithm 2 [4/3] [U_UU]
       bitmap: 0/30 pages [0KB], 65536KB chunk
@@ -3754,6 +3758,73 @@ Path: fixtures/sys/class/powercap/intel-rapl:0:0/uevent
 Lines: 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/powercap/intel-rapl:a
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/constraint_0_max_power_uw
+Lines: 1
+95000000
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/constraint_0_name
+Lines: 1
+long_term
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/constraint_0_power_limit_uw
+Lines: 1
+4090000000
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/constraint_0_time_window_us
+Lines: 1
+999424
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/constraint_1_max_power_uw
+Lines: 1
+0
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/constraint_1_name
+Lines: 1
+short_term
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/constraint_1_power_limit_uw
+Lines: 1
+4090000000
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/constraint_1_time_window_us
+Lines: 1
+2440
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/enabled
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/energy_uj
+Lines: 1
+240422366267
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/max_energy_range_uj
+Lines: 1
+262143328850
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/name
+Lines: 1
+package-10
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/powercap/intel-rapl:a/uevent
+Lines: 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Directory: fixtures/sys/class/thermal
 Mode: 775
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/mdstat.go b/mdstat.go
index 3e9362a..98e37aa 100644
--- a/mdstat.go
+++ b/mdstat.go
@@ -107,11 +107,14 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) {
 		syncedBlocks := size
 		recovering := strings.Contains(lines[syncLineIdx], "recovery")
 		resyncing := strings.Contains(lines[syncLineIdx], "resync")
+		checking := strings.Contains(lines[syncLineIdx], "check")
 
 		// Append recovery and resyncing state info.
-		if recovering || resyncing {
+		if recovering || resyncing || checking {
 			if recovering {
 				state = "recovering"
+			} else if checking {
+				state = "checking"
 			} else {
 				state = "resyncing"
 			}
diff --git a/sysfs/class_powercap.go b/sysfs/class_powercap.go
index c8b0055..00908bb 100644
--- a/sysfs/class_powercap.go
+++ b/sysfs/class_powercap.go
@@ -100,11 +100,11 @@ func (rz RaplZone) GetEnergyMicrojoules() (uint64, error) {
 // provided back as an integer, and stripped from the returned name. Usage
 // count is used when the index value is absent from the name.
 func getIndexAndName(countNameUsages map[string]int, name string) (int, string) {
-	length := len(name)
-	if length >= 2 {
-		index, err := strconv.Atoi(name[length-1:])
-		if name[length-2:length-1] == "-" && err == nil {
-			return index, name[:length-2]
+	s := strings.Split(name, "-")
+	if len(s) == 2 {
+		index, err := strconv.Atoi(s[1])
+		if err == nil {
+			return index, s[0]
 		}
 	}
 	// return count as the index, since name didn't have an index at the end


Test patch:
diff --git a/cpuinfo_test.go b/cpuinfo_test.go
index 954f4e7..bfec15d 100644
--- a/cpuinfo_test.go
+++ b/cpuinfo_test.go
@@ -203,6 +203,18 @@ timebase	: 512000000
 platform	: pSeries
 model		: IBM,8233-E8B
 machine		: CHRP IBM,8233-E8B
+`
+
+	cpuinfoRiscv64 = `
+processor	: 0
+hart		: 0
+isa		: rv64imafdcsu
+mmu		: sv48
+
+processor	: 1
+hart		: 1
+isa		: rv64imafdcsu
+mmu		: sv48
 `
 )
 
@@ -345,3 +357,19 @@ func TestCPUInfoParsePPC(t *testing.T) {
 		t.Errorf("want cpu mhz %v, have %v", want, have)
 	}
 }
+
+func TestCPUInfoParseRISCV64(t *testing.T) {
+	cpuinfo, err := parseCPUInfoRISCV([]byte(cpuinfoRiscv64))
+	if err != nil || cpuinfo == nil {
+		t.Fatalf("unable to parse ppc 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 := "1", cpuinfo[1].CoreID; want != have {
+		t.Errorf("want CoreId %v, have %v", want, have)
+	}
+	if want, have := "rv64imafdcsu", cpuinfo[1].ModelName; want != have {
+		t.Errorf("want ModelName %v, have %v", want, have)
+	}
+}
diff --git a/mdstat_test.go b/mdstat_test.go
index f2d2648..5404765 100644
--- a/mdstat_test.go
+++ b/mdstat_test.go
@@ -40,6 +40,7 @@ func TestFS_MDStat(t *testing.T) {
 		"md219": {Name: "md219", ActivityState: "inactive", DisksTotal: 0, DisksFailed: 0, DisksActive: 0, DisksSpare: 3, BlocksTotal: 7932, BlocksSynced: 7932},
 		"md00":  {Name: "md00", ActivityState: "active", DisksActive: 1, DisksTotal: 1, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 4186624, BlocksSynced: 4186624},
 		"md101": {Name: "md101", ActivityState: "active", DisksActive: 3, DisksTotal: 3, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 322560, BlocksSynced: 322560},
+		"md201": {Name: "md201", ActivityState: "checking", DisksActive: 2, DisksTotal: 2, DisksFailed: 0, DisksSpare: 0, BlocksTotal: 1993728, BlocksSynced: 114176},
 	}
 
 	if want, have := len(refs), len(mdStats); want != have {
diff --git a/sysfs/class_powercap_test.go b/sysfs/class_powercap_test.go
index 0186ade..6643709 100644
--- a/sysfs/class_powercap_test.go
+++ b/sysfs/class_powercap_test.go
@@ -56,7 +56,7 @@ func TestNewRaplValues(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if len(zones) != 2 {
+	if len(zones) != 3 {
 		t.Fatal("wrong number of RAPL values")
 	}
 	microjoules, err := zones[0].GetEnergyMicrojoules()
@@ -66,4 +66,7 @@ func TestNewRaplValues(t *testing.T) {
 	if microjoules != 240422366267 {
 		t.Fatal("wrong microjoule number")
 	}
+	if zones[2].Index != 10 {
+		t.Fatal("wrong index number")
+	}
 }