1// +build openbsd 2 3package cpu 4 5import ( 6 "bytes" 7 "context" 8 "encoding/binary" 9 "fmt" 10 "os/exec" 11 "runtime" 12 "strconv" 13 "strings" 14 "syscall" 15 16 "github.com/shirou/gopsutil/internal/common" 17 "golang.org/x/sys/unix" 18) 19 20// sys/sched.h 21var ( 22 CPUser = 0 23 CPNice = 1 24 CPSys = 2 25 CPIntr = 3 26 CPIdle = 4 27 CPUStates = 5 28) 29 30// sys/sysctl.h 31const ( 32 CTLKern = 1 // "high kernel": proc, limits 33 CTLHw = 6 // CTL_HW 34 SMT = 24 // HW_SMT 35 NCpuOnline = 25 // HW_NCPUONLINE 36 KernCptime = 40 // KERN_CPTIME 37 KernCptime2 = 71 // KERN_CPTIME2 38) 39 40var ClocksPerSec = float64(128) 41 42func init() { 43 func() { 44 getconf, err := exec.LookPath("getconf") 45 if err != nil { 46 return 47 } 48 out, err := invoke.Command(getconf, "CLK_TCK") 49 // ignore errors 50 if err == nil { 51 i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) 52 if err == nil { 53 ClocksPerSec = float64(i) 54 } 55 } 56 }() 57 func() { 58 v, err := unix.Sysctl("kern.osrelease") // can't reuse host.PlatformInformation because of circular import 59 if err != nil { 60 return 61 } 62 v = strings.ToLower(v) 63 version, err := strconv.ParseFloat(v, 64) 64 if err != nil { 65 return 66 } 67 if version >= 6.4 { 68 CPIntr = 4 69 CPIdle = 5 70 CPUStates = 6 71 } 72 }() 73} 74 75func smt() (bool, error) { 76 mib := []int32{CTLHw, SMT} 77 buf, _, err := common.CallSyscall(mib) 78 if err != nil { 79 return false, err 80 } 81 82 var ret bool 83 br := bytes.NewReader(buf) 84 if err := binary.Read(br, binary.LittleEndian, &ret); err != nil { 85 return false, err 86 } 87 88 return ret, nil 89} 90 91func Times(percpu bool) ([]TimesStat, error) { 92 return TimesWithContext(context.Background(), percpu) 93} 94 95func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { 96 var ret []TimesStat 97 98 var ncpu int 99 if percpu { 100 ncpu, _ = Counts(true) 101 } else { 102 ncpu = 1 103 } 104 105 smt, err := smt() 106 if err == syscall.EOPNOTSUPP { 107 // if hw.smt is not applicable for this platform (e.g. i386), 108 // pretend it's enabled 109 smt = true 110 } else if err != nil { 111 return nil, err 112 } 113 114 for i := 0; i < ncpu; i++ { 115 j := i 116 if !smt { 117 j *= 2 118 } 119 120 var cpuTimes = make([]int32, CPUStates) 121 var mib []int32 122 if percpu { 123 mib = []int32{CTLKern, KernCptime2, int32(j)} 124 } else { 125 mib = []int32{CTLKern, KernCptime} 126 } 127 buf, _, err := common.CallSyscall(mib) 128 if err != nil { 129 return ret, err 130 } 131 132 br := bytes.NewReader(buf) 133 err = binary.Read(br, binary.LittleEndian, &cpuTimes) 134 if err != nil { 135 return ret, err 136 } 137 c := TimesStat{ 138 User: float64(cpuTimes[CPUser]) / ClocksPerSec, 139 Nice: float64(cpuTimes[CPNice]) / ClocksPerSec, 140 System: float64(cpuTimes[CPSys]) / ClocksPerSec, 141 Idle: float64(cpuTimes[CPIdle]) / ClocksPerSec, 142 Irq: float64(cpuTimes[CPIntr]) / ClocksPerSec, 143 } 144 if percpu { 145 c.CPU = fmt.Sprintf("cpu%d", j) 146 } else { 147 c.CPU = "cpu-total" 148 } 149 ret = append(ret, c) 150 } 151 152 return ret, nil 153} 154 155// Returns only one (minimal) CPUInfoStat on OpenBSD 156func Info() ([]InfoStat, error) { 157 return InfoWithContext(context.Background()) 158} 159 160func InfoWithContext(ctx context.Context) ([]InfoStat, error) { 161 var ret []InfoStat 162 var err error 163 164 c := InfoStat{} 165 166 var u32 uint32 167 if u32, err = unix.SysctlUint32("hw.cpuspeed"); err != nil { 168 return nil, err 169 } 170 c.Mhz = float64(u32) 171 172 mib := []int32{CTLHw, NCpuOnline} 173 buf, _, err := common.CallSyscall(mib) 174 if err != nil { 175 return nil, err 176 } 177 178 var ncpu int32 179 br := bytes.NewReader(buf) 180 err = binary.Read(br, binary.LittleEndian, &ncpu) 181 if err != nil { 182 return nil, err 183 } 184 c.Cores = ncpu 185 186 if c.ModelName, err = unix.Sysctl("hw.model"); err != nil { 187 return nil, err 188 } 189 190 return append(ret, c), nil 191} 192 193func CountsWithContext(ctx context.Context, logical bool) (int, error) { 194 return runtime.NumCPU(), nil 195} 196