1package cpu 2 3import ( 4 "context" 5 "fmt" 6 "os/exec" 7 "reflect" 8 "regexp" 9 "runtime" 10 "strconv" 11 "strings" 12 "unsafe" 13 14 "github.com/shirou/gopsutil/internal/common" 15 "golang.org/x/sys/unix" 16) 17 18var ClocksPerSec = float64(128) 19var cpuMatch = regexp.MustCompile(`^CPU:`) 20var originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Family\s*=\s*(.+)\s+Model\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`) 21var featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`) 22var featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`) 23var cpuEnd = regexp.MustCompile(`^Trying to mount root`) 24var cpuCores = regexp.MustCompile(`FreeBSD/SMP: (\d*) package\(s\) x (\d*) core\(s\)`) 25var cpuTimesSize int 26var emptyTimes cpuTimes 27 28func init() { 29 getconf, err := exec.LookPath("getconf") 30 if err != nil { 31 return 32 } 33 out, err := invoke.Command(getconf, "CLK_TCK") 34 // ignore errors 35 if err == nil { 36 i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) 37 if err == nil { 38 ClocksPerSec = float64(i) 39 } 40 } 41} 42 43func timeStat(name string, t *cpuTimes) *TimesStat { 44 return &TimesStat{ 45 User: float64(t.User) / ClocksPerSec, 46 Nice: float64(t.Nice) / ClocksPerSec, 47 System: float64(t.Sys) / ClocksPerSec, 48 Idle: float64(t.Idle) / ClocksPerSec, 49 Irq: float64(t.Intr) / ClocksPerSec, 50 CPU: name, 51 } 52} 53 54func Times(percpu bool) ([]TimesStat, error) { 55 return TimesWithContext(context.Background(), percpu) 56} 57 58func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { 59 if percpu { 60 buf, err := unix.SysctlRaw("kern.cp_times") 61 if err != nil { 62 return nil, err 63 } 64 65 // We can't do this in init due to the conflict with cpu.init() 66 if cpuTimesSize == 0 { 67 cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size()) 68 } 69 70 ncpus := len(buf) / cpuTimesSize 71 ret := make([]TimesStat, 0, ncpus) 72 for i := 0; i < ncpus; i++ { 73 times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize])) 74 if *times == emptyTimes { 75 // CPU not present 76 continue 77 } 78 ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times)) 79 } 80 return ret, nil 81 } 82 83 buf, err := unix.SysctlRaw("kern.cp_time") 84 if err != nil { 85 return nil, err 86 } 87 88 times := (*cpuTimes)(unsafe.Pointer(&buf[0])) 89 return []TimesStat{*timeStat("cpu-total", times)}, nil 90} 91 92// Returns only one InfoStat on FreeBSD. The information regarding core 93// count, however is accurate and it is assumed that all InfoStat attributes 94// are the same across CPUs. 95func Info() ([]InfoStat, error) { 96 return InfoWithContext(context.Background()) 97} 98 99func InfoWithContext(ctx context.Context) ([]InfoStat, error) { 100 const dmesgBoot = "/var/run/dmesg.boot" 101 102 c, num, err := parseDmesgBoot(dmesgBoot) 103 if err != nil { 104 return nil, err 105 } 106 107 var u32 uint32 108 if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil { 109 return nil, err 110 } 111 c.Mhz = float64(u32) 112 113 if u32, err = unix.SysctlUint32("hw.ncpu"); err != nil { 114 return nil, err 115 } 116 c.Cores = int32(u32) 117 118 if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { 119 return nil, err 120 } 121 122 ret := make([]InfoStat, num) 123 for i := 0; i < num; i++ { 124 ret[i] = c 125 } 126 127 return ret, nil 128} 129 130func parseDmesgBoot(fileName string) (InfoStat, int, error) { 131 c := InfoStat{} 132 lines, _ := common.ReadLines(fileName) 133 cpuNum := 1 // default cpu num is 1 134 for _, line := range lines { 135 if matches := cpuEnd.FindStringSubmatch(line); matches != nil { 136 break 137 } else if matches := originMatch.FindStringSubmatch(line); matches != nil { 138 c.VendorID = matches[1] 139 c.Family = matches[3] 140 c.Model = matches[4] 141 t, err := strconv.ParseInt(matches[5], 10, 32) 142 if err != nil { 143 return c, 0, fmt.Errorf("unable to parse FreeBSD CPU stepping information from %q: %v", line, err) 144 } 145 c.Stepping = int32(t) 146 } else if matches := featuresMatch.FindStringSubmatch(line); matches != nil { 147 for _, v := range strings.Split(matches[1], ",") { 148 c.Flags = append(c.Flags, strings.ToLower(v)) 149 } 150 } else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil { 151 for _, v := range strings.Split(matches[1], ",") { 152 c.Flags = append(c.Flags, strings.ToLower(v)) 153 } 154 } else if matches := cpuCores.FindStringSubmatch(line); matches != nil { 155 t, err := strconv.ParseInt(matches[1], 10, 32) 156 if err != nil { 157 return c, 0, fmt.Errorf("unable to parse FreeBSD CPU Nums from %q: %v", line, err) 158 } 159 cpuNum = int(t) 160 t2, err := strconv.ParseInt(matches[2], 10, 32) 161 if err != nil { 162 return c, 0, fmt.Errorf("unable to parse FreeBSD CPU cores from %q: %v", line, err) 163 } 164 c.Cores = int32(t2) 165 } 166 } 167 168 return c, cpuNum, nil 169} 170 171func CountsWithContext(ctx context.Context, logical bool) (int, error) { 172 return runtime.NumCPU(), nil 173} 174