metric/cpu/metrics_openbsd.go (74 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//go:build openbsd
// +build openbsd
package cpu
/*
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/mount.h>
#include <sys/sched.h>
#include <sys/swap.h>
#include <stdlib.h>
#include <unistd.h>
*/
import "C"
import (
"syscall"
"unsafe"
"github.com/elastic/elastic-agent-libs/opt"
)
// Get is the OpenBSD implementation of get
func Get(m *Monitor) (CPUMetrics, error) {
// see man 2 sysctl
loadGlobal := [C.CPUSTATES]C.long{
C.CP_USER,
C.CP_NICE,
C.CP_SYS,
C.CP_INTR,
C.CP_IDLE,
}
// first, fetch global CPU data
err := sysctlGetCPUTimes(0, 0, &loadGlobal)
if err != nil {
return CPUMetrics{}, err
}
self := CPU{}
self.User = opt.UintWith(loadGlobal[0])
self.Nice = opt.UintWith(loadGlobal[1])
self.Sys = opt.UintWith(loadGlobal[2])
self.Irq = opt.UintWith(loadGlobal[3])
self.Idle = opt.UintWith(loadGlobal[4])
// Get count of available CPUs
ncpuMIB := [2]int32{C.CTL_HW, C.HW_NCPU}
callSize := uintptr(0)
var ncpu int
// The first call nulls out the retun pointer, so we instead fetch the expected size of expected return value.
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&ncpuMIB[0])), 2, 0, uintptr(unsafe.Pointer(&callSize)), 0, 0)
if errno != 0 || callSize == 0 {
return CPUMetrics{}, errno
}
// Populate the cpu count
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&ncpuMIB[0])), 2, uintptr(unsafe.Pointer(&ncpu)), uintptr(unsafe.Pointer(&callSize)), 0, 0)
if errno != 0 || callSize == 0 {
return CPUMetrics{}, errno
}
loadPerCPU := [C.CPUSTATES]C.long{
C.CP_USER,
C.CP_NICE,
C.CP_SYS,
C.CP_INTR,
C.CP_IDLE,
}
perCPU := make([]CPU, ncpu)
// iterate over metrics for each CPU
for i := 0; i < ncpu; i++ {
sysctlGetCPUTimes(ncpu, i, &loadPerCPU)
perCPU[i].User = opt.UintWith(loadGlobal[0])
perCPU[i].Nice = opt.UintWith(loadGlobal[1])
perCPU[i].Sys = opt.UintWith(loadGlobal[2])
perCPU[i].Irq = opt.UintWith(loadGlobal[3])
perCPU[i].Idle = opt.UintWith(loadGlobal[4])
}
metrics := CPUMetrics{totals: self, list: perCPU}
return metrics, nil
}
// sysctlGetCPUTimes runs the CTL_KERN::KERN_CPTIME sysctl command against the host.
func sysctlGetCPUTimes(ncpu int, curcpu int, load *[C.CPUSTATES]C.long) error {
var mib []int32
// Use the correct mib based on the number of CPUs and fill out the
// current CPU number in case of SMP. (0 indexed cf. self.List)
if ncpu == 0 {
mib = []int32{C.CTL_KERN, C.KERN_CPTIME}
} else {
mib = []int32{C.CTL_KERN, C.KERN_CPTIME2, int32(curcpu)}
}
len := len(mib)
n := uintptr(0)
// First we determine how much memory we'll need to pass later on (via `n`)
_, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), uintptr(len), 0, uintptr(unsafe.Pointer(&n)), 0, 0)
if errno != 0 || n == 0 {
return errno
}
_, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), uintptr(len), uintptr(unsafe.Pointer(load)), uintptr(unsafe.Pointer(&n)), 0, 0)
if errno != 0 || n == 0 {
return errno
}
return nil
}