add diskstats on Darwin (#593)

* Add diskstats collector for Darwin

* Update year in the header

* Update README.md

* Add github.com/lufia/iostat to vendored packages

* Change stats to follow naming guidelines

* Add a entry of github.com/lufia/iostat into vendor.json

* Remove /proc/diskstats from description
This commit is contained in:
kadota kyohei 2017-07-06 20:51:24 +09:00 committed by Johannes 'fish' Ziemke
parent ab3414e6fd
commit a077024f51
10 changed files with 440 additions and 1 deletions

View file

@ -24,7 +24,7 @@ Name | Description | OS
arp | Exposes ARP statistics from `/proc/net/arp`. | Linux
conntrack | Shows conntrack statistics (does nothing if no `/proc/sys/net/netfilter/` present). | Linux
cpu | Exposes CPU statistics | Darwin, Dragonfly, FreeBSD, Linux
diskstats | Exposes disk I/O statistics from `/proc/diskstats`. | Linux
diskstats | Exposes disk I/O statistics. | Darwin, Linux
edac | Exposes error detection and correction statistics. | Linux
entropy | Exposes available entropy. | Linux
exec | Exposes execution statistics. | Dragonfly, FreeBSD

View file

@ -0,0 +1,177 @@
// 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 !nodiskstats
package collector
import (
"fmt"
"github.com/lufia/iostat"
"github.com/prometheus/client_golang/prometheus"
)
const (
diskSubsystem = "disk"
)
type typedDescFunc struct {
typedDesc
value func(stat *iostat.DriveStats) float64
}
type diskstatsCollector struct {
descs []typedDescFunc
}
func init() {
Factories["diskstats"] = NewDiskstatsCollector
}
// NewDiskstatsCollector returns a new Collector exposing disk device stats.
func NewDiskstatsCollector() (Collector, error) {
var diskLabelNames = []string{"device"}
return &diskstatsCollector{
descs: []typedDescFunc{
{
typedDesc: typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, diskSubsystem, "reads_completed_total"),
"The total number of reads completed successfully.",
diskLabelNames,
nil,
),
valueType: prometheus.CounterValue,
},
value: func(stat *iostat.DriveStats) float64 {
return float64(stat.NumRead)
},
},
{
typedDesc: typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, diskSubsystem, "read_sectors_total"),
"The total number of sectors read successfully.",
diskLabelNames,
nil,
),
valueType: prometheus.CounterValue,
},
value: func(stat *iostat.DriveStats) float64 {
return float64(stat.NumRead) / float64(stat.BlockSize)
},
},
{
typedDesc: typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, diskSubsystem, "read_seconds_total"),
"The total number of seconds spent by all reads.",
diskLabelNames,
nil,
),
valueType: prometheus.CounterValue,
},
value: func(stat *iostat.DriveStats) float64 {
return stat.TotalReadTime.Seconds()
},
},
{
typedDesc: typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, diskSubsystem, "writes_completed_total"),
"The total number of writes completed successfully.",
diskLabelNames,
nil,
),
valueType: prometheus.CounterValue,
},
value: func(stat *iostat.DriveStats) float64 {
return float64(stat.NumWrite)
},
},
{
typedDesc: typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, diskSubsystem, "written_sectors_total"),
"The total number of sectors written successfully.",
diskLabelNames,
nil,
),
valueType: prometheus.CounterValue,
},
value: func(stat *iostat.DriveStats) float64 {
return float64(stat.NumWrite) / float64(stat.BlockSize)
},
},
{
typedDesc: typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, diskSubsystem, "write_seconds_total"),
"This is the total number of seconds spent by all writes.",
diskLabelNames,
nil,
),
valueType: prometheus.CounterValue,
},
value: func(stat *iostat.DriveStats) float64 {
return stat.TotalWriteTime.Seconds()
},
},
{
typedDesc: typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, diskSubsystem, "read_bytes_total"),
"The total number of bytes read successfully.",
diskLabelNames,
nil,
),
valueType: prometheus.CounterValue,
},
value: func(stat *iostat.DriveStats) float64 {
return float64(stat.BytesRead)
},
},
{
typedDesc: typedDesc{
desc: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, diskSubsystem, "written_bytes_total"),
"The total number of bytes written successfully.",
diskLabelNames,
nil,
),
valueType: prometheus.CounterValue,
},
value: func(stat *iostat.DriveStats) float64 {
return float64(stat.BytesWritten)
},
},
},
}, nil
}
func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error {
diskStats, err := iostat.ReadDriveStats()
if err != nil {
return fmt.Errorf("couldn't get diskstats: %s", err)
}
for _, stats := range diskStats {
for _, desc := range c.descs {
v := desc.value(stats)
ch <- desc.mustNewConstMetric(v, stats.Name)
}
}
return nil
}

29
vendor/github.com/lufia/iostat/LICENSE generated vendored Normal file
View file

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2017, kadota kyohei
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

7
vendor/github.com/lufia/iostat/README.md generated vendored Normal file
View file

@ -0,0 +1,7 @@
# iostat - reports I/O statistics
[![GoDoc](https://godoc.org/github.com/lufia/iostat?status.svg)](https://godoc.org/github.com/lufia/iostat)
*iostat* reports I/O statistics. currently supported OSes are:
* macOS

20
vendor/github.com/lufia/iostat/iostat.go generated vendored Normal file
View file

@ -0,0 +1,20 @@
// Package iostat presents I/O statistics.
package iostat
import "time"
// DriveStats represents I/O statistics of a drive.
type DriveStats struct {
Name string // drive name
Size int64 // total drive size in bytes
BlockSize int64 // block size in bytes
BytesRead int64
BytesWritten int64
NumRead int64
NumWrite int64
TotalReadTime time.Duration
TotalWriteTime time.Duration
ReadLatency time.Duration
WriteLatency time.Duration
}

128
vendor/github.com/lufia/iostat/iostat_darwin.c generated vendored Normal file
View file

@ -0,0 +1,128 @@
#include <stdint.h>
#include "iostat_darwin.h"
#define IOKIT 1 /* to get io_name_t in device_types.h */
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/IOBSD.h>
static int getdrivestat(io_registry_entry_t d, DriveStats *stat);
static int fillstat(io_registry_entry_t d, DriveStats *stat);
int
readdrivestat(DriveStats a[], int n)
{
mach_port_t port;
CFMutableDictionaryRef match;
io_iterator_t drives;
io_registry_entry_t d;
kern_return_t status;
int na, rv;
IOMasterPort(bootstrap_port, &port);
match = IOServiceMatching("IOMedia");
CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
status = IOServiceGetMatchingServices(port, match, &drives);
if(status != KERN_SUCCESS)
return -1;
na = 0;
while(na < n && (d=IOIteratorNext(drives)) > 0){
rv = getdrivestat(d, &a[na]);
if(rv < 0)
return -1;
if(rv > 0)
na++;
IOObjectRelease(d);
}
IOObjectRelease(drives);
return na;
}
static int
getdrivestat(io_registry_entry_t d, DriveStats *stat)
{
io_registry_entry_t parent;
kern_return_t status;
CFDictionaryRef props;
CFStringRef name;
CFNumberRef num;
int rv;
memset(stat, 0, sizeof *stat);
status = IORegistryEntryGetParentEntry(d, kIOServicePlane, &parent);
if(status != KERN_SUCCESS)
return -1;
if(!IOObjectConformsTo(parent, "IOBlockStorageDriver")){
IOObjectRelease(parent);
return 0;
}
status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions);
if(status != KERN_SUCCESS){
IOObjectRelease(parent);
return -1;
}
name = (CFStringRef)CFDictionaryGetValue(props, CFSTR(kIOBSDNameKey));
CFStringGetCString(name, stat->name, NAMELEN, CFStringGetSystemEncoding());
num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaSizeKey));
CFNumberGetValue(num, kCFNumberSInt64Type, &stat->size);
num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaPreferredBlockSizeKey));
CFNumberGetValue(num, kCFNumberSInt64Type, &stat->blocksize);
CFRelease(props);
rv = fillstat(parent, stat);
IOObjectRelease(parent);
if(rv < 0)
return -1;
return 1;
}
static struct {
char *key;
size_t off;
} statstab[] = {
{kIOBlockStorageDriverStatisticsBytesReadKey, offsetof(DriveStats, read)},
{kIOBlockStorageDriverStatisticsBytesWrittenKey, offsetof(DriveStats, written)},
{kIOBlockStorageDriverStatisticsReadsKey, offsetof(DriveStats, nread)},
{kIOBlockStorageDriverStatisticsWritesKey, offsetof(DriveStats, nwrite)},
{kIOBlockStorageDriverStatisticsTotalReadTimeKey, offsetof(DriveStats, readtime)},
{kIOBlockStorageDriverStatisticsTotalWriteTimeKey, offsetof(DriveStats, writetime)},
{kIOBlockStorageDriverStatisticsLatentReadTimeKey, offsetof(DriveStats, readlat)},
{kIOBlockStorageDriverStatisticsLatentWriteTimeKey, offsetof(DriveStats, writelat)},
};
static int
fillstat(io_registry_entry_t d, DriveStats *stat)
{
CFDictionaryRef props, v;
CFNumberRef num;
kern_return_t status;
typeof(statstab[0]) *bp, *ep;
status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions);
if(status != KERN_SUCCESS)
return -1;
v = (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOBlockStorageDriverStatisticsKey));
if(v == NULL){
CFRelease(props);
return -1;
}
ep = &statstab[sizeof(statstab)/sizeof(statstab[0])];
for(bp = &statstab[0]; bp < ep; bp++){
CFStringRef s;
s = CFStringCreateWithCString(kCFAllocatorDefault, bp->key, CFStringGetSystemEncoding());
num = (CFNumberRef)CFDictionaryGetValue(v, s);
if(num)
CFNumberGetValue(num, kCFNumberSInt64Type, ((char*)stat)+bp->off);
CFRelease(s);
}
CFRelease(props);
return 0;
}

37
vendor/github.com/lufia/iostat/iostat_darwin.go generated vendored Normal file
View file

@ -0,0 +1,37 @@
// +build darwin
package iostat
// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit
// #include <stdint.h>
// #include "iostat_darwin.h"
import "C"
import (
"time"
)
// ReadDriveStats returns statictics of each of the drives.
func ReadDriveStats() ([]*DriveStats, error) {
var buf [C.NDRIVE]C.DriveStats
n, err := C.readdrivestat(&buf[0], C.int(len(buf)))
if err != nil {
return nil, err
}
stats := make([]*DriveStats, n)
for i := 0; i < int(n); i++ {
stats[i] = &DriveStats{
Name: C.GoString(&buf[i].name[0]),
Size: int64(buf[i].size),
BlockSize: int64(buf[i].blocksize),
BytesRead: int64(buf[i].read),
BytesWritten: int64(buf[i].written),
NumRead: int64(buf[i].nread),
NumWrite: int64(buf[i].nwrite),
TotalReadTime: time.Duration(buf[i].readtime),
TotalWriteTime: time.Duration(buf[i].writetime),
ReadLatency: time.Duration(buf[i].readlat),
WriteLatency: time.Duration(buf[i].writelat),
}
}
return stats, nil
}

23
vendor/github.com/lufia/iostat/iostat_darwin.h generated vendored Normal file
View file

@ -0,0 +1,23 @@
typedef struct DriveStats DriveStats;
enum {
NDRIVE = 16,
NAMELEN = 31
};
struct DriveStats {
char name[NAMELEN+1];
int64_t size;
int64_t blocksize;
int64_t read;
int64_t written;
int64_t nread;
int64_t nwrite;
int64_t readtime;
int64_t writetime;
int64_t readlat;
int64_t writelat;
};
extern int readdrivestat(DriveStats a[], int n);

12
vendor/github.com/lufia/iostat/iostat_linux.go generated vendored Normal file
View file

@ -0,0 +1,12 @@
// +build !darwin
package iostat
import (
"errors"
)
// ReadDriveStats returns statictics of each of the drives.
func ReadDriveStats() ([]*DriveStats, error) {
return nil, errors.New("not implement")
}

6
vendor/vendor.json vendored
View file

@ -50,6 +50,12 @@
"revision": "0826b98aaa29c0766956cb40d45cf7482a597671",
"revisionTime": "2015-04-13T19:18:30Z"
},
{
"checksumSHA1": "82wQShWC1Udgn1UbTK42s0l/IpY=",
"path": "github.com/lufia/iostat",
"revision": "8c7e013c17ce06e6651d2affabe369045b98d3b7",
"revisionTime": "2017-06-03T08:40:47Z"
},
{
"checksumSHA1": "bKMZjd2wPw13VwoE7mBeSv5djFA=",
"path": "github.com/matttproud/golang_protobuf_extensions/pbutil",