github.com/jaypipes/ghw@v0.21.1/pkg/cpu/cpu_darwin.go (about)

     1  package cpu
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/pkg/errors"
     6  	"os/exec"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  var (
    12  	hasARMArchitecture = false                   // determine if ARM
    13  	sysctlOutput       = make(map[string]string) // store all the sysctl output
    14  )
    15  
    16  func (i *Info) load() error {
    17  	err := populateSysctlOutput()
    18  	if err != nil {
    19  		return errors.Wrap(err, "unable to populate sysctl map")
    20  	}
    21  
    22  	i.TotalCores = getTotalCores()
    23  	i.TotalThreads = getTotalThreads()
    24  	i.Processors = getProcessors()
    25  
    26  	return nil
    27  }
    28  
    29  // getProcessors some more info https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_system_capabilities
    30  func getProcessors() []*Processor {
    31  	p := make([]*Processor, getProcTopCount())
    32  	for i, _ := range p {
    33  		p[i] = new(Processor)
    34  		p[i].Vendor = sysctlOutput[fmt.Sprintf("hw.perflevel%s.name", strconv.Itoa(i))]
    35  		p[i].Model = getVendor()
    36  		p[i].NumCores = getNumberCoresFromPerfLevel(i)
    37  		p[i].Capabilities = getCapabilities()
    38  		p[i].Cores = make([]*ProcessorCore, getTotalCores())
    39  	}
    40  	return p
    41  }
    42  
    43  // getCapabilities valid for ARM, see https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics
    44  func getCapabilities() []string {
    45  	var caps []string
    46  
    47  	// add ARM capabilities
    48  	if hasARMArchitecture {
    49  		for cap, isEnabled := range sysctlOutput {
    50  			if isEnabled == "1" {
    51  				// capabilities with keys with a common prefix
    52  				commonPrefix := "hw.optional.arm."
    53  				if strings.HasPrefix(cap, commonPrefix) {
    54  					caps = append(caps, strings.TrimPrefix(cap, commonPrefix))
    55  				}
    56  				// not following prefix convention but are important
    57  				if cap == "hw.optional.AdvSIMD_HPFPCvt" {
    58  					caps = append(caps, "AdvSIMD_HPFPCvt")
    59  				}
    60  				if cap == "hw.optional.armv8_crc32" {
    61  					caps = append(caps, "armv8_crc32")
    62  				}
    63  			}
    64  		}
    65  
    66  		// hw.optional.AdvSIMD and hw.optional.floatingpoint are always enabled (see linked doc)
    67  		caps = append(caps, "AdvSIMD")
    68  		caps = append(caps, "floatingpoint")
    69  	}
    70  
    71  	return caps
    72  }
    73  
    74  // populateSysctlOutput to populate a map to quickly retrieve values later
    75  func populateSysctlOutput() error {
    76  	// get sysctl output
    77  	o, err := exec.Command("sysctl", "-a").CombinedOutput()
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	// clean up and store sysctl output
    83  	oS := strings.Split(string(o), "\n")
    84  	for _, l := range oS {
    85  		if l != "" {
    86  			s := strings.SplitN(l, ":", 2)
    87  			if len(s) < 2 {
    88  				continue
    89  			}
    90  			k, v := strings.TrimSpace(s[0]), strings.TrimSpace(s[1])
    91  			sysctlOutput[k] = v
    92  
    93  			// see if it's possible to determine if ARM
    94  			if k == "hw.optional.arm64" && v == "1" {
    95  				hasARMArchitecture = true
    96  			}
    97  		}
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func getNumberCoresFromPerfLevel(i int) uint32 {
   104  	key := fmt.Sprintf("hw.perflevel%s.physicalcpu_max", strconv.Itoa(i))
   105  	nCores := sysctlOutput[key]
   106  	return stringToUint32(nCores)
   107  }
   108  
   109  func getVendor() string {
   110  	v := sysctlOutput["machdep.cpu.brand_string"]
   111  	return v
   112  }
   113  
   114  func getProcTopCount() int {
   115  	pC, ok := sysctlOutput["hw.nperflevels"]
   116  	if !ok {
   117  		// most likely intel so no performance/efficiency core seperation
   118  		return 1
   119  	}
   120  	i, _ := strconv.Atoi(pC)
   121  	return i
   122  }
   123  
   124  // num of physical cores
   125  func getTotalCores() uint32 {
   126  	nCores := sysctlOutput["hw.physicalcpu_max"]
   127  	return stringToUint32(nCores)
   128  }
   129  
   130  func getTotalThreads() uint32 {
   131  	nThreads := sysctlOutput["machdep.cpu.thread_count"]
   132  	return stringToUint32(nThreads)
   133  }
   134  
   135  func stringToUint32(s string) uint32 {
   136  	o, _ := strconv.ParseUint(s, 10, 0)
   137  	return uint32(o)
   138  }