Correct buffer_bytes > INT_MAX on BSD/amd64. (#712)

* Correct buffer_bytes > INT_MAX on BSD/amd64.

The sysctl vfs.bufspace returns either an int or a long, depending on
the value.  Large values of vfs.bufspace will result in error messages
like:

  couldn't get meminfo: cannot allocate memory

This will detect the returned data type, and cast appropriately.

* Added explicit length checks per feedback.

* Flatten Value() to make it easier to read.

* Simplify per feedback.

* Fix style.

* Doc updates.
This commit is contained in:
Derek Marcotte 2017-10-25 14:55:22 -04:00 committed by Johannes 'fish' Ziemke
parent 715ebd1ced
commit 0eecaa9547
2 changed files with 73 additions and 38 deletions

View File

@ -39,7 +39,7 @@ func (c *meminfoCollector) getMemInfo() (map[string]float64, error) {
{name: "inactive_bytes", mib: "vm.stats.vm.v_inactive_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: "wired_bytes", mib: "vm.stats.vm.v_wire_count", conversion: fromPage},
{name: "cache_bytes", mib: "vm.stats.vm.v_cache_count", conversion: fromPage}, {name: "cache_bytes", mib: "vm.stats.vm.v_cache_count", conversion: fromPage},
{name: "buffer_bytes", mib: "vfs.bufspace"}, {name: "buffer_bytes", mib: "vfs.bufspace", dataType: bsdSysctlTypeCLong},
{name: "free_bytes", mib: "vm.stats.vm.v_free_count", conversion: fromPage}, {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: "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_in_bytes_total", mib: "vm.stats.vm.v_swappgsin", conversion: fromPage},

View File

@ -35,9 +35,11 @@ const (
bsdSysctlTypeUint32 bsdSysctlType = iota bsdSysctlTypeUint32 bsdSysctlType = iota
bsdSysctlTypeUint64 bsdSysctlTypeUint64
bsdSysctlTypeStructTimeval bsdSysctlTypeStructTimeval
bsdSysctlTypeCLong
) )
// Contains all the info needed to map a single bsd-sysctl to a prometheus value. // Contains all the info needed to map a single bsd-sysctl to a prometheus
// value.
type bsdSysctl struct { type bsdSysctl struct {
// Prometheus name // Prometheus name
name string name string
@ -72,6 +74,23 @@ func (b bsdSysctl) Value() (float64, error) {
tmp64, err = unix.SysctlUint64(b.mib) tmp64, err = unix.SysctlUint64(b.mib)
tmpf64 = float64(tmp64) tmpf64 = float64(tmp64)
case bsdSysctlTypeStructTimeval: case bsdSysctlTypeStructTimeval:
tmpf64, err = b.getStructTimeval()
case bsdSysctlTypeCLong:
tmpf64, err = b.getCLong()
}
if err != nil {
return 0, err
}
if b.conversion != nil {
return b.conversion(tmpf64), nil
}
return tmpf64, nil
}
func (b bsdSysctl) getStructTimeval() (float64, error) {
raw, err := unix.SysctlRaw(b.mib) raw, err := unix.SysctlRaw(b.mib)
if err != nil { if err != nil {
return 0, err return 0, err
@ -105,18 +124,34 @@ func (b bsdSysctl) Value() (float64, error) {
unix := float64(*(*C.time_t)(secondsUp)) unix := float64(*(*C.time_t)(secondsUp))
usec := float64(*(*C.suseconds_t)(unsafe.Pointer(susecondsUp))) usec := float64(*(*C.suseconds_t)(unsafe.Pointer(susecondsUp)))
// This conversion maintains the usec precision. Using // This conversion maintains the usec precision. Using the time
// the time package did not. // package did not.
tmpf64 = unix + (usec / float64(1000*1000)) return (unix + (usec / float64(1000*1000))), nil
} }
func (b bsdSysctl) getCLong() (float64, error) {
raw, err := unix.SysctlRaw(b.mib)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if b.conversion != nil { if len(raw) == C.sizeof_long {
return b.conversion(tmpf64), nil return float64(*(*C.long)(unsafe.Pointer(&raw[0]))), nil
} }
return tmpf64, nil if len(raw) == C.sizeof_int {
// This is valid for at least vfs.bufspace, and the default
// long handler - which can clamp longs to 32-bits:
// https://github.com/freebsd/freebsd/blob/releng/10.3/sys/kern/vfs_bio.c#L338
// https://github.com/freebsd/freebsd/blob/releng/10.3/sys/kern/kern_sysctl.c#L1062
return float64(*(*C.int)(unsafe.Pointer(&raw[0]))), nil
}
return 0, fmt.Errorf(
"length of bytes received from sysctl (%d) does not match expected bytes (long: %d), (int: %d)",
len(raw),
C.sizeof_long,
C.sizeof_int,
)
} }