From 477fe4665ad22cb2f391547e978231cc5c1da6c7 Mon Sep 17 00:00:00 2001 From: Derek Marcotte Date: Thu, 4 Jan 2018 06:23:26 -0500 Subject: [PATCH] Move FreeBSD/DragonflyBSD out of meminfo add kvm. (#547) * Move FreeBSD/DragonflyBSD out of meminfo add kvm. This gives us SwapUsed, and everything under one roof. * Fix typos per review. * Update to use newer API. * Remove premature optimization per PR feedback. --- collector/kvm_bsd.c | 52 +++++++++++++ collector/kvm_bsd.go | 42 +++++++++++ collector/kvm_bsd.h | 19 +++++ collector/meminfo.go | 2 +- collector/meminfo_bsd.go | 58 -------------- collector/memory_bsd.go | 158 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 272 insertions(+), 59 deletions(-) create mode 100644 collector/kvm_bsd.c create mode 100644 collector/kvm_bsd.go create mode 100644 collector/kvm_bsd.h delete mode 100644 collector/meminfo_bsd.go create mode 100644 collector/memory_bsd.go diff --git a/collector/kvm_bsd.c b/collector/kvm_bsd.c new file mode 100644 index 00000000..ad86d33e --- /dev/null +++ b/collector/kvm_bsd.c @@ -0,0 +1,52 @@ +// Copyright 2017 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. + +// +build !nomeminfo +// +build freebsd dragonfly + +#include +#include +#include +#include +#include + +int _kvm_swap_used_pages(uint64_t *out) { + const int total_only = 1; // from kvm_getswapinfo(3) + + kvm_t *kd; + struct kvm_swap current; + + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL); + if (kd == NULL) { + return -1; + } + + if (kvm_getswapinfo(kd, ¤t, total_only, 0) == -1) { + goto error1; + } + + if (kvm_close(kd) != 0) { + return -1; + } + kd = NULL; + + *out = current.ksw_used; + return 0; + +error1: + if (kd != NULL) { + kvm_close(kd); + } + + return -1; +} diff --git a/collector/kvm_bsd.go b/collector/kvm_bsd.go new file mode 100644 index 00000000..b4e95e6a --- /dev/null +++ b/collector/kvm_bsd.go @@ -0,0 +1,42 @@ +// Copyright 2017 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. + +// +build !nomeminfo +// +build freebsd dragonfly + +package collector + +import ( + "fmt" + "sync" +) + +// #cgo LDFLAGS: -lkvm +// #include "kvm_bsd.h" +import "C" + +type kvm struct { + mu sync.Mutex + hasErr bool +} + +func (k *kvm) SwapUsedPages() (value uint64, err error) { + k.mu.Lock() + defer k.mu.Unlock() + if C._kvm_swap_used_pages((*C.uint64_t)(&value)) == -1 { + k.hasErr = true + return 0, fmt.Errorf("couldn't get kvm stats") + } + + return value, nil +} diff --git a/collector/kvm_bsd.h b/collector/kvm_bsd.h new file mode 100644 index 00000000..0cfbfa70 --- /dev/null +++ b/collector/kvm_bsd.h @@ -0,0 +1,19 @@ +// Copyright 2017 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. + +// +build !nomeminfo +// +build freebsd dragonfly + +#include + +int _kvm_swap_used_pages(uint64_t *out); diff --git a/collector/meminfo.go b/collector/meminfo.go index 68959efe..ffe343a2 100644 --- a/collector/meminfo.go +++ b/collector/meminfo.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// +build darwin dragonfly freebsd linux openbsd +// +build darwin linux openbsd // +build !nomeminfo package collector diff --git a/collector/meminfo_bsd.go b/collector/meminfo_bsd.go deleted file mode 100644 index d8eedb96..00000000 --- a/collector/meminfo_bsd.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015 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. - -// +build freebsd dragonfly -// +build !nomeminfo - -package collector - -import ( - "fmt" - - "golang.org/x/sys/unix" -) - -func (c *meminfoCollector) getMemInfo() (map[string]float64, error) { - info := make(map[string]float64) - - tmp32, err := unix.SysctlUint32("vm.stats.vm.v_page_size") - if err != nil { - return nil, fmt.Errorf("sysctl(vm.stats.vm.v_page_size) failed: %s", err) - } - size := float64(tmp32) - fromPage := func(v float64) float64 { - return v * size - } - - for _, ctl := range []bsdSysctl{ - {name: "active_bytes", mib: "vm.stats.vm.v_active_count", conversion: fromPage}, - {name: "inactive_bytes", mib: "vm.stats.vm.v_inactive_count", conversion: fromPage}, - {name: "wired_bytes", mib: "vm.stats.vm.v_wire_count", conversion: fromPage}, - {name: "cache_bytes", mib: "vm.stats.vm.v_cache_count", conversion: fromPage}, - {name: "buffer_bytes", mib: "vfs.bufspace", dataType: bsdSysctlTypeCLong}, - {name: "free_bytes", mib: "vm.stats.vm.v_free_count", conversion: fromPage}, - {name: "size_bytes", mib: "vm.stats.vm.v_page_count", conversion: fromPage}, - {name: "swap_in_bytes_total", mib: "vm.stats.vm.v_swappgsin", conversion: fromPage}, - {name: "swap_out_bytes_total", mib: "vm.stats.vm.v_swappgsout", conversion: fromPage}, - {name: "swap_size_bytes", mib: "vm.swap_total", dataType: bsdSysctlTypeUint64}, - } { - v, err := ctl.Value() - if err != nil { - return nil, err - } - - info[ctl.name] = v - } - - return info, nil -} diff --git a/collector/memory_bsd.go b/collector/memory_bsd.go new file mode 100644 index 00000000..8c7265e3 --- /dev/null +++ b/collector/memory_bsd.go @@ -0,0 +1,158 @@ +// Copyright 2017 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. + +// +build freebsd dragonfly +// +build !nomeminfo + +package collector + +import ( + "fmt" + + "github.com/prometheus/client_golang/prometheus" + "golang.org/x/sys/unix" +) + +const ( + memorySubsystem = "memory" +) + +type memoryCollector struct { + pageSize uint64 + sysctls []bsdSysctl + kvm kvm +} + +func init() { + registerCollector("meminfo", defaultEnabled, NewMemoryCollector) +} + +// NewMemoryCollector returns a new Collector exposing memory stats. +func NewMemoryCollector() (Collector, error) { + tmp32, err := unix.SysctlUint32("vm.stats.vm.v_page_size") + if err != nil { + return nil, fmt.Errorf("sysctl(vm.stats.vm.v_page_size) failed: %s", err) + } + size := float64(tmp32) + + fromPage := func(v float64) float64 { + return v * size + } + + return &memoryCollector{ + pageSize: uint64(tmp32), + sysctls: []bsdSysctl{ + // Descriptions via: https://wiki.freebsd.org/Memory + { + name: "active_bytes", + description: "Recently used by userland", + mib: "vm.stats.vm.v_active_count", + conversion: fromPage, + }, + { + name: "inactive_bytes", + description: "Not recently used by userland", + mib: "vm.stats.vm.v_inactive_count", + conversion: fromPage, + }, + { + name: "wired_bytes", + description: "Locked in memory by kernel, mlock, etc", + mib: "vm.stats.vm.v_wire_count", + conversion: fromPage, + }, + { + name: "cache_bytes", + description: "Almost free, backed by swap or files, available for re-allocation", + mib: "vm.stats.vm.v_cache_count", + conversion: fromPage, + }, + { + name: "buffer_bytes", + description: "Disk IO Cache entries for non ZFS filesystems, only usable by kernel", + mib: "vfs.bufspace", + dataType: bsdSysctlTypeCLong, + }, + { + name: "free_bytes", + description: "Unallocated, available for allocation", + mib: "vm.stats.vm.v_free_count", + conversion: fromPage, + }, + { + name: "size_bytes", + description: "Total physical memory size", + mib: "vm.stats.vm.v_page_count", + conversion: fromPage, + }, + { + name: "swap_size_bytes", + description: "Total swap memory size", + mib: "vm.swap_total", + dataType: bsdSysctlTypeUint64, + }, + // Descriptions via: top(1) + { + name: "swap_in_bytes_total", + description: "Bytes paged in from swap devices", + mib: "vm.stats.vm.v_swappgsin", + valueType: prometheus.CounterValue, + conversion: fromPage, + }, + { + name: "swap_out_bytes_total", + description: "Bytes paged out to swap devices", + mib: "vm.stats.vm.v_swappgsout", + valueType: prometheus.CounterValue, + conversion: fromPage, + }, + }, + }, nil +} + +// Update checks relevant sysctls for current memory usage, and kvm for swap +// usage. +func (c *memoryCollector) Update(ch chan<- prometheus.Metric) error { + for _, m := range c.sysctls { + v, err := m.Value() + if err != nil { + return fmt.Errorf("couldn't get memory: %s", err) + } + + // Most are gauges. + if m.valueType == 0 { + m.valueType = prometheus.GaugeValue + } + + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, memorySubsystem, m.name), + m.description, + nil, nil, + ), m.valueType, v) + } + + swapUsed, err := c.kvm.SwapUsedPages() + if err != nil { + return fmt.Errorf("couldn't get kvm: %s", err) + } + + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, memorySubsystem, "swap_used_bytes"), + "Currently allocated swap", + nil, nil, + ), prometheus.GaugeValue, float64(swapUsed*c.pageSize)) + + return nil +}