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