| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- // Use and distribution licensed under the Apache license version 2.
- //
- // See the COPYING file in the root project directory for full text.
- //
- package cpu
- import (
- "bufio"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "github.com/jaypipes/ghw/pkg/context"
- "github.com/jaypipes/ghw/pkg/linuxpath"
- "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
- var totThreads uint32
- for _, p := range i.Processors {
- totCores += p.NumCores
- totThreads += p.NumThreads
- }
- i.TotalCores = totCores
- i.TotalThreads = totThreads
- 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)
- r, err := os.Open(paths.ProcCpuinfo)
- if err != nil {
- return nil
- }
- defer util.SafeClose(r)
- // An array of maps of attributes describing the logical 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())
- 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
- }
- // Iterate on /sys/devices/system/cpu/cpuN, not on /proc/cpuinfo
- Entries, err := ioutil.ReadDir(paths.SysDevicesSystemCPU)
- if err != nil {
- return nil
- }
- for _, lcore := range Entries {
- matches := regexForCpulCore.FindStringSubmatch(lcore.Name())
- if len(matches) < 2 {
- continue
- }
- lcoreID, error := strconv.Atoi(matches[1])
- if error != 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"], " ")
- }
- 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"]
- }
- 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
- }
- proc.NumThreads += 1
- core.LogicalProcessors = append(core.LogicalProcessors, lcoreID)
- }
- return procs
- }
- func CoresForNode(ctx *context.Context, 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.
- paths := linuxpath.New(ctx)
- path := filepath.Join(
- paths.SysDevicesSystemNode,
- 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{
- 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 := util.SafeIntFromFile(ctx, coreIDPath)
- core := findCoreByID(coreID)
- core.LogicalProcessors = append(
- core.LogicalProcessors,
- procID,
- )
- }
- for _, c := range cores {
- c.NumThreads = uint32(len(c.LogicalProcessors))
- }
- return cores, nil
- }
|