Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 72 additions & 69 deletions pkg/cpu/cpu_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"

Expand All @@ -19,6 +20,10 @@ import (
"github.com/jaypipes/ghw/pkg/util"
)

var (
regexForCpulCore = regexp.MustCompile("^cpu([0-9]+)$")
)

func (i *Info) load() error {
i.Processors = processorsGet(i.ctx)
var totCores uint32
Expand All @@ -32,6 +37,24 @@ func (i *Info) load() error {
return nil
}

func ProcByID(procs []*Processor, id int) *Processor {
for pid := range procs {
if procs[pid].ID == id {
return procs[pid]
}
}
return nil
}

func CoreByID(cores []*ProcessorCore, id int) *ProcessorCore {
for cid := range cores {
if cores[cid].Index == id {
return cores[cid]
}
}
return nil
}

func processorsGet(ctx *context.Context) []*Processor {
procs := make([]*Processor, 0)
paths := linuxpath.New(ctx)
Expand All @@ -46,6 +69,7 @@ func processorsGet(ctx *context.Context) []*Processor {
procAttrs := make([]map[string]string, 0)
curProcAttrs := make(map[string]string)

// Parse /proc/cpuinfo
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
Expand All @@ -64,85 +88,64 @@ func processorsGet(ctx *context.Context) []*Processor {
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
// Iterate on /sys/devices/system/cpu/cpuN, not on /proc/cpuinfo
Entries, err := ioutil.ReadDir(paths.SysDevicesSystemCPU)
if err != nil {
return nil
}

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 {
for _, lcore := range Entries {
matches := regexForCpulCore.FindStringSubmatch(lcore.Name())
if len(matches) < 2 {
continue
}
p.NumCores = uint32(numCores)
numThreads, err := strconv.Atoi(first["siblings"])
if err != nil {

lcoreID, error := strconv.Atoi(matches[1])
if error != 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
// Fetch CPU ID
physIdPath := filepath.Join(paths.SysDevicesSystemCPU, fmt.Sprintf("cpu%d", lcoreID), "topology", "physical_package_id")
cpuID := util.SafeIntFromFile(ctx, physIdPath)

proc := ProcByID(procs, cpuID)
if proc == nil {
proc = &Processor{ID: cpuID}
// Assumes /proc/cpuinfo is in order of logical cpu id, then
// procAttrs[lcoreID] describes logical cpu `lcoreID`.
// Once got a more robust way of fetching the following info,
// can we drop /proc/cpuinfo.
if len(procAttrs[lcoreID]["flags"]) != 0 { // x86
proc.Capabilities = strings.Split(procAttrs[lcoreID]["flags"], " ")
} else if len(procAttrs[lcoreID]["Features"]) != 0 { // ARM64
proc.Capabilities = strings.Split(procAttrs[lcoreID]["Features"], " ")
}
coreID, err := strconv.Atoi(procAttrs[lpidx]["core id"])
if err != nil {
continue
if len(procAttrs[lcoreID]["model name"]) != 0 {
proc.Model = procAttrs[lcoreID]["model name"]
} else if len(procAttrs[lcoreID]["uarch"]) != 0 { // SiFive
proc.Model = procAttrs[lcoreID]["uarch"]
}
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)
if len(procAttrs[lcoreID]["vendor_id"]) != 0 {
proc.Vendor = procAttrs[lcoreID]["vendor_id"]
} else if len(procAttrs[lcoreID]["isa"]) != 0 { // RISCV64
proc.Vendor = procAttrs[lcoreID]["isa"]
}
procs = append(procs, proc)
}

// Fetch Core ID
coreIdPath := filepath.Join(paths.SysDevicesSystemCPU, fmt.Sprintf("cpu%d", lcoreID), "topology", "core_id")
coreID := util.SafeIntFromFile(ctx, coreIdPath)
core := CoreByID(proc.Cores, coreID)
if core == nil {
core = &ProcessorCore{Index: coreID, NumThreads: 1}
proc.Cores = append(proc.Cores, core)
proc.NumCores += 1
} else {
core.NumThreads += 1
}
p.Cores = cores
procs = append(procs, p)
proc.NumThreads += 1
core.LogicalProcessors = append(core.LogicalProcessors, lcoreID)
}
return procs
}
Expand Down
73 changes: 73 additions & 0 deletions pkg/cpu/cpu_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package cpu_test

import (
"os"
"path/filepath"
"testing"

"github.com/jaypipes/ghw/pkg/cpu"
"github.com/jaypipes/ghw/pkg/option"
"github.com/jaypipes/ghw/testdata"
)

// nolint: gocyclo
func TestArmCPU(t *testing.T) {
if _, ok := os.LookupEnv("GHW_TESTING_SKIP_CPU"); ok {
t.Skip("Skipping CPU tests.")
}

testdataPath, err := testdata.SnapshotsDirectory()
if err != nil {
t.Fatalf("Expected nil err, but got %v", err)
}

multiNumaSnapshot := filepath.Join(testdataPath, "linux-arm64-c288e0776090cd558ef793b2a4e61939.tar.gz")

info, err := cpu.New(option.WithSnapshot(option.SnapshotOptions{
Path: multiNumaSnapshot,
}))

if err != nil {
t.Fatalf("Expected nil err, but got %v", err)
}
if info == nil {
t.Fatalf("Expected non-nil CPUInfo, but got nil")
}

if len(info.Processors) == 0 {
t.Fatalf("Expected >0 processors but got 0.")
}

for _, p := range info.Processors {
if p.NumCores == 0 {
t.Fatalf("Expected >0 cores but got 0.")
}
if p.NumThreads == 0 {
t.Fatalf("Expected >0 threads but got 0.")
}
if len(p.Capabilities) == 0 {
t.Fatalf("Expected >0 capabilities but got 0.")
}
if !p.HasCapability(p.Capabilities[0]) {
t.Fatalf("Expected p to have capability %s, but did not.",
p.Capabilities[0])
}
if len(p.Cores) == 0 {
t.Fatalf("Expected >0 cores in processor, but got 0.")
}
for _, c := range p.Cores {
if c.NumThreads == 0 {
t.Fatalf("Expected >0 threads but got 0.")
}
if len(c.LogicalProcessors) == 0 {
t.Fatalf("Expected >0 logical processors but got 0.")
}
}
}
}
2 changes: 2 additions & 0 deletions pkg/linuxpath/path_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type Paths struct {
SysBlock string
SysDevicesSystemNode string
SysDevicesSystemMemory string
SysDevicesSystemCPU string
SysBusPciDevices string
SysClassDRM string
SysClassDMI string
Expand All @@ -84,6 +85,7 @@ func New(ctx *context.Context) *Paths {
SysBlock: filepath.Join(ctx.Chroot, roots.Sys, "block"),
SysDevicesSystemNode: filepath.Join(ctx.Chroot, roots.Sys, "devices", "system", "node"),
SysDevicesSystemMemory: filepath.Join(ctx.Chroot, roots.Sys, "devices", "system", "memory"),
SysDevicesSystemCPU: filepath.Join(ctx.Chroot, roots.Sys, "devices", "system", "cpu"),
SysBusPciDevices: filepath.Join(ctx.Chroot, roots.Sys, "bus", "pci", "devices"),
SysClassDRM: filepath.Join(ctx.Chroot, roots.Sys, "class", "drm"),
SysClassDMI: filepath.Join(ctx.Chroot, roots.Sys, "class", "dmi"),
Expand Down
Binary file not shown.