diff --git a/README.md b/README.md index ee806e1a..cc0c363e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Which collectors are used is controlled by the `--collectors.enabled` flag. Name | Description | OS ---------|-------------|---- conntrack | Shows conntrack statistics (does nothing if no `/proc/sys/net/netfilter/` present). | Linux -cpu | Exposes CPU statistics | Dragonfly, FreeBSD +cpu | Exposes CPU statistics | Darwin, Dragonfly, FreeBSD diskstats | Exposes disk I/O statistics from `/proc/diskstats`. | Linux entropy | Exposes available entropy. | Linux filefd | Exposes file descriptor statistics from `/proc/sys/fs/file-nr`. | Linux diff --git a/collector/cpu_darwin.go b/collector/cpu_darwin.go new file mode 100644 index 00000000..174e541f --- /dev/null +++ b/collector/cpu_darwin.go @@ -0,0 +1,119 @@ +// Copyright 2016 The Prometheus Authors +// Licensed 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. + +// Based on gopsutil/cpu/cpu_darwin_cgo.go @ ae251eb which is licensed under +// BSD. See https://github.com/shirou/gopsutil/blob/master/LICENSE for details. + +// +build !nocpu + +package collector + +import ( + "bytes" + "encoding/binary" + "fmt" + "strconv" + "unsafe" + + "github.com/prometheus/client_golang/prometheus" +) + +/* +#cgo LDFLAGS: +#include +#include +#include +#include +#include +#include +#if TARGET_OS_MAC +#include +#endif +#include +#include +*/ +import "C" + +// default value. from time.h +const ClocksPerSec = float64(128) + +type statCollector struct { + cpu *prometheus.Desc +} + +func init() { + Factories["cpu"] = NewCPUCollector +} + +// Takes a prometheus registry and returns a new Collector exposing +// CPU stats. +func NewCPUCollector() (Collector, error) { + return &statCollector{ + cpu: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, "", "cpu"), + "Seconds the cpus spent in each mode.", + []string{"cpu", "mode"}, nil, + ), + }, nil +} + +func (c *statCollector) Update(ch chan<- prometheus.Metric) error { + var ( + count C.mach_msg_type_number_t + cpuload *C.processor_cpu_load_info_data_t + ncpu C.natural_t + ) + + status := C.host_processor_info(C.host_t(C.mach_host_self()), + C.PROCESSOR_CPU_LOAD_INFO, + &ncpu, + (*C.processor_info_array_t)(unsafe.Pointer(&cpuload)), + &count) + + if status != C.KERN_SUCCESS { + return fmt.Errorf("host_processor_info error=%d", status) + } + + // jump through some cgo casting hoops and ensure we properly free + // the memory that cpuload points to + target := C.vm_map_t(C.mach_task_self_) + address := C.vm_address_t(uintptr(unsafe.Pointer(cpuload))) + defer C.vm_deallocate(target, address, C.vm_size_t(ncpu)) + + // the body of struct processor_cpu_load_info + // aka processor_cpu_load_info_data_t + var cpu_ticks [C.CPU_STATE_MAX]uint32 + + // copy the cpuload array to a []byte buffer + // where we can binary.Read the data + size := int(ncpu) * binary.Size(cpu_ticks) + buf := (*[1 << 30]byte)(unsafe.Pointer(cpuload))[:size:size] + + bbuf := bytes.NewBuffer(buf) + + for i := 0; i < int(ncpu); i++ { + err := binary.Read(bbuf, binary.LittleEndian, &cpu_ticks) + if err != nil { + return err + } + for k, v := range map[string]int{ + "user": C.CPU_STATE_USER, + "system": C.CPU_STATE_SYSTEM, + "nice": C.CPU_STATE_NICE, + "idle": C.CPU_STATE_IDLE, + } { + ch <- prometheus.MustNewConstMetric(c.cpu, prometheus.CounterValue, float64(cpu_ticks[v])/ClocksPerSec, "cpu"+strconv.Itoa(i), k) + } + } + return nil +}