Skip to content

Commit 5e86b55

Browse files
committed
Merge pull request #88 from siavashs/freebsd
Add support for FreeBSD
2 parents 0f0daef + 8c4a5b0 commit 5e86b55

25 files changed

+704
-19
lines changed

collector/cpu_freebsd.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// +build !nocpu
2+
3+
package collector
4+
5+
import (
6+
"errors"
7+
"os"
8+
"strconv"
9+
"unsafe"
10+
11+
"github.com/prometheus/client_golang/prometheus"
12+
)
13+
14+
/*
15+
#cgo LDFLAGS: -lkvm
16+
#include <fcntl.h>
17+
#include <kvm.h>
18+
#include <sys/param.h>
19+
#include <sys/pcpu.h>
20+
#include <sys/resource.h>
21+
*/
22+
import "C"
23+
24+
type statCollector struct {
25+
cpu *prometheus.CounterVec
26+
}
27+
28+
func init() {
29+
Factories["cpu"] = NewStatCollector
30+
}
31+
32+
// Takes a prometheus registry and returns a new Collector exposing
33+
// CPU stats.
34+
func NewStatCollector() (Collector, error) {
35+
return &statCollector{
36+
cpu: prometheus.NewCounterVec(
37+
prometheus.CounterOpts{
38+
Namespace: Namespace,
39+
Name: "cpu_seconds_total",
40+
Help: "Seconds the CPU spent in each mode.",
41+
},
42+
[]string{"cpu", "mode"},
43+
),
44+
}, nil
45+
}
46+
47+
// Expose CPU stats using KVM.
48+
func (c *statCollector) Update(ch chan<- prometheus.Metric) (err error) {
49+
if os.Geteuid() != 0 && os.Getegid() != 2 {
50+
return errors.New("caller should be either root user or kmem group to access /dev/mem")
51+
}
52+
53+
var errbuf *C.char
54+
kd := C.kvm_open(nil, nil, nil, C.O_RDONLY, errbuf)
55+
if errbuf != nil {
56+
return errors.New("failed to call kvm_open()")
57+
}
58+
defer C.kvm_close(kd)
59+
60+
ncpus := C.kvm_getncpus(kd)
61+
for i := 0; i < int(ncpus); i++ {
62+
pcpu := C.kvm_getpcpu(kd, C.int(i))
63+
cp_time := ((*C.struct_pcpu)(unsafe.Pointer(pcpu))).pc_cp_time
64+
c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "user"}).Set(float64(cp_time[C.CP_USER]))
65+
c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "nice"}).Set(float64(cp_time[C.CP_NICE]))
66+
c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "system"}).Set(float64(cp_time[C.CP_SYS]))
67+
c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "interrupt"}).Set(float64(cp_time[C.CP_INTR]))
68+
c.cpu.With(prometheus.Labels{"cpu": strconv.Itoa(i), "mode": "idle"}).Set(float64(cp_time[C.CP_IDLE]))
69+
}
70+
c.cpu.Collect(ch)
71+
return err
72+
}

collector/devstat_freebsd.go

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// +build !nodevstat
2+
3+
package collector
4+
5+
import (
6+
"errors"
7+
"fmt"
8+
9+
"github.com/prometheus/client_golang/prometheus"
10+
)
11+
12+
/*
13+
#cgo LDFLAGS: -ldevstat -lkvm
14+
#include <devstat.h>
15+
#include <fcntl.h>
16+
#include <libgeom.h>
17+
#include <limits.h>
18+
#include <stdio.h>
19+
#include <stdlib.h>
20+
#include <string.h>
21+
22+
typedef struct {
23+
uint64_t read;
24+
uint64_t write;
25+
uint64_t free;
26+
} Bytes;
27+
28+
typedef struct {
29+
uint64_t other;
30+
uint64_t read;
31+
uint64_t write;
32+
uint64_t free;
33+
} Transfers;
34+
35+
typedef struct {
36+
double other;
37+
double read;
38+
double write;
39+
double free;
40+
} Duration;
41+
42+
typedef struct {
43+
char device[DEVSTAT_NAME_LEN];
44+
int unit;
45+
Bytes bytes;
46+
Transfers transfers;
47+
Duration duration;
48+
long busyTime;
49+
uint64_t blocks;
50+
} Stats;
51+
52+
int _get_ndevs() {
53+
struct statinfo current;
54+
int num_devices;
55+
56+
current.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
57+
if (current.dinfo == NULL)
58+
return -2;
59+
60+
devstat_checkversion(NULL);
61+
62+
if (devstat_getdevs(NULL, &current) == -1)
63+
return -1;
64+
65+
return current.dinfo->numdevs;
66+
}
67+
68+
Stats _get_stats(int i) {
69+
struct statinfo current;
70+
int num_devices;
71+
72+
current.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
73+
devstat_getdevs(NULL, &current);
74+
75+
num_devices = current.dinfo->numdevs;
76+
Stats stats;
77+
uint64_t bytes_read, bytes_write, bytes_free;
78+
uint64_t transfers_other, transfers_read, transfers_write, transfers_free;
79+
long double duration_other, duration_read, duration_write, duration_free;
80+
long double busy_time;
81+
uint64_t blocks;
82+
83+
strcpy(stats.device, current.dinfo->devices[i].device_name);
84+
stats.unit = current.dinfo->devices[i].unit_number;
85+
devstat_compute_statistics(&current.dinfo->devices[i],
86+
NULL,
87+
1.0,
88+
DSM_TOTAL_BYTES_READ, &bytes_read,
89+
DSM_TOTAL_BYTES_WRITE, &bytes_write,
90+
DSM_TOTAL_BYTES_FREE, &bytes_free,
91+
DSM_TOTAL_TRANSFERS_OTHER, &transfers_other,
92+
DSM_TOTAL_TRANSFERS_READ, &transfers_read,
93+
DSM_TOTAL_TRANSFERS_WRITE, &transfers_write,
94+
DSM_TOTAL_TRANSFERS_FREE, &transfers_free,
95+
DSM_TOTAL_DURATION_OTHER, &duration_other,
96+
DSM_TOTAL_DURATION_READ, &duration_read,
97+
DSM_TOTAL_DURATION_WRITE, &duration_write,
98+
DSM_TOTAL_DURATION_FREE, &duration_free,
99+
DSM_TOTAL_BUSY_TIME, &busy_time,
100+
DSM_TOTAL_BLOCKS, &blocks,
101+
DSM_NONE);
102+
103+
stats.bytes.read = bytes_read;
104+
stats.bytes.write = bytes_write;
105+
stats.bytes.free = bytes_free;
106+
stats.transfers.other = transfers_other;
107+
stats.transfers.read = transfers_read;
108+
stats.transfers.write = transfers_write;
109+
stats.transfers.free = transfers_free;
110+
stats.duration.other = duration_other;
111+
stats.duration.read = duration_read;
112+
stats.duration.write = duration_write;
113+
stats.duration.free = duration_free;
114+
stats.busyTime = busy_time;
115+
stats.blocks = blocks;
116+
117+
return stats;
118+
}
119+
*/
120+
import "C"
121+
122+
const (
123+
devstatSubsystem = "devstat"
124+
)
125+
126+
type devstatCollector struct {
127+
bytes *prometheus.CounterVec
128+
bytes_total *prometheus.CounterVec
129+
transfers *prometheus.CounterVec
130+
duration *prometheus.CounterVec
131+
busyTime *prometheus.CounterVec
132+
blocks *prometheus.CounterVec
133+
}
134+
135+
func init() {
136+
Factories["devstat"] = NewDevstatCollector
137+
}
138+
139+
// Takes a prometheus registry and returns a new Collector exposing
140+
// Device stats.
141+
func NewDevstatCollector() (Collector, error) {
142+
return &devstatCollector{
143+
bytes: prometheus.NewCounterVec(
144+
prometheus.CounterOpts{
145+
Namespace: Namespace,
146+
Subsystem: devstatSubsystem,
147+
Name: "bytes_total",
148+
Help: "The total number of bytes in transactions.",
149+
},
150+
[]string{"device", "type"},
151+
),
152+
transfers: prometheus.NewCounterVec(
153+
prometheus.CounterOpts{
154+
Namespace: Namespace,
155+
Subsystem: devstatSubsystem,
156+
Name: "transfers_total",
157+
Help: "The total number of transactions.",
158+
},
159+
[]string{"device", "type"},
160+
),
161+
duration: prometheus.NewCounterVec(
162+
prometheus.CounterOpts{
163+
Namespace: Namespace,
164+
Subsystem: devstatSubsystem,
165+
Name: "duration_seconds_total",
166+
Help: "The total duration of transactions in seconds.",
167+
},
168+
[]string{"device", "type"},
169+
),
170+
busyTime: prometheus.NewCounterVec(
171+
prometheus.CounterOpts{
172+
Namespace: Namespace,
173+
Subsystem: devstatSubsystem,
174+
Name: "busy_time_seconds_total",
175+
Help: "Total time the device had one or more transactions outstanding in seconds.",
176+
},
177+
[]string{"device"},
178+
),
179+
blocks: prometheus.NewCounterVec(
180+
prometheus.CounterOpts{
181+
Namespace: Namespace,
182+
Subsystem: devstatSubsystem,
183+
Name: "blocks_transferred_total",
184+
Help: "The total number of blocks transferred.",
185+
},
186+
[]string{"device"},
187+
),
188+
}, nil
189+
}
190+
191+
func (c *devstatCollector) Update(ch chan<- prometheus.Metric) (err error) {
192+
count := C._get_ndevs()
193+
if count == -1 {
194+
return errors.New("devstat_getdevs() failed")
195+
}
196+
if count == -2 {
197+
return errors.New("calloc() failed")
198+
}
199+
200+
for i := C.int(0); i < count; i++ {
201+
stats := C._get_stats(i)
202+
device := fmt.Sprintf("%s%d", C.GoString(&stats.device[0]), stats.unit)
203+
// Free metrics are disabled for now, please see PR #88 for more details.
204+
c.bytes.With(prometheus.Labels{"device": device, "type": "read"}).Set(float64(stats.bytes.read))
205+
c.bytes.With(prometheus.Labels{"device": device, "type": "write"}).Set(float64(stats.bytes.write))
206+
//c.bytes.With(prometheus.Labels{"device": device, "type": "free"}).Set(float64(stats.bytes.free))
207+
c.transfers.With(prometheus.Labels{"device": device, "type": "other"}).Set(float64(stats.transfers.other))
208+
c.transfers.With(prometheus.Labels{"device": device, "type": "read"}).Set(float64(stats.transfers.read))
209+
c.transfers.With(prometheus.Labels{"device": device, "type": "write"}).Set(float64(stats.transfers.write))
210+
//c.transfers.With(prometheus.Labels{"device": device, "type": "free"}).Set(float64(stats.transfers.free))
211+
c.duration.With(prometheus.Labels{"device": device, "type": "other"}).Set(float64(stats.duration.other))
212+
c.duration.With(prometheus.Labels{"device": device, "type": "read"}).Set(float64(stats.duration.read))
213+
c.duration.With(prometheus.Labels{"device": device, "type": "write"}).Set(float64(stats.duration.write))
214+
//c.duration.With(prometheus.Labels{"device": device, "type": "free"}).Set(float64(stats.duration.free))
215+
c.busyTime.With(prometheus.Labels{"device": device}).Set(float64(stats.busyTime))
216+
c.blocks.With(prometheus.Labels{"device": device}).Set(float64(stats.blocks))
217+
}
218+
219+
c.bytes.Collect(ch)
220+
c.transfers.Collect(ch)
221+
c.duration.Collect(ch)
222+
c.busyTime.Collect(ch)
223+
c.blocks.Collect(ch)
224+
225+
return err
226+
}

0 commit comments

Comments
 (0)