Repository: prometheus/procfs
Pull request #210 (2019-09-05 01:18:08)

add cpu topology information
Parse /sys/devices/system/cpu/cpuN/topology and /sys/devices/system/cpu/cpuN/thermal_throttle

pgier: @discordianfish @SuperQ  PTAL

Patch:
diff --git a/fixtures.ttar b/fixtures.ttar
index 71cc583..0b29055 100644
--- a/fixtures.ttar
+++ b/fixtures.ttar
@@ -3615,6 +3615,52 @@ Mode: 775
 Path: fixtures/sys/devices/system/cpu/cpu0/cpufreq
 SymlinkTo: ../cpufreq/policy0
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpu0/thermal_throttle
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu0/thermal_throttle/core_throttle_count
+Lines: 1
+10084
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu0/thermal_throttle/package_throttle_count
+Lines: 1
+34818
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpu0/topology
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu0/topology/core_id
+Lines: 1
+0
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu0/topology/core_siblings
+Lines: 1
+ff
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu0/topology/core_siblings_list
+Lines: 1
+0-7
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu0/topology/physical_package_id
+Lines: 1
+0
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu0/topology/thread_siblings
+Lines: 1
+11
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu0/topology/thread_siblings_list
+Lines: 1
+0,4
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Directory: fixtures/sys/devices/system/cpu/cpu1
 Mode: 775
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -3676,6 +3722,52 @@ Lines: 1
 <unsupported>
 Mode: 664
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpu1/thermal_throttle
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/thermal_throttle/core_throttle_count
+Lines: 1
+523
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/thermal_throttle/package_throttle_count
+Lines: 1
+34818
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpu1/topology
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/topology/core_id
+Lines: 1
+1
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/topology/core_siblings
+Lines: 1
+ff
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/topology/core_siblings_list
+Lines: 1
+0-7
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/topology/physical_package_id
+Lines: 1
+0
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/topology/thread_siblings
+Lines: 1
+22
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/topology/thread_siblings_list
+Lines: 1
+1,5
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Directory: fixtures/sys/devices/system/cpu/cpufreq
 Mode: 775
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/sysfs/system_cpu.go b/sysfs/system_cpu.go
index 001039c..ddb9ebf 100644
--- a/sysfs/system_cpu.go
+++ b/sysfs/system_cpu.go
@@ -25,6 +25,28 @@ import (
 	"github.com/prometheus/procfs/internal/util"
 )
 
+// CPU represents a path to a CPU located in /sys/devices/system/cpu/cpu[0-9]*
+type CPU string
+
+// Number returns the ID number of the given CPU
+func (c CPU) Number() string {
+	return strings.TrimPrefix(filepath.Base(string(c)), "cpu")
+}
+
+// CPUTopology contains data located in /sys/devices/system/cpu/cpu[0-9]*/topology
+type CPUTopology struct {
+	CoreID             string
+	CoreSiblingsList   string
+	PhysicalPackageID  string
+	ThreadSiblingsList string
+}
+
+// CPUThermalThrottle contains data from /sys/devices/system/cpu/cpu[0-9]*/thermal_throttle
+type CPUThermalThrottle struct {
+	CoreThrottleCount    uint64
+	PackageThrottleCount uint64
+}
+
 // SystemCPUCpufreqStats contains stats from devices/system/cpu/cpu[0-9]*/cpufreq/...
 type SystemCPUCpufreqStats struct {
 	Name                     string
@@ -42,9 +64,80 @@ type SystemCPUCpufreqStats struct {
 	SetSpeed                 string
 }
 
-// TODO: Add topology support.
+// CPUs returns a slice of all CPUs in /sys/devices/system/cpu
+func (fs FS) CPUs() ([]CPU, error) {
+	cpuPaths, err := filepath.Glob(fs.sys.Path("devices/system/cpu/cpu[0-9]*"))
+	if err != nil {
+		return nil, err
+	}
+	cpus := make([]CPU, len(cpuPaths))
+	for i, cpu := range cpuPaths {
+		cpus[i] = CPU(cpu)
+	}
+	return cpus, nil
+}
 
-// TODO: Add thermal_throttle support.
+// Topology gets the topology information for a single CPU from /sys/devices/system/cpu/cpuN/topology
+func (c CPU) Topology() (*CPUTopology, error) {
+	cpuTopologyPath := filepath.Join(string(c), "topology")
+	if _, err := os.Stat(cpuTopologyPath); err != nil {
+		return nil, err
+	}
+	t, err := parseCPUTopology(cpuTopologyPath)
+	if err != nil {
+		return nil, err
+	}
+	return t, nil
+}
+
+func parseCPUTopology(cpuPath string) (*CPUTopology, error) {
+	t := CPUTopology{}
+	var err error
+	t.CoreID, err = util.SysReadFile(filepath.Join(cpuPath, "core_id"))
+	if err != nil {
+		return nil, err
+	}
+	t.PhysicalPackageID, err = util.SysReadFile(filepath.Join(cpuPath, "physical_package_id"))
+	if err != nil {
+		return nil, err
+	}
+	t.CoreSiblingsList, err = util.SysReadFile(filepath.Join(cpuPath, "core_siblings_list"))
+	if err != nil {
+		return nil, err
+	}
+	t.ThreadSiblingsList, err = util.SysReadFile(filepath.Join(cpuPath, "thread_siblings_list"))
+	if err != nil {
+		return nil, err
+	}
+	return &t, nil
+}
+
+// ThermalThrottle gets the cpu throttle count information for a single CPU from /sys/devices/system/cpu/cpuN/thermal_throttle
+func (c CPU) ThermalThrottle() (*CPUThermalThrottle, error) {
+	cpuPath := filepath.Join(string(c), "thermal_throttle")
+	if _, err := os.Stat(cpuPath); err != nil {
+		return nil, err
+	}
+	t, err := parseCPUThermalThrottle(cpuPath)
+	if err != nil {
+		return nil, err
+	}
+	return t, nil
+}
+
+func parseCPUThermalThrottle(cpuPath string) (*CPUThermalThrottle, error) {
+	t := CPUThermalThrottle{}
+	var err error
+	t.PackageThrottleCount, err = util.ReadUintFromFile(filepath.Join(cpuPath, "package_throttle_count"))
+	if err != nil {
+		return nil, err
+	}
+	t.CoreThrottleCount, err = util.ReadUintFromFile(filepath.Join(cpuPath, "core_throttle_count"))
+	if err != nil {
+		return nil, err
+	}
+	return &t, nil
+}
 
 // SystemCpufreq returns CPU frequency metrics for all CPUs.
 func (fs FS) SystemCpufreq() ([]SystemCPUCpufreqStats, error) {


Test patch:
diff --git a/sysfs/system_cpu_test.go b/sysfs/system_cpu_test.go
index 36317d8..eb6ab7a 100644
--- a/sysfs/system_cpu_test.go
+++ b/sysfs/system_cpu_test.go
@@ -24,6 +24,72 @@ func makeUint64(v uint64) *uint64 {
 	return &v
 }
 
+func TestCPUTopology(t *testing.T) {
+	fs, err := NewFS(sysTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+	cpus, err := fs.CPUs()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if want, have := 2, len(cpus); want != have {
+		t.Errorf("incorrect number of CPUs, have %v, want %v", want, have)
+	}
+	if want, have := "0", cpus[0].Number(); want != have {
+		t.Errorf("incorrect name, have %v, want %v", want, have)
+	}
+	cpu0Topology, err := cpus[0].Topology()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if want, have := "0", cpu0Topology.CoreID; want != have {
+		t.Errorf("incorrect core ID, have %v, want %v", want, have)
+	}
+	if want, have := "0-7", cpu0Topology.CoreSiblingsList; want != have {
+		t.Errorf("incorrect core siblings list, have %v, want %v", want, have)
+	}
+	cpu1Topology, err := cpus[1].Topology()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if want, have := "0", cpu1Topology.PhysicalPackageID; want != have {
+		t.Errorf("incorrect package ID, have %v, want %v", want, have)
+	}
+	if want, have := "1,5", cpu1Topology.ThreadSiblingsList; want != have {
+		t.Errorf("incorrect thread siblings list, have %v, want %v", want, have)
+	}
+}
+
+func TestCPUThermalThrottle(t *testing.T) {
+	fs, err := NewFS(sysTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+	cpus, err := fs.CPUs()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if want, have := 2, len(cpus); want != have {
+		t.Errorf("incorrect number of CPUs, have %v, want %v", want, have)
+	}
+	cpu0Throttle, err := cpus[0].ThermalThrottle()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if want, have := uint64(34818), cpu0Throttle.PackageThrottleCount; want != have {
+		t.Errorf("incorrect package throttle count, have %v, want %v", want, have)
+	}
+
+	cpu1Throttle, err := cpus[1].ThermalThrottle()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if want, have := uint64(523), cpu1Throttle.CoreThrottleCount; want != have {
+		t.Errorf("incorrect core throttle count, have %v, want %v", want, have)
+	}
+}
+
 func TestSystemCpufreq(t *testing.T) {
 	fs, err := NewFS(sysTestFixtures)
 	if err != nil {