diff --git a/collector/cpu_freebsd.go b/collector/cpu_freebsd.go index aa02c1a9..a04007a1 100644 --- a/collector/cpu_freebsd.go +++ b/collector/cpu_freebsd.go @@ -28,9 +28,26 @@ import ( #cgo LDFLAGS: -lkvm #include #include +#include #include #include #include +#include +#include + +long _clockrate() { + struct clockinfo clockrate; + size_t size = sizeof(clockrate); + int res = sysctlbyname("kern.clockrate", &clockrate, &size, NULL, 0); + if (res == -1) { + return -1; + } + if (size != sizeof(clockrate)) { + return -2; + } + return clockrate.stathz > 0 ? clockrate.stathz : clockrate.hz; +} + */ import "C" @@ -70,16 +87,37 @@ func (c *statCollector) Update(ch chan<- prometheus.Metric) (err error) { } defer C.kvm_close(kd) + // The cp_time variable is an array of CPUSTATES long integers -- in + // the same format as the kern.cp_time sysctl. According to the + // comments in sys/kern/kern_clock.c, the frequency of this timer will + // be stathz (or hz, if stathz is zero). + clockrate, err := getClockRate() + if err != nil { + return err + } + ncpus := C.kvm_getncpus(kd) for i := 0; i < int(ncpus); i++ { pcpu := C.kvm_getpcpu(kd, C.int(i)) cp_time := ((*C.struct_pcpu)(unsafe.Pointer(pcpu))).pc_cp_time - c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "user"}).Set(float64(cp_time[C.CP_USER])) - c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "nice"}).Set(float64(cp_time[C.CP_NICE])) - c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "system"}).Set(float64(cp_time[C.CP_SYS])) - c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "interrupt"}).Set(float64(cp_time[C.CP_INTR])) - c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "idle"}).Set(float64(cp_time[C.CP_IDLE])) + c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "user"}).Set(float64(cp_time[C.CP_USER])/clockrate) + c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "nice"}).Set(float64(cp_time[C.CP_NICE])/clockrate) + c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "system"}).Set(float64(cp_time[C.CP_SYS])/clockrate) + c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "interrupt"}).Set(float64(cp_time[C.CP_INTR])/clockrate) + c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "idle"}).Set(float64(cp_time[C.CP_IDLE])/clockrate) } c.cpu.Collect(ch) return err } + +func getClockRate() (float64, error) { + clockrate := C._clockrate() + if clockrate == -1 { + return 0, errors.New("sysctl(kern.clockrate) failed") + } else if clockrate == -2 { + return 0, errors.New("sysctl(kern.clockrate) failed, wrong buffer size") + } else if clockrate <= 0 { + return 0, errors.New("sysctl(kern.clockrate) bad clocktime") + } + return float64(clockrate), nil +}