git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/sysinfo/cpu.go (about)

     1  // Copyright © 2016 Zlatko Čalušić
     2  //
     3  // Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
     4  
     5  package sysinfo
     6  
     7  import (
     8  	"bufio"
     9  	"fmt"
    10  	"os"
    11  	"regexp"
    12  	"runtime"
    13  	"strconv"
    14  	"strings"
    15  )
    16  
    17  // CPU information.
    18  type CPU struct {
    19  	Vendor  string `json:"vendor,omitempty"`
    20  	Model   string `json:"model,omitempty"`
    21  	Speed   uint   `json:"speed,omitempty"`   // CPU clock rate in MHz
    22  	Cache   uint   `json:"cache,omitempty"`   // CPU cache size in KB
    23  	Cpus    uint   `json:"cpus,omitempty"`    // number of physical CPUs
    24  	Cores   uint   `json:"cores,omitempty"`   // number of physical CPU cores
    25  	Threads uint   `json:"threads,omitempty"` // number of logical (HT) CPU cores
    26  }
    27  
    28  var (
    29  	reTwoColumns = regexp.MustCompile("\t+: ")
    30  	reExtraSpace = regexp.MustCompile(" +")
    31  	reCacheSize  = regexp.MustCompile(`^(\d+) KB$`)
    32  )
    33  
    34  func (si *SysInfo) getCPUInfo() {
    35  	si.CPU.Threads = uint(runtime.NumCPU())
    36  
    37  	f, err := os.Open("/proc/cpuinfo")
    38  	if err != nil {
    39  		return
    40  	}
    41  	defer f.Close()
    42  
    43  	cpu := make(map[string]bool)
    44  	core := make(map[string]bool)
    45  
    46  	var cpuID string
    47  
    48  	s := bufio.NewScanner(f)
    49  	for s.Scan() {
    50  		if sl := reTwoColumns.Split(s.Text(), 2); sl != nil {
    51  			switch sl[0] {
    52  			case "physical id":
    53  				cpuID = sl[1]
    54  				cpu[cpuID] = true
    55  			case "core id":
    56  				coreID := fmt.Sprintf("%s/%s", cpuID, sl[1])
    57  				core[coreID] = true
    58  			case "vendor_id":
    59  				if si.CPU.Vendor == "" {
    60  					si.CPU.Vendor = sl[1]
    61  				}
    62  			case "model name":
    63  				if si.CPU.Model == "" {
    64  					// CPU model, as reported by /proc/cpuinfo, can be a bit ugly. Clean up...
    65  					model := reExtraSpace.ReplaceAllLiteralString(sl[1], " ")
    66  					si.CPU.Model = strings.Replace(model, "- ", "-", 1)
    67  				}
    68  			case "cache size":
    69  				if si.CPU.Cache == 0 {
    70  					if m := reCacheSize.FindStringSubmatch(sl[1]); m != nil {
    71  						if cache, err := strconv.ParseUint(m[1], 10, 64); err == nil {
    72  							si.CPU.Cache = uint(cache)
    73  						}
    74  					}
    75  				}
    76  			}
    77  		}
    78  	}
    79  	if s.Err() != nil {
    80  		return
    81  	}
    82  
    83  	// getNodeInfo() must have run first, to detect if we're dealing with a virtualized CPU! Detecting number of
    84  	// physical processors and/or cores is totally unreliable in virtualized environments, so let's not do it.
    85  	if si.Node.Hostname == "" || si.Node.Hypervisor != "" {
    86  		return
    87  	}
    88  
    89  	si.CPU.Cpus = uint(len(cpu))
    90  	si.CPU.Cores = uint(len(core))
    91  }