1616package collector
1717
1818import (
19- "errors"
2019 "strconv"
2120 "unsafe"
2221
2322 "github.com/prometheus/client_golang/prometheus"
23+ "golang.org/x/sys/unix"
2424)
2525
26- /*
27- #cgo LDFLAGS:
28- #include <fcntl.h>
29- #include <stdlib.h>
30- #include <sys/param.h>
31- #include <sys/pcpu.h>
32- #include <sys/resource.h>
33- #include <sys/sysctl.h>
34- #include <sys/time.h>
35-
36- static int mibs_set_up = 0;
37-
38- static int mib_kern_cp_times[2];
39- static size_t mib_kern_cp_times_len = 2;
40-
41- static const int mib_hw_ncpu[] = {CTL_HW, HW_NCPU};
42- static const size_t mib_hw_ncpu_len = 2;
43-
44- static const int mib_kern_clockrate[] = {CTL_KERN, KERN_CLOCKRATE};
45- static size_t mib_kern_clockrate_len = 2;
26+ type clockinfo struct {
27+ hz int32 // clock frequency
28+ tick int32 // micro-seconds per hz tick
29+ spare int32
30+ stathz int32 // statistics clock frequency
31+ profhz int32 // profiling clock frequency
32+ }
4633
47- // Setup method for MIBs not available as constants.
48- // Calls to this method must be synchronized externally.
49- int setupSysctlMIBs() {
50- int ret = sysctlnametomib("kern.cp_times", mib_kern_cp_times, &mib_kern_cp_times_len);
51- if (ret == 0) mibs_set_up = 1;
52- return ret;
34+ type cputime struct {
35+ user float64
36+ nice float64
37+ sys float64
38+ intr float64
39+ idle float64
5340}
5441
55- int getCPUTimes(int *ncpu, double **cpu_times, size_t *cp_times_length) {
42+ func getCPUTimes () ([]cputime , error ) {
43+ const states = 5
5644
57- // Assert that mibs are set up through setupSysctlMIBs
58- if (!mibs_set_up) {
59- return -1;
45+ clockb , err := unix . SysctlRaw ( "kern.clockrate" )
46+ if err != nil {
47+ return nil , err
6048 }
61-
62- // Retrieve number of cpu cores
63- size_t ncpu_size = sizeof(*ncpu);
64- if (sysctl(mib_hw_ncpu, mib_hw_ncpu_len, ncpu, &ncpu_size, NULL, 0) == -1 ||
65- sizeof(*ncpu) != ncpu_size) {
66- return -1;
49+ clock := * (* clockinfo )(unsafe .Pointer (& clockb [0 ]))
50+ cpb , err := unix .SysctlRaw ("kern.cp_times" )
51+ if err != nil {
52+ return nil , err
6753 }
6854
69- // Retrieve clockrate
70- struct clockinfo clockrate;
71- size_t clockrate_size = sizeof(clockrate);
72- if (sysctl(mib_kern_clockrate, mib_kern_clockrate_len, &clockrate, &clockrate_size, NULL, 0) == -1 ||
73- sizeof(clockrate) != clockrate_size) {
74- return -1;
55+ var cpufreq float64
56+ if clock .stathz > 0 {
57+ cpufreq = float64 (clock .stathz )
58+ } else {
59+ cpufreq = float64 (clock .hz )
7560 }
76-
77- // Retrieve cp_times values
78- *cp_times_length = (*ncpu) * CPUSTATES;
79-
80- long cp_times[*cp_times_length];
81- size_t cp_times_size = sizeof(cp_times);
82-
83- if (sysctl(mib_kern_cp_times, mib_kern_cp_times_len, &cp_times, &cp_times_size, NULL, 0) == -1 ||
84- sizeof(cp_times) != cp_times_size) {
85- return -1;
61+ var times []float64
62+ for len (cpb ) >= int (unsafe .Sizeof (int (0 ))) {
63+ t := * (* int )(unsafe .Pointer (& cpb [0 ]))
64+ times = append (times , float64 (t )/ cpufreq )
65+ cpb = cpb [unsafe .Sizeof (int (0 )):]
8666 }
8767
88- // Compute absolute time for different CPU states
89- long cpufreq = clockrate.stathz > 0 ? clockrate.stathz : clockrate.hz;
90- *cpu_times = (double *) malloc(sizeof(double)*(*cp_times_length));
91- for (int i = 0; i < (*cp_times_length); i++) {
92- (*cpu_times)[i] = ((double) cp_times[i]) / cpufreq;
68+ cpus := make ([]cputime , len (times )/ states )
69+ for i := 0 ; i < len (times ); i += states {
70+ cpu := & cpus [i / states ]
71+ cpu .user = times [i ]
72+ cpu .nice = times [i + 1 ]
73+ cpu .sys = times [i + 2 ]
74+ cpu .intr = times [i + 3 ]
75+ cpu .idle = times [i + 4 ]
9376 }
94-
95- return 0;
96-
77+ return cpus , nil
9778}
9879
99- void freeCPUTimes(double *cpu_times) {
100- free(cpu_times);
101- }
102-
103- */
104- import "C"
105-
106- const maxCPUTimesLen = C .MAXCPU * C .CPUSTATES
107-
10880type statCollector struct {
10981 cpu * prometheus.CounterVec
11082}
@@ -116,9 +88,6 @@ func init() {
11688// Takes a prometheus registry and returns a new Collector exposing
11789// CPU stats.
11890func NewStatCollector () (Collector , error ) {
119- if C .setupSysctlMIBs () == - 1 {
120- return nil , errors .New ("could not initialize sysctl MIBs" )
121- }
12291 return & statCollector {
12392 cpu : prometheus .NewCounterVec (
12493 prometheus.CounterOpts {
@@ -133,7 +102,6 @@ func NewStatCollector() (Collector, error) {
133102
134103// Expose CPU stats using sysctl.
135104func (c * statCollector ) Update (ch chan <- prometheus.Metric ) (err error ) {
136-
137105 // We want time spent per-cpu per CPUSTATE.
138106 // CPUSTATES (number of CPUSTATES) is defined as 5U.
139107 // Order: CP_USER | CP_NICE | CP_SYS | CP_IDLE | CP_INTR
@@ -145,29 +113,17 @@ func (c *statCollector) Update(ch chan<- prometheus.Metric) (err error) {
145113 //
146114 // Look into sys/kern/kern_clock.c for details.
147115
148- var ncpu C.int
149- var cpuTimesC * C.double
150- var cpuTimesLength C.size_t
151- if C .getCPUTimes (& ncpu , & cpuTimesC , & cpuTimesLength ) == - 1 {
152- return errors .New ("could not retrieve CPU times" )
116+ cpuTimes , err := getCPUTimes ()
117+ if err != nil {
118+ return err
153119 }
154- defer C .freeCPUTimes (cpuTimesC )
155- if cpuTimesLength > maxCPUTimesLen {
156- return errors .New ("more CPU's than MAXCPU?" )
120+ for cpu , t := range cpuTimes {
121+ c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "user" }).Set (t .user )
122+ c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "nice" }).Set (t .nice )
123+ c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "system" }).Set (t .sys )
124+ c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "interrupt" }).Set (t .intr )
125+ c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "idle" }).Set (t .idle )
157126 }
158-
159- // Convert C.double array to Go array (https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices).
160- cpuTimes := (* [maxCPUTimesLen ]C.double )(unsafe .Pointer (cpuTimesC ))[:cpuTimesLength :cpuTimesLength ]
161-
162- for cpu := 0 ; cpu < int (ncpu ); cpu ++ {
163- base_idx := C .CPUSTATES * cpu
164- c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "user" }).Set (float64 (cpuTimes [base_idx + C .CP_USER ]))
165- c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "nice" }).Set (float64 (cpuTimes [base_idx + C .CP_NICE ]))
166- c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "system" }).Set (float64 (cpuTimes [base_idx + C .CP_SYS ]))
167- c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "interrupt" }).Set (float64 (cpuTimes [base_idx + C .CP_INTR ]))
168- c .cpu .With (prometheus.Labels {"cpu" : strconv .Itoa (cpu ), "mode" : "idle" }).Set (float64 (cpuTimes [base_idx + C .CP_IDLE ]))
169- }
170-
171127 c .cpu .Collect (ch )
172128 return err
173129}
0 commit comments