]> arthur.barton.de Git - netdata.git/blob - src/freebsd_sysctl.c
Add device and software interrupts charts to FreeBSD plugin
[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: struct semid_kernel, struct shmid_kernel, struct msqid_kernel
10 #define _KERNEL
11 #include <sys/sem.h>
12 #include <sys/shm.h>
13 #include <sys/msg.h>
14 #undef _KERNEL
15 // NEEDED BY: do_disk_io
16 #define RRD_TYPE_DISK "disk"
17
18 // FreeBSD calculates load averages once every 5 seconds
19 #define MIN_LOADAVG_UPDATE_EVERY 5
20
21 int do_freebsd_sysctl(int update_every, usec_t dt) {
22     (void)dt;
23
24     static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1,
25         do_loadavg = -1, do_all_processes = -1, do_disk_io = -1, do_swap = -1, do_ram = -1, do_swapio = -1,
26         do_pgfaults = -1, do_committed = -1, do_ipc_semaphores = -1, do_ipc_shared_mem = -1, do_ipc_msg_queues = -1,
27         do_dev_intr = -1, do_soft_intr = -1;
28
29     if (unlikely(do_cpu == -1)) {
30         do_cpu                  = config_get_boolean("plugin:freebsd:sysctl", "cpu utilization", 1);
31         do_cpu_cores            = config_get_boolean("plugin:freebsd:sysctl", "per cpu core utilization", 1);
32         do_interrupts           = config_get_boolean("plugin:freebsd:sysctl", "cpu interrupts", 1);
33         do_dev_intr             = config_get_boolean("plugin:freebsd:sysctl", "device interrupts", 1);
34         do_soft_intr            = config_get_boolean("plugin:freebsd:sysctl", "software interrupts", 1);
35         do_context              = config_get_boolean("plugin:freebsd:sysctl", "context switches", 1);
36         do_forks                = config_get_boolean("plugin:freebsd:sysctl", "processes started", 1);
37         do_processes            = config_get_boolean("plugin:freebsd:sysctl", "processes running", 1);
38         do_loadavg              = config_get_boolean("plugin:freebsd:sysctl", "enable load average", 1);
39         do_all_processes        = config_get_boolean("plugin:freebsd:sysctl", "enable total processes", 1);
40         do_disk_io              = config_get_boolean("plugin:freebsd:sysctl", "stats for all disks", 1);
41         do_swap                 = config_get_boolean("plugin:freebsd:sysctl", "system swap", 1);
42         do_ram                  = config_get_boolean("plugin:freebsd:sysctl", "system ram", 1);
43         do_swapio               = config_get_boolean("plugin:freebsd:sysctl", "swap i/o", 1);
44         do_pgfaults             = config_get_boolean("plugin:freebsd:sysctl", "memory page faults", 1);
45         do_committed            = config_get_boolean("plugin:freebsd:sysctl", "committed memory", 1);
46         do_ipc_semaphores       = config_get_boolean("plugin:freebsd:sysctl", "ipc semaphores", 1);
47         do_ipc_shared_mem       = config_get_boolean("plugin:freebsd:sysctl", "ipc shared memory", 1);
48         do_ipc_msg_queues       = config_get_boolean("plugin:freebsd:sysctl", "ipc message queues", 1);
49     }
50
51     RRDSET *st;
52
53     int system_pagesize = getpagesize(); // wouldn't it be better to get value directly from hw.pagesize?
54     int i;
55
56 // NEEDED BY: do_loadavg
57     static usec_t last_loadavg_usec = 0;
58     struct loadavg sysload;
59
60 // NEEDED BY: do_cpu, do_cpu_cores
61     long cp_time[CPUSTATES];
62
63 // NEEDED BY: do_cpu_cores
64     int ncpus;
65     static long *pcpu_cp_time = NULL;
66     char cpuid[8]; // no more than 4 digits expected
67
68 // NEEDED BY: do_all_processes, do_processes
69     struct vmtotal vmtotal_data;
70
71 // NEEDED BY: do_context, do_forks
72     u_int u_int_data;
73
74 // NEEDED BY: do_interrupts
75     size_t intrcnt_size;
76     unsigned long nintr = 0;
77     static unsigned long *intrcnt = NULL;
78     unsigned long long totalintr = 0;
79
80 // NEEDED BY: do_disk_io
81     #define BINTIME_SCALE 5.42101086242752217003726400434970855712890625e-17 // this is 1000/2^64
82     int numdevs;
83     static void *devstat_data = NULL;
84     struct devstat *dstat;
85     struct cur_dstat {
86         collected_number duration_read_ms;
87         collected_number duration_write_ms;
88         collected_number busy_time_ms;
89     } cur_dstat;
90     struct prev_dstat {
91         collected_number bytes_read;
92         collected_number bytes_write;
93         collected_number operations_read;
94         collected_number operations_write;
95         collected_number duration_read_ms;
96         collected_number duration_write_ms;
97         collected_number busy_time_ms;
98     } prev_dstat;
99
100     // NEEDED BY: do_swap
101     size_t mibsize, size;
102     int mib[3]; // CTL_MAXNAME = 24 maximum mib components (sysctl.h)
103     struct xswdev xsw;
104     struct total_xsw {
105         collected_number bytes_used;
106         collected_number bytes_total;
107     } total_xsw = {0, 0};
108
109     // NEEDED BY: do_swapio, do_ram
110     struct vmmeter vmmeter_data;
111
112     // NEEDED BY: do_ram
113     int vfs_bufspace_count;
114
115     // NEEDED BY: do_ipc_semaphores
116     struct ipc_sem {
117         int semmni;
118         collected_number sets;
119         collected_number semaphores;
120     } ipc_sem = {0, 0, 0};
121     static struct semid_kernel *ipc_sem_data = NULL;
122
123     // NEEDED BY: do_ipc_shared_mem
124     struct ipc_shm {
125         u_long shmmni;
126         collected_number segs;
127         collected_number segsize;
128     } ipc_shm = {0, 0, 0};
129     static struct shmid_kernel *ipc_shm_data = NULL;
130
131     // NEEDED BY: do_ipc_msg_queues
132     struct ipc_msq {
133         int msgmni;
134         collected_number queues;
135         collected_number messages;
136         collected_number usedsize;
137         collected_number allocsize;
138     } ipc_msq = {0, 0, 0, 0, 0};
139     static struct msqid_kernel *ipc_msq_data = NULL;
140
141     // --------------------------------------------------------------------
142
143     if (last_loadavg_usec <= dt) {
144         if (likely(do_loadavg)) {
145             if (unlikely(GETSYSCTL("vm.loadavg", sysload))) {
146                 do_loadavg = 0;
147                 error("DISABLED: system.load");
148             } else {
149
150                 st = rrdset_find_bytype("system", "load");
151                 if (unlikely(!st)) {
152                     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);
153                     rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
154                     rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
155                     rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
156                 }
157                 else rrdset_next(st);
158
159                 rrddim_set(st, "load1", (collected_number) ((double)sysload.ldavg[0] / sysload.fscale * 1000));
160                 rrddim_set(st, "load5", (collected_number) ((double)sysload.ldavg[1] / sysload.fscale * 1000));
161                 rrddim_set(st, "load15", (collected_number) ((double)sysload.ldavg[2] / sysload.fscale * 1000));
162                 rrdset_done(st);
163             }
164         }
165
166         last_loadavg_usec = st->update_every * USEC_PER_SEC;
167     }
168     else last_loadavg_usec -= dt;
169
170     // --------------------------------------------------------------------
171
172     if (likely(do_all_processes | do_processes | do_committed)) {
173         if (unlikely(GETSYSCTL("vm.vmtotal", vmtotal_data))) {
174             do_all_processes = 0;
175             error("DISABLED: system.active_processes");
176             do_processes = 0;
177             error("DISABLED: system.processes");
178             do_committed = 0;
179             error("DISABLED: mem.committed");
180         } else {
181             if (likely(do_all_processes)) {
182
183                 st = rrdset_find_bytype("system", "active_processes");
184                 if (unlikely(!st)) {
185                     st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
186                     rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE);
187                 }
188                 else rrdset_next(st);
189
190                 rrddim_set(st, "active", (vmtotal_data.t_rq + vmtotal_data.t_dw + vmtotal_data.t_pw + vmtotal_data.t_sl + vmtotal_data.t_sw));
191                 rrdset_done(st);
192             }
193
194             // --------------------------------------------------------------------
195
196             if (likely(do_processes)) {
197
198                 st = rrdset_find_bytype("system", "processes");
199                 if (unlikely(!st)) {
200                     st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
201
202                     rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
203                     rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
204                 }
205                 else rrdset_next(st);
206
207                 rrddim_set(st, "running", vmtotal_data.t_rq);
208                 rrddim_set(st, "blocked", (vmtotal_data.t_dw + vmtotal_data.t_pw));
209                 rrdset_done(st);
210             }
211
212             // --------------------------------------------------------------------
213
214             if (likely(do_committed)) {
215                 st = rrdset_find("mem.committed");
216                 if (unlikely(!st)) {
217                     st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA);
218                     st->isdetail = 1;
219
220                     rrddim_add(st, "Committed_AS", NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
221                 }
222                 else rrdset_next(st);
223
224                 rrddim_set(st, "Committed_AS", vmtotal_data.t_rm);
225                 rrdset_done(st);
226             }
227         }
228     }
229
230     // --------------------------------------------------------------------
231
232     if (likely(do_cpu)) {
233         if (unlikely(CPUSTATES != 5)) {
234             error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES);
235             do_cpu = 0;
236             error("DISABLED: system.cpu");
237         } else {
238             if (unlikely(GETSYSCTL("kern.cp_time", cp_time))) {
239                 do_cpu = 0;
240                 error("DISABLED: system.cpu");
241             } else {
242
243                 st = rrdset_find_bytype("system", "cpu");
244                 if (unlikely(!st)) {
245                     st = rrdset_create("system", "cpu", NULL, "cpu", "system.cpu", "Total CPU utilization", "percentage", 100, update_every, RRDSET_TYPE_STACKED);
246
247                     rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
248                     rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
249                     rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
250                     rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
251                     rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
252                     rrddim_hide(st, "idle");
253                 }
254                 else rrdset_next(st);
255
256                 rrddim_set(st, "user", cp_time[0]);
257                 rrddim_set(st, "nice", cp_time[1]);
258                 rrddim_set(st, "system", cp_time[2]);
259                 rrddim_set(st, "interrupt", cp_time[3]);
260                 rrddim_set(st, "idle", cp_time[4]);
261                 rrdset_done(st);
262             }
263         }
264     }
265
266     // --------------------------------------------------------------------
267
268     if (likely(do_cpu_cores)) {
269         if (unlikely(CPUSTATES != 5)) {
270             error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES);
271             do_cpu_cores = 0;
272             error("DISABLED: cpu.cpuXX");
273         } else {
274             if (unlikely(GETSYSCTL("kern.smp.cpus", ncpus))) {
275                 do_cpu_cores = 0;
276                 error("DISABLED: cpu.cpuXX");
277             } else {
278                 pcpu_cp_time = reallocz(pcpu_cp_time, sizeof(cp_time) * ncpus);
279
280                 for (i = 0; i < ncpus; i++) {
281                     if (unlikely(getsysctl("kern.cp_times", pcpu_cp_time, sizeof(cp_time) * ncpus))) {
282                         do_cpu_cores = 0;
283                         error("DISABLED: cpu.cpuXX");
284                         break;
285                     }
286                     if (unlikely(ncpus > 9999)) {
287                         error("FREEBSD: There are more than 4 digits in cpu cores number");
288                         do_cpu_cores = 0;
289                         error("DISABLED: cpu.cpuXX");
290                         break;
291                     }
292                     snprintfz(cpuid, 8, "cpu%d", i);
293
294                     st = rrdset_find_bytype("cpu", cpuid);
295                     if (unlikely(!st)) {
296                         st = rrdset_create("cpu", cpuid, NULL, "utilization", "cpu.cpu", "Core utilization", "percentage", 1000, update_every, RRDSET_TYPE_STACKED);
297
298                         rrddim_add(st, "user", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
299                         rrddim_add(st, "nice", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
300                         rrddim_add(st, "system", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
301                         rrddim_add(st, "interrupt", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
302                         rrddim_add(st, "idle", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
303                         rrddim_hide(st, "idle");
304                     }
305                     else rrdset_next(st);
306
307                     rrddim_set(st, "user", pcpu_cp_time[i * 5 + 0]);
308                     rrddim_set(st, "nice", pcpu_cp_time[i * 5 + 1]);
309                     rrddim_set(st, "system", pcpu_cp_time[i * 5 + 2]);
310                     rrddim_set(st, "interrupt", pcpu_cp_time[i * 5 + 3]);
311                     rrddim_set(st, "idle", pcpu_cp_time[i * 5 + 4]);
312                     rrdset_done(st);
313                 }
314             }
315         }
316     }
317
318     // --------------------------------------------------------------------
319
320     if (likely(do_interrupts)) {
321         if (unlikely(sysctlbyname("hw.intrcnt", NULL, &intrcnt_size, NULL, 0) == -1)) {
322             error("FREEBSD: sysctl(hw.intrcnt...) failed: %s", strerror(errno));
323             do_interrupts = 0;
324             error("DISABLED: system.intr");
325         } else {
326             nintr = intrcnt_size / sizeof(u_long);
327             intrcnt = reallocz(intrcnt, nintr * sizeof(u_long));
328             if (unlikely(getsysctl("hw.intrcnt", intrcnt, nintr * sizeof(u_long)))){
329                 do_interrupts = 0;
330                 error("DISABLED: system.intr");
331             } else {
332                 for (i = 0; i < nintr; i++)
333                     totalintr += intrcnt[i];
334
335                 st = rrdset_find_bytype("system", "intr");
336                 if (unlikely(!st)) {
337                     st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "Total Hardware Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
338                     st->isdetail = 1;
339
340                     rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
341                 }
342                 else rrdset_next(st);
343
344                 rrddim_set(st, "interrupts", totalintr);
345                 rrdset_done(st);
346             }
347         }
348     }
349
350     // --------------------------------------------------------------------
351
352     if (likely(do_dev_intr)) {
353         if (unlikely(GETSYSCTL("vm.stats.sys.v_intr", u_int_data))) {
354             do_dev_intr = 0;
355             error("DISABLED: system.dev_intr");
356         } else {
357
358             st = rrdset_find_bytype("system", "dev_intr");
359             if (unlikely(!st)) {
360                 st = rrdset_create("system", "dev_intr", NULL, "interrupts", NULL, "Device Interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_LINE);
361
362                 rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
363             }
364             else rrdset_next(st);
365
366             rrddim_set(st, "interrupts", u_int_data);
367             rrdset_done(st);
368         }
369     }
370
371     // --------------------------------------------------------------------
372
373     if (likely(do_soft_intr)) {
374         if (unlikely(GETSYSCTL("vm.stats.sys.v_soft", u_int_data))) {
375             do_soft_intr = 0;
376             error("DISABLED: system.dev_intr");
377         } else {
378
379             st = rrdset_find_bytype("system", "soft_intr");
380             if (unlikely(!st)) {
381                 st = rrdset_create("system", "soft_intr", NULL, "interrupts", NULL, "Software Interrupts", "interrupts/s", 1100, update_every, RRDSET_TYPE_LINE);
382
383                 rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
384             }
385             else rrdset_next(st);
386
387             rrddim_set(st, "interrupts", u_int_data);
388             rrdset_done(st);
389         }
390     }
391
392     // --------------------------------------------------------------------
393
394     if (likely(do_context)) {
395         if (unlikely(GETSYSCTL("vm.stats.sys.v_swtch", u_int_data))) {
396             do_context = 0;
397             error("DISABLED: system.ctxt");
398         } else {
399
400             st = rrdset_find_bytype("system", "ctxt");
401             if (unlikely(!st)) {
402                 st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
403
404                 rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL);
405             }
406             else rrdset_next(st);
407
408             rrddim_set(st, "switches", u_int_data);
409             rrdset_done(st);
410         }
411     }
412
413     // --------------------------------------------------------------------
414
415     if (likely(do_forks)) {
416         if (unlikely(GETSYSCTL("vm.stats.vm.v_forks", u_int_data))) {
417             do_forks = 0;
418             error("DISABLED: system.forks");
419         } else {
420
421             st = rrdset_find_bytype("system", "forks");
422             if (unlikely(!st)) {
423                 st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
424                 st->isdetail = 1;
425
426                 rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL);
427             }
428             else rrdset_next(st);
429
430             rrddim_set(st, "started", u_int_data);
431             rrdset_done(st);
432         }
433     }
434
435     // --------------------------------------------------------------------
436
437     if (likely(do_disk_io)) {
438         if (unlikely(GETSYSCTL("kern.devstat.numdevs", numdevs))) {
439             do_disk_io = 0;
440             error("DISABLED: disk.io");
441         } else {
442             devstat_data = reallocz(devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs); // there is generation number before devstat structures
443             if (unlikely(getsysctl("kern.devstat.all", devstat_data, sizeof(long) + sizeof(struct devstat) * numdevs))) {
444                 do_disk_io = 0;
445                 error("DISABLED: disk.io");
446             } else {
447                 dstat = devstat_data + sizeof(long); // skip generation number
448                 collected_number total_disk_reads = 0;
449                 collected_number total_disk_writes = 0;
450
451                 for (i = 0; i < numdevs; i++) {
452                     if ((dstat[i].device_type == (DEVSTAT_TYPE_IF_SCSI | DEVSTAT_TYPE_DIRECT)) || (dstat[i].device_type == (DEVSTAT_TYPE_IF_IDE | DEVSTAT_TYPE_DIRECT))) {
453
454                         // --------------------------------------------------------------------
455
456                         st = rrdset_find_bytype(RRD_TYPE_DISK, dstat[i].device_name);
457                         if (unlikely(!st)) {
458                             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);
459
460                             rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_INCREMENTAL);
461                             rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_INCREMENTAL);
462                         }
463                         else rrdset_next(st);
464
465                         total_disk_reads += dstat[i].bytes[DEVSTAT_READ];
466                         total_disk_writes += dstat[i].bytes[DEVSTAT_WRITE];
467                         prev_dstat.bytes_read = rrddim_set(st, "reads", dstat[i].bytes[DEVSTAT_READ]);
468                         prev_dstat.bytes_write = rrddim_set(st, "writes", dstat[i].bytes[DEVSTAT_WRITE]);
469                         rrdset_done(st);
470
471                         // --------------------------------------------------------------------
472
473                         st = rrdset_find_bytype("disk_ops", dstat[i].device_name);
474                         if (unlikely(!st)) {
475                             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);
476                             st->isdetail = 1;
477
478                             rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
479                             rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
480                         }
481                         else rrdset_next(st);
482
483                         prev_dstat.operations_read = rrddim_set(st, "reads", dstat[i].operations[DEVSTAT_READ]);
484                         prev_dstat.operations_write = rrddim_set(st, "writes", dstat[i].operations[DEVSTAT_WRITE]);
485                         rrdset_done(st);
486
487                         // --------------------------------------------------------------------
488
489                         st = rrdset_find_bytype("disk_qops", dstat[i].device_name);
490                         if (unlikely(!st)) {
491                             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);
492                             st->isdetail = 1;
493
494                             rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
495                         }
496                         else rrdset_next(st);
497
498                         rrddim_set(st, "operations", dstat[i].start_count - dstat[i].end_count);
499                         rrdset_done(st);
500
501                         // --------------------------------------------------------------------
502
503                         st = rrdset_find_bytype("disk_util", dstat[i].device_name);
504                         if (unlikely(!st)) {
505                             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);
506                             st->isdetail = 1;
507
508                             rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
509                         }
510                         else rrdset_next(st);
511
512                         cur_dstat.busy_time_ms = dstat[i].busy_time.sec * 1000 + dstat[i].busy_time.frac * BINTIME_SCALE;
513                         prev_dstat.busy_time_ms = rrddim_set(st, "utilization", cur_dstat.busy_time_ms);
514                         rrdset_done(st);
515
516                         // --------------------------------------------------------------------
517
518                         st = rrdset_find_bytype("disk_iotime", dstat[i].device_name);
519                         if (unlikely(!st)) {
520                             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);
521                             st->isdetail = 1;
522
523                             rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
524                             rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
525                         }
526                         else rrdset_next(st);
527
528                         cur_dstat.duration_read_ms = dstat[i].duration[DEVSTAT_READ].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE;
529                         cur_dstat.duration_write_ms = dstat[i].duration[DEVSTAT_WRITE].sec * 1000 + dstat[i].duration[DEVSTAT_READ].frac * BINTIME_SCALE;
530                         prev_dstat.duration_read_ms = rrddim_set(st, "reads", cur_dstat.duration_read_ms);
531                         prev_dstat.duration_write_ms = rrddim_set(st, "writes", cur_dstat.duration_write_ms);
532                         rrdset_done(st);
533
534                         // --------------------------------------------------------------------
535                         // calculate differential charts
536                         // only if this is not the first time we run
537
538                         if (likely(dt)) {
539
540                             // --------------------------------------------------------------------
541
542                             st = rrdset_find_bytype("disk_await", dstat[i].device_name);
543                             if (unlikely(!st)) {
544                                 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);
545                                 st->isdetail = 1;
546
547                                 rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
548                                 rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
549                             }
550                             else rrdset_next(st);
551
552                             rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ? 
553                                 (cur_dstat.duration_read_ms - prev_dstat.duration_read_ms) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0);
554                             rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ?
555                                 (cur_dstat.duration_write_ms - prev_dstat.duration_write_ms) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0);
556                             rrdset_done(st);
557
558                             // --------------------------------------------------------------------
559
560                             st = rrdset_find_bytype("disk_avgsz", dstat[i].device_name);
561                             if (unlikely(!st)) {
562                                 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);
563                                 st->isdetail = 1;
564
565                                 rrddim_add(st, "reads", NULL, 1, 1024, RRDDIM_ABSOLUTE);
566                                 rrddim_add(st, "writes", NULL, -1, 1024, RRDDIM_ABSOLUTE);
567                             }
568                             else rrdset_next(st);
569
570                             rrddim_set(st, "reads", (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) ?
571                                 (dstat[i].bytes[DEVSTAT_READ] - prev_dstat.bytes_read) / (dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) : 0);
572                             rrddim_set(st, "writes", (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) ?
573                                 (dstat[i].bytes[DEVSTAT_WRITE] - prev_dstat.bytes_write) / (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write) : 0);
574                             rrdset_done(st);
575
576                             // --------------------------------------------------------------------
577
578                             st = rrdset_find_bytype("disk_svctm", dstat[i].device_name);
579                             if (unlikely(!st)) {
580                                 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);
581                                 st->isdetail = 1;
582
583                                 rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
584                             }
585                             else rrdset_next(st);
586
587                             rrddim_set(st, "svctm", ((dstat[i].operations[DEVSTAT_READ] - prev_dstat.operations_read) + (dstat[i].operations[DEVSTAT_WRITE] - prev_dstat.operations_write)) ?
588                                 (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);
589                             rrdset_done(st);
590                         }
591                     }
592
593                     // --------------------------------------------------------------------
594
595                     st = rrdset_find_bytype("system", "io");
596                     if (unlikely(!st)) {
597                         st = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
598                         rrddim_add(st, "in",  NULL,  1, 1024, RRDDIM_INCREMENTAL);
599                         rrddim_add(st, "out", NULL, -1, 1024, RRDDIM_INCREMENTAL);
600                     }
601                     else rrdset_next(st);
602
603                     rrddim_set(st, "in", total_disk_reads);
604                     rrddim_set(st, "out", total_disk_writes);
605                     rrdset_done(st);
606                 }
607             }
608         }
609     }
610
611     // --------------------------------------------------------------------
612
613
614     if (likely(do_swap)) {
615         mibsize = sizeof mib / sizeof mib[0];
616         if (unlikely(sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)) {
617             error("FREEBSD: sysctl(%s...) failed: %s", "vm.swap_info", strerror(errno));
618             do_swap = 0;
619             error("DISABLED: disk.io");
620         } else {
621             for (i = 0; ; i++) {
622                 mib[mibsize] = i;
623                 size = sizeof(xsw);
624                 if (unlikely(sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1 )) {
625                     if (unlikely(errno != ENOENT)) {
626                         error("FREEBSD: sysctl(%s...) failed: %s", "vm.swap_info", strerror(errno));
627                         do_swap = 0;
628                         error("DISABLED: disk.io");
629                     } else {
630                         if (unlikely(size != sizeof(xsw))) {
631                             error("FREEBSD: sysctl(%s...) expected %lu, got %lu", "vm.swap_info", (unsigned long)sizeof(xsw), (unsigned long)size);
632                             do_swap = 0;
633                             error("DISABLED: disk.io");
634                         } else break;
635                     }
636                 }
637                 total_xsw.bytes_used += xsw.xsw_used * system_pagesize;
638                 total_xsw.bytes_total += xsw.xsw_nblks * system_pagesize;
639             }
640
641             if (likely(do_swap)) {
642                 st = rrdset_find("system.swap");
643                 if (unlikely(!st)) {
644                     st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
645                     st->isdetail = 1;
646
647                     rrddim_add(st, "free",    NULL, 1, 1048576, RRDDIM_ABSOLUTE);
648                     rrddim_add(st, "used",    NULL, 1, 1048576, RRDDIM_ABSOLUTE);
649                 }
650                 else rrdset_next(st);
651
652                 rrddim_set(st, "used", total_xsw.bytes_used);
653                 rrddim_set(st, "free", total_xsw.bytes_total - total_xsw.bytes_used);
654                 rrdset_done(st);
655             }
656         }
657     }
658
659     // --------------------------------------------------------------------
660
661     if (likely(do_ram)) {
662         if (unlikely(GETSYSCTL("vm.stats.vm.v_active_count",    vmmeter_data.v_active_count) ||
663                      GETSYSCTL("vm.stats.vm.v_inactive_count",  vmmeter_data.v_inactive_count) ||
664                      GETSYSCTL("vm.stats.vm.v_wire_count",      vmmeter_data.v_wire_count) ||
665                      GETSYSCTL("vm.stats.vm.v_cache_count",     vmmeter_data.v_cache_count) ||
666                      GETSYSCTL("vfs.bufspace",                  vfs_bufspace_count) ||
667                      GETSYSCTL("vm.stats.vm.v_free_count",      vmmeter_data.v_free_count))) {
668             do_swapio = 0;
669             error("DISABLED: system.swapio");
670         } else {
671             st = rrdset_find("system.ram");
672             if (unlikely(!st)) {
673                 st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
674
675                 rrddim_add(st, "active",    NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
676                 rrddim_add(st, "inactive",  NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
677                 rrddim_add(st, "wired",     NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
678                 rrddim_add(st, "cache",     NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
679                 rrddim_add(st, "buffers",   NULL, 1, 1024, RRDDIM_ABSOLUTE);
680                 rrddim_add(st, "free",      NULL, system_pagesize, 1024, RRDDIM_ABSOLUTE);
681             }
682             else rrdset_next(st);
683
684             rrddim_set(st, "active",    vmmeter_data.v_active_count);
685             rrddim_set(st, "inactive",  vmmeter_data.v_inactive_count);
686             rrddim_set(st, "wired",     vmmeter_data.v_wire_count);
687             rrddim_set(st, "cache",     vmmeter_data.v_cache_count);
688             rrddim_set(st, "buffers",   vfs_bufspace_count);
689             rrddim_set(st, "free",      vmmeter_data.v_free_count);
690             rrdset_done(st);
691         }
692     }
693
694     // --------------------------------------------------------------------
695
696     if (likely(do_swapio)) {
697         if (unlikely(GETSYSCTL("vm.stats.vm.v_swappgsin", vmmeter_data.v_swappgsin) || GETSYSCTL("vm.stats.vm.v_swappgsout", vmmeter_data.v_swappgsout))) {
698             do_swapio = 0;
699             error("DISABLED: system.swapio");
700         } else {
701             st = rrdset_find("system.swapio");
702             if (unlikely(!st)) {
703                 st = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
704
705                 rrddim_add(st, "in",  NULL, system_pagesize, 1024, RRDDIM_INCREMENTAL);
706                 rrddim_add(st, "out", NULL, -system_pagesize, 1024, RRDDIM_INCREMENTAL);
707             }
708             else rrdset_next(st);
709
710             rrddim_set(st, "in", vmmeter_data.v_swappgsin);
711             rrddim_set(st, "out", vmmeter_data.v_swappgsout);
712             rrdset_done(st);
713         }
714     }
715
716     // --------------------------------------------------------------------
717
718     if (likely(do_pgfaults)) {
719         if (unlikely(GETSYSCTL("vm.stats.vm.v_vm_faults",   vmmeter_data.v_vm_faults) ||
720                      GETSYSCTL("vm.stats.vm.v_io_faults",   vmmeter_data.v_io_faults) ||
721                      GETSYSCTL("vm.stats.vm.v_cow_faults",  vmmeter_data.v_cow_faults) ||
722                      GETSYSCTL("vm.stats.vm.v_cow_optim",   vmmeter_data.v_cow_optim) ||
723                      GETSYSCTL("vm.stats.vm.v_intrans",     vmmeter_data.v_intrans))) {
724             do_pgfaults = 0;
725             error("DISABLED: mem.pgfaults");
726         } else {
727             st = rrdset_find("mem.pgfaults");
728             if (unlikely(!st)) {
729                 st = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
730                 st->isdetail = 1;
731
732                 rrddim_add(st, "memory", NULL, 1, 1, RRDDIM_INCREMENTAL);
733                 rrddim_add(st, "io_requiring", NULL, 1, 1, RRDDIM_INCREMENTAL);
734                 rrddim_add(st, "cow", NULL, 1, 1, RRDDIM_INCREMENTAL);
735                 rrddim_add(st, "cow_optimized", NULL, 1, 1, RRDDIM_INCREMENTAL);
736                 rrddim_add(st, "in_transit", NULL, 1, 1, RRDDIM_INCREMENTAL);
737             }
738             else rrdset_next(st);
739
740             rrddim_set(st, "memory", vmmeter_data.v_vm_faults);
741             rrddim_set(st, "io_requiring", vmmeter_data.v_io_faults);
742             rrddim_set(st, "cow", vmmeter_data.v_cow_faults);
743             rrddim_set(st, "cow_optimized", vmmeter_data.v_cow_optim);
744             rrddim_set(st, "in_transit", vmmeter_data.v_intrans);
745             rrdset_done(st);
746         }
747     }
748
749     // --------------------------------------------------------------------
750
751     if (likely(do_ipc_semaphores)) {
752         if (unlikely(GETSYSCTL("kern.ipc.semmni", ipc_sem.semmni))) {
753             do_ipc_semaphores = 0;
754             error("DISABLED: system.ipc_semaphores");
755             error("DISABLED: system.ipc_semaphore_arrays");
756         } else {
757             ipc_sem_data = reallocz(ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni);
758             if (unlikely(getsysctl("kern.ipc.sema", ipc_sem_data, sizeof(struct semid_kernel) * ipc_sem.semmni))) {
759                 do_ipc_semaphores = 0;
760                 error("DISABLED: system.ipc_semaphores");
761                 error("DISABLED: system.ipc_semaphore_arrays");
762             } else {
763                 for (i = 0; i < ipc_sem.semmni; i++) {
764                     if (unlikely(ipc_sem_data[i].u.sem_perm.mode & SEM_ALLOC)) {
765                         ipc_sem.sets += 1;
766                         ipc_sem.semaphores += ipc_sem_data[i].u.sem_nsems;
767                     }
768                 }
769
770                 // --------------------------------------------------------------------
771
772                 st = rrdset_find("system.ipc_semaphores");
773                 if (unlikely(!st)) {
774                     st = rrdset_create("system", "ipc_semaphores", NULL, "ipc semaphores", NULL, "IPC Semaphores", "semaphores", 1000, rrd_update_every, RRDSET_TYPE_AREA);
775                     rrddim_add(st, "semaphores", NULL, 1, 1, RRDDIM_ABSOLUTE);
776                 }
777                 else rrdset_next(st);
778
779                 rrddim_set(st, "semaphores", ipc_sem.semaphores);
780                 rrdset_done(st);
781
782                 // --------------------------------------------------------------------
783
784                 st = rrdset_find("system.ipc_semaphore_arrays");
785                 if (unlikely(!st)) {
786                     st = rrdset_create("system", "ipc_semaphore_arrays", NULL, "ipc semaphores", NULL, "IPC Semaphore Arrays", "arrays", 1000, rrd_update_every, RRDSET_TYPE_AREA);
787                     rrddim_add(st, "arrays", NULL, 1, 1, RRDDIM_ABSOLUTE);
788                 }
789                 else rrdset_next(st);
790
791                 rrddim_set(st, "arrays", ipc_sem.sets);
792                 rrdset_done(st);
793             }
794         }
795     }
796
797     // --------------------------------------------------------------------
798
799     if (likely(do_ipc_shared_mem)) {
800         if (unlikely(GETSYSCTL("kern.ipc.shmmni", ipc_shm.shmmni))) {
801             do_ipc_shared_mem = 0;
802             error("DISABLED: system.ipc_shared_mem_segs");
803             error("DISABLED: system.ipc_shared_mem_size");
804         } else {
805             ipc_shm_data = reallocz(ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni);
806             if (unlikely(getsysctl("kern.ipc.shmsegs", ipc_shm_data, sizeof(struct shmid_kernel) * ipc_shm.shmmni))) {
807                 do_ipc_shared_mem = 0;
808                 error("DISABLED: system.ipc_shared_mem_segs");
809                 error("DISABLED: system.ipc_shared_mem_size");
810             } else {
811                 for (i = 0; i < ipc_shm.shmmni; i++) {
812                     if (unlikely(ipc_shm_data[i].u.shm_perm.mode & 0x0800)) {
813                         ipc_shm.segs += 1;
814                         ipc_shm.segsize += ipc_shm_data[i].u.shm_segsz;
815                     }
816                 }
817
818                 // --------------------------------------------------------------------
819
820                 st = rrdset_find("system.ipc_shared_mem_segs");
821                 if (unlikely(!st)) {
822                     st = rrdset_create("system", "ipc_shared_mem_segs", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments", "segments", 1000, rrd_update_every, RRDSET_TYPE_AREA);
823                     rrddim_add(st, "segments", NULL, 1, 1, RRDDIM_ABSOLUTE);
824                 }
825                 else rrdset_next(st);
826
827                 rrddim_set(st, "segments", ipc_shm.segs);
828                 rrdset_done(st);
829
830                 // --------------------------------------------------------------------
831
832                 st = rrdset_find("system.ipc_shared_mem_size");
833                 if (unlikely(!st)) {
834                     st = rrdset_create("system", "ipc_shared_mem_size", NULL, "ipc shared memory", NULL, "IPC Shared Memory Segments Size", "kilobytes", 1000, rrd_update_every, RRDSET_TYPE_AREA);
835                     rrddim_add(st, "allocated", NULL, 1, 1024, RRDDIM_ABSOLUTE);
836                 }
837                 else rrdset_next(st);
838
839                 rrddim_set(st, "allocated", ipc_shm.segsize);
840                 rrdset_done(st);
841             }
842         }
843     }
844
845     // --------------------------------------------------------------------
846
847     if (likely(do_ipc_msg_queues)) {
848         if (unlikely(GETSYSCTL("kern.ipc.msgmni", ipc_msq.msgmni))) {
849             do_ipc_msg_queues = 0;
850             error("DISABLED: system.ipc_msq_queues");
851             error("DISABLED: system.ipc_msq_messages");
852             error("DISABLED: system.ipc_msq_size");
853         } else {
854             ipc_msq_data = reallocz(ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni);
855             if (unlikely(getsysctl("kern.ipc.msqids", ipc_msq_data, sizeof(struct msqid_kernel) * ipc_msq.msgmni))) {
856                 do_ipc_msg_queues = 0;
857                 error("DISABLED: system.ipc_msq_queues");
858                 error("DISABLED: system.ipc_msq_messages");
859                 error("DISABLED: system.ipc_msq_size");
860             } else {
861                 for (i = 0; i < ipc_msq.msgmni; i++) {
862                     if (unlikely(ipc_msq_data[i].u.msg_qbytes != 0)) {
863                         ipc_msq.queues += 1;
864                         ipc_msq.messages += ipc_msq_data[i].u.msg_qnum;
865                         ipc_msq.usedsize += ipc_msq_data[i].u.msg_cbytes;
866                         ipc_msq.allocsize += ipc_msq_data[i].u.msg_qbytes;
867                     }
868                 }
869
870                 // --------------------------------------------------------------------
871
872                 st = rrdset_find("system.ipc_msq_queues");
873                 if (unlikely(!st)) {
874                     st = rrdset_create("system", "ipc_msq_queues", NULL, "ipc message queues", NULL, "Number of IPC Message Queues", "queues", 900, rrd_update_every, RRDSET_TYPE_AREA);
875                     rrddim_add(st, "queues", NULL, 1, 1, RRDDIM_ABSOLUTE);
876                 }
877                 else rrdset_next(st);
878
879                 rrddim_set(st, "queues", ipc_msq.queues);
880                 rrdset_done(st);
881
882                 // --------------------------------------------------------------------
883
884                 st = rrdset_find("system.ipc_msq_messages");
885                 if (unlikely(!st)) {
886                     st = rrdset_create("system", "ipc_msq_messages", NULL, "ipc message queues", NULL, "Number of Messages in IPC Message Queues", "messages", 1000, rrd_update_every, RRDSET_TYPE_AREA);
887                     rrddim_add(st, "messages", NULL, 1, 1, RRDDIM_ABSOLUTE);
888                 }
889                 else rrdset_next(st);
890
891                 rrddim_set(st, "messages", ipc_msq.messages);
892                 rrdset_done(st);
893
894                 // --------------------------------------------------------------------
895
896                 st = rrdset_find("system.ipc_msq_size");
897                 if (unlikely(!st)) {
898                     st = rrdset_create("system", "ipc_msq_size", NULL, "ipc message queues", NULL, "Size of IPC Message Queues", "bytes", 1100, rrd_update_every, RRDSET_TYPE_LINE);
899                     rrddim_add(st, "allocated", NULL, 1, 1, RRDDIM_ABSOLUTE);
900                     rrddim_add(st, "used", NULL, 1, 1, RRDDIM_ABSOLUTE);
901                 }
902                 else rrdset_next(st);
903
904                 rrddim_set(st, "allocated", ipc_msq.allocsize);
905                 rrddim_set(st, "used", ipc_msq.usedsize);
906                 rrdset_done(st);
907
908             }
909         }
910     }
911
912     return 0;
913 }