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 }