225 lines
5.6 KiB
Go
225 lines
5.6 KiB
Go
// Use and distribution licensed under the Apache license version 2.
|
|
//
|
|
// See the COPYING file in the root project directory for full text.
|
|
//
|
|
|
|
package ghw
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func (ctx *context) cpuFillInfo(info *CPUInfo) error {
|
|
info.Processors = ctx.processorsGet()
|
|
var totCores uint32
|
|
var totThreads uint32
|
|
for _, p := range info.Processors {
|
|
totCores += p.NumCores
|
|
totThreads += p.NumThreads
|
|
}
|
|
info.TotalCores = totCores
|
|
info.TotalThreads = totThreads
|
|
return nil
|
|
}
|
|
|
|
// Processors has been DEPRECATED in 0.2 and will be REMOVED in 1.0. Please use
|
|
// the CPUInfo.Processors attribute.
|
|
// TODO(jaypipes): Remove in 1.0
|
|
func Processors() []*Processor {
|
|
ctx := contextFromEnv()
|
|
return ctx.processorsGet()
|
|
}
|
|
|
|
func (ctx *context) processorsGet() []*Processor {
|
|
procs := make([]*Processor, 0)
|
|
|
|
r, err := os.Open(ctx.pathProcCpuinfo())
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer safeClose(r)
|
|
|
|
// An array of maps of attributes describing the logical processor
|
|
procAttrs := make([]map[string]string, 0)
|
|
curProcAttrs := make(map[string]string)
|
|
|
|
scanner := bufio.NewScanner(r)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" {
|
|
// Output of /proc/cpuinfo has a blank newline to separate logical
|
|
// processors, so here we collect up all the attributes we've
|
|
// collected for this logical processor block
|
|
procAttrs = append(procAttrs, curProcAttrs)
|
|
// Reset the current set of processor attributes...
|
|
curProcAttrs = make(map[string]string)
|
|
continue
|
|
}
|
|
parts := strings.Split(line, ":")
|
|
key := strings.TrimSpace(parts[0])
|
|
value := strings.TrimSpace(parts[1])
|
|
curProcAttrs[key] = value
|
|
}
|
|
|
|
// Build a set of physical processor IDs which represent the physical
|
|
// package of the CPU
|
|
setPhysicalIDs := make(map[int]bool)
|
|
for _, attrs := range procAttrs {
|
|
pid, err := strconv.Atoi(attrs["physical id"])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
setPhysicalIDs[pid] = true
|
|
}
|
|
|
|
for pid := range setPhysicalIDs {
|
|
p := &Processor{
|
|
Id: pid,
|
|
}
|
|
// The indexes into the array of attribute maps for each logical
|
|
// processor within the physical processor
|
|
lps := make([]int, 0)
|
|
for x := range procAttrs {
|
|
lppid, err := strconv.Atoi(procAttrs[x]["physical id"])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if pid == lppid {
|
|
lps = append(lps, x)
|
|
}
|
|
}
|
|
first := procAttrs[lps[0]]
|
|
p.Model = first["model name"]
|
|
p.Vendor = first["vendor_id"]
|
|
numCores, err := strconv.Atoi(first["cpu cores"])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
p.NumCores = uint32(numCores)
|
|
numThreads, err := strconv.Atoi(first["siblings"])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
p.NumThreads = uint32(numThreads)
|
|
|
|
// The flags field is a space-separated list of CPU capabilities
|
|
p.Capabilities = strings.Split(first["flags"], " ")
|
|
|
|
cores := make([]*ProcessorCore, 0)
|
|
for _, lpidx := range lps {
|
|
lpid, err := strconv.Atoi(procAttrs[lpidx]["processor"])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
coreID, err := strconv.Atoi(procAttrs[lpidx]["core id"])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
var core *ProcessorCore
|
|
for _, c := range cores {
|
|
if c.ID == coreID {
|
|
c.LogicalProcessors = append(
|
|
c.LogicalProcessors,
|
|
lpid,
|
|
)
|
|
c.NumThreads = uint32(len(c.LogicalProcessors))
|
|
core = c
|
|
}
|
|
}
|
|
if core == nil {
|
|
coreLps := make([]int, 1)
|
|
coreLps[0] = lpid
|
|
core = &ProcessorCore{
|
|
ID: coreID,
|
|
Index: len(cores),
|
|
NumThreads: 1,
|
|
LogicalProcessors: coreLps,
|
|
}
|
|
cores = append(cores, core)
|
|
}
|
|
}
|
|
p.Cores = cores
|
|
procs = append(procs, p)
|
|
}
|
|
return procs
|
|
}
|
|
|
|
func (ctx *context) coresForNode(nodeID int) ([]*ProcessorCore, error) {
|
|
// The /sys/devices/system/node/nodeX directory contains a subdirectory
|
|
// called 'cpuX' for each logical processor assigned to the node. Each of
|
|
// those subdirectories contains a topology subdirectory which has a
|
|
// core_id file that indicates the 0-based identifier of the physical core
|
|
// the logical processor (hardware thread) is on.
|
|
path := filepath.Join(
|
|
ctx.pathSysDevicesSystemNode(),
|
|
fmt.Sprintf("node%d", nodeID),
|
|
)
|
|
cores := make([]*ProcessorCore, 0)
|
|
|
|
findCoreByID := func(coreID int) *ProcessorCore {
|
|
for _, c := range cores {
|
|
if c.Id == coreID {
|
|
return c
|
|
}
|
|
}
|
|
|
|
c := &ProcessorCore{
|
|
// TODO(jaypipes): Deprecated in 0.2, remove in 1.0
|
|
Id: coreID,
|
|
ID: coreID,
|
|
Index: len(cores),
|
|
LogicalProcessors: make([]int, 0),
|
|
}
|
|
cores = append(cores, c)
|
|
return c
|
|
}
|
|
|
|
files, err := ioutil.ReadDir(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, file := range files {
|
|
filename := file.Name()
|
|
if !strings.HasPrefix(filename, "cpu") {
|
|
continue
|
|
}
|
|
if filename == "cpumap" || filename == "cpulist" {
|
|
// There are two files in the node directory that start with 'cpu'
|
|
// but are not subdirectories ('cpulist' and 'cpumap'). Ignore
|
|
// these files.
|
|
continue
|
|
}
|
|
// Grab the logical processor ID by cutting the integer from the
|
|
// /sys/devices/system/node/nodeX/cpuX filename
|
|
cpuPath := filepath.Join(path, filename)
|
|
procID, err := strconv.Atoi(filename[3:])
|
|
if err != nil {
|
|
_, _ = fmt.Fprintf(
|
|
os.Stderr,
|
|
"failed to determine procID from %s. Expected integer after 3rd char.",
|
|
filename,
|
|
)
|
|
continue
|
|
}
|
|
coreIDPath := filepath.Join(cpuPath, "topology", "core_id")
|
|
coreID := safeIntFromFile(coreIDPath)
|
|
core := findCoreByID(coreID)
|
|
core.LogicalProcessors = append(
|
|
core.LogicalProcessors,
|
|
procID,
|
|
)
|
|
}
|
|
|
|
for _, c := range cores {
|
|
c.NumThreads = uint32(len(c.LogicalProcessors))
|
|
}
|
|
|
|
return cores, nil
|
|
}
|