1package cpu 2 3import ( 4 "context" 5 "fmt" 6 "reflect" 7 "regexp" 8 "runtime" 9 "strconv" 10 "strings" 11 "unsafe" 12 13 "github.com/shirou/gopsutil/v3/internal/common" 14 "github.com/tklauser/go-sysconf" 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+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 cpuTimesSize int 25var emptyTimes cpuTimes 26 27func init() { 28 clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK) 29 // ignore errors 30 if err == nil { 31 ClocksPerSec = float64(clkTck) 32 } 33} 34 35func timeStat(name string, t *cpuTimes) *TimesStat { 36 return &TimesStat{ 37 User: float64(t.User) / ClocksPerSec, 38 Nice: float64(t.Nice) / ClocksPerSec, 39 System: float64(t.Sys) / ClocksPerSec, 40 Idle: float64(t.Idle) / ClocksPerSec, 41 Irq: float64(t.Intr) / ClocksPerSec, 42 CPU: name, 43 } 44} 45 46func Times(percpu bool) ([]TimesStat, error) { 47 return TimesWithContext(context.Background(), percpu) 48} 49 50func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { 51 if percpu { 52 buf, err := unix.SysctlRaw("kern.cp_times") 53 if err != nil { 54 return nil, err 55 } 56 57 // We can't do this in init due to the conflict with cpu.init() 58 if cpuTimesSize == 0 { 59 cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size()) 60 } 61 62 ncpus := len(buf) / cpuTimesSize 63 ret := make([]TimesStat, 0, ncpus) 64 for i := 0; i < ncpus; i++ { 65 times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize])) 66 if *times == emptyTimes { 67 // CPU not present 68 continue 69 } 70 ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times)) 71 } 72 return ret, nil 73 } 74 75 buf, err := unix.SysctlRaw("kern.cp_time") 76 if err != nil { 77 return nil, err 78 } 79 80 times := (*cpuTimes)(unsafe.Pointer(&buf[0])) 81 return []TimesStat{*timeStat("cpu-total", times)}, nil 82} 83 84// Returns only one InfoStat on DragonflyBSD. The information regarding core 85// count, however is accurate and it is assumed that all InfoStat attributes 86// are the same across CPUs. 87func Info() ([]InfoStat, error) { 88 return InfoWithContext(context.Background()) 89} 90 91func InfoWithContext(ctx context.Context) ([]InfoStat, error) { 92 const dmesgBoot = "/var/run/dmesg.boot" 93 94 c, err := parseDmesgBoot(dmesgBoot) 95 if err != nil { 96 return nil, err 97 } 98 99 var u32 uint32 100 if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil { 101 return nil, err 102 } 103 c.Mhz = float64(u32) 104 105 var num int 106 var buf string 107 if buf, err = unix.Sysctl("hw.cpu_topology.tree"); err != nil { 108 return nil, err 109 } 110 num = strings.Count(buf, "CHIP") 111 c.Cores = int32(strings.Count(string(buf), "CORE") / num) 112 113 if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { 114 return nil, err 115 } 116 117 ret := make([]InfoStat, num) 118 for i := 0; i < num; i++ { 119 ret[i] = c 120 } 121 122 return ret, nil 123} 124 125func parseDmesgBoot(fileName string) (InfoStat, error) { 126 c := InfoStat{} 127 lines, _ := common.ReadLines(fileName) 128 for _, line := range lines { 129 if matches := cpuEnd.FindStringSubmatch(line); matches != nil { 130 break 131 } else if matches := originMatch.FindStringSubmatch(line); matches != nil { 132 c.VendorID = matches[1] 133 t, err := strconv.ParseInt(matches[2], 10, 32) 134 if err != nil { 135 return c, fmt.Errorf("unable to parse DragonflyBSD CPU stepping information from %q: %v", line, err) 136 } 137 c.Stepping = int32(t) 138 } else if matches := featuresMatch.FindStringSubmatch(line); matches != nil { 139 for _, v := range strings.Split(matches[1], ",") { 140 c.Flags = append(c.Flags, strings.ToLower(v)) 141 } 142 } else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil { 143 for _, v := range strings.Split(matches[1], ",") { 144 c.Flags = append(c.Flags, strings.ToLower(v)) 145 } 146 } 147 } 148 149 return c, nil 150} 151 152func CountsWithContext(ctx context.Context, logical bool) (int, error) { 153 return runtime.NumCPU(), nil 154} 155