1package cpu 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "os/exec" 8 "regexp" 9 "sort" 10 "strconv" 11 "strings" 12 13 "github.com/shirou/gopsutil/internal/common" 14) 15 16var ClocksPerSec = float64(128) 17 18func init() { 19 getconf, err := exec.LookPath("/usr/bin/getconf") 20 if err != nil { 21 return 22 } 23 out, err := invoke.Command(getconf, "CLK_TCK") 24 // ignore errors 25 if err == nil { 26 i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) 27 if err == nil { 28 ClocksPerSec = float64(i) 29 } 30 } 31} 32 33func Times(percpu bool) ([]TimesStat, error) { 34 return TimesWithContext(context.Background(), percpu) 35} 36 37func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { 38 return []TimesStat{}, common.ErrNotImplementedError 39} 40 41func Info() ([]InfoStat, error) { 42 return InfoWithContext(context.Background()) 43} 44 45func InfoWithContext(ctx context.Context) ([]InfoStat, error) { 46 psrInfo, err := exec.LookPath("/usr/sbin/psrinfo") 47 if err != nil { 48 return nil, fmt.Errorf("cannot find psrinfo: %s", err) 49 } 50 psrInfoOut, err := invoke.CommandWithContext(ctx, psrInfo, "-p", "-v") 51 if err != nil { 52 return nil, fmt.Errorf("cannot execute psrinfo: %s", err) 53 } 54 55 isaInfo, err := exec.LookPath("/usr/bin/isainfo") 56 if err != nil { 57 return nil, fmt.Errorf("cannot find isainfo: %s", err) 58 } 59 isaInfoOut, err := invoke.CommandWithContext(ctx, isaInfo, "-b", "-v") 60 if err != nil { 61 return nil, fmt.Errorf("cannot execute isainfo: %s", err) 62 } 63 64 procs, err := parseProcessorInfo(string(psrInfoOut)) 65 if err != nil { 66 return nil, fmt.Errorf("error parsing psrinfo output: %s", err) 67 } 68 69 flags, err := parseISAInfo(string(isaInfoOut)) 70 if err != nil { 71 return nil, fmt.Errorf("error parsing isainfo output: %s", err) 72 } 73 74 result := make([]InfoStat, 0, len(flags)) 75 for _, proc := range procs { 76 procWithFlags := proc 77 procWithFlags.Flags = flags 78 result = append(result, procWithFlags) 79 } 80 81 return result, nil 82} 83 84var flagsMatch = regexp.MustCompile(`[\w\.]+`) 85 86func parseISAInfo(cmdOutput string) ([]string, error) { 87 words := flagsMatch.FindAllString(cmdOutput, -1) 88 89 // Sanity check the output 90 if len(words) < 4 || words[1] != "bit" || words[3] != "applications" { 91 return nil, errors.New("attempted to parse invalid isainfo output") 92 } 93 94 flags := make([]string, len(words)-4) 95 for i, val := range words[4:] { 96 flags[i] = val 97 } 98 sort.Strings(flags) 99 100 return flags, nil 101} 102 103var psrInfoMatch = regexp.MustCompile(`The physical processor has (?:([\d]+) virtual processor \(([\d]+)\)|([\d]+) cores and ([\d]+) virtual processors[^\n]+)\n(?:\s+ The core has.+\n)*\s+.+ \((\w+) ([\S]+) family (.+) model (.+) step (.+) clock (.+) MHz\)\n[\s]*(.*)`) 104 105const ( 106 psrNumCoresOffset = 1 107 psrNumCoresHTOffset = 3 108 psrNumHTOffset = 4 109 psrVendorIDOffset = 5 110 psrFamilyOffset = 7 111 psrModelOffset = 8 112 psrStepOffset = 9 113 psrClockOffset = 10 114 psrModelNameOffset = 11 115) 116 117func parseProcessorInfo(cmdOutput string) ([]InfoStat, error) { 118 matches := psrInfoMatch.FindAllStringSubmatch(cmdOutput, -1) 119 120 var infoStatCount int32 121 result := make([]InfoStat, 0, len(matches)) 122 for physicalIndex, physicalCPU := range matches { 123 var step int32 124 var clock float64 125 126 if physicalCPU[psrStepOffset] != "" { 127 stepParsed, err := strconv.ParseInt(physicalCPU[psrStepOffset], 10, 32) 128 if err != nil { 129 return nil, fmt.Errorf("cannot parse value %q for step as 32-bit integer: %s", physicalCPU[9], err) 130 } 131 step = int32(stepParsed) 132 } 133 134 if physicalCPU[psrClockOffset] != "" { 135 clockParsed, err := strconv.ParseInt(physicalCPU[psrClockOffset], 10, 64) 136 if err != nil { 137 return nil, fmt.Errorf("cannot parse value %q for clock as 32-bit integer: %s", physicalCPU[10], err) 138 } 139 clock = float64(clockParsed) 140 } 141 142 var err error 143 var numCores int64 144 var numHT int64 145 switch { 146 case physicalCPU[psrNumCoresOffset] != "": 147 numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresOffset], 10, 32) 148 if err != nil { 149 return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[1], err) 150 } 151 152 for i := 0; i < int(numCores); i++ { 153 result = append(result, InfoStat{ 154 CPU: infoStatCount, 155 PhysicalID: strconv.Itoa(physicalIndex), 156 CoreID: strconv.Itoa(i), 157 Cores: 1, 158 VendorID: physicalCPU[psrVendorIDOffset], 159 ModelName: physicalCPU[psrModelNameOffset], 160 Family: physicalCPU[psrFamilyOffset], 161 Model: physicalCPU[psrModelOffset], 162 Stepping: step, 163 Mhz: clock, 164 }) 165 infoStatCount++ 166 } 167 case physicalCPU[psrNumCoresHTOffset] != "": 168 numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresHTOffset], 10, 32) 169 if err != nil { 170 return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[3], err) 171 } 172 173 numHT, err = strconv.ParseInt(physicalCPU[psrNumHTOffset], 10, 32) 174 if err != nil { 175 return nil, fmt.Errorf("cannot parse value %q for hyperthread count as 32-bit integer: %s", physicalCPU[4], err) 176 } 177 178 for i := 0; i < int(numCores); i++ { 179 result = append(result, InfoStat{ 180 CPU: infoStatCount, 181 PhysicalID: strconv.Itoa(physicalIndex), 182 CoreID: strconv.Itoa(i), 183 Cores: int32(numHT) / int32(numCores), 184 VendorID: physicalCPU[psrVendorIDOffset], 185 ModelName: physicalCPU[psrModelNameOffset], 186 Family: physicalCPU[psrFamilyOffset], 187 Model: physicalCPU[psrModelOffset], 188 Stepping: step, 189 Mhz: clock, 190 }) 191 infoStatCount++ 192 } 193 default: 194 return nil, errors.New("values for cores with and without hyperthreading are both set") 195 } 196 } 197 return result, nil 198} 199