Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix cpu_openbsd.go once and for all #1244

Merged
merged 6 commits into from Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
156 changes: 57 additions & 99 deletions cpu/cpu_openbsd.go
Expand Up @@ -4,141 +4,99 @@
package cpu

import (
"bytes"
"context"
"encoding/binary"
"fmt"
"runtime"
"strconv"
"strings"
"syscall"
"unsafe"

"github.com/shirou/gopsutil/v3/internal/common"
"github.com/tklauser/go-sysconf"
"golang.org/x/sys/unix"
)

// sys/sched.h
var (
CPUser = 0
cpNice = 1
cpSys = 2
cpIntr = 3
cpIdle = 4
cpUStates = 5
)

// sys/sysctl.h
const (
ctlKern = 1 // "high kernel": proc, limits
ctlHw = 6 // CTL_HW
sMT = 24 // HW_sMT
kernCptime = 40 // KERN_CPTIME
kernCptime2 = 71 // KERN_CPTIME2
// sys/sched.h
cpuOnline = 0x0001 // CPUSTATS_ONLINE

// sys/sysctl.h
ctlKern = 1 // "high kernel": proc, limits
ctlHw = 6 // CTL_HW
smt = 24 // HW_SMT
kernCpTime = 40 // KERN_CPTIME
kernCPUStats = 85 // KERN_CPUSTATS
)

var ClocksPerSec = float64(128)

type cpuStats struct {
// cs_time[CPUSTATES]
User uint64
Nice uint64
Sys uint64
Spin uint64
Intr uint64
Idle uint64

// cs_flags
Flags uint64
}

func init() {
clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
// ignore errors
if err == nil {
ClocksPerSec = float64(clkTck)
}

func() {
v, err := unix.Sysctl("kern.osrelease") // can't reuse host.PlatformInformation because of circular import
if err != nil {
return
}
v = strings.ToLower(v)
version, err := strconv.ParseFloat(v, 64)
if err != nil {
return
}
if version >= 6.4 {
cpIntr = 4
cpIdle = 5
cpUStates = 6
}
}()
}

func smt() (bool, error) {
mib := []int32{ctlHw, sMT}
buf, _, err := common.CallSyscall(mib)
if err != nil {
return false, err
}

var ret bool
br := bytes.NewReader(buf)
if err := binary.Read(br, binary.LittleEndian, &ret); err != nil {
return false, err
}

return ret, nil
}

func Times(percpu bool) ([]TimesStat, error) {
return TimesWithContext(context.Background(), percpu)
}

func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
var ret []TimesStat

var ncpu int
if percpu {
ncpu, _ = Counts(true)
} else {
ncpu = 1
func TimesWithContext(ctx context.Context, percpu bool) (ret []TimesStat, err error) {
if !percpu {
mib := []int32{ctlKern, kernCpTime}
buf, _, err := common.CallSyscall(mib)
if err != nil {
return ret, err
}
times := (*cpuTimes)(unsafe.Pointer(&buf[0]))
stat := TimesStat{
CPU: "cpu-total",
User: float64(times.User) / ClocksPerSec,
Nice: float64(times.Nice) / ClocksPerSec,
System: float64(times.Sys) / ClocksPerSec,
Idle: float64(times.Idle) / ClocksPerSec,
Irq: float64(times.Intr) / ClocksPerSec,
}
return []TimesStat{stat}, nil
}

smt, err := smt()
if err == syscall.EOPNOTSUPP {
// if hw.smt is not applicable for this platform (e.g. i386),
// pretend it's enabled
smt = true
} else if err != nil {
return nil, err
ncpu, err := unix.SysctlUint32("hw.ncpu")
if err != nil {
return
}

for i := 0; i < ncpu; i++ {
j := i
if !smt {
j *= 2
}

cpuTimes := make([]int32, cpUStates)
var mib []int32
if percpu {
mib = []int32{ctlKern, kernCptime2, int32(j)}
} else {
mib = []int32{ctlKern, kernCptime}
}
var i uint32
for i = 0; i < ncpu; i++ {
mib := []int32{ctlKern, kernCPUStats, int32(i)}
buf, _, err := common.CallSyscall(mib)
if err != nil {
return ret, err
}

br := bytes.NewReader(buf)
err = binary.Read(br, binary.LittleEndian, &cpuTimes)
if err != nil {
return ret, err
}
c := TimesStat{
User: float64(cpuTimes[CPUser]) / ClocksPerSec,
Nice: float64(cpuTimes[cpNice]) / ClocksPerSec,
System: float64(cpuTimes[cpSys]) / ClocksPerSec,
Idle: float64(cpuTimes[cpIdle]) / ClocksPerSec,
Irq: float64(cpuTimes[cpIntr]) / ClocksPerSec,
}
if percpu {
c.CPU = fmt.Sprintf("cpu%d", j)
} else {
c.CPU = "cpu-total"
stats := (*cpuStats)(unsafe.Pointer(&buf[0]))
if (stats.Flags & cpuOnline) == 0 {
continue
}
ret = append(ret, c)
ret = append(ret, TimesStat{
CPU: fmt.Sprintf("cpu%d", i),
User: float64(stats.User) / ClocksPerSec,
Nice: float64(stats.Nice) / ClocksPerSec,
System: float64(stats.Sys) / ClocksPerSec,
Idle: float64(stats.Idle) / ClocksPerSec,
Irq: float64(stats.Intr) / ClocksPerSec,
})
}

return ret, nil
Expand Down
10 changes: 10 additions & 0 deletions cpu/cpu_openbsd_386.go
@@ -0,0 +1,10 @@
package cpu

type cpuTimes struct {
User uint32
Nice uint32
Sys uint32
Spin uint32
Intr uint32
Idle uint32
}
10 changes: 10 additions & 0 deletions cpu/cpu_openbsd_amd64.go
@@ -0,0 +1,10 @@
package cpu

type cpuTimes struct {
User uint64
Nice uint64
Sys uint64
Spin uint64
Intr uint64
Idle uint64
}