]> arthur.barton.de Git - netdata.git/blob - src/freebsd_sysctl.c
use usec_t for microseconds time variables
[netdata.git] / src / freebsd_sysctl.c
1 #include "common.h"
2
3 // NEEDED BY: struct vmtotal, struct vmmeter
4 #include <sys/vmmeter.h>
5 // NEEDED BY: struct devstat
6 #include <sys/devicestat.h>
7 // NEEDED BY: struct xswdev
8 #include <vm/vm_param.h>
9 // NEEDED BY: do_disk_io
10 #define RRD_TYPE_DISK "disk"
11
12 // FreeBSD calculates load averages once every 5 seconds
13 #define MIN_LOADAVG_UPDATE_EVERY 5
14
15 int do_freebsd_sysctl(int update_every, usec_t dt) {
16     (void)dt;
17
18     static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1,
19         do_loadavg = -1, do_all_processes = -1, do_disk_io = -1, do_swap = -1, do_ram = -1, do_swapio = -1,
20         do_pgfaults = -1, do_committed = -1;
21
22     if (unlikely(do_cpu == -1)) {
23         do_cpu                  = config_get_boolean("plugin:freebsd:sysctl", "cpu utilization", 1);
24         do_cpu_cores            = config_get_boolean("plugin:freebsd:sysctl", "per cpu core utilization", 1);
25         do_interrupts           = config_get_boolean("plugin:freebsd:sysctl", "cpu interrupts", 1);
26         do_context              = config_get_boolean("plugin:freebsd:sysctl", "context switches", 1);
27         do_forks                = config_get_boolean("plugin:freebsd:sysctl", "processes started", 1);
28         do_processes            = config_get_boolean("plugin:freebsd:sysctl", "processes running", 1);
29         do_loadavg              = config_get_boolean("plugin:freebsd:sysctl", "enable load average", 1);
30         do_all_processes        = config_get_boolean("plugin:freebsd:sysctl", "enable total processes", 1);
31         do_disk_io              = config_get_boolean("plugin:freebsd:sysctl", "stats for all disks", 1);
32         do_swap                 = config_get_boolean("plugin:freebsd:sysctl", "system swap", 1);
33         do_ram                  = config_get_boolean("plugin:freebsd:sysctl", "system ram", 1);
34         do_swapio               = config_get_boolean("plugin:freebsd:sysctl", "swap i/o", 1);
35         do_pgfaults             = config_get_boolean("plugin:freebsd:sysctl", "memory page faults", 1);
36         do_committed            = config_get_boolean("plugin:freebsd:sysctl", "committed memory", 1);
37     }
38
39     RRDSET *st;
40
41     int system_pagesize = getpagesize(); // wouldn't it be better to get value directly from hw.pagesize?
42     int i;
43
44 // NEEDED BY: do_loadavg
45     static usec_t last_loadavg_usec = 0;
46     struct loadavg sysload;
47
48 // NEEDED BY: do_cpu, do_cpu_cores
49     long cp_time[CPUSTATES];
50
51 // NEEDED BY: do_cpu_cores
52     int ncpus;
53     static long *pcpu_cp_time = NULL;
54     char cpuid[8]; // no more than 4 digits expected
55
56 // NEEDED BY: do_all_processes, do_processes
57     struct vmtotal vmtotal_data;
58
59 // NEEDED BY: do_context, do_forks
60     u_int u_int_data;
61
62 // NEEDED BY: do_interrupts
63     size_t intrcnt_size;
64     unsigned long nintr = 0;
65     static unsigned long *intrcnt = NULL;
66     unsigned long long totalintr = 0;
67
68 // NEEDED BY: do_disk_io
69     #define BINTIME_SCALE 5.42101086242752217003726400434970855712890625e-17 // this is 1000/2^64
70     int numdevs;
71     static void *devstat_data = NULL;
72     struct devstat *dstat;
73     struct cur_dstat {
74         collected_number duration_read_ms;
75         collected_number duration_write_ms;
76         collected_number busy_time_ms;
77     } cur_dstat;
78     struct prev_dstat {
79         collected_number bytes_read;
80         collected_number bytes_write;
81         collected_number operations_read;
82         collected_number operations_write;
83         collected_number duration_read_ms;
84         collected_number duration_write_ms;
85         collected_number busy_time_ms;
86     } prev_dstat;
87
88     // NEEDED BY: do_swap
89     size_t mibsize, size;
90     int mib[3]; // CTL_MAXNAME = 24 maximum mib components (sysctl.h)
91     struct xswdev xsw;
92     struct total_xsw {
93         collected_number bytes_used;
94         collected_number bytes_total;
95     } total_xsw = {0, 0};
96
97     // NEEDED BY: do_swapio, do_ram
98     struct vmmeter vmmeter_data;
99
100     // NEEDED BY: do_ram
101     int vfs_bufspace_count;
102
103     // --------------------------------------------------------------------
104
105     if (last_loadavg_usec <= dt) {
106         if (likely(do_loadavg)) {
107             if (unlikely(GETSYSCTL("vm.loadavg", sysload))) {
108                 do_loadavg = 0;
109                 error("DISABLED: system.load");
110             } else {
111
112                 st = rrdset_find_bytype("system", "load");
113                 if (unlikely(!st)) {
114                     st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY) ? MIN_LOADAVG_UPDATE_EVERY : update_every, RRDSET_TYPE_LINE);
115                     rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
116                     rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
117                     rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
118                 }
119                 else rrdset_next(st);
120
121                 rrddim_set(st, "load1", (collected_number) ((double)sysload.ldavg[0] / sysload.fscale * 1000));
122                 rrddim_set(st, "load5", (collected_number) ((double)sysload.ldavg[1] / sysload.fscale * 1000));
123                 rrddim_set(st, "load15", (collected_number) ((double)sysload.ldavg[2] / sysload.fscale * 1000));
124                 rrdset_done(st);
125             }
126         }
127
128         last_loadavg_usec = st->update_every * USEC_PER_SEC;
129     }
130     else last_loadavg_usec -= dt;
131
132     // --------------------------------------------------------------------
133
134     if (likely(do_all_processes | do_processes | do_committed)) {
135         if (unlikely(GETSYSCTL("vm.vmtotal", vmtotal_data))) {
136             do_all_processes = 0;
137             error("DISABLED: system.active_processes");
138             do_processes = 0;
139             error("DISABLED: system.processes");
140             do_committed = 0;
141             error("DISABLED: mem.committed");
142         } else {
143             if (likely(do_all_processes)) {
144
145                 st = rrdset_find_bytype("system", "active_processes");
146                 if (unlikely(!st)) {
147                     st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
148                     rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE);
149                 }
150                 else rrdset_next(st);
151
152                 rrddim_set(st, "active", (vmtotal_data.t_rq + vmtotal_data.t_dw + vmtotal_data.t_pw + vmtotal_data.t_sl + vmtotal_data.t_sw));
153                 rrdset_done(st);
154             }
155
156             // --------------------------------------------------------------------
157
158             if (likely(do_processes)) {
159
160                 st = rrdset_find_bytype("system", "processes");
161                 if (unlikely(!st)) {
162                     st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
163
164                     rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
165                     rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
166                 }
167                 else rrdset_next(st);
168
169                 rrddim_set(st, "running", vmtotal_data.t_rq);
170                 rrddim_set(st, "blocked", (vmtotal_data.t_dw + vmtotal_data.t_pw));
171                 rrdset_done(st);
172             }
173
174             // --------------------------------------------------------------------
175
176             if (likely(do_committed)) {
177                 st = rrdset_find("mem.committed");
178                 if (unlikely(!st)) {
179                     st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA);
180                     st->isdetail = 1;
181
182                     rrddim_add(st, "Committed_AS", NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
183                 }
184                 else rrdset_next(st);
185
186                 rrddim_set(st, "Committed_AS", vmtotal_data.t_rm);
187                 rrdset_done(st);
188             }
189         }
190     }
191
192     // --------------------------------------------------------------------
193
194     if (likely(do_cpu)) {
195         if (unlikely(CPUSTATES != 5)) {
196             error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES);
197             do_cpu = 0;
198             error("DISABLED: system.cpu");
199         } else {
200             if (unlikely(GETSYSCTL("kern.cp_time", cp_time))) {
201                 do_cpu = 0;
202                 error("DISABLED: system.cpu");
203             } else {
204
205                 st = rrdset_find_bytype("system", "cpu");
206                 if (unlikely(!st)) {
207                     st = rrdset_create("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED);
208
209                     rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
210                     rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
211                     rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
212                     rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
213                     rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
214                     rrddim_hide(st, "idle");
215                 }
216                 else rrdset_next(st);
217
218                 rrddim_set(st, "user", cp_time[0]);
219                 rrddim_set(st, "nice", cp_time[1]);
220                 rrddim_set(st, "system", cp_time[2]);
221                 rrddim_set(st, "interrupt", cp_time[3]);
222                 rrddim_set(st, "idle", cp_time[4]);
223                 rrdset_done(st);
224             }
225         }
226     }
227
228     // --------------------------------------------------------------------
229
230     if (likely(do_cpu_cores)) {
231         if (unlikely(CPUSTATES != 5)) {
232             error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES);
233             do_cpu_cores = 0;
234             error("DISABLED: cpu.cpuXX");
235         } else {
236             if (unlikely(GETSYSCTL("kern.smp.cpus", ncpus))) {
237                 do_cpu_cores = 0;
238                 error("DISABLED: cpu.cpuXX");
239             } else {
240                 pcpu_cp_time = reallocz(pcpu_cp_time, sizeof(cp_time) * ncpus);
241
242                 for (i = 0; i < ncpus; i++) {
243                     if (unlikely(getsysctl("kern.cp_times", pcpu_cp_time, sizeof(cp_time) * ncpus))) {
244                         do_cpu_cores = 0;
245                         error("DISABLED: cpu.cpuXX");
246                         break;
247                     }
248                     if (unlikely(ncpus > 9999)) {
249                         error("FREEBSD: There are more than 4 digits in cpu cores number");
250                         do_cpu_cores = 0;
251                         error("DISABLED: cpu.cpuXX");
252                         break;
253                     }
254                     snprintfz(cpuid, 8, "cpu%d", i);
255
256                     st = rrdset_find_bytype("cpu", cpuid);
257                     if (unlikely(!st)) {
258                         st = rrdset_create("cpu", cpuid, NULL, "utilization", "cpu.cpu", "Core utilization", "percentage", 1000, update_every, RRDSET_TYPE_STACKED);
259
260                         rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
261                         rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
262                         rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
263                         rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
264                         rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
265                         rrddim_hide(st, "idle");
266                     }
267                     else rrdset_next(st);
268
269                     rrddim_set(st, "user", pcpu_cp_time[i * 5 + 0]);
270                     rrddim_set(st, "nice", pcpu_cp_time[i * 5 + 1]);
271                     rrddim_set(st, "system", pcpu_cp_time[i * 5 + 2]);
272                     rrddim_set(st, "interrupt", pcpu_cp_time[i * 5 + 3]);
273                     rrddim_set(st, "idle", pcpu_cp_time[i * 5 + 4]);
274                     rrdset_done(st);
275                 }
276             }
277         }
278     }
279
280     // --------------------------------------------------------------------
281
282     if (likely(do_interrupts)) {
283         if (unlikely(sysctlbyname("hw.intrcnt", NULL, &intrcnt_size, NULL, 0) == -1)) {
284             error("FREEBSD: sysctl(hw.intrcnt...) failed: %s", strerror(errno));
285             do_interrupts = 0;
286             error("DISABLED: system.intr");
287         } else {
288             nintr = intrcnt_size / sizeof(u_long);
289             intrcnt = reallocz(intrcnt, nintr * sizeof(u_long));
290             if (unlikely(getsysctl("hw.intrcnt", intrcnt, nintr * sizeof(u_long)))){
291                 do_interrupts = 0;
292                 error("DISABLED: system.intr");
293             } else {
294                 for (i = 0; i < nintr; i++)
295                     totalintr += intrcnt[i];
296
297                 st = rrdset_find_bytype("system", "intr");
298                 if (unlikely(!st)) {
299                     st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "Total Device Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
300                     st->isdetail = 1;
301
302                     rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
303                 }
304                 else rrdset_next(st);
305
306                 rrddim_set(st, "interrupts", totalintr);
307                 rrdset_done(st);
308             }
309         }
310     }
311
312     // --------------------------------------------------------------------
313
314     if (likely(do_context)) {
315         if (unlikely(GETSYSCTL("vm.stats.sys.v_swtch", u_int_data))) {
316             do_context = 0;
317             error("DISABLED: system.ctxt");
318         } else {
319
320             st = rrdset_find_bytype("system", "ctxt");
321             if (unlikely(!st)) {
322                 st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
323
324                 rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL);
325             }
326             else rrdset_next(st);
327
328             rrddim_set(st, "switches", u_int_data);
329             rrdset_done(st);
330         }
331     }
332
333     // --------------------------------------------------------------------
334
335     if (likely(do_forks)) {
336         if (unlikely(GETSYSCTL("vm.stats.vm.v_forks", u_int_data))) {
337             do_forks = 0;
338             error("DISABLED: system.forks");
339         } else {
340
341             st = rrdset_find_bytype("system", "forks");
342             if (unlikely(!st)) {
343                 st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
344                 st->isdetail = 1;
345
346                 rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL);
347             }
348             else rrdset_next(st);
349
350             rrddim_set(st, "started", u_int_data);
351             rrdset_done(st);
352         }
353     }
354
355     // --------------------------------------------------------------------
356
357     if (likely(do_disk_io)) {
358         if (unlikely(GETSYSCTL("kern.devstat.numdevs", numdevs))) {
359             do_disk_io = 0;
360             error("DISABLED: disk.io");
361         } else {
362             devstat_data = reallocz(devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs); // there is generation number before devstat structures
363             if (unlikely(getsysctl("kern.devstat.all", devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs))) {
364                 do_disk_io = 0;
365                 error("DISABLED: disk.io");
366             } else {
367                 dstat = devstat_data + sizeof(long); // skip generation number
368                 collected_number total_disk_reads = 0;
369                 collected_number total_disk_writes = 0;
370
371                 for (i = 0; i < numdevs; i++) {
372                     if ((dstat[i].device_type == (DEVSTAT_TYPE_IF_SCSI | DEVSTAT_TYPE_DIRECT)) || (dstat[i].device_type == (DEVSTAT_TYPE_IF_IDE | DEVSTAT_TYPE_DIRECT))) {
373
374                         // --------------------------------------------------------------------
375
376                         st = rrdset_find_bytype(RRD_TYPE_DISK, dstat[i].device_name);
377                         if (unlikely(!st)) {
378                             st = rrdset_create(RRD_TYPE_DISK, dstat[i].device_name, NULL, dstat[i].device_name, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
379
380                             rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL);
381                             rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL);
382                         }
383                         else rrdset_next(st);
384
385                         total_disk_reads += dstat[i].bytes[DEVSTAT_READ];
386                         total_disk_writes += dstat[i].bytes[DEVSTAT_WRITE];
387                         prev_dstat.bytes_read = rrddim_set(st, "reads", dstat[i].bytes[DEVSTAT_READ]);
388                         prev_dstat.bytes_write = rrddim_set(st, "writes", dstat[i].bytes[DEVSTAT_WRITE]);
389                         rrdset_done(st);
390
391                         // --------------------------------------------------------------------
392
393                         st = rrdset_find_bytype("disk_ops", dstat[i].device_name);
394                         if (unlikely(!st)) {
395                             st = rrdset_create("disk_ops", dstat[i].device_name, NULL, dstat[i].device_name, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
396                             st->isdetail = 1;
397
398                             rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
399                             rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
400                         }
401                         else rrdset_next(st);
402
403                         prev_dstat.operations_read = rrddim_set(st, "reads", dstat[i].operations[DEVSTAT_READ]);
404                         prev_dstat.operations_write = rrddim_set(st, "writes", dstat[i].operations[DEVSTAT_WRITE]);
405                         rrdset_done(st);
406
407                         // --------------------------------------------------------------------
408
409                         st = rrdset_find_bytype("disk_qops", dstat[i].device_name);
410                         if (unlikely(!st)) {
411                             st = rrdset_create("disk_qops", dstat[i].device_name, NULL, dstat[i].device_name, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
412                             st->isdetail = 1;
413
414                             rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
415                         }
416                         else rrdset_next(st);
417
418                         rrddim_set(st, "operations", dstat[i].start_count - dstat[i].end_count);
419                         rrdset_done(st);
420
421                         // --------------------------------------------------------------------
422
423                         st = rrdset_find_bytype("disk_util", dstat[i].device_name);
424                         if (unlikely(!st)) {
425                             st = rrdset_create("disk_util", dstat[i].device_name, NULL, dstat[i].device_name, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
426                             st->isdetail = 1;
427
428                             rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
429                         }
430                         else rrdset_next(st);
431
432                         cur_dstat.busy_time_ms = dstat[i].busy_time.sec * 1000 + dstat[i].busy_time.frac * BINTIME_SCALE;
433                         prev_dstat.busy_time_ms = rrddim_set(st, "utilization", cur_dstat.busy_time_ms);
434                         rrdset_done(st);
435
436                         // --------------------------------------------------------------------
437
438                         st = rrdset_find_bytype("disk_iotime", dstat[i].device_name);
439                         if (unlikely(!st)) {
440                             st = rrdset_create("disk_iotime", dstat[i].device_name, NULL, dstat[i].device_name, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
441                             st->isdetail = 1;
442
443                             rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
444                             rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
445                         }
446                         else rrdset_next(st);
447
448                         cur_dstat.duration_read_ms = dstat[i].duration[DEVSTAT_READ].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE;
449                         cur_dstat.duration_write_ms = dstat[i].duration[DEVSTAT_WRITE].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE;
450                         prev_dstat.duration_read_ms = rrddim_set(st, "reads", cur_dstat.duration_read_ms);
451                         prev_dstat.duration_write_ms = rrddim_set(st, "writes", cur_dstat.duration_write_ms);
452                         rrdset_done(st);
453
454                         // --------------------------------------------------------------------
455                         // calculate differential charts
456                         // only if this is not the first time we run
457
458                         if (likely(dt)) {
459
460                             // --------------------------------------------------------------------
461
462                             st = rrdset_find_bytype("disk_await", dstat[i].device_name);
463                             if (unlikely(!st)) {
464                                 st = rrdset_create("disk_await", dstat[i].device_name, NULL, dstat[i].device_name, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
465                                 st->isdetail = 1;
466
467                                 rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
468                                 rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
469                             }
470                             else rrdset_next(st);
471
472                             rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ? 
473                                 (cur_dstat.duration_read_ms - prev_dstat.duration_read_ms) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0);
474                             rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ?
475                                 (cur_dstat.duration_write_ms - prev_dstat.duration_write_ms) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0);
476                             rrdset_done(st);
477
478                             // --------------------------------------------------------------------
479
480                             st = rrdset_find_bytype("disk_avgsz", dstat[i].device_name);
481                             if (unlikely(!st)) {
482                                 st = rrdset_create("disk_avgsz", dstat[i].device_name, NULL, dstat[i].device_name, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
483                                 st->isdetail = 1;
484
485                                 rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE);
486                                 rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE);
487                             }
488                             else rrdset_next(st);
489
490                             rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ?
491                                 (dstat[i].bytes[DEVSTAT_READ] - prev_dstat.bytes_read) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0);
492                             rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ?
493                                 (dstat[i].bytes[DEVSTAT_WRITE] - prev_dstat.bytes_write) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0);
494                             rrdset_done(st);
495
496                             // --------------------------------------------------------------------
497
498                             st = rrdset_find_bytype("disk_svctm", dstat[i].device_name);
499                             if (unlikely(!st)) {
500                                 st = rrdset_create("disk_svctm", dstat[i].device_name, NULL, dstat[i].device_name, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
501                                 st->isdetail = 1;
502
503                                 rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
504                             }
505                             else rrdset_next(st);
506
507                             rrddim_set(st, "svctm", ((dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) + (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write)) ?
508                                 (cur_dstat.busy_time_ms - prev_dstat.busy_time_ms) / ((dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) + (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write)) : 0);
509                             rrdset_done(st);
510                         }
511                     }
512
513                     // --------------------------------------------------------------------
514
515                     st = rrdset_find_bytype("system", "io");
516                     if (unlikely(!st)) {
517                         st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
518                         rrddim_add(st, "in",  NULL,  1, 1024, RRDDIM_INCREMENTAL);
519                         rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL);
520                     }
521                     else rrdset_next(st);
522
523                     rrddim_set(st, "in", total_disk_reads);
524                     rrddim_set(st, "out", total_disk_writes);
525                     rrdset_done(st);
526                 }
527             }
528         }
529     }
530
531     // --------------------------------------------------------------------
532
533
534     if (likely(do_swap)) {
535         mibsize = sizeof mib / sizeof mib[0];
536         if (unlikely(sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)) {
537             error("FREEBSD: sysctl(%s...) failed: %s", "vm.swap_info", strerror(errno));
538             do_swap = 0;
539             error("DISABLED: disk.io");
540         } else {
541             for (i = 0; ; i++) {
542                 mib[mibsize] = i;
543                 size = sizeof(xsw);
544                 if (unlikely(sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1 )) {
545                     if (unlikely(errno != ENOENT)) {
546                         error("FREEBSD: sysctl(%s...) failed: %s", "vm.swap_info", strerror(errno));
547                         do_swap = 0;
548                         error("DISABLED: disk.io");
549                     } else {
550                         if (unlikely(size != sizeof(xsw))) {
551                             error("FREEBSD: sysctl(%s...) expected %lu, got %lu", "vm.swap_info", (unsigned long)sizeof(xsw), (unsigned long)size);
552                             do_swap = 0;
553                             error("DISABLED: disk.io");
554                         } else break;
555                     }
556                 }
557                 total_xsw.bytes_used += xsw.xsw_used * system_pagesize;
558                 total_xsw.bytes_total += xsw.xsw_nblks * system_pagesize;
559             }
560
561             if (likely(do_swap)) {
562                 st = rrdset_find("system.swap");
563                 if (unlikely(!st)) {
564                     st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
565                     st->isdetail = 1;
566
567                     rrddim_add(st, "free",    NULL, 1, 1048576, RRDDIM_ABSOLUTE);
568                     rrddim_add(st, "used",    NULL, 1, 1048576, RRDDIM_ABSOLUTE);
569                 }
570                 else rrdset_next(st);
571
572                 rrddim_set(st, "used", total_xsw.bytes_used);
573                 rrddim_set(st, "free", total_xsw.bytes_total - total_xsw.bytes_used);
574                 rrdset_done(st);
575             }
576         }
577     }
578
579     // --------------------------------------------------------------------
580
581     if (likely(do_ram)) {
582         if (unlikely(GETSYSCTL("vm.stats.vm.v_active_count",    vmmeter_data.v_active_count) ||
583                      GETSYSCTL("vm.stats.vm.v_inactive_count",  vmmeter_data.v_inactive_count) ||
584                      GETSYSCTL("vm.stats.vm.v_wire_count",      vmmeter_data.v_wire_count) ||
585                      GETSYSCTL("vm.stats.vm.v_cache_count",     vmmeter_data.v_cache_count) ||
586                      GETSYSCTL("vfs.bufspace",                  vfs_bufspace_count) ||
587                      GETSYSCTL("vm.stats.vm.v_free_count",      vmmeter_data.v_free_count))) {
588             do_swapio = 0;
589             error("DISABLED: system.swapio");
590         } else {
591             st = rrdset_find("system.ram");
592             if (unlikely(!st)) {
593                 st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
594
595                 rrddim_add(st, "active",    NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
596                 rrddim_add(st, "inactive",  NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
597                 rrddim_add(st, "wired",     NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
598                 rrddim_add(st, "cache",     NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
599                 rrddim_add(st, "buffers",   NULL, 1, 1024, RRDDIM_ABSOLUTE);
600                 rrddim_add(st, "free",      NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
601             }
602             else rrdset_next(st);
603
604             rrddim_set(st, "active",    vmmeter_data.v_active_count);
605             rrddim_set(st, "inactive",  vmmeter_data.v_inactive_count);
606             rrddim_set(st, "wired",     vmmeter_data.v_wire_count);
607             rrddim_set(st, "cache",     vmmeter_data.v_cache_count);
608             rrddim_set(st, "buffers",   vfs_bufspace_count);
609             rrddim_set(st, "free",      vmmeter_data.v_free_count);
610             rrdset_done(st);
611         }
612     }
613
614     // --------------------------------------------------------------------
615
616     if (likely(do_swapio)) {
617         if (unlikely(GETSYSCTL("vm.stats.vm.v_swappgsin", vmmeter_data.v_swappgsin) || GETSYSCTL("vm.stats.vm.v_vnodepgsout", vmmeter_data.v_swappgsout))) {
618             do_swapio = 0;
619             error("DISABLED: system.swapio");
620         } else {
621             st = rrdset_find("system.swapio");
622             if (unlikely(!st)) {
623                 st = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
624
625                 rrddim_add(st, "in",  NULL, system_pagesize, 1024, RRDDIM_INCREMENTAL);
626                 rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRDDIM_INCREMENTAL);
627             }
628             else rrdset_next(st);
629
630             rrddim_set(st, "in", vmmeter_data.v_swappgsin);
631             rrddim_set(st, "out", vmmeter_data.v_swappgsout);
632             rrdset_done(st);
633         }
634     }
635
636     // --------------------------------------------------------------------
637
638     if (likely(do_pgfaults)) {
639         if (unlikely(GETSYSCTL("vm.stats.vm.v_vm_faults",   vmmeter_data.v_vm_faults) ||
640                      GETSYSCTL("vm.stats.vm.v_io_faults",   vmmeter_data.v_io_faults) ||
641                      GETSYSCTL("vm.stats.vm.v_cow_faults",  vmmeter_data.v_cow_faults) ||
642                      GETSYSCTL("vm.stats.vm.v_cow_optim",   vmmeter_data.v_cow_optim) ||
643                      GETSYSCTL("vm.stats.vm.v_intrans",     vmmeter_data.v_intrans))) {
644             do_pgfaults = 0;
645             error("DISABLED: mem.pgfaults");
646         } else {
647             st = rrdset_find("mem.pgfaults");
648             if (unlikely(!st)) {
649                 st = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
650                 st->isdetail = 1;
651
652                 rrddim_add(st, "memory", NULL, 1, 1, RRDDIM_INCREMENTAL);
653                 rrddim_add(st, "io_requiring", NULL, 1, 1, RRDDIM_INCREMENTAL);
654                 rrddim_add(st, "cow", NULL, 1, 1, RRDDIM_INCREMENTAL);
655                 rrddim_add(st, "cow_optimized", NULL, 1, 1, RRDDIM_INCREMENTAL);
656                 rrddim_add(st, "in_transit", NULL, 1, 1, RRDDIM_INCREMENTAL);
657             }
658             else rrdset_next(st);
659
660             rrddim_set(st, "memory", vmmeter_data.v_vm_faults);
661             rrddim_set(st, "io_requiring", vmmeter_data.v_io_faults);
662             rrddim_set(st, "cow", vmmeter_data.v_cow_faults);
663             rrddim_set(st, "cow_optimized", vmmeter_data.v_cow_optim);
664             rrddim_set(st, "in_transit", vmmeter_data.v_intrans);
665             rrdset_done(st);
666         }
667     }
668
669     return 0;
670 }